Skip to content

Commit

Permalink
rename metrics
Browse files Browse the repository at this point in the history
closes #4
  • Loading branch information
thraxil committed Sep 17, 2017
1 parent 3b78c34 commit d0b7625
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGES.org
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
- log failure to graphite when fetch fails (if ~FailureMetric~ is
defined for the endpoint)
- metric ignore list
- metric renaming
16 changes: 16 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ Timeout = 3000
URL = "http://localhost:14002/debug/vars"
Prefix = "apps.app2"
FailureMetric = "apps.app1.failure"

[[rename]]
From = "BuckHashSys"
To = "buck_hash_sys"

[[rename]]
From = "Sys"
To = "sys"

#+END_SRC

The top level fields that need to be set are:
Expand Down Expand Up @@ -86,6 +95,13 @@ Optionall, it also supports:
~CheckInterval~ and ~Timeout~ can also be individually overridden for
each endpoint as well.

Finally, you can tell it to rename metrics. Eg, expvar generally
outputs camelCase variables, but perhaps you have standardized on all
lower case and underscores for your graphite metrics. Just add as many
~[[[[rename]]]]~ blocks as you need (yeah, you have to explicitly do
each. If you want to add support for general renaming policies, I'd
take a patch). That can also be overridden on a per-endpoint basis.

** Additional Features

Samlare will respond to a ~SIGHUP~ signal by reloading its config
Expand Down
22 changes: 16 additions & 6 deletions endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type endpoint struct {
checkInterval int
timeout int
ignoreMetrics map[string]struct{}
renames map[string]string
graphiteServer Submitable
fetcher fetcher
logger log.Logger
Expand All @@ -35,7 +36,7 @@ type metric struct {
Value float64
}

func newEndpoint(c endpointconfig, interval int, timeout int, ignoreMetrics []string, g Submitable, fetcher fetcher, logger log.Logger) *endpoint {
func newEndpoint(c endpointconfig, interval int, timeout int, ignoreMetrics []string, renameMetrics []renameConfig, g Submitable, fetcher fetcher, logger log.Logger) *endpoint {
if c.CheckInterval != 0 {
interval = c.CheckInterval
}
Expand All @@ -49,6 +50,10 @@ func newEndpoint(c endpointconfig, interval int, timeout int, ignoreMetrics []st
for _, m := range ignoreMetrics {
ignoreMap[m] = struct{}{}
}
renameMap := make(map[string]string)
for _, r := range c.Renames {
renameMap[r.From] = r.To
}

return &endpoint{
url: c.URL,
Expand All @@ -57,6 +62,7 @@ func newEndpoint(c endpointconfig, interval int, timeout int, ignoreMetrics []st
failureMetric: c.FailureMetric,
timeout: timeout,
ignoreMetrics: ignoreMap,
renames: renameMap,
graphiteServer: g,
fetcher: fetcher,
logger: logger,
Expand Down Expand Up @@ -102,7 +108,7 @@ func (e *endpoint) Gather(ctx context.Context) []metric {
}
e.logger.Log("msg", "good fetch")

metrics = metricsFromMap(m, e.prefix, e.ignoreMetrics)
metrics = metricsFromMap(m, e.prefix, e.ignoreMetrics, e.renames)
// success
if e.failureMetric != "" {
s := metric{Name: e.failureMetric, Value: 0.0}
Expand All @@ -112,19 +118,23 @@ func (e *endpoint) Gather(ctx context.Context) []metric {
return metrics
}

func metricsFromMap(m map[string]interface{}, prefix string, ignore map[string]struct{}) []metric {
func metricsFromMap(m map[string]interface{}, prefix string, ignore map[string]struct{}, renames map[string]string) []metric {
var metrics []metric
for k, v := range m {
_, exists := ignore[k]
if exists {
_, ok := ignore[k]
if ok {
continue
}
t, ok := renames[k]
if ok {
k = t
}
key := fmt.Sprintf("%s.%s", prefix, k)
switch vv := v.(type) {
case float64:
metrics = append(metrics, metric{key, vv})
case map[string]interface{}:
nmetrics := metricsFromMap(vv, key, ignore)
nmetrics := metricsFromMap(vv, key, ignore, renames)
for _, met := range nmetrics {
metrics = append(metrics, met)
}
Expand Down
41 changes: 30 additions & 11 deletions endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,38 @@ func Test_metricsFromMap(t *testing.T) {
var f interface{}
json.Unmarshal([]byte(data), &f)
ignore := make(map[string]struct{})
metrics := metricsFromMap(f.(map[string]interface{}), "", ignore)
renames := make(map[string]string)
metrics := metricsFromMap(f.(map[string]interface{}), "", ignore, renames)
if len(metrics) != 2 {
t.Error("wrong number of metrics found")
}

data = `{"foo": 10, "bar": 5, "baz": {"blah": 3}}`
json.Unmarshal([]byte(data), &f)
metrics = metricsFromMap(f.(map[string]interface{}), "", ignore)
metrics = metricsFromMap(f.(map[string]interface{}), "", ignore, renames)
if len(metrics) != 3 {
t.Error("wrong number of metrics found")
}

}

func Test_metricsFromMapRename(t *testing.T) {
data := `{"foo": 10, "bar": 5}`
var f interface{}
json.Unmarshal([]byte(data), &f)
ignore := make(map[string]struct{})
renames := make(map[string]string)
renames["foo"] = "FOO"
renames["bar"] = "BAR"
metrics := metricsFromMap(f.(map[string]interface{}), "", ignore, renames)
if len(metrics) != 2 {
t.Error("wrong number of metrics found")
}
if metrics[0].Name != ".FOO" || metrics[1].Name != ".BAR" {
t.Error("didn't rename metric correctly", metrics[0].Name, metrics[1].Name)
}
}

func dummyLogger() log.Logger {
devNull, _ := os.Open("/dev/null")
w := log.NewSyncWriter(devNull)
Expand All @@ -47,7 +65,7 @@ func Test_newEndpoint(t *testing.T) {
Timeout: 60,
}
g := newGraphiteServer("1.2.3.4", 2003)
e := newEndpoint(c, 60, 60, []string{}, g, httpFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, httpFetcher{}, dummyLogger())
if e.url != c.URL {
t.Error("lost the URL")
}
Expand All @@ -67,7 +85,7 @@ func Test_Submit(t *testing.T) {
Timeout: 60,
}
var g dummyGraphite
e := newEndpoint(c, 60, 60, []string{}, g, httpFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, httpFetcher{}, dummyLogger())
var m []metric
err := e.Submit(m)
if err != nil {
Expand Down Expand Up @@ -117,7 +135,7 @@ func Test_Fetch(t *testing.T) {
Timeout: 60,
}
g := newGraphiteServer("1.2.3.4", 2003)
e := newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx := context.TODO()

Expand All @@ -126,7 +144,8 @@ func Test_Fetch(t *testing.T) {
t.Error("dummy fetcher shouldn't fail")
}
ignore := make(map[string]struct{})
metrics := metricsFromMap(d, "", ignore)
renames := make(map[string]string)
metrics := metricsFromMap(d, "", ignore, renames)
if len(metrics) != 2 {
t.Error("wrong number of metrics found")
}
Expand All @@ -141,7 +160,7 @@ func Test_Gather(t *testing.T) {
Timeout: 60,
}
g := newGraphiteServer("1.2.3.4", 2003)
e := newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx := context.TODO()
metrics := e.Gather(ctx)
Expand All @@ -161,7 +180,7 @@ func Test_GatherIgnoreMetric(t *testing.T) {
IgnoreMetrics: []string{"foo"},
}
g := newGraphiteServer("1.2.3.4", 2003)
e := newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx := context.TODO()
metrics := e.Gather(ctx)
Expand All @@ -181,7 +200,7 @@ func Test_GatherWithFailureMetric(t *testing.T) {
FailureMetric: "failure",
}
g := newGraphiteServer("1.2.3.4", 2003)
e := newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx := context.TODO()
metrics := e.Gather(ctx)
Expand All @@ -200,7 +219,7 @@ func Test_Run(t *testing.T) {
Timeout: 60,
}
var g dummyGraphite
e := newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e := newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx, cancel := context.WithCancel(context.TODO())

Expand All @@ -216,7 +235,7 @@ func Test_Run(t *testing.T) {
Timeout: 60,
}

e = newEndpoint(c, 60, 60, []string{}, g, dummyFetcher{}, dummyLogger())
e = newEndpoint(c, 60, 60, []string{}, []renameConfig{}, g, dummyFetcher{}, dummyLogger())

ctx, cancel = context.WithCancel(context.TODO())

Expand Down
9 changes: 8 additions & 1 deletion samlare.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import (
"github.com/go-kit/kit/log"
)

type renameConfig struct {
From string
To string
}

type endpointconfig struct {
URL string
Prefix string
CheckInterval int
Timeout int
FailureMetric string
IgnoreMetrics []string
Renames []renameConfig
}

type config struct {
Expand All @@ -26,6 +32,7 @@ type config struct {
CheckInterval int
Timeout int
IgnoreMetrics []string
Renames []renameConfig

Endpoints map[string]endpointconfig
}
Expand Down Expand Up @@ -90,7 +97,7 @@ func startEndpoints(conf *config, logger log.Logger) context.CancelFunc {

for k, endpoint := range conf.Endpoints {
elogger := log.With(logger, "endpoint", k)
e := newEndpoint(endpoint, conf.CheckInterval, conf.Timeout, conf.IgnoreMetrics, g, httpFetcher{}, elogger)
e := newEndpoint(endpoint, conf.CheckInterval, conf.Timeout, conf.IgnoreMetrics, conf.Renames, g, httpFetcher{}, elogger)
go e.Run(ctx)
}

Expand Down
9 changes: 9 additions & 0 deletions sample-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ CheckInterval = 60000
Timeout = 3000
IgnoreMetrics = ["cmdline", "foo"]

[[rename]]
From = "Sys"
To = "sys"

[[rename]]
From = "BuckHashSys"
To = "buck_hash_sys"


[endpoints]

[endpoints.app1]
Expand Down

0 comments on commit d0b7625

Please sign in to comment.