Skip to content

Commit bb0a38b

Browse files
authored
feat: Implement aggregator for agent metrics (#7259)
1 parent b6666cf commit bb0a38b

File tree

12 files changed

+714
-74
lines changed

12 files changed

+714
-74
lines changed

agent/agent.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"os"
1717
"os/user"
1818
"path/filepath"
19-
"reflect"
2019
"sort"
2120
"strconv"
2221
"strings"
@@ -1236,11 +1235,11 @@ func (a *agent) startReportingConnectionStats(ctx context.Context) {
12361235
// Convert from microseconds to milliseconds.
12371236
stats.ConnectionMedianLatencyMS /= 1000
12381237

1239-
lastStat := a.latestStat.Load()
1240-
if lastStat != nil && reflect.DeepEqual(lastStat, stats) {
1241-
a.logger.Info(ctx, "skipping stat because nothing changed")
1242-
return
1243-
}
1238+
// Collect agent metrics.
1239+
// Agent metrics are changing all the time, so there is no need to perform
1240+
// reflect.DeepEqual to see if stats should be transferred.
1241+
stats.Metrics = collectMetrics()
1242+
12441243
a.latestStat.Store(stats)
12451244

12461245
select {

agent/metrics.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package agent
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"tailscale.com/util/clientmetric"
8+
9+
"github.com/coder/coder/codersdk/agentsdk"
10+
)
11+
12+
func collectMetrics() []agentsdk.AgentMetric {
13+
// Tailscale metrics
14+
metrics := clientmetric.Metrics()
15+
collected := make([]agentsdk.AgentMetric, 0, len(metrics))
16+
for _, m := range metrics {
17+
if isIgnoredMetric(m.Name()) {
18+
continue
19+
}
20+
21+
collected = append(collected, agentsdk.AgentMetric{
22+
Name: m.Name(),
23+
Type: asMetricType(m.Type()),
24+
Value: float64(m.Value()),
25+
})
26+
}
27+
return collected
28+
}
29+
30+
// isIgnoredMetric checks if the metric should be ignored, as Coder agent doesn't use related features.
31+
// Expected metric families: magicsock_*, derp_*, tstun_*, netcheck_*, portmap_*, etc.
32+
func isIgnoredMetric(metricName string) bool {
33+
if strings.HasPrefix(metricName, "dns_") ||
34+
strings.HasPrefix(metricName, "controlclient_") ||
35+
strings.HasPrefix(metricName, "peerapi_") ||
36+
strings.HasPrefix(metricName, "profiles_") ||
37+
strings.HasPrefix(metricName, "tstun_") {
38+
return true
39+
}
40+
return false
41+
}
42+
43+
func asMetricType(typ clientmetric.Type) agentsdk.AgentMetricType {
44+
switch typ {
45+
case clientmetric.TypeGauge:
46+
return agentsdk.AgentMetricTypeGauge
47+
case clientmetric.TypeCounter:
48+
return agentsdk.AgentMetricTypeCounter
49+
default:
50+
panic(fmt.Sprintf("unknown metric type: %d", typ))
51+
}
52+
}

cli/server.go

+14
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
723723
return xerrors.Errorf("register agent stats prometheus metric: %w", err)
724724
}
725725
defer closeAgentStatsFunc()
726+
727+
metricsAggregator, err := prometheusmetrics.NewMetricsAggregator(logger, options.PrometheusRegistry, 0)
728+
if err != nil {
729+
return xerrors.Errorf("can't initialize metrics aggregator: %w", err)
730+
}
731+
732+
cancelMetricsAggregator := metricsAggregator.Run(ctx)
733+
defer cancelMetricsAggregator()
734+
735+
options.UpdateAgentMetrics = metricsAggregator.Update
736+
err = options.PrometheusRegistry.Register(metricsAggregator)
737+
if err != nil {
738+
return xerrors.Errorf("can't register metrics aggregator as collector: %w", err)
739+
}
726740
}
727741

728742
//nolint:revive

coderd/apidoc/docs.go

+45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import (
3838
"cdr.dev/slog"
3939

4040
"github.com/coder/coder/buildinfo"
41+
"github.com/coder/coder/codersdk/agentsdk"
42+
4143
// Used for swagger docs.
4244
_ "github.com/coder/coder/coderd/apidoc"
4345
"github.com/coder/coder/coderd/audit"
@@ -146,6 +148,8 @@ type Options struct {
146148
SSHConfig codersdk.SSHConfigResponse
147149

148150
HTTPClient *http.Client
151+
152+
UpdateAgentMetrics func(ctx context.Context, username, workspaceName, agentName string, metrics []agentsdk.AgentMetric)
149153
}
150154

151155
// @title Coder API

0 commit comments

Comments
 (0)