Skip to content

Commit 8880be1

Browse files
committed
support for excluding histograms based on regex, unit conversion and upper range limits
1 parent 9989564 commit 8880be1

File tree

7 files changed

+93
-41
lines changed

7 files changed

+93
-41
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ $ metrics-exporter -config config.yaml
1010
```
1111
The configuration, specified in yaml format specifies the cockroach db URL the proxy connects to and the port the proxy it listens to.
1212
The log-10 linear format precision is configurable, specifing the lower range (in nanoseconds) and the number of linear bins for each logarithmic bin.
13+
Optionally, the user can specify the upper range (in nanoseconds), the unit (seconds,milliseconds,microseconds) to convert the bucket ranges, and a regex expression to exclude matching histograms.
1314
The tls section allows the user to specify CA, cert and private key to connect to the backend. The same configuration is used to configure the HTTPS endpoint that the proxy listen to.
1415

1516
```text
@@ -18,6 +19,9 @@ port: 8888
1819
bucket:
1920
startns: 100000
2021
bins: 10
22+
endns: 100000000000
23+
unit: millseconds
24+
exclude: (.*internal)
2125
tls:
2226
ca: ./certs/ca.crt
2327
privatekey: ./certs/client.root.key

internal/lib/config.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"errors"
1414
"io/ioutil"
1515
"log"
16+
"math"
1617
"net/url"
1718
"os"
1819

@@ -50,11 +51,18 @@ Log10 Bucket Configuration
5051
5152
* Bins: the number of linear buckets for each log10 bucket
5253
* Startns: The lower range in nanoseconds.
53-
54+
* Endns: Optional upper range
55+
* Exclude: Regex of histogram names to exclude
56+
* Unit: Time unit to use for the log10 buckets
57+
*
5458
*/
5559
type BucketConfig struct {
5660
Bins int
5761
Startns int
62+
// optional
63+
Endns int
64+
Exclude string
65+
Unit string
5866
}
5967

6068
func (b *BucketConfig) checkConfig() error {
@@ -64,6 +72,21 @@ func (b *BucketConfig) checkConfig() error {
6472
return errors.New("Invalid Bucket Configuration")
6573
}
6674

75+
func (b *BucketConfig) UnitDiv() float64 {
76+
var div float64 = 1
77+
if b.Unit != "" {
78+
switch b.Unit {
79+
case "seconds":
80+
div = math.Pow10(9)
81+
case "milliseconds":
82+
div = math.Pow10(6)
83+
case "microseconds":
84+
div = math.Pow10(3)
85+
}
86+
}
87+
return div
88+
}
89+
6790
/*
6891
TlsConfig Configuration
6992
* Ca: CA certificate file location

internal/lib/histograms.go

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,26 @@
77
package lib
88

99
import (
10-
"bytes"
11-
"fmt"
12-
"log"
1310
"math"
14-
"os"
1511

1612
dto "github.com/prometheus/client_model/go"
17-
"github.com/prometheus/common/expfmt"
1813
"google.golang.org/protobuf/proto"
1914
)
2015

2116
type log10Bucket struct {
2217
BinNums int
2318
Curr float64
2419
Max float64
20+
UnitDiv float64
2521
}
2622

27-
func createLog10Bucket(start float64, max float64, bins int) *log10Bucket {
23+
func createLog10Bucket(start float64, max float64, bins int, div float64) *log10Bucket {
2824
return &log10Bucket{
2925
Curr: start,
3026
Max: max,
31-
BinNums: bins}
27+
BinNums: bins,
28+
UnitDiv: div,
29+
}
3230
}
3331

3432
// Computes the next bin
@@ -60,8 +58,16 @@ func (currLog10Bucket *log10Bucket) addLog10Buckets(
6058
count := currHdrBucket.GetCumulativeCount()
6159
// last bucket has le = +Inf.
6260
if le == math.Inf(1) {
61+
for currLog10Bucket.binUpperBound() < currLog10Bucket.Max {
62+
bucket := &dto.Bucket{
63+
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
64+
CumulativeCount: proto.Uint64(count),
65+
}
66+
currLog10Bucket.nextBin()
67+
newBuckets = append(newBuckets, bucket)
68+
}
6369
return append(newBuckets, &dto.Bucket{
64-
UpperBound: proto.Float64(currLog10Bucket.binUpperBound()),
70+
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
6571
CumulativeCount: proto.Uint64(count)})
6672

6773
}
@@ -84,9 +90,11 @@ func (currLog10Bucket *log10Bucket) addLog10Buckets(
8490
adj := math.Floor(float64(count-pcount) * (le - currLog10Bucket.binUpperBound()) / (le - ple))
8591
res := count - uint64(adj)
8692
bucket := &dto.Bucket{
87-
UpperBound: proto.Float64(currLog10Bucket.binUpperBound()),
93+
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
8894
CumulativeCount: proto.Uint64(res),
8995
}
96+
//fmt.Printf("%+v", currLog10Bucket)
97+
//fmt.Printf("%+v", bucket)
9098
currLog10Bucket.nextBin()
9199
newBuckets = append(newBuckets, bucket)
92100
}
@@ -102,10 +110,13 @@ func TranslateHistogram(config *BucketConfig, mf *dto.MetricFamily) {
102110
max := 0.0
103111
if len(m.Histogram.Bucket) >= 2 {
104112
max = m.Histogram.Bucket[len(m.Histogram.Bucket)-2].GetUpperBound()
113+
if config.Endns > 0 {
114+
max = float64(config.Endns)
115+
}
105116
requiredBuckets = requiredBuckets + int(math.Ceil(math.Log10(float64(max))))*bins
106117
}
107118
newBuckets := make([]*dto.Bucket, 0, requiredBuckets)
108-
currLog10Bucket := createLog10Bucket(float64(config.Startns), max, bins)
119+
currLog10Bucket := createLog10Bucket(float64(config.Startns), max, bins, config.UnitDiv())
109120

110121
for _, curr := range m.GetHistogram().GetBucket() {
111122
newBuckets = currLog10Bucket.addLog10Buckets(curr, prev, newBuckets)
@@ -114,21 +125,3 @@ func TranslateHistogram(config *BucketConfig, mf *dto.MetricFamily) {
114125
m.Histogram.Bucket = newBuckets
115126
}
116127
}
117-
118-
func TranslateFromFile(config *BucketConfig, filename string) {
119-
log.Println("Reading from file :" + filename)
120-
var parser expfmt.TextParser
121-
var r, err = os.Open(filename)
122-
if err != nil {
123-
panic("File not found")
124-
}
125-
metricFamilies, _ := parser.TextToMetricFamilies(r)
126-
for _, mf := range metricFamilies {
127-
if mf.GetType() == dto.MetricType_HISTOGRAM {
128-
TranslateHistogram(config, mf)
129-
}
130-
var buf bytes.Buffer
131-
expfmt.MetricFamilyToText(&buf, mf)
132-
fmt.Println(buf.String())
133-
}
134-
}

internal/lib/histograms_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestHistogramConversion(t *testing.T) {
4242
var buf bytes.Buffer
4343
expfmt.MetricFamilyToText(&buf, mf)
4444

45-
assert.Equal(buf.String(), output)
45+
assert.Equal(output, buf.String())
4646
}
4747
}
4848

@@ -58,7 +58,7 @@ func TestMultiStoreConversion(t *testing.T) {
5858
TranslateHistogram(config, mf)
5959
var buf bytes.Buffer
6060
expfmt.MetricFamilyToText(&buf, mf)
61-
assert.Equal(buf.String(), multistoreout)
61+
assert.Equal(multistoreout, buf.String())
6262
}
6363

6464
}
@@ -75,7 +75,7 @@ func TestIdentityConversion(t *testing.T) {
7575
TranslateHistogram(config, mf)
7676
var buf bytes.Buffer
7777
expfmt.MetricFamilyToText(&buf, mf)
78-
assert.Equal(buf.String(), output)
78+
assert.Equal(output, buf.String())
7979
}
8080

8181
}

internal/lib/reader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (r *MetricsReader) ReadMetrics(ctx context.Context) (map[string]*dto.Metric
4949
return nil, err
5050
}
5151
defer data.Body.Close()
52-
if data.StatusCode != 200 {
52+
if data.StatusCode != http.StatusOK {
5353
return nil, errors.New(data.Status)
5454
}
5555
var parser expfmt.TextParser

internal/lib/writer.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,48 @@ package lib
88

99
import (
1010
"context"
11-
"net/http"
11+
"io"
12+
"regexp"
1213

1314
dto "github.com/prometheus/client_model/go"
1415
"github.com/prometheus/common/expfmt"
1516
)
1617

1718
// A MetricsWriter write metrics, after transforming them based on the configuration supplied.
1819
type MetricsWriter struct {
19-
Config *Config
20+
Config *Config
21+
Exclude *regexp.Regexp
2022
}
2123

2224
// Create a MetricsWriter
2325
func CreateMetricsWriter(config *Config) *MetricsWriter {
26+
27+
var exp *regexp.Regexp
28+
if config.Bucket.Exclude != "" {
29+
30+
exp = regexp.MustCompile(config.Bucket.Exclude)
31+
}
32+
2433
return &MetricsWriter{
25-
Config: config,
34+
Config: config,
35+
Exclude: exp,
2636
}
2737
}
2838

2939
// Write the metrics, converting HDR Histogram into Log10 linear histograms.
3040
func (w *MetricsWriter) WriteMetrics(
3141
ctx context.Context,
3242
metricFamilies map[string]*dto.MetricFamily,
33-
h http.ResponseWriter) {
43+
out io.Writer) {
3444
for _, mf := range metricFamilies {
3545
if mf.GetType() == dto.MetricType_HISTOGRAM {
46+
//log.Println("processing " + mf.GetName())
47+
if w.Exclude != nil && w.Exclude.MatchString(mf.GetName()) {
48+
// log.Println("Skipping " + mf.GetName())
49+
continue
50+
}
3651
TranslateHistogram(&w.Config.Bucket, mf)
3752
}
38-
expfmt.MetricFamilyToText(h, mf)
53+
expfmt.MetricFamilyToText(out, mf)
3954
}
4055
}

main.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import (
1616
"fmt"
1717
"log"
1818
"net/http"
19+
"os"
1920
"runtime"
2021
"runtime/debug"
2122

2223
"internal/lib"
2324

2425
"github.com/NYTimes/gziphandler"
26+
"github.com/prometheus/common/expfmt"
2527
)
2628

2729
var (
@@ -44,6 +46,18 @@ func printVersionInfo(buildVersion string) {
4446
}
4547
}
4648

49+
func translateFromFile(ctx context.Context, config *lib.BucketConfig, filename string, writer *lib.MetricsWriter) {
50+
log.Println("Reading from file :" + filename)
51+
var parser expfmt.TextParser
52+
var r, err = os.Open(filename)
53+
if err != nil {
54+
panic("File not found")
55+
}
56+
metricFamilies, _ := parser.TextToMetricFamilies(r)
57+
writer.WriteMetrics(ctx, metricFamilies, os.Stdout)
58+
59+
}
60+
4761
func main() {
4862
configLocation := flag.String("config", "", "YAML configuration")
4963
printVersion := flag.Bool("version", false, "print version and exit")
@@ -72,14 +86,17 @@ func main() {
7286
}
7387
}
7488

89+
writer := lib.CreateMetricsWriter(config)
90+
ctx := context.Background()
91+
7592
if *localFile != "" {
76-
lib.TranslateFromFile(&config.Bucket, *localFile)
93+
log.Printf("Reading with:\n%+v\n\n", config)
94+
translateFromFile(ctx, &config.Bucket, *localFile, writer)
7795
return
7896
}
7997

8098
reader := lib.CreateMetricsReader(config, transport)
81-
writer := lib.CreateMetricsWriter(config)
82-
ctx := context.Background()
99+
83100
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84101
defer r.Body.Close()
85102

0 commit comments

Comments
 (0)