-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SHOW DIAGNOSTICS command #2061
SHOW DIAGNOSTICS command #2061
Changes from all commits
fba41df
1c440f8
e79d063
b7162b0
f6644d4
895b781
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,12 @@ const ( | |
retentionPolicyMinDuration = time.Hour | ||
) | ||
|
||
var startTime time.Time | ||
|
||
func init() { | ||
startTime = time.Now().UTC() | ||
} | ||
|
||
// Server represents a collection of metadata and raw metric data. | ||
type Server struct { | ||
mu sync.RWMutex | ||
|
@@ -93,6 +99,10 @@ type Server struct { | |
// is just getting the request after being off duty for running CQs then | ||
// it will recompute all of them | ||
lastContinuousQueryRun time.Time | ||
|
||
// Build information. | ||
Version string | ||
CommitHash string | ||
} | ||
|
||
// NewServer returns a new instance of Server. | ||
|
@@ -308,6 +318,7 @@ func (s *Server) load() error { | |
if err := sh.open(s.shardPath(sh.ID), s.client.Conn(sh.ID)); err != nil { | ||
return fmt.Errorf("cannot open shard store: id=%d, err=%s", sh.ID, err) | ||
} | ||
s.stats.Inc("shardsOpen") | ||
} | ||
} | ||
} | ||
|
@@ -2032,6 +2043,8 @@ func (s *Server) ExecuteQuery(q *influxql.Query, database string, user *User) Re | |
res = s.executeShowFieldKeysStatement(stmt, database, user) | ||
case *influxql.ShowStatsStatement: | ||
res = s.executeShowStatsStatement(stmt, user) | ||
case *influxql.ShowDiagnosticsStatement: | ||
res = s.executeShowDiagnosticsStatement(stmt, user) | ||
case *influxql.GrantStatement: | ||
res = s.executeGrantStatement(stmt, user) | ||
case *influxql.RevokeStatement: | ||
|
@@ -2664,6 +2677,143 @@ func (s *Server) executeShowStatsStatement(stmt *influxql.ShowStatsStatement, us | |
return &Result{Series: rows} | ||
} | ||
|
||
func (s *Server) executeShowDiagnosticsStatement(stmt *influxql.ShowDiagnosticsStatement, user *User) *Result { | ||
s.mu.RLock() | ||
defer s.mu.RUnlock() | ||
|
||
rows := make([]*influxql.Row, 0) | ||
now := time.Now().UTC().Format(time.RFC3339Nano) | ||
|
||
var m runtime.MemStats | ||
runtime.ReadMemStats(&m) | ||
|
||
hostname, err := os.Hostname() | ||
if err != nil { | ||
hostname = "unknown" | ||
} | ||
|
||
diags := []struct { | ||
name string | ||
fields map[string]interface{} | ||
}{ | ||
{ | ||
name: "build", | ||
fields: map[string]interface{}{ | ||
"version": s.Version, | ||
"commitHash": s.CommitHash, | ||
}, | ||
}, | ||
{ | ||
name: "server", | ||
fields: map[string]interface{}{ | ||
"id": s.id, | ||
"path": s.path, | ||
"authEnabled": s.authenticationEnabled, | ||
"index": s.index, | ||
"retentionAutoCreate": s.RetentionAutoCreate, | ||
"numShards": len(s.shards), | ||
}, | ||
}, | ||
{ | ||
name: "cq", | ||
fields: map[string]interface{}{ | ||
"lastRun": s.lastContinuousQueryRun, | ||
}, | ||
}, | ||
{ | ||
name: "system", | ||
fields: map[string]interface{}{ | ||
"startTime": startTime, | ||
"uptime": time.Since(startTime).String(), | ||
"hostname": hostname, | ||
"pid": os.Getpid(), | ||
"os": runtime.GOOS, | ||
"arch": runtime.GOARCH, | ||
"numcpu": runtime.NumCPU(), | ||
}, | ||
}, | ||
{ | ||
name: "memory", | ||
fields: map[string]interface{}{ | ||
"alloc": m.Alloc, | ||
"totalAlloc": m.TotalAlloc, | ||
"sys": m.Sys, | ||
"lookups": m.Lookups, | ||
"mallocs": m.Mallocs, | ||
"frees": m.Frees, | ||
"heapAlloc": m.HeapAlloc, | ||
"heapSys": m.HeapSys, | ||
"heapIdle": m.HeapIdle, | ||
"heapInUse": m.HeapInuse, | ||
"heapReleased": m.HeapReleased, | ||
"heapObjects": m.HeapObjects, | ||
"pauseTotalNs": m.PauseTotalNs, | ||
"numGC": m.NumGC, | ||
}, | ||
}, | ||
{ | ||
name: "go", | ||
fields: map[string]interface{}{ | ||
"goMaxProcs": runtime.GOMAXPROCS(0), | ||
"numGoroutine": runtime.NumGoroutine(), | ||
"version": runtime.Version(), | ||
}, | ||
}, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'd be easier to use this data from a client or another system if it were a type:
And then you can get the current There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe name it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about something along those lines this morning, but it wasn't clear exactly what to do. The Let me know what you have in mind, as I was going to something like this next. |
||
|
||
for _, d := range diags { | ||
row := &influxql.Row{Columns: []string{"time"}} | ||
row.Name = d.name | ||
|
||
// Get sorted list of keys. | ||
sortedKeys := make([]string, 0, len(d.fields)) | ||
for k, _ := range d.fields { | ||
sortedKeys = append(sortedKeys, k) | ||
} | ||
sort.Strings(sortedKeys) | ||
|
||
values := []interface{}{now} | ||
for _, k := range sortedKeys { | ||
row.Columns = append(row.Columns, k) | ||
values = append(values, d.fields[k]) | ||
} | ||
row.Values = append(row.Values, values) | ||
rows = append(rows, row) | ||
} | ||
|
||
// Shard groups. | ||
shardGroupsRow := &influxql.Row{Columns: []string{}} | ||
shardGroupsRow.Name = "shardGroups" | ||
shardGroupsRow.Columns = append(shardGroupsRow.Columns, "time", "database", "retentionPolicy", "id", | ||
"startTime", "endTime", "duration", "numShards") | ||
// Check all shard groups. | ||
for _, db := range s.databases { | ||
for _, rp := range db.policies { | ||
for _, g := range rp.shardGroups { | ||
shardGroupsRow.Values = append(shardGroupsRow.Values, []interface{}{now, db.name, rp.Name, | ||
g.ID, g.StartTime, g.EndTime, g.Duration().String(), len(g.Shards)}) | ||
} | ||
} | ||
} | ||
rows = append(rows, shardGroupsRow) | ||
|
||
// Shards | ||
shardsRow := &influxql.Row{Columns: []string{}} | ||
shardsRow.Name = "shards" | ||
shardsRow.Columns = append(shardsRow.Columns, "time", "id", "dataNodes", "index", "path") | ||
for _, sh := range s.shards { | ||
var nodes []string | ||
for _, n := range sh.DataNodeIDs { | ||
nodes = append(nodes, strconv.FormatUint(n, 10)) | ||
shardsRow.Values = append(shardsRow.Values, []interface{}{now, sh.ID, strings.Join(nodes, ","), | ||
sh.index, sh.store.Path()}) | ||
} | ||
} | ||
rows = append(rows, shardsRow) | ||
|
||
return &Result{Series: rows} | ||
} | ||
|
||
// filterMeasurementsByExpr filters a list of measurements by a tags expression. | ||
func filterMeasurementsByExpr(measurements Measurements, expr influxql.Expr) (Measurements, error) { | ||
// Create a list to hold result measurements. | ||
|
@@ -3634,19 +3784,19 @@ func copyURL(u *url.URL) *url.URL { | |
return other | ||
} | ||
|
||
func (s *Server) StartReportingLoop(version string, clusterID uint64) chan struct{} { | ||
s.reportStats(version, clusterID) | ||
func (s *Server) StartReportingLoop(clusterID uint64) chan struct{} { | ||
s.reportStats(clusterID) | ||
|
||
ticker := time.NewTicker(24 * time.Hour) | ||
for { | ||
select { | ||
case <-ticker.C: | ||
s.reportStats(version, clusterID) | ||
s.reportStats(clusterID) | ||
} | ||
} | ||
} | ||
|
||
func (s *Server) reportStats(version string, clusterID uint64) { | ||
func (s *Server) reportStats(clusterID uint64) { | ||
s.mu.RLock() | ||
defer s.mu.RUnlock() | ||
|
||
|
@@ -3663,7 +3813,7 @@ func (s *Server) reportStats(version string, clusterID uint64) { | |
"name":"reports", | ||
"columns":["os", "arch", "version", "server_id", "id", "num_series", "num_measurements", "num_databases"], | ||
"points":[["%s", "%s", "%s", "%x", "%x", "%d", "%d", "%d"]] | ||
}]`, runtime.GOOS, runtime.GOARCH, version, s.ID(), clusterID, numSeries, numMeasurements, numDatabases) | ||
}]`, runtime.GOOS, runtime.GOARCH, s.Version, s.ID(), clusterID, numSeries, numMeasurements, numDatabases) | ||
|
||
data := bytes.NewBufferString(json) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important the server object get this stuff, so it be dumped by the diags. I am open to other approaches.