mirror of https://github.com/grafana/grafana
Conflicts: src/app/features/dashboard/partials/shareDashboard.htmlpull/1629/head
commit
49a0ea53c7
@ -0,0 +1,72 @@ |
||||
package metrics |
||||
|
||||
import "sync/atomic" |
||||
|
||||
// Counters hold an int64 value that can be incremented and decremented.
|
||||
type Counter interface { |
||||
Clear() |
||||
Count() int64 |
||||
Dec(int64) |
||||
Inc(int64) |
||||
Snapshot() Counter |
||||
} |
||||
|
||||
// NewCounter constructs a new StandardCounter.
|
||||
func NewCounter() Counter { |
||||
return &StandardCounter{0} |
||||
} |
||||
|
||||
// CounterSnapshot is a read-only copy of another Counter.
|
||||
type CounterSnapshot int64 |
||||
|
||||
// Clear panics.
|
||||
func (CounterSnapshot) Clear() { |
||||
panic("Clear called on a CounterSnapshot") |
||||
} |
||||
|
||||
// Count returns the count at the time the snapshot was taken.
|
||||
func (c CounterSnapshot) Count() int64 { return int64(c) } |
||||
|
||||
// Dec panics.
|
||||
func (CounterSnapshot) Dec(int64) { |
||||
panic("Dec called on a CounterSnapshot") |
||||
} |
||||
|
||||
// Inc panics.
|
||||
func (CounterSnapshot) Inc(int64) { |
||||
panic("Inc called on a CounterSnapshot") |
||||
} |
||||
|
||||
// Snapshot returns the snapshot.
|
||||
func (c CounterSnapshot) Snapshot() Counter { return c } |
||||
|
||||
// StandardCounter is the standard implementation of a Counter and uses the
|
||||
// sync/atomic package to manage a single int64 value.
|
||||
type StandardCounter struct { |
||||
count int64 |
||||
} |
||||
|
||||
// Clear sets the counter to zero.
|
||||
func (c *StandardCounter) Clear() { |
||||
atomic.StoreInt64(&c.count, 0) |
||||
} |
||||
|
||||
// Count returns the current count.
|
||||
func (c *StandardCounter) Count() int64 { |
||||
return atomic.LoadInt64(&c.count) |
||||
} |
||||
|
||||
// Dec decrements the counter by the given amount.
|
||||
func (c *StandardCounter) Dec(i int64) { |
||||
atomic.AddInt64(&c.count, -i) |
||||
} |
||||
|
||||
// Inc increments the counter by the given amount.
|
||||
func (c *StandardCounter) Inc(i int64) { |
||||
atomic.AddInt64(&c.count, i) |
||||
} |
||||
|
||||
// Snapshot returns a read-only copy of the counter.
|
||||
func (c *StandardCounter) Snapshot() Counter { |
||||
return CounterSnapshot(c.Count()) |
||||
} |
@ -0,0 +1,39 @@ |
||||
package metrics |
||||
|
||||
type comboCounterRef struct { |
||||
usageCounter Counter |
||||
metricCounter Counter |
||||
} |
||||
|
||||
func NewComboCounterRef(name string) Counter { |
||||
cr := &comboCounterRef{} |
||||
cr.usageCounter = UsageStats.GetOrRegister(name, NewCounter).(Counter) |
||||
cr.metricCounter = MetricStats.GetOrRegister(name, NewCounter).(Counter) |
||||
return cr |
||||
} |
||||
|
||||
func (c comboCounterRef) Clear() { |
||||
c.usageCounter.Clear() |
||||
c.metricCounter.Clear() |
||||
} |
||||
|
||||
func (c comboCounterRef) Count() int64 { |
||||
panic("Count called on a combocounter ref") |
||||
} |
||||
|
||||
// Dec panics.
|
||||
func (c comboCounterRef) Dec(i int64) { |
||||
c.usageCounter.Dec(i) |
||||
c.metricCounter.Dec(i) |
||||
} |
||||
|
||||
// Inc panics.
|
||||
func (c comboCounterRef) Inc(i int64) { |
||||
c.usageCounter.Inc(i) |
||||
c.metricCounter.Inc(i) |
||||
} |
||||
|
||||
// Snapshot returns the snapshot.
|
||||
func (c comboCounterRef) Snapshot() Counter { |
||||
panic("snapshot called on a combocounter ref") |
||||
} |
@ -0,0 +1,25 @@ |
||||
package metrics |
||||
|
||||
var UsageStats = NewRegistry() |
||||
var MetricStats = NewRegistry() |
||||
|
||||
var ( |
||||
M_Instance_Start = NewComboCounterRef("instance.start") |
||||
|
||||
M_Page_Status_200 = NewComboCounterRef("page.status.200") |
||||
M_Page_Status_500 = NewComboCounterRef("page.status.500") |
||||
M_Page_Status_404 = NewComboCounterRef("page.status.404") |
||||
|
||||
M_Api_Status_500 = NewComboCounterRef("api.status.500") |
||||
M_Api_Status_404 = NewComboCounterRef("api.status.404") |
||||
|
||||
M_Api_User_SignUp = NewComboCounterRef("api.user.signup") |
||||
M_Api_Dashboard_Get = NewComboCounterRef("api.dashboard.get") |
||||
M_Api_Dashboard_Post = NewComboCounterRef("api.dashboard.post") |
||||
M_Api_Admin_User_Create = NewComboCounterRef("api.admin.user_create") |
||||
M_Api_Login_Post = NewComboCounterRef("api.login.post") |
||||
M_Api_Login_OAuth = NewComboCounterRef("api.login.oauth") |
||||
M_Api_Org_Create = NewComboCounterRef("api.org.create") |
||||
|
||||
M_Models_Dashboard_Insert = NewComboCounterRef("models.dashboard.insert") |
||||
) |
@ -0,0 +1,102 @@ |
||||
package metrics |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"sync" |
||||
) |
||||
|
||||
// DuplicateMetric is the error returned by Registry.Register when a metric
|
||||
// already exists. If you mean to Register that metric you must first
|
||||
// Unregister the existing metric.
|
||||
type DuplicateMetric string |
||||
|
||||
func (err DuplicateMetric) Error() string { |
||||
return fmt.Sprintf("duplicate metric: %s", string(err)) |
||||
} |
||||
|
||||
type Registry interface { |
||||
// Call the given function for each registered metric.
|
||||
Each(func(string, interface{})) |
||||
|
||||
// Get the metric by the given name or nil if none is registered.
|
||||
Get(string) interface{} |
||||
|
||||
// Gets an existing metric or registers the given one.
|
||||
// The interface can be the metric to register if not found in registry,
|
||||
// or a function returning the metric for lazy instantiation.
|
||||
GetOrRegister(string, interface{}) interface{} |
||||
|
||||
// Register the given metric under the given name.
|
||||
Register(string, interface{}) error |
||||
} |
||||
|
||||
// The standard implementation of a Registry is a mutex-protected map
|
||||
// of names to metrics.
|
||||
type StandardRegistry struct { |
||||
metrics map[string]interface{} |
||||
mutex sync.Mutex |
||||
} |
||||
|
||||
// Create a new registry.
|
||||
func NewRegistry() Registry { |
||||
return &StandardRegistry{metrics: make(map[string]interface{})} |
||||
} |
||||
|
||||
// Call the given function for each registered metric.
|
||||
func (r *StandardRegistry) Each(f func(string, interface{})) { |
||||
for name, i := range r.registered() { |
||||
f(name, i) |
||||
} |
||||
} |
||||
|
||||
// Get the metric by the given name or nil if none is registered.
|
||||
func (r *StandardRegistry) Get(name string) interface{} { |
||||
r.mutex.Lock() |
||||
defer r.mutex.Unlock() |
||||
return r.metrics[name] |
||||
} |
||||
|
||||
// Gets an existing metric or creates and registers a new one. Threadsafe
|
||||
// alternative to calling Get and Register on failure.
|
||||
// The interface can be the metric to register if not found in registry,
|
||||
// or a function returning the metric for lazy instantiation.
|
||||
func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { |
||||
r.mutex.Lock() |
||||
defer r.mutex.Unlock() |
||||
if metric, ok := r.metrics[name]; ok { |
||||
return metric |
||||
} |
||||
if v := reflect.ValueOf(i); v.Kind() == reflect.Func { |
||||
i = v.Call(nil)[0].Interface() |
||||
} |
||||
r.register(name, i) |
||||
return i |
||||
} |
||||
|
||||
// Register the given metric under the given name. Returns a DuplicateMetric
|
||||
// if a metric by the given name is already registered.
|
||||
func (r *StandardRegistry) Register(name string, i interface{}) error { |
||||
r.mutex.Lock() |
||||
defer r.mutex.Unlock() |
||||
return r.register(name, i) |
||||
} |
||||
|
||||
func (r *StandardRegistry) register(name string, i interface{}) error { |
||||
if _, ok := r.metrics[name]; ok { |
||||
return DuplicateMetric(name) |
||||
} |
||||
|
||||
r.metrics[name] = i |
||||
return nil |
||||
} |
||||
|
||||
func (r *StandardRegistry) registered() map[string]interface{} { |
||||
metrics := make(map[string]interface{}, len(r.metrics)) |
||||
r.mutex.Lock() |
||||
defer r.mutex.Unlock() |
||||
for name, i := range r.metrics { |
||||
metrics[name] = i |
||||
} |
||||
return metrics |
||||
} |
@ -0,0 +1,63 @@ |
||||
package metrics |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"net/http" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/log" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
func StartUsageReportLoop() chan struct{} { |
||||
M_Instance_Start.Inc(1) |
||||
|
||||
ticker := time.NewTicker(time.Hour) |
||||
for { |
||||
select { |
||||
case <-ticker.C: |
||||
sendUsageStats() |
||||
} |
||||
} |
||||
} |
||||
|
||||
func sendUsageStats() { |
||||
log.Trace("Sending anonymous usage stats to stats.grafana.org") |
||||
|
||||
version := strings.Replace(setting.BuildVersion, ".", "_", -1) |
||||
|
||||
metrics := map[string]interface{}{} |
||||
report := map[string]interface{}{ |
||||
"version": version, |
||||
"metrics": metrics, |
||||
} |
||||
|
||||
// statsQuery := m.GetSystemStatsQuery{}
|
||||
// if err := bus.Dispatch(&statsQuery); err != nil {
|
||||
// log.Error(3, "Failed to get system stats", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
UsageStats.Each(func(name string, i interface{}) { |
||||
switch metric := i.(type) { |
||||
case Counter: |
||||
if metric.Count() > 0 { |
||||
metrics[name+".count"] = metric.Count() |
||||
metric.Clear() |
||||
} |
||||
} |
||||
}) |
||||
|
||||
// metrics["stats.dashboards.count"] = statsQuery.Result.DashboardCount
|
||||
// metrics["stats.users.count"] = statsQuery.Result.UserCount
|
||||
// metrics["stats.orgs.count"] = statsQuery.Result.OrgCount
|
||||
|
||||
out, _ := json.Marshal(report) |
||||
data := bytes.NewBuffer(out) |
||||
|
||||
client := http.Client{Timeout: time.Duration(5 * time.Second)} |
||||
|
||||
go client.Post("https://stats.grafana.org/grafana-usage-report", "application/json", data) |
||||
} |
@ -0,0 +1,11 @@ |
||||
package models |
||||
|
||||
type SystemStats struct { |
||||
DashboardCount int |
||||
UserCount int |
||||
OrgCount int |
||||
} |
||||
|
||||
type GetSystemStatsQuery struct { |
||||
Result *SystemStats |
||||
} |
@ -0,0 +1,36 @@ |
||||
package sqlstore |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/bus" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
func init() { |
||||
bus.AddHandler("sql", GetSystemStats) |
||||
} |
||||
|
||||
func GetSystemStats(query *m.GetSystemStatsQuery) error { |
||||
var rawSql = `SELECT |
||||
( |
||||
SELECT COUNT(*) |
||||
FROM ` + dialect.Quote("user") + ` |
||||
) AS user_count, |
||||
( |
||||
SELECT COUNT(*) |
||||
FROM ` + dialect.Quote("org") + ` |
||||
) AS org_count, |
||||
( |
||||
SELECT COUNT(*) |
||||
FROM ` + dialect.Quote("dashboard") + ` |
||||
) AS dashboard_count |
||||
` |
||||
|
||||
var stats m.SystemStats |
||||
_, err := x.Sql(rawSql).Get(&stats) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
query.Result = &stats |
||||
return err |
||||
} |
Loading…
Reference in new issue