From bd6c73c47de45e8a590557f62718fd2dbf518fab Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Wed, 9 Oct 2024 11:34:39 -0400 Subject: [PATCH] feat(tracing): Misc. OTel tracing improvements Signed-off-by: Dave Henderson --- cmd/relayproxy/api/opentelemetry/otel.go | 169 +++++++++++++--- cmd/relayproxy/api/opentelemetry/otel_test.go | 188 ++++++++++++++++++ cmd/relayproxy/api/server.go | 6 +- cmd/relayproxy/api/server_test.go | 7 +- cmd/relayproxy/main.go | 8 +- go.mod | 22 +- go.sum | 36 +++- .../docs/relay_proxy/configure_relay_proxy.md | 2 +- .../docs/relay_proxy/monitor_relay_proxy.md | 17 +- 9 files changed, 407 insertions(+), 48 deletions(-) create mode 100644 cmd/relayproxy/api/opentelemetry/otel_test.go diff --git a/cmd/relayproxy/api/opentelemetry/otel.go b/cmd/relayproxy/api/opentelemetry/otel.go index 6844b00300b8..862006adf77f 100644 --- a/cmd/relayproxy/api/opentelemetry/otel.go +++ b/cmd/relayproxy/api/opentelemetry/otel.go @@ -2,20 +2,25 @@ package opentelemetry import ( "context" - "net/url" + "fmt" + "os" + "strconv" + "time" "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/config" + "go.opentelemetry.io/contrib/exporters/autoexport" + "go.opentelemetry.io/contrib/samplers/jaegerremote" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" ) type OtelService struct { otelTraceProvider *sdktrace.TracerProvider - otelExporter *otlptrace.Exporter + otelExporter sdktrace.SpanExporter } func NewOtelService() OtelService { @@ -23,47 +28,161 @@ func NewOtelService() OtelService { } // Init the OpenTelemetry service -func (s *OtelService) Init(ctx context.Context, config config.Config) error { - // parsing the OpenTelemetry endpoint - u, err := url.Parse(config.OpenTelemetryOtlpEndpoint) +func (s *OtelService) Init(ctx context.Context, zapLog *zap.Logger, config config.Config) error { + // OTEL_SDK_DISABLED is not supported by the Go SDK, but is a standard env + // var defined by the OTel spec. We'll use it to disable the trace provider. + if disabled, _ := strconv.ParseBool(os.Getenv("OTEL_SDK_DISABLED")); disabled { + otel.SetTracerProvider(noop.NewTracerProvider()) + return nil + } + + // support the openTelemetryOtlpEndpoint config element + if config.OpenTelemetryOtlpEndpoint != "" && + os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") == "" { + os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", config.OpenTelemetryOtlpEndpoint) + } + + exporter, err := autoexport.NewSpanExporter(ctx) if err != nil { - return err + return fmt.Errorf("initializing OTel exporter: %w", err) } - var opts []otlptracehttp.Option - if u.Scheme == "http" { - opts = append(opts, otlptracehttp.WithInsecure()) + serviceName := "go-feature-flag" + if v := os.Getenv("OTEL_SERVICE_NAME"); v != "" { + serviceName = v } - opts = append(opts, otlptracehttp.WithEndpoint(u.Host)) - client := otlptracehttp.NewClient(opts...) - s.otelExporter, err = otlptrace.New(ctx, client) + sampler, err := initSampler(serviceName) if err != nil { - return err + return fmt.Errorf("initializing OTel sampler: %w", err) } + resource, err := initResource(ctx, serviceName, config.Version) + if err != nil { + return fmt.Errorf("initializing OTel resources: %w", err) + } + + s.otelExporter = exporter s.otelTraceProvider = sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher(s.otelExporter), - sdktrace.WithResource(resource.NewSchemaless( - attribute.String("service.name", "go-feature-flag"), - attribute.String("service.version", config.Version), - )), + sdktrace.WithBatcher(exporter), + sdktrace.WithSampler(sampler), + sdktrace.WithResource(resource), ) + otel.SetTracerProvider(s.otelTraceProvider) + + // log OTel errors to zap rather than the default log package + otel.SetErrorHandler(otelErrHandler(func(err error) { + zapLog.Error("OTel error", zap.Error(err)) + })) + return nil } +type otelErrHandler func(err error) + +func (o otelErrHandler) Handle(err error) { + o(err) +} + +var _ otel.ErrorHandler = otelErrHandler(nil) + +func initResource(ctx context.Context, serviceName, version string) (*resource.Resource, error) { + return resource.New(ctx, + resource.WithFromEnv(), + resource.WithProcessPID(), + resource.WithProcessExecutableName(), + resource.WithProcessExecutablePath(), + resource.WithProcessOwner(), + resource.WithProcessRuntimeName(), + resource.WithProcessRuntimeVersion(), + resource.WithProcessRuntimeDescription(), + resource.WithHost(), + resource.WithTelemetrySDK(), + resource.WithOS(), + resource.WithContainer(), + resource.WithAttributes( + semconv.ServiceNameKey.String(serviceName), + semconv.ServiceVersionKey.String(version), + ), + ) +} + +// initSampler determines which sampling strategy to use. If OTEL_TRACES_SAMPLER +// is unset, we'll always sample. +// If it's set to jaeger_remote, we'll use the Jaeger sampling server (supports +// JAEGER_SAMPLER_MANAGER_HOST_PORT, JAEGER_SAMPLER_REFRESH_INTERVAL, and +// JAEGER_SAMPLER_MAX_OPERATIONS). +// If it's set to any other value, we return nil and sdktrace.NewTracerProvider +// will set up the initSampler from the environment. +func initSampler(serviceName string) (sdktrace.Sampler, error) { + sampler, ok := os.LookupEnv("OTEL_TRACES_SAMPLER") + if !ok { + return sdktrace.AlwaysSample(), nil + } + + if sampler != "jaeger_remote" { + return nil, nil + } + + samplerURL, samplerRefreshInterval, maxOperations, err := jaegerRemoteSamplerOpts() + if err != nil { + return nil, err + } + + return jaegerremote.New( + serviceName, + jaegerremote.WithSamplingServerURL(samplerURL), + jaegerremote.WithSamplingRefreshInterval(samplerRefreshInterval), + jaegerremote.WithMaxOperations(maxOperations), + jaegerremote.WithInitialSampler(sdktrace.AlwaysSample()), + ), nil +} + +const ( + defaultSamplerURL = "http://localhost:5778/sampling" + defaultSamplingRefreshInterval = 1 * time.Minute + defaultSamplingMaxOperations = 256 +) + +func jaegerRemoteSamplerOpts() (string, time.Duration, int, error) { + samplerURL := defaultSamplerURL + if v := os.Getenv("JAEGER_SAMPLER_MANAGER_HOST_PORT"); v != "" { + samplerURL = v + } + + samplerRefreshInterval := defaultSamplingRefreshInterval + if v := os.Getenv("JAEGER_SAMPLER_REFRESH_INTERVAL"); v != "" { + d, err := time.ParseDuration(v) + if err != nil { + return "", 0, 0, fmt.Errorf("parsing JAEGER_SAMPLER_REFRESH_INTERVAL: %w", err) + } + samplerRefreshInterval = d + } + + maxOperations := defaultSamplingMaxOperations + if v := os.Getenv("JAEGER_SAMPLER_MAX_OPERATIONS"); v != "" { + i, err := strconv.Atoi(v) + if err != nil { + return "", 0, 0, fmt.Errorf("parsing JAEGER_SAMPLER_MAX_OPERATIONS: %w", err) + } + maxOperations = i + } + + return samplerURL, samplerRefreshInterval, maxOperations, nil +} + // Stop the OpenTelemetry service -func (s *OtelService) Stop() error { +func (s *OtelService) Stop(ctx context.Context) error { if s.otelExporter != nil { - err := s.otelExporter.Shutdown(context.Background()) + err := s.otelExporter.Shutdown(ctx) if err != nil { return err } } + if s.otelTraceProvider != nil { - err := s.otelTraceProvider.Shutdown(context.Background()) + err := s.otelTraceProvider.Shutdown(ctx) if err != nil { return err } diff --git a/cmd/relayproxy/api/opentelemetry/otel_test.go b/cmd/relayproxy/api/opentelemetry/otel_test.go new file mode 100644 index 000000000000..43afbdc68d0c --- /dev/null +++ b/cmd/relayproxy/api/opentelemetry/otel_test.go @@ -0,0 +1,188 @@ +package opentelemetry + +import ( + "context" + "errors" + "os" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/config" + "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/log" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" +) + +func TestInitSampler(t *testing.T) { + t.Run("OTEL_TRACES_SAMPLER unset", func(t *testing.T) { + sampler, err := initSampler("test") + require.NoError(t, err) + assert.Equal(t, sdktrace.AlwaysSample(), sampler) + }) + + t.Run("OTEL_TRACES_SAMPLER set to non-jaeger_remote", func(t *testing.T) { + t.Setenv("OTEL_TRACES_SAMPLER", "always_on") + sampler, err := initSampler("test") + require.NoError(t, err) + assert.Nil(t, sampler) + }) + + t.Run("OTEL_TRACES_SAMPLER set to jaeger_remote", func(t *testing.T) { + t.Setenv("OTEL_TRACES_SAMPLER", "jaeger_remote") + sampler, err := initSampler("test") + require.NoError(t, err) + + // not really any way to assert on the sampler other than calling + // Description()... + assert.Equal(t, "JaegerRemoteSampler{}", sampler.Description()) + }) +} + +func TestJaegerRemoteSamplerOpts(t *testing.T) { + t.Run("defaults", func(t *testing.T) { + url, refreshInterval, maxOperations, err := jaegerRemoteSamplerOpts() + require.NoError(t, err) + assert.Equal(t, defaultSamplerURL, url) + assert.Equal(t, defaultSamplingRefreshInterval, refreshInterval) + assert.Equal(t, defaultSamplingMaxOperations, maxOperations) + }) + + t.Run("JAEGER_SAMPLER_MANAGER_HOST_PORT set", func(t *testing.T) { + expected := "http://example.com:1234" + t.Setenv("JAEGER_SAMPLER_MANAGER_HOST_PORT", expected) + + url, _, _, err := jaegerRemoteSamplerOpts() + require.NoError(t, err) + assert.Equal(t, expected, url) + }) + + t.Run("JAEGER_SAMPLER_REFRESH_INTERVAL set", func(t *testing.T) { + expected := 42 * time.Second + t.Setenv("JAEGER_SAMPLER_REFRESH_INTERVAL", expected.String()) + + _, refreshInterval, _, err := jaegerRemoteSamplerOpts() + require.NoError(t, err) + assert.Equal(t, expected, refreshInterval) + }) + + t.Run("JAEGER_SAMPLER_MAX_OPERATIONS set", func(t *testing.T) { + expected := 42 + t.Setenv("JAEGER_SAMPLER_MAX_OPERATIONS", strconv.Itoa(expected)) + + _, _, maxOperations, err := jaegerRemoteSamplerOpts() + require.NoError(t, err) + assert.Equal(t, expected, maxOperations) + }) + + t.Run("invalid JAEGER_SAMPLER_REFRESH_INTERVAL", func(t *testing.T) { + t.Setenv("JAEGER_SAMPLER_REFRESH_INTERVAL", "bogus") + + _, _, _, err := jaegerRemoteSamplerOpts() + require.Error(t, err) + }) + + t.Run("invalid JAEGER_SAMPLER_MAX_OPERATIONS", func(t *testing.T) { + t.Setenv("JAEGER_SAMPLER_MAX_OPERATIONS", "bogus") + + _, _, _, err := jaegerRemoteSamplerOpts() + require.Error(t, err) + }) +} + +func TestInitResource(t *testing.T) { + t.Run("defaults, no env", func(t *testing.T) { + res, err := initResource(context.Background(), "test", "1.2.3") + require.NoError(t, err) + + rmap := map[string]attribute.Value{} + for _, attr := range res.Attributes() { + rmap[string(attr.Key)] = attr.Value + } + + // just spot-check a few things + assert.Equal(t, "test", rmap["service.name"].AsString()) + assert.Equal(t, "1.2.3", rmap["service.version"].AsString()) + assert.Equal(t, "go", rmap["process.runtime.name"].AsString()) + }) + + t.Run("with OTEL_RESOURCE_ATTRIBUTES set", func(t *testing.T) { + t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "key1=val1,key2=val2") + + res, err := initResource(context.Background(), "test", "1.2.3") + require.NoError(t, err) + + rmap := map[string]attribute.Value{} + for _, attr := range res.Attributes() { + rmap[string(attr.Key)] = attr.Value + } + + assert.Equal(t, "val1", rmap["key1"].AsString()) + assert.Equal(t, "val2", rmap["key2"].AsString()) + }) +} + +func TestInit(t *testing.T) { + logger := log.InitLogger().ZapLogger + + svc := NewOtelService() + + t.Run("no config", func(t *testing.T) { + err := svc.Init(context.Background(), logger, config.Config{}) + require.NoError(t, err) + assert.NotNil(t, otel.GetTracerProvider()) + }) + + t.Run("disabled", func(t *testing.T) { + t.Setenv("OTEL_SDK_DISABLED", "true") + err := svc.Init(context.Background(), logger, config.Config{}) + require.NoError(t, err) + assert.Equal(t, noop.NewTracerProvider(), otel.GetTracerProvider()) + }) + + t.Run("support openTelemetryOtlpEndpoint", func(t *testing.T) { + err := svc.Init(context.Background(), logger, config.Config{ + OpenTelemetryOtlpEndpoint: "https://example.com:4318", + }) + require.NoError(t, err) + assert.Equal(t, "https://example.com:4318", os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + }) + + t.Run("OTEL_EXPORTER_OTLP_ENDPOINT takes precedence", func(t *testing.T) { + t.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "https://example.com:4318") + + err := svc.Init(context.Background(), logger, config.Config{ + OpenTelemetryOtlpEndpoint: "https://bogus.com:4317", + }) + require.NoError(t, err) + assert.Equal(t, "https://example.com:4318", os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + }) + + t.Run("error handler logs to zap", func(t *testing.T) { + obs, logs := observer.New(zap.InfoLevel) + testLogger := zap.New(obs) + + expectedErr := errors.New("test error") + + err := svc.Init(context.Background(), testLogger, config.Config{}) + require.NoError(t, err) + + otel.GetErrorHandler().Handle(expectedErr) + + require.Len(t, logs.All(), 1) + + want := []observer.LoggedEntry{{ + Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "OTel error"}, + Context: []zapcore.Field{zap.Error(expectedErr)}, + }} + + assert.Equal(t, want, logs.AllUntimed()) + }) +} diff --git a/cmd/relayproxy/api/server.go b/cmd/relayproxy/api/server.go index 85a69f2e44a3..d3facca2cde8 100644 --- a/cmd/relayproxy/api/server.go +++ b/cmd/relayproxy/api/server.go @@ -119,7 +119,7 @@ func (s *Server) Start() { } // start the OpenTelemetry tracing service - err := s.otelService.Init(context.Background(), *s.config) + err := s.otelService.Init(context.Background(), s.zapLog, *s.config) if err != nil { s.zapLog.Error("error while initializing Otel", zap.Error(err)) // we can continue because otel is not mandatory to start the server @@ -148,8 +148,8 @@ func (s *Server) StartAwsLambda() { } // Stop shutdown the API server -func (s *Server) Stop() { - err := s.otelService.Stop() +func (s *Server) Stop(ctx context.Context) { + err := s.otelService.Stop(ctx) if err != nil { s.zapLog.Error("impossible to stop otel", zap.Error(err)) } diff --git a/cmd/relayproxy/api/server_test.go b/cmd/relayproxy/api/server_test.go index 1d3ceb9b8cdd..266fa399ef57 100644 --- a/cmd/relayproxy/api/server_test.go +++ b/cmd/relayproxy/api/server_test.go @@ -1,6 +1,7 @@ package api_test import ( + "context" "net/http" "strings" "testing" @@ -52,7 +53,7 @@ func Test_Starting_RelayProxy_with_monitoring_on_same_port(t *testing.T) { s := api.New(proxyConf, services, log.ZapLogger) go func() { s.Start() }() - defer s.Stop() + defer s.Stop(context.Background()) time.Sleep(10 * time.Millisecond) @@ -106,7 +107,7 @@ func Test_Starting_RelayProxy_with_monitoring_on_different_port(t *testing.T) { s := api.New(proxyConf, services, log.ZapLogger) go func() { s.Start() }() - defer s.Stop() + defer s.Stop(context.Background()) time.Sleep(10 * time.Millisecond) @@ -175,7 +176,7 @@ func Test_CheckOFREPAPIExists(t *testing.T) { s := api.New(proxyConf, services, log.ZapLogger) go func() { s.Start() }() - defer s.Stop() + defer s.Stop(context.Background()) time.Sleep(10 * time.Millisecond) diff --git a/cmd/relayproxy/main.go b/cmd/relayproxy/main.go index 52a31f5984e4..b4be86d4b2a7 100644 --- a/cmd/relayproxy/main.go +++ b/cmd/relayproxy/main.go @@ -1,8 +1,10 @@ package main import ( + "context" "fmt" "os" + "time" "github.com/spf13/pflag" "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/api" @@ -100,7 +102,11 @@ func main() { if proxyConf.StartAsAwsLambda { apiServer.StartAwsLambda() } else { + defer func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + apiServer.Stop(ctx) + }() apiServer.Start() - defer func() { _ = apiServer.Stop }() } } diff --git a/go.mod b/go.mod index dfd5d941387d..bc7e20fb9b33 100644 --- a/go.mod +++ b/go.mod @@ -50,10 +50,10 @@ require ( github.com/xitongsys/parquet-go v1.6.2 github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d go.mongodb.org/mongo-driver v1.17.1 + go.opentelemetry.io/contrib/exporters/autoexport v0.55.0 go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.55.0 + go.opentelemetry.io/contrib/samplers/jaegerremote v0.24.0 go.opentelemetry.io/otel v1.30.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 go.opentelemetry.io/otel/sdk v1.30.0 go.uber.org/zap v1.27.0 golang.org/x/net v0.30.0 @@ -191,7 +191,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.59.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/samber/lo v1.44.0 // indirect @@ -215,11 +215,25 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect go.einride.tech/aip v0.68.0 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.55.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.6.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.52.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.6.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 // indirect + go.opentelemetry.io/otel/log v0.6.0 // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.6.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect go.opentelemetry.io/otel/trace v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index fd3f40dbe047..ebce4efaf496 100644 --- a/go.sum +++ b/go.sum @@ -792,8 +792,8 @@ github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= +github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg= @@ -925,8 +925,12 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/bridges/prometheus v0.55.0 h1:1oZYcP3wuazG3O1563m8cs5vc/pBMTymRwgG9yvxMm8= +go.opentelemetry.io/contrib/bridges/prometheus v0.55.0/go.mod h1:sU48aWFqiqBXo2RBtq7KarczkW8uK6RdIU54y4VzpZs= go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/exporters/autoexport v0.55.0 h1:8kNP8SX9id5TY2feLB+79aFxE0kqzh3KvjF1nAfGxVM= +go.opentelemetry.io/contrib/exporters/autoexport v0.55.0/go.mod h1:WhcvzeuTOr58aYsJ7S4ubY1xMs0WXAPaqTQnxr8bRHk= go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.55.0 h1:dJfUeXRQiU+7IhOeqXV7f1hJA47cCOBmCY8uyygIEZg= go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.55.0/go.mod h1:Uk7Flfuk5HGTeggDwlwanunnSDcJydFRihfXT1Z5fEs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= @@ -935,18 +939,42 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+n go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/contrib/propagators/b3 v1.30.0 h1:vumy4r1KMyaoQRltX7cJ37p3nluzALX9nugCjNNefuY= go.opentelemetry.io/contrib/propagators/b3 v1.30.0/go.mod h1:fRbvRsaeVZ82LIl3u0rIvusIel2UUf+JcaaIpy5taho= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.24.0 h1:LcynmmCFYwa0AySFI+yLEeZpwQi2yQdEkv5+zA3TwcI= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.24.0/go.mod h1:oPzwx3Gp7+u62wvez470Yq/Yu0O7hpVl5SFBRGI4964= go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.6.0 h1:WYsDPt0fM4KZaMhLvY+x6TVXd85P/KNl3Ez3t+0+kGs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.6.0/go.mod h1:vfY4arMmvljeXPNJOE0idEwuoPMjAPCWmBMmj6R5Ksw= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0 h1:QSKmLBzbFULSyHzOdO9JsN9lpE4zkrz1byYGmJecdVE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0/go.mod h1:sTQ/NH8Yrirf0sJ5rWqVu+oT82i4zL9FaF6rWcqnptM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 h1:WypxHH02KX2poqqbaadmkMYalGyy/vil4HE4PM4nRJc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0/go.mod h1:U79SV99vtvGSEBeeHnpgGJfTsnsdkWLpPN/CcHAzBSI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 h1:VrMAbeJz4gnVDg2zEzjHG4dEH86j4jO6VYB+NgtGD8s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0/go.mod h1:qqN/uFdpeitTvm+JDqqnjm517pmQRYxTORbETHq5tOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8= +go.opentelemetry.io/otel/exporters/prometheus v0.52.0 h1:kmU3H0b9ufFSi8IQCcxack+sWUblKkFbqWYs6YiACGQ= +go.opentelemetry.io/otel/exporters/prometheus v0.52.0/go.mod h1:+wsAp2+JhuGXX7YRkjlkx6hyWY3ogFPfNA4x3nyiAh0= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.6.0 h1:bZHOb8k/CwwSt0DgvgaoOhBXWNdWqFWaIsGTtg1H3KE= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.6.0/go.mod h1:XlV163j81kDdIt5b5BXCjdqVfqJFy/LJrHA697SorvQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0 h1:IyFlqNsi8VT/nwYlLJfdM0y1gavxGpEvnf6FtVfZ6X4= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0/go.mod h1:bxiX8eUeKoAEQmbq/ecUT8UqZwCjZW52yJrXJUSozsk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 h1:kn1BudCgwtE7PxLqcZkErpD8GKqLZ6BSzeW9QihQJeM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0/go.mod h1:ljkUDtAMdleoi9tIG1R6dJUpVwDcYjw3J2Q6Q/SuiC0= +go.opentelemetry.io/otel/log v0.6.0 h1:nH66tr+dmEgW5y+F9LanGJUBYPrRgP4g2EkmPE3LeK8= +go.opentelemetry.io/otel/log v0.6.0/go.mod h1:KdySypjQHhP069JX0z/t26VHwa8vSwzgaKmXtIB3fJM= go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= -go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= -go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/sdk/log v0.6.0 h1:4J8BwXY4EeDE9Mowg+CyhWVBhTSLXVXodiXxS/+PGqI= +go.opentelemetry.io/otel/sdk/log v0.6.0/go.mod h1:L1DN8RMAduKkrwRAFDEX3E3TLOq46+XMGSbUfHU/+vE= +go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM= +go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= diff --git a/website/docs/relay_proxy/configure_relay_proxy.md b/website/docs/relay_proxy/configure_relay_proxy.md index 38d7b0c9e3be..46b2e8d295f7 100644 --- a/website/docs/relay_proxy/configure_relay_proxy.md +++ b/website/docs/relay_proxy/configure_relay_proxy.md @@ -47,7 +47,7 @@ In case of an array of string, you can add multiple values separated by a comma | `notifier` | [notifier](#notifier) | **none** | Notifiers is the configuration on where to notify a flag change. | | `authorizedKeys` | [authorizedKeys](#type-authorizedkeys) | **none** | List of authorized API keys. | | `evaluationContextEnrichment` | object | **none** | It is a free field that will be merged with the evaluation context sent during the evaluation. It is useful to add common attributes to all the evaluations, such as a server version, environment, etc.

These fields will be included in the custom attributes of the evaluation context.

If in the evaluation context you have a field with the same name, it will be override by the `evaluationContextEnrichment`. | -| `openTelemetryOtlpEndpoint` | string | **none** | Endpoint of your OpenTelemetry OTLP collector, used to send traces to it and you will be able to forward them to your OpenTelemetry solution with the appropriate provider. | +| `openTelemetryOtlpEndpoint` | string | **none** | Endpoint of your OpenTelemetry OTLP collector, used to send traces to it and you will be able to forward them to your OpenTelemetry solution with the appropriate provider. (deprecated, can be overridden with `$OTEL_EXPORTER_OTLP_ENDPOINT`) | | `kafka` | object | **none** | Settings for the Kafka exporter. Mandatory when using the 'kafka' exporter type, and ignored otherwise. | | `projectID` | string | **none** | ID of GCP project. Mandatory when using PubSub exporter. | | `topic` | string | **none** | Name of PubSub topic on which messages will be published. Mandatory when using PubSub exporter. | diff --git a/website/docs/relay_proxy/monitor_relay_proxy.md b/website/docs/relay_proxy/monitor_relay_proxy.md index 5d13662fa2d6..740c3e1df0f0 100644 --- a/website/docs/relay_proxy/monitor_relay_proxy.md +++ b/website/docs/relay_proxy/monitor_relay_proxy.md @@ -9,18 +9,21 @@ description: Monitoring and Tracing of the relay proxy. The **relay proxy** is able to trace the requests it is handling. This is done by using OpenTelemetry. ### Configuration -To configure the tracing, you need to set in the configuration the endpoint to your OTLP collector. -```yaml -# ... -openTelemetryOtlpEndpoint: http://localhost:4318 -# ... -``` + +By default, the relay proxy will attempt to send traces to an OpenTelemetry +collector or compatible agent running at `http://localhost:4318` using the +`http/protobuf` protocol. +To override the endpoint, set the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable. +To override the protocol, set the `OTEL_EXPORTER_OTLP_PROTOCOL` environment variable. +See [the OpenTelemetry documentation](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) for more information. All your requests will be traced and sent to the collector with the service name **`go-feature-flag`**. +To disable tracing, set the `OTEL_SDK_DISABLED` environment variable to `true`. + :::note If you want to try the OpenTelemetry integration locally, follow this [README](https://github.com/thomaspoignant/go-feature-flag/tree/main/cmd/relayproxy/testdata/opentelemetry) -to setup Jaeger and see your traces. +to setup Jaeger and see your traces. ::: ## Monitoring