Skip to content

Commit 376c681

Browse files
authored
feat: Move from datadog to generic otel (#1567)
1 parent 2a85d3d commit 376c681

File tree

7 files changed

+169
-306
lines changed

7 files changed

+169
-306
lines changed

cli/server.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import (
3131
"golang.org/x/xerrors"
3232
"google.golang.org/api/idtoken"
3333
"google.golang.org/api/option"
34-
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
34+
35+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
3536

3637
"cdr.dev/slog"
3738
"cdr.dev/slog/sloggers/sloghuman"
@@ -44,6 +45,7 @@ import (
4445
"github.com/coder/coder/coderd/database/databasefake"
4546
"github.com/coder/coder/coderd/devtunnel"
4647
"github.com/coder/coder/coderd/gitsshkey"
48+
"github.com/coder/coder/coderd/tracing"
4749
"github.com/coder/coder/coderd/turnconn"
4850
"github.com/coder/coder/codersdk"
4951
"github.com/coder/coder/cryptorand"
@@ -82,7 +84,7 @@ func server() *cobra.Command {
8284
turnRelayAddress string
8385
tunnel bool
8486
stunServers []string
85-
traceDatadog bool
87+
trace bool
8688
secureAuthCookie bool
8789
sshKeygenAlgorithmRaw string
8890
spooky bool
@@ -98,11 +100,20 @@ func server() *cobra.Command {
98100
logger = logger.Leveled(slog.LevelDebug)
99101
}
100102

101-
if traceDatadog {
102-
tracer.Start(tracer.WithLogStartup(false), tracer.WithLogger(&datadogLogger{
103-
logger: logger.Named("datadog"),
104-
}))
105-
defer tracer.Stop()
103+
var tracerProvider *sdktrace.TracerProvider
104+
var err error
105+
if trace {
106+
tracerProvider, err = tracing.TracerProvider(cmd.Context(), "coderd")
107+
if err != nil {
108+
logger.Warn(cmd.Context(), "failed to start telemetry exporter", slog.Error(err))
109+
} else {
110+
defer func() {
111+
// allow time for traces to flush even if command context is cancelled
112+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
113+
defer cancel()
114+
_ = tracerProvider.Shutdown(ctx)
115+
}()
116+
}
106117
}
107118

108119
printLogo(cmd, spooky)
@@ -216,6 +227,7 @@ func server() *cobra.Command {
216227
SecureAuthCookie: secureAuthCookie,
217228
SSHKeygenAlgorithm: sshKeygenAlgorithm,
218229
TURNServer: turnServer,
230+
TracerProvider: tracerProvider,
219231
}
220232

221233
if oauth2GithubClientSecret != "" {
@@ -480,7 +492,7 @@ func server() *cobra.Command {
480492
cliflag.StringArrayVarP(root.Flags(), &stunServers, "stun-server", "", "CODER_STUN_SERVERS", []string{
481493
"stun:stun.l.google.com:19302",
482494
}, "Specify URLs for STUN servers to enable P2P connections.")
483-
cliflag.BoolVarP(root.Flags(), &traceDatadog, "trace-datadog", "", "CODER_TRACE_DATADOG", false, "Send tracing data to a datadog agent")
495+
cliflag.BoolVarP(root.Flags(), &trace, "trace", "", "CODER_TRACE", false, "Specifies if application tracing data is collected")
484496
cliflag.StringVarP(root.Flags(), &turnRelayAddress, "turn-relay-address", "", "CODER_TURN_RELAY_ADDRESS", "127.0.0.1",
485497
"Specifies the address to bind TURN connections.")
486498
cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false, "Specifies if the 'Secure' property is set on browser session cookies")
@@ -719,11 +731,3 @@ func serveHandler(ctx context.Context, logger slog.Logger, handler http.Handler,
719731

720732
return func() { _ = srv.Close() }
721733
}
722-
723-
type datadogLogger struct {
724-
logger slog.Logger
725-
}
726-
727-
func (d *datadogLogger) Log(msg string) {
728-
d.logger.Debug(context.Background(), msg)
729-
}

cli/server_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,11 @@ func TestServer(t *testing.T) {
280280
require.NoError(t, err)
281281
<-done
282282
})
283-
t.Run("DatadogTracerNoLeak", func(t *testing.T) {
283+
t.Run("TracerNoLeak", func(t *testing.T) {
284284
t.Parallel()
285285
ctx, cancelFunc := context.WithCancel(context.Background())
286286
defer cancelFunc()
287-
root, _ := clitest.New(t, "server", "--dev", "--tunnel=false", "--address", ":0", "--trace-datadog=true")
287+
root, _ := clitest.New(t, "server", "--dev", "--tunnel=false", "--address", ":0", "--trace=true")
288288
done := make(chan struct{})
289289
go func() {
290290
defer close(done)

coderd/coderd.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"golang.org/x/xerrors"
1616
"google.golang.org/api/idtoken"
1717

18-
chitrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5"
18+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
1919

2020
"cdr.dev/slog"
2121
"github.com/coder/coder/buildinfo"
@@ -25,6 +25,7 @@ import (
2525
"github.com/coder/coder/coderd/httpapi"
2626
"github.com/coder/coder/coderd/httpmw"
2727
"github.com/coder/coder/coderd/rbac"
28+
"github.com/coder/coder/coderd/tracing"
2829
"github.com/coder/coder/coderd/turnconn"
2930
"github.com/coder/coder/codersdk"
3031
"github.com/coder/coder/site"
@@ -51,6 +52,7 @@ type Options struct {
5152
SSHKeygenAlgorithm gitsshkey.Algorithm
5253
TURNServer *turnconn.Server
5354
Authorizer rbac.Authorizer
55+
TracerProvider *sdktrace.TracerProvider
5456
}
5557

5658
// New constructs the Coder API into an HTTP handler.
@@ -92,7 +94,7 @@ func New(options *Options) (http.Handler, func()) {
9294
})
9395
},
9496
httpmw.Prometheus,
95-
chitrace.Middleware(),
97+
tracing.HTTPMW(api.TracerProvider, "coderd.http"),
9698
)
9799

98100
r.Route("/api/v2", func(r chi.Router) {

coderd/tracing/exporter.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package tracing
2+
3+
import (
4+
"context"
5+
6+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
7+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
8+
"go.opentelemetry.io/otel/sdk/resource"
9+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
10+
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
11+
"golang.org/x/xerrors"
12+
)
13+
14+
// TracerProvider creates a grpc otlp exporter and configures a trace provider.
15+
// Caller is responsible for calling TracerProvider.Shutdown to ensure all data is flushed.
16+
func TracerProvider(ctx context.Context, service string) (*sdktrace.TracerProvider, error) {
17+
res, err := resource.New(ctx,
18+
resource.WithAttributes(
19+
// the service name used to display traces in backends
20+
semconv.ServiceNameKey.String(service),
21+
),
22+
)
23+
if err != nil {
24+
return nil, xerrors.Errorf("creating otlp resource: %w", err)
25+
}
26+
27+
// By default we send span data to a local otel collector.
28+
// The endpoint we push to can be configured with env vars.
29+
// See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
30+
exporter, err := otlptrace.New(ctx, otlptracegrpc.NewClient(otlptracegrpc.WithInsecure()))
31+
if err != nil {
32+
return nil, xerrors.Errorf("creating otlp exporter: %w", err)
33+
}
34+
35+
tracerProvider := sdktrace.NewTracerProvider(
36+
sdktrace.WithBatcher(exporter),
37+
sdktrace.WithResource(res),
38+
)
39+
40+
return tracerProvider, nil
41+
}

coderd/tracing/httpmw.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package tracing
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/go-chi/chi/middleware"
8+
"github.com/go-chi/chi/v5"
9+
"go.opentelemetry.io/otel/attribute"
10+
"go.opentelemetry.io/otel/codes"
11+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
12+
)
13+
14+
// HTTPMW adds tracing to http routes.
15+
func HTTPMW(tracerProvider *sdktrace.TracerProvider, name string) func(http.Handler) http.Handler {
16+
return func(next http.Handler) http.Handler {
17+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
18+
// do not trace if exporter has not be initialized
19+
if tracerProvider == nil {
20+
next.ServeHTTP(rw, r)
21+
return
22+
}
23+
24+
// start span with default span name. Span name will be updated to "method route" format once request finishes.
25+
_, span := tracerProvider.Tracer(name).Start(r.Context(), fmt.Sprintf("%s %s", r.Method, r.RequestURI))
26+
defer span.End()
27+
28+
wrw := middleware.NewWrapResponseWriter(rw, r.ProtoMajor)
29+
30+
// pass the span through the request context and serve the request to the next middleware
31+
next.ServeHTTP(rw, r)
32+
33+
// set the resource name as we get it only once the handler is executed
34+
route := chi.RouteContext(r.Context()).RoutePattern()
35+
if route != "" {
36+
span.SetName(fmt.Sprintf("%s %s", r.Method, route))
37+
}
38+
span.SetAttributes(attribute.KeyValue{
39+
Key: "http.method",
40+
Value: attribute.StringValue(r.Method),
41+
})
42+
span.SetAttributes(attribute.KeyValue{
43+
Key: "http.route",
44+
Value: attribute.StringValue(route),
45+
})
46+
span.SetAttributes(attribute.KeyValue{
47+
Key: "http.path",
48+
Value: attribute.StringValue(r.URL.EscapedPath()),
49+
})
50+
51+
// set the status code
52+
status := wrw.Status()
53+
// 0 status means one has not yet been sent in which case net/http library will write StatusOK
54+
if status == 0 {
55+
status = http.StatusOK
56+
}
57+
span.SetAttributes(attribute.KeyValue{
58+
Key: "http.status_code",
59+
Value: attribute.IntValue(status),
60+
})
61+
62+
// if 5XX we set the span to "error" status
63+
if status >= 500 {
64+
span.SetStatus(codes.Error, fmt.Sprintf("%d: %s", status, http.StatusText(status)))
65+
}
66+
})
67+
}
68+
}

go.mod

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ require (
103103
github.com/tabbed/pqtype v0.1.1
104104
github.com/unrolled/secure v1.10.0
105105
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1
106+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0
106107
go.uber.org/atomic v1.9.0
107108
go.uber.org/goleak v1.1.12
108109
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
@@ -118,7 +119,6 @@ require (
118119
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f
119120
google.golang.org/api v0.79.0
120121
google.golang.org/protobuf v1.28.0
121-
gopkg.in/DataDog/dd-trace-go.v1 v1.38.1
122122
gopkg.in/natefinch/lumberjack.v2 v2.0.0
123123
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
124124
nhooyr.io/websocket v1.8.7
@@ -128,10 +128,6 @@ require (
128128
require (
129129
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
130130
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
131-
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect
132-
github.com/DataDog/datadog-go v4.8.2+incompatible // indirect
133-
github.com/DataDog/datadog-go/v5 v5.0.2 // indirect
134-
github.com/DataDog/sketches-go v1.0.0 // indirect
135131
github.com/Microsoft/go-winio v0.5.2 // indirect
136132
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
137133
github.com/OneOfOne/xxhash v1.2.8 // indirect
@@ -141,7 +137,7 @@ require (
141137
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
142138
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
143139
github.com/beorn7/perks v1.0.1 // indirect
144-
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
140+
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
145141
github.com/cespare/xxhash/v2 v2.1.2 // indirect
146142
github.com/charmbracelet/bubbles v0.10.3 // indirect
147143
github.com/charmbracelet/bubbletea v0.20.0 // indirect
@@ -150,46 +146,46 @@ require (
150146
github.com/containerd/continuity v0.2.2 // indirect
151147
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
152148
github.com/davecgh/go-spew v1.1.1 // indirect
153-
github.com/dgraph-io/ristretto v0.1.0 // indirect
154149
github.com/dlclark/regexp2 v1.4.0 // indirect
155150
github.com/docker/cli v20.10.13+incompatible // indirect
156151
github.com/docker/docker v20.10.13+incompatible // indirect
157152
github.com/docker/go-connections v0.4.0 // indirect
158153
github.com/docker/go-units v0.4.0 // indirect
159-
github.com/dustin/go-humanize v1.0.0 // indirect
160154
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb // indirect
161155
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible // indirect
162156
github.com/ghodss/yaml v1.0.0 // indirect
157+
github.com/gin-gonic/gin v1.7.0 // indirect
158+
github.com/go-chi/chi v1.5.4
159+
github.com/go-logr/logr v1.2.3 // indirect
160+
github.com/go-logr/stdr v1.2.2 // indirect
163161
github.com/go-playground/locales v0.14.0 // indirect
164162
github.com/go-playground/universal-translator v0.18.0 // indirect
165163
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
166164
github.com/gobwas/glob v0.2.3 // indirect
167165
github.com/gobwas/ws v1.1.0 // indirect
168166
github.com/godbus/dbus/v5 v5.1.0 // indirect
169167
github.com/gogo/protobuf v1.3.2 // indirect
170-
github.com/golang/glog v1.0.0 // indirect
171168
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
172169
github.com/golang/protobuf v1.5.2 // indirect
173170
github.com/golang/snappy v0.0.4 // indirect
174171
github.com/google/go-cmp v0.5.8 // indirect
175172
github.com/google/go-querystring v1.1.0 // indirect
176173
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
177174
github.com/gorilla/mux v1.8.0 // indirect
175+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
178176
github.com/hashicorp/errwrap v1.1.0 // indirect
179177
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
180178
github.com/hashicorp/go-multierror v1.1.1 // indirect
181179
github.com/hashicorp/hcl v1.0.0 // indirect
182180
github.com/imdario/mergo v0.3.12 // indirect
183181
github.com/inconshreveable/mousetrap v1.0.0 // indirect
184-
github.com/josharian/intern v1.0.0 // indirect
185182
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
186183
github.com/klauspost/compress v1.15.0 // indirect
187184
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
188185
github.com/klauspost/reedsolomon v1.9.15 // indirect
189186
github.com/kr/fs v0.1.0 // indirect
190187
github.com/leodido/go-urn v1.2.1 // indirect
191188
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
192-
github.com/mailru/easyjson v0.7.7 // indirect
193189
github.com/mattn/go-colorable v0.1.12 // indirect
194190
github.com/mattn/go-runewidth v0.0.13 // indirect
195191
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
@@ -206,7 +202,6 @@ require (
206202
github.com/opencontainers/image-spec v1.0.2 // indirect
207203
github.com/opencontainers/runc v1.1.0 // indirect
208204
github.com/pelletier/go-toml/v2 v2.0.0-beta.7.0.20220408132554-2377ac4bc04c // indirect
209-
github.com/philhofer/fwd v1.1.1 // indirect
210205
github.com/pion/dtls/v2 v2.1.4 // indirect
211206
github.com/pion/ice/v2 v2.2.6 // indirect
212207
github.com/pion/interceptor v0.1.11 // indirect
@@ -234,7 +229,6 @@ require (
234229
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
235230
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
236231
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
237-
github.com/tinylib/msgp v1.1.2 // indirect
238232
github.com/tjfoc/gmsm v1.4.1 // indirect
239233
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
240234
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
@@ -243,6 +237,12 @@ require (
243237
github.com/zclconf/go-cty v1.10.0 // indirect
244238
github.com/zeebo/errs v1.2.2 // indirect
245239
go.opencensus.io v0.23.0 // indirect
240+
go.opentelemetry.io/otel v1.7.0
241+
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect
242+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0
243+
go.opentelemetry.io/otel/sdk v1.7.0
244+
go.opentelemetry.io/otel/trace v1.7.0 // indirect
245+
go.opentelemetry.io/proto/otlp v0.16.0 // indirect
246246
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
247247
google.golang.org/appengine v1.6.7 // indirect
248248
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy