Commit e0c60488 by Torkel Ödegaard

feat(instrumentation): added gauge and http endpoint

parent 1a05ae2e
...@@ -235,7 +235,7 @@ func Register(r *macaron.Macaron) { ...@@ -235,7 +235,7 @@ func Register(r *macaron.Macaron) {
r.Get("/search/", Search) r.Get("/search/", Search)
// metrics // metrics
r.Get("/metrics/test", GetTestMetrics) r.Get("/metrics/test", wrap(GetTestMetrics))
// metrics // metrics
r.Get("/metrics", wrap(GetInternalMetrics)) r.Get("/metrics", wrap(GetInternalMetrics))
......
...@@ -30,7 +30,7 @@ func InitAppPluginRoutes(r *macaron.Macaron) { ...@@ -30,7 +30,7 @@ func InitAppPluginRoutes(r *macaron.Macaron) {
} }
handlers = append(handlers, AppPluginRoute(route, plugin.Id)) handlers = append(handlers, AppPluginRoute(route, plugin.Id))
r.Route(url, route.Method, handlers...) r.Route(url, route.Method, handlers...)
log.Info("Plugins: Adding proxy route %s", url) log.Debug("Plugins: Adding proxy route %s", url)
} }
} }
} }
......
...@@ -15,8 +15,8 @@ var ( ...@@ -15,8 +15,8 @@ var (
NotFound = func() Response { NotFound = func() Response {
return ApiError(404, "Not found", nil) return ApiError(404, "Not found", nil)
} }
ServerError = func() Response { ServerError = func(err error) Response {
return ApiError(500, "Server error", nil) return ApiError(500, "Server error", err)
} }
) )
...@@ -38,7 +38,7 @@ func wrap(action interface{}) macaron.Handler { ...@@ -38,7 +38,7 @@ func wrap(action interface{}) macaron.Handler {
if err == nil && val != nil && len(val) > 0 { if err == nil && val != nil && len(val) > 0 {
res = val[0].Interface().(Response) res = val[0].Interface().(Response)
} else { } else {
res = ServerError() res = ServerError(err)
} }
res.WriteTo(c.Resp) res.WriteTo(c.Resp)
......
package api package api
import ( import (
"encoding/json"
"math/rand" "math/rand"
"net/http"
"strconv" "strconv"
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
...@@ -37,7 +39,7 @@ func GetTestMetrics(c *middleware.Context) { ...@@ -37,7 +39,7 @@ func GetTestMetrics(c *middleware.Context) {
c.JSON(200, &result) c.JSON(200, &result)
} }
func GetInternalMetrics(c middleware.Context) Response { func GetInternalMetrics(c *middleware.Context) Response {
snapshots := metrics.MetricStats.GetSnapshots() snapshots := metrics.MetricStats.GetSnapshots()
resp := make(map[string]interface{}) resp := make(map[string]interface{})
...@@ -66,5 +68,17 @@ func GetInternalMetrics(c middleware.Context) Response { ...@@ -66,5 +68,17 @@ func GetInternalMetrics(c middleware.Context) Response {
} }
} }
return Json(200, resp) var b []byte
var err error
if b, err = json.MarshalIndent(resp, "", " "); err != nil {
return ApiError(500, "body json marshal", err)
}
return &NormalResponse{
body: b,
status: 200,
header: http.Header{
"Content-Type": []string{"application/json"},
},
}
} }
// includes code from
// https://raw.githubusercontent.com/rcrowley/go-metrics/master/sample.go
// Copyright 2012 Richard Crowley. All rights reserved.
package metrics
import "sync/atomic"
// Gauges hold an int64 value that can be set arbitrarily.
type Gauge interface {
Metric
Update(int64)
Value() int64
}
func NewGauge(meta *MetricMeta) Gauge {
if UseNilMetrics {
return NilGauge{}
}
return &StandardGauge{
MetricMeta: meta,
value: 0,
}
}
func RegGauge(meta *MetricMeta) Gauge {
g := NewGauge(meta)
MetricStats.Register(g)
return g
}
// GaugeSnapshot is a read-only copy of another Gauge.
type GaugeSnapshot struct {
*MetricMeta
value int64
}
// Snapshot returns the snapshot.
func (g GaugeSnapshot) Snapshot() Metric { return g }
// Update panics.
func (GaugeSnapshot) Update(int64) {
panic("Update called on a GaugeSnapshot")
}
// Value returns the value at the time the snapshot was taken.
func (g GaugeSnapshot) Value() int64 { return g.value }
// NilGauge is a no-op Gauge.
type NilGauge struct{ *MetricMeta }
// Snapshot is a no-op.
func (NilGauge) Snapshot() Metric { return NilGauge{} }
// Update is a no-op.
func (NilGauge) Update(v int64) {}
// Value is a no-op.
func (NilGauge) Value() int64 { return 0 }
// StandardGauge is the standard implementation of a Gauge and uses the
// sync/atomic package to manage a single int64 value.
type StandardGauge struct {
*MetricMeta
value int64
}
// Snapshot returns a read-only copy of the gauge.
func (g *StandardGauge) Snapshot() Metric {
return GaugeSnapshot{MetricMeta: g.MetricMeta, value: g.value}
}
// Update updates the gauge's value.
func (g *StandardGauge) Update(v int64) {
atomic.StoreInt64(&g.value, v)
}
// Value returns the gauge's current value.
func (g *StandardGauge) Value() int64 {
return atomic.LoadInt64(&g.value)
}
...@@ -61,7 +61,6 @@ func (this *GraphitePublisher) Publish(metrics []Metric) { ...@@ -61,7 +61,6 @@ func (this *GraphitePublisher) Publish(metrics []Metric) {
this.addFloat(buf, metricName+".p90", percentiles[2], now) this.addFloat(buf, metricName+".p90", percentiles[2], now)
this.addFloat(buf, metricName+".p99", percentiles[3], now) this.addFloat(buf, metricName+".p99", percentiles[3], now)
} }
} }
log.Trace("Metrics: GraphitePublisher.Publish() \n%s", buf) log.Trace("Metrics: GraphitePublisher.Publish() \n%s", buf)
......
package metrics
import (
"net/url"
"time"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/influxdata/influxdb/client"
)
type InfluxPublisher struct {
database string
tags map[string]string
prefix string
client *client.Client
prevCounts map[string]int64
}
func CreateInfluxPublisher() (*InfluxPublisher, error) {
influxSection, err := setting.Cfg.GetSection("metrics.influxdb")
if err != nil {
return nil, nil
}
publisher := &InfluxPublisher{
tags: make(map[string]string),
}
urlStr := influxSection.Key("url").MustString("localhost:2003")
urlParsed, err := url.Parse(urlStr)
if err != nil {
log.Error(3, "Metics: InfluxPublisher: failed to init influxdb publisher", err)
return nil, nil
}
publisher.database = influxSection.Key("database").MustString("grafana_metrics")
publisher.prefix = influxSection.Key("prefix").MustString("prefix")
publisher.prevCounts = make(map[string]int64)
username := influxSection.Key("User").MustString("grafana")
password := influxSection.Key("Password").MustString("grafana")
publisher.client, err = client.NewClient(client.Config{
URL: *urlParsed,
Username: username,
Password: password,
})
tagsSec, err := setting.Cfg.GetSection("metrics.influxdb.tags")
if err != nil {
log.Error(3, "Metics: InfluxPublisher: failed to init influxdb settings no metrics.influxdb.tags section")
return nil, nil
}
for _, key := range tagsSec.Keys() {
publisher.tags[key.Name()] = key.String()
}
if err != nil {
log.Error(3, "Metics: InfluxPublisher: failed to init influxdb publisher", err)
}
return publisher, nil
}
func (this *InfluxPublisher) Publish(metrics []Metric) {
bp := client.BatchPoints{
Time: time.Now(),
Database: this.database,
Tags: map[string]string{},
}
for key, value := range this.tags {
bp.Tags[key] = value
}
for _, m := range metrics {
switch metric := m.(type) {
case Counter:
this.addPoint(&bp, metric, "count", metric.Count())
case Timer:
percentiles := metric.Percentiles([]float64{0.25, 0.75, 0.90, 0.99})
this.addPoint(&bp, metric, "count", metric.Count())
this.addPoint(&bp, metric, "min", metric.Min())
this.addPoint(&bp, metric, "max", metric.Max())
this.addPoint(&bp, metric, "mean", metric.Mean())
this.addPoint(&bp, metric, "std", metric.StdDev())
this.addPoint(&bp, metric, "p25", percentiles[0])
this.addPoint(&bp, metric, "p75", percentiles[1])
this.addPoint(&bp, metric, "p90", percentiles[2])
this.addPoint(&bp, metric, "p99", percentiles[2])
}
}
_, err := this.client.Write(bp)
if err != nil {
log.Error(3, "Metrics: InfluxPublisher: publish error", err)
}
}
func (this *InfluxPublisher) addPoint(bp *client.BatchPoints, metric Metric, metricTag string, value interface{}) {
tags := metric.GetTagsCopy()
tags["metric"] = metricTag
bp.Points = append(bp.Points, client.Point{
Measurement: metric.Name(),
Tags: tags,
Fields: map[string]interface{}{"value": value},
})
}
func (this *InfluxPublisher) addCountPoint(bp *client.BatchPoints, metric Metric, value int64) {
tags := metric.GetTagsCopy()
tags["metric"] = "count"
name := metric.Name()
delta := value
if last, ok := this.prevCounts[name]; ok {
delta = calculateDelta(last, value)
}
this.prevCounts[name] = value
bp.Points = append(bp.Points, client.Point{
Measurement: name,
Tags: tags,
Fields: map[string]interface{}{"value": delta},
})
}
package metrics package metrics
import "github.com/Unknwon/log"
var MetricStats = NewRegistry() var MetricStats = NewRegistry()
var UseNilMetrics bool = true var UseNilMetrics bool = true
...@@ -31,7 +29,6 @@ var ( ...@@ -31,7 +29,6 @@ var (
) )
func initMetricVars(settings *MetricSettings) { func initMetricVars(settings *MetricSettings) {
log.Info("Init metric vars")
UseNilMetrics = settings.Enabled == false UseNilMetrics = settings.Enabled == false
M_Instance_Start = RegCounter("instance_start") M_Instance_Start = RegCounter("instance_start")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment