2020-01-10 15:53:14 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Note to readers: All of the tag iteration and field searching code does not bother to
|
|
|
|
// handle escaped spaces or commas because all of the keys we intend to migrate do not
|
|
|
|
// contain them. In particular, we have no package import paths with spaces or commas and
|
|
|
|
// it is impossible to have a function name with a space or comma in it. Additionally, all
|
|
|
|
// of the monkit/v3 field names do not have spaces or commas. This could become invalid if
|
|
|
|
// someone decides to break it, but this code is also temporary.
|
|
|
|
|
|
|
|
// MetricDowngrade downgrades known v3 metrics into v2 versions for backwards compat.
|
|
|
|
type MetricDowngrade struct {
|
|
|
|
dest MetricDest
|
|
|
|
}
|
|
|
|
|
2020-02-18 23:31:25 +00:00
|
|
|
// knownMetrics is decoded in main.go using a provided toml configuration file.
|
|
|
|
var knownMetrics map[string]string
|
|
|
|
|
2020-01-10 15:53:14 +00:00
|
|
|
// NewMetricDowngrade constructs a MetricDowngrade that passes known v3 metrics as
|
|
|
|
// v2 metrics to the provided dest.
|
|
|
|
func NewMetricDowngrade(dest MetricDest) *MetricDowngrade {
|
|
|
|
return &MetricDowngrade{
|
|
|
|
dest: dest,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Metric implements MetricDest
|
|
|
|
func (k *MetricDowngrade) Metric(application, instance string, key []byte, val float64, ts time.Time) error {
|
|
|
|
comma := bytes.IndexByte(key, ',')
|
|
|
|
if comma < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if string(key[:comma]) == "function_times" {
|
|
|
|
return k.handleFunctionTimes(application, instance, key[comma+1:], val, ts)
|
|
|
|
}
|
|
|
|
if string(key[:comma]) == "function" {
|
|
|
|
return k.handleFunction(application, instance, key[comma+1:], val, ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
v2key, ok := knownMetrics[string(key[:comma])]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
space := bytes.LastIndexByte(key, ' ')
|
|
|
|
if space < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make([]byte, 0, len(v2key)+1+len(key)-space)
|
|
|
|
out = append(out, v2key...)
|
|
|
|
out = append(out, '.')
|
|
|
|
out = append(out, key[space+1:]...)
|
|
|
|
|
|
|
|
return k.dest.Metric(application, instance, out, val, ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *MetricDowngrade) handleFunctionTimes(application, instance string, key []byte, val float64, ts time.Time) error {
|
|
|
|
var name, kind, scope string
|
|
|
|
iterateTags(key, func(tag []byte) {
|
|
|
|
if len(tag) < 6 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case string(tag[:5]) == "name=":
|
|
|
|
name = string(tag[5:])
|
|
|
|
case string(tag[:5]) == "kind=":
|
|
|
|
kind = string(tag[5:])
|
|
|
|
case string(tag[:6]) == "scope=":
|
|
|
|
scope = string(tag[6:])
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if name == "" || kind == "" || scope == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
space := bytes.LastIndexByte(key, ' ')
|
|
|
|
if space < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make([]byte, 0, len(scope)+1+len(name)+1+len(kind)+7+(len(key)-space))
|
|
|
|
out = append(out, scope...)
|
|
|
|
out = append(out, '.')
|
|
|
|
out = append(out, name...)
|
|
|
|
out = append(out, '.')
|
|
|
|
out = append(out, kind...)
|
|
|
|
out = append(out, "_times_"...)
|
|
|
|
out = append(out, key[space+1:]...)
|
|
|
|
|
|
|
|
return k.dest.Metric(application, instance, out, val, ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *MetricDowngrade) handleFunction(application, instance string, key []byte, val float64, ts time.Time) error {
|
|
|
|
var name, scope string
|
|
|
|
iterateTags(key, func(tag []byte) {
|
|
|
|
if len(tag) < 6 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case string(tag[:5]) == "name=":
|
|
|
|
name = string(tag[5:])
|
|
|
|
case string(tag[:6]) == "scope=":
|
|
|
|
scope = string(tag[6:])
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if name == "" || scope == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
space := bytes.LastIndexByte(key, ' ')
|
|
|
|
if space < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make([]byte, 0, len(scope)+1+len(name)+1+(len(key)-space))
|
|
|
|
out = append(out, scope...)
|
|
|
|
out = append(out, '.')
|
|
|
|
out = append(out, name...)
|
|
|
|
out = append(out, '.')
|
|
|
|
out = append(out, key[space+1:]...)
|
|
|
|
|
|
|
|
return k.dest.Metric(application, instance, out, val, ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func iterateTags(key []byte, cb func([]byte)) {
|
|
|
|
for len(key) > 0 {
|
|
|
|
comma := bytes.IndexByte(key, ',')
|
|
|
|
if comma == -1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
cb(key[:comma])
|
|
|
|
key = key[comma+1:]
|
|
|
|
}
|
|
|
|
space := bytes.IndexByte(key, ' ')
|
|
|
|
if space >= 0 {
|
|
|
|
cb(key[:space])
|
|
|
|
}
|
|
|
|
}
|