Skip to content

Commit 7f85a50

Browse files
committed
Add monitoring package
1 parent cfb84f5 commit 7f85a50

File tree

7 files changed

+486
-48
lines changed

7 files changed

+486
-48
lines changed

cli/server.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"github.com/coder/coder/coderd/database/databasefake"
4545
"github.com/coder/coder/coderd/devtunnel"
4646
"github.com/coder/coder/coderd/gitsshkey"
47+
"github.com/coder/coder/coderd/monitoring"
4748
"github.com/coder/coder/coderd/turnconn"
4849
"github.com/coder/coder/codersdk"
4950
"github.com/coder/coder/cryptorand"
@@ -73,6 +74,7 @@ func server() *cobra.Command {
7374
oauth2GithubClientSecret string
7475
oauth2GithubAllowedOrganizations []string
7576
oauth2GithubAllowSignups bool
77+
telemetryLevelRaw string
7678
tlsCertFile string
7779
tlsClientCAFile string
7880
tlsClientAuth string
@@ -192,6 +194,11 @@ func server() *cobra.Command {
192194
return xerrors.Errorf("parse ssh keygen algorithm %s: %w", sshKeygenAlgorithmRaw, err)
193195
}
194196

197+
telemetryLevel, err := monitoring.ParseTelemetryLevel(telemetryLevelRaw)
198+
if err != nil {
199+
return xerrors.Errorf("parse telemetry level %s: %w", telemetryLevelRaw, err)
200+
}
201+
195202
turnServer, err := turnconn.New(&turn.RelayAddressGeneratorStatic{
196203
RelayAddress: net.ParseIP(turnRelayAddress),
197204
Address: turnRelayAddress,
@@ -249,6 +256,13 @@ func server() *cobra.Command {
249256
}
250257
}
251258

259+
options.Monitor = monitoring.New(cmd.Context(), &monitoring.Options{
260+
Database: options.Database,
261+
Logger: options.Logger,
262+
RefreshInterval: time.Hour,
263+
TelemetryLevel: telemetryLevel,
264+
})
265+
252266
handler, closeCoderd := coderd.New(options)
253267
client := codersdk.New(localURL)
254268
if tlsEnable {
@@ -461,6 +475,8 @@ func server() *cobra.Command {
461475
"Specifies organizations the user must be a member of to authenticate with GitHub.")
462476
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
463477
"Specifies whether new users can sign up with GitHub.")
478+
cliflag.StringVarP(root.Flags(), &telemetryLevelRaw, "telemetry", "", "CODER_TELEMETRY", "all", "The level of telemetry to send. "+
479+
`Accepted values are "all", "core", or "none"`)
464480
cliflag.BoolVarP(root.Flags(), &tlsEnable, "tls-enable", "", "CODER_TLS_ENABLE", false, "Specifies if TLS will be enabled")
465481
cliflag.StringVarP(root.Flags(), &tlsCertFile, "tls-cert-file", "", "CODER_TLS_CERT_FILE", "",
466482
"Specifies the path to the certificate for TLS. It requires a PEM-encoded file. "+
@@ -569,16 +585,16 @@ func newProvisionerDaemon(ctx context.Context, client *codersdk.Client, logger s
569585
func printLogo(cmd *cobra.Command, spooky bool) {
570586
if spooky {
571587
_, _ = fmt.Fprintf(cmd.OutOrStdout(), `
572-
▄████▄ ▒█████ ▓█████▄ ▓█████ ██▀███
588+
▄████▄ ▒█████ ▓█████▄ ▓█████ ██▀███
573589
▒██▀ ▀█ ▒██▒ ██▒▒██▀ ██▌▓█ ▀ ▓██ ▒ ██▒
574590
▒▓█ ▄ ▒██░ ██▒░██ █▌▒███ ▓██ ░▄█ ▒
575-
▒▓▓▄ ▄██▒▒██ ██░░▓█▄ ▌▒▓█ ▄ ▒██▀▀█▄
591+
▒▓▓▄ ▄██▒▒██ ██░░▓█▄ ▌▒▓█ ▄ ▒██▀▀█▄
576592
▒ ▓███▀ ░░ ████▓▒░░▒████▓ ░▒████▒░██▓ ▒██▒
577593
░ ░▒ ▒ ░░ ▒░▒░▒░ ▒▒▓ ▒ ░░ ▒░ ░░ ▒▓ ░▒▓░
578594
░ ▒ ░ ▒ ▒░ ░ ▒ ▒ ░ ░ ░ ░▒ ░ ▒░
579-
░ ░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░
580-
░ ░ ░ ░ ░ ░ ░ ░
581-
░ ░
595+
░ ░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░
596+
░ ░ ░ ░ ░ ░ ░ ░
597+
░ ░
582598
583599
`)
584600
return

coderd/coderd.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/coder/coder/coderd/gitsshkey"
2525
"github.com/coder/coder/coderd/httpapi"
2626
"github.com/coder/coder/coderd/httpmw"
27+
"github.com/coder/coder/coderd/monitoring"
2728
"github.com/coder/coder/coderd/rbac"
2829
"github.com/coder/coder/coderd/turnconn"
2930
"github.com/coder/coder/codersdk"
@@ -47,6 +48,7 @@ type Options struct {
4748
GoogleTokenValidator *idtoken.Validator
4849
GithubOAuth2Config *GithubOAuth2Config
4950
ICEServers []webrtc.ICEServer
51+
Monitor *monitoring.Monitor
5052
SecureAuthCookie bool
5153
SSHKeygenAlgorithm gitsshkey.Algorithm
5254
TURNServer *turnconn.Server
@@ -95,7 +97,7 @@ func New(options *Options) (http.Handler, func()) {
9597
next.ServeHTTP(middleware.NewWrapResponseWriter(w, r.ProtoMajor), r)
9698
})
9799
},
98-
httpmw.Prometheus,
100+
httpmw.Prometheus(options.Monitor),
99101
chitrace.Middleware(),
100102
)
101103

coderd/coderdtest/coderdtest.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"github.com/coder/coder/coderd/database/databasefake"
4646
"github.com/coder/coder/coderd/database/postgres"
4747
"github.com/coder/coder/coderd/gitsshkey"
48+
"github.com/coder/coder/coderd/monitoring"
4849
"github.com/coder/coder/coderd/turnconn"
4950
"github.com/coder/coder/codersdk"
5051
"github.com/coder/coder/cryptorand"
@@ -144,9 +145,15 @@ func New(t *testing.T, options *Options) *codersdk.Client {
144145
AzureCertificates: options.AzureCertificates,
145146
GithubOAuth2Config: options.GithubOAuth2Config,
146147
GoogleTokenValidator: options.GoogleTokenValidator,
147-
SSHKeygenAlgorithm: options.SSHKeygenAlgorithm,
148-
TURNServer: turnServer,
149-
APIRateLimit: options.APIRateLimit,
148+
Monitor: monitoring.New(ctx, &monitoring.Options{
149+
Database: db,
150+
Logger: slogtest.Make(t, nil),
151+
RefreshInterval: time.Minute,
152+
TelemetryLevel: monitoring.TelemetryLevelNone,
153+
}),
154+
SSHKeygenAlgorithm: options.SSHKeygenAlgorithm,
155+
TURNServer: turnServer,
156+
APIRateLimit: options.APIRateLimit,
150157
})
151158
t.Cleanup(func() {
152159
cancelFunc()

coderd/database/databasefake/databasefake.go

+1
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ func (q *fakeQuerier) GetWorkspaceResourcesByJobID(_ context.Context, jobID uuid
10301030
return resources, nil
10311031
}
10321032

1033+
// revive:disable-next-line:flag-parameter
10331034
func (q *fakeQuerier) GetWorkspaces(_ context.Context, deleted bool) ([]database.Workspace, error) {
10341035
q.mutex.RLock()
10351036
defer q.mutex.RUnlock()

coderd/httpmw/prometheus.go

+50-39
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,30 @@ import (
99
chimw "github.com/go-chi/chi/v5/middleware"
1010

1111
"github.com/prometheus/client_golang/prometheus"
12-
"github.com/prometheus/client_golang/prometheus/promauto"
12+
13+
"github.com/coder/coder/coderd/monitoring"
1314
)
1415

1516
var (
16-
requestsProcessed = promauto.NewCounterVec(prometheus.CounterOpts{
17+
requestsProcessed = prometheus.NewCounterVec(prometheus.CounterOpts{
1718
Namespace: "coderd",
1819
Subsystem: "api",
1920
Name: "requests_processed_total",
2021
Help: "The total number of processed API requests",
2122
}, []string{"code", "method", "path"})
22-
requestsConcurrent = promauto.NewGauge(prometheus.GaugeOpts{
23+
requestsConcurrent = prometheus.NewGauge(prometheus.GaugeOpts{
2324
Namespace: "coderd",
2425
Subsystem: "api",
2526
Name: "concurrent_requests",
2627
Help: "The number of concurrent API requests",
2728
})
28-
websocketsConcurrent = promauto.NewGauge(prometheus.GaugeOpts{
29+
websocketsConcurrent = prometheus.NewGauge(prometheus.GaugeOpts{
2930
Namespace: "coderd",
3031
Subsystem: "api",
3132
Name: "concurrent_websockets",
3233
Help: "The total number of concurrent API websockets",
3334
})
34-
websocketsDist = promauto.NewHistogramVec(prometheus.HistogramOpts{
35+
websocketsDist = prometheus.NewHistogramVec(prometheus.HistogramOpts{
3536
Namespace: "coderd",
3637
Subsystem: "api",
3738
Name: "websocket_durations_ms",
@@ -45,7 +46,7 @@ var (
4546
durationToFloatMs(30 * time.Hour),
4647
},
4748
}, []string{"path"})
48-
requestsDist = promauto.NewHistogramVec(prometheus.HistogramOpts{
49+
requestsDist = prometheus.NewHistogramVec(prometheus.HistogramOpts{
4950
Namespace: "coderd",
5051
Subsystem: "api",
5152
Name: "request_latencies_ms",
@@ -58,45 +59,55 @@ func durationToFloatMs(d time.Duration) float64 {
5859
return float64(d.Milliseconds())
5960
}
6061

61-
func Prometheus(next http.Handler) http.Handler {
62-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63-
var (
64-
start = time.Now()
65-
method = r.Method
66-
rctx = chi.RouteContext(r.Context())
67-
)
68-
sw, ok := w.(chimw.WrapResponseWriter)
69-
if !ok {
70-
panic("dev error: http.ResponseWriter is not chimw.WrapResponseWriter")
71-
}
62+
func Prometheus(monitor *monitoring.Monitor) func(http.Handler) http.Handler {
63+
monitor.MustRegister(
64+
monitoring.TelemetryLevelNone,
65+
requestsProcessed,
66+
requestsConcurrent,
67+
websocketsConcurrent,
68+
requestsDist,
69+
)
7270

73-
var (
74-
dist *prometheus.HistogramVec
75-
distOpts []string
76-
)
77-
// We want to count websockets separately.
78-
if isWebsocketUpgrade(r) {
79-
websocketsConcurrent.Inc()
80-
defer websocketsConcurrent.Dec()
71+
return func(next http.Handler) http.Handler {
72+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
73+
var (
74+
start = time.Now()
75+
method = r.Method
76+
rctx = chi.RouteContext(r.Context())
77+
)
78+
sw, ok := w.(chimw.WrapResponseWriter)
79+
if !ok {
80+
panic("dev error: http.ResponseWriter is not chimw.WrapResponseWriter")
81+
}
8182

82-
dist = websocketsDist
83-
} else {
84-
requestsConcurrent.Inc()
85-
defer requestsConcurrent.Dec()
83+
var (
84+
dist *prometheus.HistogramVec
85+
distOpts []string
86+
)
87+
// We want to count websockets separately.
88+
if isWebsocketUpgrade(r) {
89+
websocketsConcurrent.Inc()
90+
defer websocketsConcurrent.Dec()
8691

87-
dist = requestsDist
88-
distOpts = []string{method}
89-
}
92+
dist = websocketsDist
93+
} else {
94+
requestsConcurrent.Inc()
95+
defer requestsConcurrent.Dec()
9096

91-
next.ServeHTTP(w, r)
97+
dist = requestsDist
98+
distOpts = []string{method}
99+
}
92100

93-
path := rctx.RoutePattern()
94-
distOpts = append(distOpts, path)
95-
statusStr := strconv.Itoa(sw.Status())
101+
next.ServeHTTP(w, r)
96102

97-
requestsProcessed.WithLabelValues(statusStr, method, path).Inc()
98-
dist.WithLabelValues(distOpts...).Observe(float64(time.Since(start)) / 1e6)
99-
})
103+
path := rctx.RoutePattern()
104+
distOpts = append(distOpts, path)
105+
statusStr := strconv.Itoa(sw.Status())
106+
107+
requestsProcessed.WithLabelValues(statusStr, method, path).Inc()
108+
dist.WithLabelValues(distOpts...).Observe(float64(time.Since(start)) / 1e6)
109+
})
110+
}
100111
}
101112

102113
func isWebsocketUpgrade(r *http.Request) bool {

0 commit comments

Comments
 (0)