mirror of https://github.com/coder/coder.git
154 lines
4.7 KiB
Go
154 lines
4.7 KiB
Go
package tracing
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/hashicorp/go-multierror"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
"go.opentelemetry.io/otel/sdk/resource"
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
|
ddotel "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentelemetry"
|
|
ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
|
ddprofiler "gopkg.in/DataDog/dd-trace-go.v1/profiler"
|
|
|
|
"golang.org/x/xerrors"
|
|
"google.golang.org/grpc/credentials"
|
|
)
|
|
|
|
// TracerOpts specifies which telemetry exporters should be configured.
|
|
type TracerOpts struct {
|
|
// Default exports to a backend configured by environment variables. See:
|
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
|
|
Default bool
|
|
// DataDog exports traces and profiles to the local DataDog daemon.
|
|
DataDog bool
|
|
// Exports traces to Honeycomb.io with the provided API key.
|
|
Honeycomb string
|
|
}
|
|
|
|
// TracerProvider creates a grpc otlp exporter and configures a trace provider.
|
|
// Caller is responsible for calling TracerProvider.Shutdown to ensure all data is flushed.
|
|
func TracerProvider(ctx context.Context, service string, opts TracerOpts) (*sdktrace.TracerProvider, func(context.Context) error, error) {
|
|
res := resource.NewWithAttributes(
|
|
semconv.SchemaURL,
|
|
// the service name used to display traces in backends
|
|
semconv.ServiceNameKey.String(service),
|
|
)
|
|
|
|
var (
|
|
tracerOpts = []sdktrace.TracerProviderOption{
|
|
sdktrace.WithResource(res),
|
|
}
|
|
closers = []func(context.Context) error{}
|
|
)
|
|
|
|
if opts.DataDog {
|
|
// See more:
|
|
// https://docs.datadoghq.com/tracing/metrics/runtime_metrics/go/
|
|
dd := ddotel.NewTracerProvider(ddtracer.WithRuntimeMetrics())
|
|
closers = append(closers, func(_ context.Context) error {
|
|
// For some reason, this doesn't appear to actually wind down
|
|
// the goroutines.
|
|
return dd.Shutdown()
|
|
})
|
|
|
|
// See https://docs.datadoghq.com/profiler/enabling/go/
|
|
_ = ddprofiler.Start(
|
|
ddprofiler.WithService("coderd"),
|
|
ddprofiler.WithProfileTypes(
|
|
ddprofiler.CPUProfile,
|
|
ddprofiler.HeapProfile,
|
|
ddprofiler.GoroutineProfile,
|
|
|
|
// In the future, we may want to enable:
|
|
// ddprofiler.BlockProfile,
|
|
// ddprofiler.MutexProfile,
|
|
),
|
|
)
|
|
closers = append(closers, func(_ context.Context) error {
|
|
ddprofiler.Stop()
|
|
return nil
|
|
})
|
|
}
|
|
|
|
if opts.Default {
|
|
exporter, err := DefaultExporter(ctx)
|
|
if err != nil {
|
|
return nil, nil, xerrors.Errorf("default exporter: %w", err)
|
|
}
|
|
closers = append(closers, exporter.Shutdown)
|
|
tracerOpts = append(tracerOpts, sdktrace.WithBatcher(exporter))
|
|
}
|
|
if opts.Honeycomb != "" {
|
|
exporter, err := HoneycombExporter(ctx, opts.Honeycomb)
|
|
if err != nil {
|
|
return nil, nil, xerrors.Errorf("honeycomb exporter: %w", err)
|
|
}
|
|
closers = append(closers, exporter.Shutdown)
|
|
tracerOpts = append(tracerOpts, sdktrace.WithBatcher(exporter))
|
|
}
|
|
|
|
tracerProvider := sdktrace.NewTracerProvider(tracerOpts...)
|
|
otel.SetTracerProvider(tracerProvider)
|
|
// Ignore otel errors!
|
|
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {}))
|
|
otel.SetTextMapPropagator(
|
|
propagation.NewCompositeTextMapPropagator(
|
|
propagation.TraceContext{},
|
|
propagation.Baggage{},
|
|
),
|
|
)
|
|
otel.SetLogger(logr.Discard())
|
|
|
|
return tracerProvider, func(ctx context.Context) error {
|
|
var merr error
|
|
err := tracerProvider.ForceFlush(ctx)
|
|
if err != nil {
|
|
merr = multierror.Append(merr, xerrors.Errorf("tracerProvider.ForceFlush(): %w", err))
|
|
}
|
|
for i, closer := range closers {
|
|
err = closer(ctx)
|
|
if err != nil {
|
|
merr = multierror.Append(merr, xerrors.Errorf("closer() %d: %w", i, err))
|
|
}
|
|
}
|
|
err = tracerProvider.Shutdown(ctx)
|
|
if err != nil {
|
|
merr = multierror.Append(merr, xerrors.Errorf("tracerProvider.Shutdown(): %w", err))
|
|
}
|
|
|
|
return merr
|
|
}, nil
|
|
}
|
|
|
|
func DefaultExporter(ctx context.Context) (*otlptrace.Exporter, error) {
|
|
exporter, err := otlptrace.New(ctx, otlptracegrpc.NewClient(otlptracegrpc.WithInsecure()))
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("create otlp exporter: %w", err)
|
|
}
|
|
|
|
return exporter, nil
|
|
}
|
|
|
|
func HoneycombExporter(ctx context.Context, apiKey string) (*otlptrace.Exporter, error) {
|
|
opts := []otlptracegrpc.Option{
|
|
otlptracegrpc.WithEndpoint("api.honeycomb.io:443"),
|
|
otlptracegrpc.WithHeaders(map[string]string{
|
|
"x-honeycomb-team": apiKey,
|
|
}),
|
|
otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")),
|
|
}
|
|
|
|
exporter, err := otlptrace.New(ctx, otlptracegrpc.NewClient(opts...))
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("create otlp exporter: %w", err)
|
|
}
|
|
|
|
return exporter, nil
|
|
}
|