mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(instrumentation): work on settings model for internal metrics publishing, #4696
This commit is contained in:
parent
74101eaf7c
commit
6b2a4fe8e8
@ -6,6 +6,9 @@
|
||||
# possible values : production, development
|
||||
app_mode = production
|
||||
|
||||
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
|
||||
instance_name = ${HOSTNAME}
|
||||
|
||||
#################################### Paths ####################################
|
||||
[paths]
|
||||
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
|
||||
|
@ -6,6 +6,9 @@
|
||||
# possible values : production, development
|
||||
; app_mode = production
|
||||
|
||||
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
|
||||
; instance_name = ${HOSTNAME}
|
||||
|
||||
#################################### Paths ####################################
|
||||
[paths]
|
||||
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
|
||||
|
@ -64,15 +64,12 @@ func main() {
|
||||
social.NewOAuthService()
|
||||
eventpublisher.Init()
|
||||
plugins.Init()
|
||||
metrics.Init()
|
||||
|
||||
if err := notifications.Init(); err != nil {
|
||||
log.Fatal(3, "Notification service failed to initialize", err)
|
||||
}
|
||||
|
||||
if setting.ReportingEnabled {
|
||||
go metrics.StartUsageReportLoop()
|
||||
}
|
||||
|
||||
StartServer()
|
||||
exitChan <- 0
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func newMacaron() *macaron.Macaron {
|
||||
|
||||
for _, route := range plugins.StaticRoutes {
|
||||
pluginRoute := path.Join("/public/plugins/", route.PluginId)
|
||||
log.Info("Plugins: Adding route %s -> %s", pluginRoute, route.Directory)
|
||||
log.Debug("Plugins: Adding route %s -> %s", pluginRoute, route.Directory)
|
||||
mapStatic(m, route.Directory, "", pluginRoute)
|
||||
}
|
||||
|
||||
|
@ -9,40 +9,36 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics/senders"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type MetricSender interface {
|
||||
Send(metrics map[string]interface{}) error
|
||||
func Init() {
|
||||
go instrumentationLoop()
|
||||
}
|
||||
|
||||
func StartUsageReportLoop() chan struct{} {
|
||||
func instrumentationLoop() chan struct{} {
|
||||
M_Instance_Start.Inc(1)
|
||||
|
||||
hourTicker := time.NewTicker(time.Hour * 24)
|
||||
secondTicker := time.NewTicker(time.Second * 10)
|
||||
settings := readSettings()
|
||||
|
||||
sender := &receiver.GraphiteSender{
|
||||
Host: "localhost",
|
||||
Port: "2003",
|
||||
Protocol: "tcp",
|
||||
Prefix: "grafana.",
|
||||
}
|
||||
onceEveryDayTick := time.NewTicker(time.Hour * 24)
|
||||
secondTicker := time.NewTicker(time.Second * time.Duration(settings.IntervalSeconds))
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-hourTicker.C:
|
||||
case <-onceEveryDayTick.C:
|
||||
sendUsageStats()
|
||||
case <-secondTicker.C:
|
||||
sendMetricUsage(sender)
|
||||
if settings.Enabled {
|
||||
sendMetrics(settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendMetricUsage(sender MetricSender) {
|
||||
func sendMetrics(settings *MetricSettings) {
|
||||
metrics := map[string]interface{}{}
|
||||
|
||||
MetricStats.Each(func(name string, i interface{}) {
|
||||
@ -63,13 +59,16 @@ func sendMetricUsage(sender MetricSender) {
|
||||
}
|
||||
})
|
||||
|
||||
err := sender.Send(metrics)
|
||||
if err != nil {
|
||||
log.Error(1, "Failed to send metrics:", err)
|
||||
for _, publisher := range settings.Publishers {
|
||||
publisher.Publish(metrics)
|
||||
}
|
||||
}
|
||||
|
||||
func sendUsageStats() {
|
||||
if !setting.ReportingEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Sending anonymous usage stats to stats.grafana.org")
|
||||
|
||||
version := strings.Replace(setting.BuildVersion, ".", "_", -1)
|
55
pkg/metrics/publishers/graphite.go
Normal file
55
pkg/metrics/publishers/graphite.go
Normal file
@ -0,0 +1,55 @@
|
||||
package publishers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type GraphitePublisher struct {
|
||||
Address string
|
||||
Protocol string
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func CreateGraphitePublisher() (*GraphitePublisher, error) {
|
||||
graphiteSection, err := setting.Cfg.GetSection("metrics.graphite")
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
graphiteReceiver := &GraphitePublisher{}
|
||||
graphiteReceiver.Protocol = "tcp"
|
||||
graphiteReceiver.Address = graphiteSection.Key("address").MustString("localhost:2003")
|
||||
graphiteReceiver.Prefix = graphiteSection.Key("prefix").MustString("service.grafana.%(instance_name)s")
|
||||
|
||||
return graphiteReceiver, nil
|
||||
}
|
||||
|
||||
func (this *GraphitePublisher) Publish(metrics map[string]interface{}) {
|
||||
conn, err := net.DialTimeout(this.Protocol, this.Address, time.Second*5)
|
||||
|
||||
if err != nil {
|
||||
log.Error(3, "Metrics: GraphitePublisher: Failed to connect to %s!", err)
|
||||
return
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
now := time.Now().Unix()
|
||||
for key, value := range metrics {
|
||||
metricName := this.Prefix + key
|
||||
line := fmt.Sprintf("%s %d %d\n", metricName, value, now)
|
||||
buf.WriteString(line)
|
||||
}
|
||||
|
||||
log.Trace("Metrics: GraphitePublisher.Publish() \n%s", buf)
|
||||
_, err = conn.Write(buf.Bytes())
|
||||
|
||||
if err != nil {
|
||||
log.Error(3, "Metrics: GraphitePublisher: Failed to send metrics! %s", err)
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package receiver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GraphiteSender struct {
|
||||
Host string
|
||||
Port string
|
||||
Protocol string
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func (this *GraphiteSender) Send(metrics map[string]interface{}) error {
|
||||
log.Debug("GraphiteSender: Sending metrics to graphite")
|
||||
|
||||
address := fmt.Sprintf("%s:%s", this.Host, this.Port)
|
||||
conn, err := net.DialTimeout(this.Protocol, address, time.Second*5)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Graphite Sender: Failed to connec to %s!", err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
now := time.Now().Unix()
|
||||
for key, value := range metrics {
|
||||
metricName := this.Prefix + key
|
||||
line := fmt.Sprintf("%s %d %d\n", metricName, value, now)
|
||||
log.Debug("SendMetric: sending %s", line)
|
||||
buf.WriteString(line)
|
||||
}
|
||||
|
||||
_, err = conn.Write(buf.Bytes())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Graphite Sender: Failed to send metrics! %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
47
pkg/metrics/settings.go
Normal file
47
pkg/metrics/settings.go
Normal file
@ -0,0 +1,47 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics/publishers"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type MetricPublisher interface {
|
||||
Publish(metrics map[string]interface{})
|
||||
}
|
||||
|
||||
type MetricSettings struct {
|
||||
Enabled bool
|
||||
IntervalSeconds int64
|
||||
|
||||
Publishers []MetricPublisher
|
||||
}
|
||||
|
||||
func readSettings() *MetricSettings {
|
||||
var settings = &MetricSettings{
|
||||
Enabled: false,
|
||||
Publishers: make([]MetricPublisher, 0),
|
||||
}
|
||||
|
||||
var section, err = setting.Cfg.GetSection("metrics")
|
||||
if err != nil {
|
||||
log.Fatal(3, "Unable to find metrics config section")
|
||||
return nil
|
||||
}
|
||||
|
||||
settings.Enabled = section.Key("enabled").MustBool(false)
|
||||
settings.IntervalSeconds = section.Key("interval_seconds").MustInt64(10)
|
||||
|
||||
if !settings.Enabled {
|
||||
return settings
|
||||
}
|
||||
|
||||
if graphitePublisher, err := publishers.CreateGraphitePublisher(); err != nil {
|
||||
log.Error(3, "Metrics: Failed to init Graphite metric publisher", err)
|
||||
} else if graphitePublisher != nil {
|
||||
log.Info("Metrics: Internal metrics publisher Graphite initialized")
|
||||
settings.Publishers = append(settings.Publishers, graphitePublisher)
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
@ -37,9 +37,10 @@ const (
|
||||
|
||||
var (
|
||||
// App settings.
|
||||
Env string = DEV
|
||||
AppUrl string
|
||||
AppSubUrl string
|
||||
Env string = DEV
|
||||
AppUrl string
|
||||
AppSubUrl string
|
||||
InstanceName string
|
||||
|
||||
// build
|
||||
BuildVersion string
|
||||
@ -259,6 +260,12 @@ func evalEnvVarExpression(value string) string {
|
||||
envVar = strings.TrimPrefix(envVar, "${")
|
||||
envVar = strings.TrimSuffix(envVar, "}")
|
||||
envValue := os.Getenv(envVar)
|
||||
|
||||
// if env variable is hostname and it is emtpy use os.Hostname as default
|
||||
if envVar == "HOSTNAME" && envValue == "" {
|
||||
envValue, _ = os.Hostname()
|
||||
}
|
||||
|
||||
return envValue
|
||||
})
|
||||
}
|
||||
@ -395,11 +402,28 @@ func validateStaticRootPath() error {
|
||||
return fmt.Errorf("Failed to detect generated css or javascript files in static root (%s), have you executed default grunt task?", StaticRootPath)
|
||||
}
|
||||
|
||||
// func readInstanceName() string {
|
||||
// hostname, _ := os.Hostname()
|
||||
// if hostname == "" {
|
||||
// hostname = "hostname_unknown"
|
||||
// }
|
||||
//
|
||||
// instanceName := Cfg.Section("").Key("instance_name").MustString("")
|
||||
// if instanceName = "" {
|
||||
// // set value as it might be used in other places
|
||||
// Cfg.Section("").Key("instance_name").SetValue(hostname)
|
||||
// instanceName = hostname
|
||||
// }
|
||||
//
|
||||
// return
|
||||
// }
|
||||
|
||||
func NewConfigContext(args *CommandLineArgs) error {
|
||||
setHomePath(args)
|
||||
loadConfiguration(args)
|
||||
|
||||
Env = Cfg.Section("").Key("app_mode").MustString("development")
|
||||
InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
|
||||
PluginsPath = Cfg.Section("paths").Key("plugins").String()
|
||||
|
||||
server := Cfg.Section("server")
|
||||
|
@ -89,5 +89,14 @@ func TestLoadingSettings(t *testing.T) {
|
||||
So(DataPath, ShouldEqual, "/tmp/env_override")
|
||||
})
|
||||
|
||||
Convey("instance_name default to hostname even if hostname env is emtpy", func() {
|
||||
NewConfigContext(&CommandLineArgs{
|
||||
HomePath: "../../",
|
||||
})
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
So(InstanceName, ShouldEqual, hostname)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user