Skip to content

Commit

Permalink
support for excluding histograms based on regex, unit conversion and …
Browse files Browse the repository at this point in the history
…upper range limits
  • Loading branch information
sravotto committed Oct 28, 2021
1 parent 9989564 commit 8880be1
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 41 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ $ metrics-exporter -config config.yaml
```
The configuration, specified in yaml format specifies the cockroach db URL the proxy connects to and the port the proxy it listens to.
The log-10 linear format precision is configurable, specifing the lower range (in nanoseconds) and the number of linear bins for each logarithmic bin.
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.
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.

```text
Expand All @@ -18,6 +19,9 @@ port: 8888
bucket:
startns: 100000
bins: 10
endns: 100000000000
unit: millseconds
exclude: (.*internal)
tls:
ca: ./certs/ca.crt
privatekey: ./certs/client.root.key
Expand Down
25 changes: 24 additions & 1 deletion internal/lib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"errors"
"io/ioutil"
"log"
"math"
"net/url"
"os"

Expand Down Expand Up @@ -50,11 +51,18 @@ Log10 Bucket Configuration
* Bins: the number of linear buckets for each log10 bucket
* Startns: The lower range in nanoseconds.
* Endns: Optional upper range
* Exclude: Regex of histogram names to exclude
* Unit: Time unit to use for the log10 buckets
*
*/
type BucketConfig struct {
Bins int
Startns int
// optional
Endns int
Exclude string
Unit string
}

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

func (b *BucketConfig) UnitDiv() float64 {
var div float64 = 1
if b.Unit != "" {
switch b.Unit {
case "seconds":
div = math.Pow10(9)
case "milliseconds":
div = math.Pow10(6)
case "microseconds":
div = math.Pow10(3)
}
}
return div
}

/*
TlsConfig Configuration
* Ca: CA certificate file location
Expand Down
49 changes: 21 additions & 28 deletions internal/lib/histograms.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,26 @@
package lib

import (
"bytes"
"fmt"
"log"
"math"
"os"

dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"google.golang.org/protobuf/proto"
)

type log10Bucket struct {
BinNums int
Curr float64
Max float64
UnitDiv float64
}

func createLog10Bucket(start float64, max float64, bins int) *log10Bucket {
func createLog10Bucket(start float64, max float64, bins int, div float64) *log10Bucket {
return &log10Bucket{
Curr: start,
Max: max,
BinNums: bins}
BinNums: bins,
UnitDiv: div,
}
}

// Computes the next bin
Expand Down Expand Up @@ -60,8 +58,16 @@ func (currLog10Bucket *log10Bucket) addLog10Buckets(
count := currHdrBucket.GetCumulativeCount()
// last bucket has le = +Inf.
if le == math.Inf(1) {
for currLog10Bucket.binUpperBound() < currLog10Bucket.Max {
bucket := &dto.Bucket{
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
CumulativeCount: proto.Uint64(count),
}
currLog10Bucket.nextBin()
newBuckets = append(newBuckets, bucket)
}
return append(newBuckets, &dto.Bucket{
UpperBound: proto.Float64(currLog10Bucket.binUpperBound()),
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
CumulativeCount: proto.Uint64(count)})

}
Expand All @@ -84,9 +90,11 @@ func (currLog10Bucket *log10Bucket) addLog10Buckets(
adj := math.Floor(float64(count-pcount) * (le - currLog10Bucket.binUpperBound()) / (le - ple))
res := count - uint64(adj)
bucket := &dto.Bucket{
UpperBound: proto.Float64(currLog10Bucket.binUpperBound()),
UpperBound: proto.Float64(currLog10Bucket.binUpperBound() / currLog10Bucket.UnitDiv),
CumulativeCount: proto.Uint64(res),
}
//fmt.Printf("%+v", currLog10Bucket)
//fmt.Printf("%+v", bucket)
currLog10Bucket.nextBin()
newBuckets = append(newBuckets, bucket)
}
Expand All @@ -102,10 +110,13 @@ func TranslateHistogram(config *BucketConfig, mf *dto.MetricFamily) {
max := 0.0
if len(m.Histogram.Bucket) >= 2 {
max = m.Histogram.Bucket[len(m.Histogram.Bucket)-2].GetUpperBound()
if config.Endns > 0 {
max = float64(config.Endns)
}
requiredBuckets = requiredBuckets + int(math.Ceil(math.Log10(float64(max))))*bins
}
newBuckets := make([]*dto.Bucket, 0, requiredBuckets)
currLog10Bucket := createLog10Bucket(float64(config.Startns), max, bins)
currLog10Bucket := createLog10Bucket(float64(config.Startns), max, bins, config.UnitDiv())

for _, curr := range m.GetHistogram().GetBucket() {
newBuckets = currLog10Bucket.addLog10Buckets(curr, prev, newBuckets)
Expand All @@ -114,21 +125,3 @@ func TranslateHistogram(config *BucketConfig, mf *dto.MetricFamily) {
m.Histogram.Bucket = newBuckets
}
}

func TranslateFromFile(config *BucketConfig, filename string) {
log.Println("Reading from file :" + filename)
var parser expfmt.TextParser
var r, err = os.Open(filename)
if err != nil {
panic("File not found")
}
metricFamilies, _ := parser.TextToMetricFamilies(r)
for _, mf := range metricFamilies {
if mf.GetType() == dto.MetricType_HISTOGRAM {
TranslateHistogram(config, mf)
}
var buf bytes.Buffer
expfmt.MetricFamilyToText(&buf, mf)
fmt.Println(buf.String())
}
}
6 changes: 3 additions & 3 deletions internal/lib/histograms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestHistogramConversion(t *testing.T) {
var buf bytes.Buffer
expfmt.MetricFamilyToText(&buf, mf)

assert.Equal(buf.String(), output)
assert.Equal(output, buf.String())
}
}

Expand All @@ -58,7 +58,7 @@ func TestMultiStoreConversion(t *testing.T) {
TranslateHistogram(config, mf)
var buf bytes.Buffer
expfmt.MetricFamilyToText(&buf, mf)
assert.Equal(buf.String(), multistoreout)
assert.Equal(multistoreout, buf.String())
}

}
Expand All @@ -75,7 +75,7 @@ func TestIdentityConversion(t *testing.T) {
TranslateHistogram(config, mf)
var buf bytes.Buffer
expfmt.MetricFamilyToText(&buf, mf)
assert.Equal(buf.String(), output)
assert.Equal(output, buf.String())
}

}
2 changes: 1 addition & 1 deletion internal/lib/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (r *MetricsReader) ReadMetrics(ctx context.Context) (map[string]*dto.Metric
return nil, err
}
defer data.Body.Close()
if data.StatusCode != 200 {
if data.StatusCode != http.StatusOK {
return nil, errors.New(data.Status)
}
var parser expfmt.TextParser
Expand Down
25 changes: 20 additions & 5 deletions internal/lib/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,48 @@ package lib

import (
"context"
"net/http"
"io"
"regexp"

dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)

// A MetricsWriter write metrics, after transforming them based on the configuration supplied.
type MetricsWriter struct {
Config *Config
Config *Config
Exclude *regexp.Regexp
}

// Create a MetricsWriter
func CreateMetricsWriter(config *Config) *MetricsWriter {

var exp *regexp.Regexp
if config.Bucket.Exclude != "" {

exp = regexp.MustCompile(config.Bucket.Exclude)
}

return &MetricsWriter{
Config: config,
Config: config,
Exclude: exp,
}
}

// Write the metrics, converting HDR Histogram into Log10 linear histograms.
func (w *MetricsWriter) WriteMetrics(
ctx context.Context,
metricFamilies map[string]*dto.MetricFamily,
h http.ResponseWriter) {
out io.Writer) {
for _, mf := range metricFamilies {
if mf.GetType() == dto.MetricType_HISTOGRAM {
//log.Println("processing " + mf.GetName())
if w.Exclude != nil && w.Exclude.MatchString(mf.GetName()) {
// log.Println("Skipping " + mf.GetName())
continue
}
TranslateHistogram(&w.Config.Bucket, mf)
}
expfmt.MetricFamilyToText(h, mf)
expfmt.MetricFamilyToText(out, mf)
}
}
23 changes: 20 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import (
"fmt"
"log"
"net/http"
"os"
"runtime"
"runtime/debug"

"internal/lib"

"github.com/NYTimes/gziphandler"
"github.com/prometheus/common/expfmt"
)

var (
Expand All @@ -44,6 +46,18 @@ func printVersionInfo(buildVersion string) {
}
}

func translateFromFile(ctx context.Context, config *lib.BucketConfig, filename string, writer *lib.MetricsWriter) {
log.Println("Reading from file :" + filename)
var parser expfmt.TextParser
var r, err = os.Open(filename)
if err != nil {
panic("File not found")
}
metricFamilies, _ := parser.TextToMetricFamilies(r)
writer.WriteMetrics(ctx, metricFamilies, os.Stdout)

}

func main() {
configLocation := flag.String("config", "", "YAML configuration")
printVersion := flag.Bool("version", false, "print version and exit")
Expand Down Expand Up @@ -72,14 +86,17 @@ func main() {
}
}

writer := lib.CreateMetricsWriter(config)
ctx := context.Background()

if *localFile != "" {
lib.TranslateFromFile(&config.Bucket, *localFile)
log.Printf("Reading with:\n%+v\n\n", config)
translateFromFile(ctx, &config.Bucket, *localFile, writer)
return
}

reader := lib.CreateMetricsReader(config, transport)
writer := lib.CreateMetricsWriter(config)
ctx := context.Background()

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

Expand Down

0 comments on commit 8880be1

Please sign in to comment.