mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(instrumentation): added gauge and http endpoint
This commit is contained in:
@@ -235,7 +235,7 @@ func Register(r *macaron.Macaron) {
|
||||
r.Get("/search/", Search)
|
||||
|
||||
// metrics
|
||||
r.Get("/metrics/test", GetTestMetrics)
|
||||
r.Get("/metrics/test", wrap(GetTestMetrics))
|
||||
|
||||
// metrics
|
||||
r.Get("/metrics", wrap(GetInternalMetrics))
|
||||
|
||||
@@ -30,7 +30,7 @@ func InitAppPluginRoutes(r *macaron.Macaron) {
|
||||
}
|
||||
handlers = append(handlers, AppPluginRoute(route, plugin.Id))
|
||||
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 (
|
||||
NotFound = func() Response {
|
||||
return ApiError(404, "Not found", nil)
|
||||
}
|
||||
ServerError = func() Response {
|
||||
return ApiError(500, "Server error", nil)
|
||||
ServerError = func(err error) Response {
|
||||
return ApiError(500, "Server error", err)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ func wrap(action interface{}) macaron.Handler {
|
||||
if err == nil && val != nil && len(val) > 0 {
|
||||
res = val[0].Interface().(Response)
|
||||
} else {
|
||||
res = ServerError()
|
||||
res = ServerError(err)
|
||||
}
|
||||
|
||||
res.WriteTo(c.Resp)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
@@ -37,7 +39,7 @@ func GetTestMetrics(c *middleware.Context) {
|
||||
c.JSON(200, &result)
|
||||
}
|
||||
|
||||
func GetInternalMetrics(c middleware.Context) Response {
|
||||
func GetInternalMetrics(c *middleware.Context) Response {
|
||||
snapshots := metrics.MetricStats.GetSnapshots()
|
||||
|
||||
resp := make(map[string]interface{})
|
||||
@@ -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"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
82
pkg/metrics/gauge.go
Normal file
82
pkg/metrics/gauge.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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) {
|
||||
this.addFloat(buf, metricName+".p90", percentiles[2], now)
|
||||
this.addFloat(buf, metricName+".p99", percentiles[3], now)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.Trace("Metrics: GraphitePublisher.Publish() \n%s", buf)
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
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},
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package metrics
|
||||
|
||||
import "github.com/Unknwon/log"
|
||||
|
||||
var MetricStats = NewRegistry()
|
||||
var UseNilMetrics bool = true
|
||||
|
||||
@@ -31,7 +29,6 @@ var (
|
||||
)
|
||||
|
||||
func initMetricVars(settings *MetricSettings) {
|
||||
log.Info("Init metric vars")
|
||||
UseNilMetrics = settings.Enabled == false
|
||||
|
||||
M_Instance_Start = RegCounter("instance_start")
|
||||
|
||||
Reference in New Issue
Block a user