From a10f05486f0f87a6dcb97c86038f0a6b6597f35e Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky <2685025+getvictor@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:57:28 -0600 Subject: [PATCH] Added OTEL log export support (#39279) **Related issue:** Resolves #38607 Contributor docs update: https://github.com/fleetdm/fleet/pull/39285/changes Another contributor docs update: https://github.com/fleetdm/fleet/pull/39402/changes Also: - renamed OtelHandler to OtelTracingHandler - made "opentelemetry" be the default when tracing is enabled - updated OTEL dependencies # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually ## New Fleet configuration settings - [x] Setting(s) is/are explicitly excluded from GitOps ## Summary by CodeRabbit * **New Features** * Added OpenTelemetry log export capability, enabling logs to be sent to OpenTelemetry collectors. * New configuration option `logging.otel_logs_enabled` (requires tracing to be enabled). * **Chores** * Updated OpenTelemetry dependencies to v1.40.0 with latest OTLP exporters and logging support. * Updated dependencies including gRPC (v1.78.0), Google libraries, and cryptography packages. --- changes/38607-otel-logs | 1 + cmd/fleet/main.go | 11 ++- cmd/fleet/serve.go | 75 +++++++++----- cmd/fleet/vuln_process.go | 2 +- go.mod | 48 ++++----- go.sum | 98 ++++++++++--------- server/config/config.go | 15 ++- server/datastore/mysql/mysql.go | 2 +- server/platform/logging/logging.go | 42 +++++--- server/platform/logging/logging_test.go | 4 +- server/platform/logging/multi_handler.go | 61 ++++++++++++ server/platform/logging/multi_handler_test.go | 28 ++++++ server/platform/mysql/common.go | 6 +- server/service/handler.go | 2 +- server/service/middleware/otel/otel.go | 8 +- 15 files changed, 280 insertions(+), 123 deletions(-) create mode 100644 changes/38607-otel-logs create mode 100644 server/platform/logging/multi_handler.go create mode 100644 server/platform/logging/multi_handler_test.go diff --git a/changes/38607-otel-logs b/changes/38607-otel-logs new file mode 100644 index 0000000000..b9595f1452 --- /dev/null +++ b/changes/38607-otel-logs @@ -0,0 +1 @@ +Added `logging.otel_logs_enabled` contributor config option to export server logs to OpenTelemetry. diff --git a/cmd/fleet/main.go b/cmd/fleet/main.go index 41c1ecc342..77b9882991 100644 --- a/cmd/fleet/main.go +++ b/cmd/fleet/main.go @@ -14,6 +14,7 @@ import ( kitlog "github.com/go-kit/log" _ "github.com/go-sql-driver/mysql" "github.com/spf13/cobra" + otelsdklog "go.opentelemetry.io/otel/sdk/log" ) func init() { @@ -133,11 +134,13 @@ func applyDevFlags(cfg *config.FleetConfig) { } // initLogger creates a kitlog.Logger backed by slog. -func initLogger(cfg config.FleetConfig) kitlog.Logger { +func initLogger(cfg config.FleetConfig, loggerProvider *otelsdklog.LoggerProvider) kitlog.Logger { slogLogger := logging.NewSlogLogger(logging.Options{ - JSON: cfg.Logging.JSON, - Debug: cfg.Logging.Debug, - TracingEnabled: cfg.Logging.TracingEnabled, + JSON: cfg.Logging.JSON, + Debug: cfg.Logging.Debug, + TracingEnabled: cfg.Logging.TracingEnabled, + OtelLogsEnabled: cfg.Logging.OtelLogsEnabled, + LoggerProvider: loggerProvider, }) return logging.NewKitlogAdapter(slogLogger) } diff --git a/cmd/fleet/serve.go b/cmd/fleet/serve.go index 046cde30a8..e16643dad4 100644 --- a/cmd/fleet/serve.go +++ b/cmd/fleet/serve.go @@ -96,13 +96,15 @@ import ( _ "go.elastic.co/apm/module/apmsql/v2" _ "go.elastic.co/apm/module/apmsql/v2/mysql" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + otelsdklog "go.opentelemetry.io/otel/sdk/log" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "google.golang.org/grpc" _ "google.golang.org/grpc/encoding/gzip" // Because we use gzip compression for OTLP ) @@ -157,30 +159,20 @@ the way that the Fleet server works. fleet.WriteExpiredLicenseBanner(os.Stderr) } - logger := initLogger(config) - - if dev_mode.IsEnabled { - createTestBuckets(&config, logger) + // Validate OTEL server options + if config.Logging.OtelLogsEnabled && !config.Logging.TracingEnabled { + initFatal( + errors.New("logging.otel_logs_enabled requires logging.tracing_enabled to be true"), + "OTEL logs require tracing for trace correlation", + ) } - // Init tracing and metrics + // Init OTEL providers (traces, metrics, logs) + var loggerProvider *otelsdklog.LoggerProvider var tracerProvider *sdktrace.TracerProvider var meterProvider *sdkmetric.MeterProvider - if config.Logging.TracingEnabled { - ctx := context.Background() - client := otlptracegrpc.NewClient( - // Enable gzip compression to reduce message size - otlptracegrpc.WithCompressor("gzip"), - ) - otlpTraceExporter, err := otlptrace.New(ctx, client) - if err != nil { - initFatal(err, "Failed to initialize tracing") - } - // Configure batch span processor with smaller batch size to avoid exceeding message size limits (4MB default limit) - batchSpanProcessor := sdktrace.NewBatchSpanProcessor(otlpTraceExporter, - sdktrace.WithMaxExportBatchSize(256), // Reduce from default 512 to 256 - ) - // Create resource with service identification attributes + if config.OTELEnabled() { + // Create shared resource with service identification attributes res, err := resource.Merge( resource.Default(), resource.NewWithAttributes( @@ -192,14 +184,26 @@ the way that the Fleet server works. if err != nil { initFatal(err, "Failed to create OTEL resource") } + + // Initialize OTEL traces + otlpTraceExporter, err := otlptrace.New(context.Background(), otlptracegrpc.NewClient( + otlptracegrpc.WithCompressor("gzip"), + )) + if err != nil { + initFatal(err, "Failed to initialize OTEL trace exporter") + } + // Configure batch span processor with smaller batch size to avoid exceeding message size limits (4MB default limit) + batchSpanProcessor := sdktrace.NewBatchSpanProcessor(otlpTraceExporter, + sdktrace.WithMaxExportBatchSize(256), // Reduce from default 512 to 256 + ) tracerProvider = sdktrace.NewTracerProvider( sdktrace.WithResource(res), sdktrace.WithSpanProcessor(batchSpanProcessor), ) otel.SetTracerProvider(tracerProvider) - // Initialize OTEL metrics exporter - metricExporter, err := otlpmetricgrpc.New(ctx, + // Initialize OTEL metrics + metricExporter, err := otlpmetricgrpc.New(context.Background(), otlpmetricgrpc.WithCompressor("gzip"), ) if err != nil { @@ -210,6 +214,26 @@ the way that the Fleet server works. sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter)), ) otel.SetMeterProvider(meterProvider) + + // Initialize OTEL logs + if config.Logging.OtelLogsEnabled { + logExporter, err := otlploggrpc.New(context.Background(), + otlploggrpc.WithCompressor("gzip"), + ) + if err != nil { + initFatal(err, "Failed to initialize OTEL log exporter") + } + loggerProvider = otelsdklog.NewLoggerProvider( + otelsdklog.WithResource(res), + otelsdklog.WithProcessor(otelsdklog.NewBatchProcessor(logExporter)), + ) + } + } + + logger := initLogger(config, loggerProvider) + + if dev_mode.IsEnabled { + createTestBuckets(&config, logger) } allowedHostIdentifiers := map[string]bool{ @@ -1711,6 +1735,11 @@ the way that the Fleet server works. level.Error(logger).Log("msg", "failed to shutdown OTEL meter provider", "err", err) } } + if loggerProvider != nil { + if err := loggerProvider.Shutdown(ctx); err != nil { + level.Error(logger).Log("msg", "failed to shutdown OTEL logger provider", "err", err) + } + } return srv.Shutdown(ctx) }() }() diff --git a/cmd/fleet/vuln_process.go b/cmd/fleet/vuln_process.go index 3050009330..5df4f1a7b8 100644 --- a/cmd/fleet/vuln_process.go +++ b/cmd/fleet/vuln_process.go @@ -40,7 +40,7 @@ by an exit code of zero.`, applyDevFlags(&cfg) } - logger := initLogger(cfg) + logger := initLogger(cfg, nil) logger = kitlog.With(logger, fleet.CronVulnerabilities) licenseInfo, err := initLicense(&cfg, devLicense, devExpiredLicense) diff --git a/go.mod b/go.mod index 9c6a53ce96..0e98d1b331 100644 --- a/go.mod +++ b/go.mod @@ -148,29 +148,33 @@ require ( go.elastic.co/apm/module/apmsql/v2 v2.6.2 go.elastic.co/apm/v2 v2.7.0 go.etcd.io/bbolt v1.3.10 + go.opentelemetry.io/contrib/bridges/otelslog v0.15.0 go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.60.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 - go.opentelemetry.io/otel v1.39.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 - go.opentelemetry.io/otel/metric v1.39.0 - go.opentelemetry.io/otel/sdk v1.39.0 - go.opentelemetry.io/otel/sdk/metric v1.39.0 - go.opentelemetry.io/otel/trace v1.39.0 - golang.org/x/crypto v0.45.0 + go.opentelemetry.io/otel v1.40.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 + go.opentelemetry.io/otel/log v0.16.0 + go.opentelemetry.io/otel/metric v1.40.0 + go.opentelemetry.io/otel/sdk v1.40.0 + go.opentelemetry.io/otel/sdk/log v0.16.0 + go.opentelemetry.io/otel/sdk/metric v1.40.0 + go.opentelemetry.io/otel/trace v1.40.0 + golang.org/x/crypto v0.47.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/image v0.18.0 - golang.org/x/mod v0.29.0 - golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.33.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.39.0 - golang.org/x/term v0.37.0 - golang.org/x/text v0.31.0 - golang.org/x/tools v0.38.0 + golang.org/x/mod v0.31.0 + golang.org/x/net v0.49.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sync v0.19.0 + golang.org/x/sys v0.40.0 + golang.org/x/term v0.39.0 + golang.org/x/text v0.33.0 + golang.org/x/tools v0.40.0 google.golang.org/api v0.256.0 - google.golang.org/grpc v1.77.0 + google.golang.org/grpc v1.78.0 gopkg.in/guregu/null.v3 v3.5.0 gopkg.in/ini.v1 v1.67.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -270,7 +274,7 @@ require ( github.com/goreleaser/chglog v0.4.2 // indirect github.com/goreleaser/fileglob v1.3.0 // indirect github.com/gorilla/schema v1.4.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-version v1.7.0 // indirect @@ -353,9 +357,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 0ca6a05c50..a12ddbe6d9 100644 --- a/go.sum +++ b/go.sum @@ -509,8 +509,8 @@ github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5 h1:saaSiB25B1wgaxrshQh github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5/go.mod h1:itkABA+w2cw7x5nYUS/pLRef6ludkZKOigbROmCTaFw= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -943,30 +943,40 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/bridges/otelslog v0.15.0 h1:yOYhGNPZseueTTvWp5iBD3/CthrmvayUXYEX862dDi4= +go.opentelemetry.io/contrib/bridges/otelslog v0.15.0/go.mod h1:CvaNVqIfcybc+7xqZNubbE+26K6P7AKZF/l0lE2kdCk= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.60.0 h1:iLuogsToNW6QaOYPcbIwhkdRTkc0gvXzuiajObXc6WY= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.60.0/go.mod h1:XNSNQBtSOifFUw0aQUyBN0Ff+0NddEnbSATy2QlFgm8= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 h1:ZVg+kCXxd9LtAaQNKBxAvJ5NpMf7LpvEr4MIZqb0TMQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0/go.mod h1:hh0tMeZ75CCXrHd9OXRYxTlCAdxcXioWHFIpYw2rZu8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4= +go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI= +go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4= +go.opentelemetry.io/otel/sdk/log/logtest v0.16.0 h1:/XVkpZ41rVRTP4DfMgYv1nEtNmf65XPPyAdqV90TMy4= +go.opentelemetry.io/otel/sdk/log/logtest v0.16.0/go.mod h1:iOOPgQr5MY9oac/F5W86mXdeyWZGleIx3uXO98X2R6Y= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -983,8 +993,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -997,8 +1007,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1021,11 +1031,11 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1034,8 +1044,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1082,14 +1092,14 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1097,8 +1107,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1113,8 +1123,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1132,18 +1142,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1155,8 +1165,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/server/config/config.go b/server/config/config.go index e1175f2732..1aee5d7e6c 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -271,8 +271,12 @@ type LoggingConfig struct { DisableBanner bool `yaml:"disable_banner"` ErrorRetentionPeriod time.Duration `yaml:"error_retention_period"` TracingEnabled bool `yaml:"tracing_enabled"` - // TracingType can either be opentelemetry or elasticapm for whichever type of tracing wanted + // TracingType can be set to TracingTypeElasticAPM to send traces to Elastic APM. + // By default (empty or any other value), traces are sent to OpenTelemetry (OTEL). TracingType string `yaml:"tracing_type"` + // OtelLogsEnabled enables exporting logs to an OpenTelemetry collector. + // When enabled, logs are sent to both stderr and the OTLP endpoint. + OtelLogsEnabled bool `yaml:"otel_logs_enabled"` } // ActivityConfig defines configs related to activities. @@ -669,7 +673,7 @@ type FleetConfig struct { } func (f FleetConfig) OTELEnabled() bool { - return f.Logging.TracingEnabled && f.Logging.TracingType == "opentelemetry" + return f.Logging.TracingEnabled && f.Logging.TracingType != "elasticapm" } type PartnershipsConfig struct { @@ -1314,8 +1318,10 @@ func (man Manager) addConfigs() { "Amount of time to keep errors, 0 means no expiration, < 0 means disable storage of errors") man.addConfigBool("logging.tracing_enabled", false, "Enable Tracing, further configured via standard env variables") - man.addConfigString("logging.tracing_type", "opentelemetry", - "Select the kind of tracing, defaults to opentelemetry, can also be elasticapm") + man.addConfigString("logging.tracing_type", "", + "Select the kind of tracing, defaults to OpenTelemetry, can also be elasticapm") + man.addConfigBool("logging.otel_logs_enabled", false, + "Enable exporting logs to an OpenTelemetry collector (requires tracing_enabled)") // Email man.addConfigString("email.backend", "", "Provide the email backend type, acceptable values are currently \"ses\" and \"default\" or empty string which will default to SMTP") @@ -1745,6 +1751,7 @@ func (man Manager) LoadConfig() FleetConfig { ErrorRetentionPeriod: man.getConfigDuration("logging.error_retention_period"), TracingEnabled: man.getConfigBool("logging.tracing_enabled"), TracingType: man.getConfigString("logging.tracing_type"), + OtelLogsEnabled: man.getConfigBool("logging.otel_logs_enabled"), }, Firehose: FirehoseConfig{ Region: man.getConfigString("firehose.region"), diff --git a/server/datastore/mysql/mysql.go b/server/datastore/mysql/mysql.go index 53723eda8a..838621a699 100644 --- a/server/datastore/mysql/mysql.go +++ b/server/datastore/mysql/mysql.go @@ -37,7 +37,7 @@ import ( "github.com/go-sql-driver/mysql" "github.com/hashicorp/go-multierror" "github.com/jmoiron/sqlx" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) // Compile-time interface check diff --git a/server/platform/logging/logging.go b/server/platform/logging/logging.go index c4dadfa41b..b77df56076 100644 --- a/server/platform/logging/logging.go +++ b/server/platform/logging/logging.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "go.opentelemetry.io/contrib/bridges/otelslog" + otellog "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/trace" ) @@ -23,6 +25,12 @@ type Options struct { // TracingEnabled enables OpenTelemetry trace correlation. // When enabled, trace_id and span_id are automatically injected into logs. TracingEnabled bool + // OtelLogsEnabled enables exporting logs to an OpenTelemetry collector. + // When enabled, logs are sent to both the primary handler (stderr) and OTEL. + OtelLogsEnabled bool + // LoggerProvider is the OpenTelemetry LoggerProvider for log export. + // Required when OtelLogsEnabled is true. + LoggerProvider otellog.LoggerProvider } // NewSlogLogger creates a new slog.Logger with the given options. @@ -57,7 +65,13 @@ func NewSlogLogger(opts Options) *slog.Logger { // If tracing is enabled, wrap with handler that injects trace context if opts.TracingEnabled { - handler = NewOtelHandler(handler) + handler = NewOtelTracingHandler(handler) + } + + // If OTEL logs export is enabled, add otelslog handler for sending logs to collector + if opts.OtelLogsEnabled && opts.LoggerProvider != nil { + otelHandler := otelslog.NewHandler("fleet", otelslog.WithLoggerProvider(opts.LoggerProvider)) + handler = NewMultiHandler(handler, otelHandler) } return slog.New(handler) @@ -91,25 +105,25 @@ func replaceAttr(groups []string, a slog.Attr) slog.Attr { return a } -// OtelHandler wraps a slog.Handler to inject OpenTelemetry trace context +// OtelTracingHandler wraps a slog.Handler to inject OpenTelemetry trace context // (trace_id and span_id) into log records when a span is active in the context. -type OtelHandler struct { +type OtelTracingHandler struct { base slog.Handler } -// NewOtelHandler creates a new handler that wraps the base handler +// NewOtelTracingHandler creates a new handler that wraps the base handler // and injects trace context into log records. -func NewOtelHandler(base slog.Handler) *OtelHandler { - return &OtelHandler{base: base} +func NewOtelTracingHandler(base slog.Handler) *OtelTracingHandler { + return &OtelTracingHandler{base: base} } // Enabled reports whether the handler handles records at the given level. -func (h *OtelHandler) Enabled(ctx context.Context, level slog.Level) bool { +func (h *OtelTracingHandler) Enabled(ctx context.Context, level slog.Level) bool { return h.base.Enabled(ctx, level) } // Handle processes the record, adding trace context if available. -func (h *OtelHandler) Handle(ctx context.Context, r slog.Record) error { +func (h *OtelTracingHandler) Handle(ctx context.Context, r slog.Record) error { // Extract span context from the context spanCtx := trace.SpanContextFromContext(ctx) if spanCtx.IsValid() { @@ -123,14 +137,14 @@ func (h *OtelHandler) Handle(ctx context.Context, r slog.Record) error { } // WithAttrs returns a new handler with the given attributes added. -func (h *OtelHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - return &OtelHandler{base: h.base.WithAttrs(attrs)} +func (h *OtelTracingHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &OtelTracingHandler{base: h.base.WithAttrs(attrs)} } // WithGroup returns a new handler with the given group name. -func (h *OtelHandler) WithGroup(name string) slog.Handler { - return &OtelHandler{base: h.base.WithGroup(name)} +func (h *OtelTracingHandler) WithGroup(name string) slog.Handler { + return &OtelTracingHandler{base: h.base.WithGroup(name)} } -// Ensure OtelHandler implements slog.Handler at compile time. -var _ slog.Handler = (*OtelHandler)(nil) +// Ensure OtelTracingHandler implements slog.Handler at compile time. +var _ slog.Handler = (*OtelTracingHandler)(nil) diff --git a/server/platform/logging/logging_test.go b/server/platform/logging/logging_test.go index 0c49b5f855..2aee25cea0 100644 --- a/server/platform/logging/logging_test.go +++ b/server/platform/logging/logging_test.go @@ -126,7 +126,7 @@ func TestOtelHandler(t *testing.T) { t.Run("injects trace context when span is active", func(t *testing.T) { testHandler := testutils.NewTestHandler() - handler := NewOtelHandler(testHandler). + handler := NewOtelTracingHandler(testHandler). WithAttrs([]slog.Attr{slog.String("component", "test")}). WithGroup("testgroup") logger := slog.New(handler) @@ -147,7 +147,7 @@ func TestOtelHandler(t *testing.T) { t.Run("no trace context without span", func(t *testing.T) { testHandler := testutils.NewTestHandler() - handler := NewOtelHandler(testHandler). + handler := NewOtelTracingHandler(testHandler). WithAttrs([]slog.Attr{slog.String("component", "test")}). WithGroup("testgroup") logger := slog.New(handler) diff --git a/server/platform/logging/multi_handler.go b/server/platform/logging/multi_handler.go new file mode 100644 index 0000000000..ebc443d3e1 --- /dev/null +++ b/server/platform/logging/multi_handler.go @@ -0,0 +1,61 @@ +package logging + +import ( + "context" + "errors" + "log/slog" +) + +// MultiHandler sends log records to multiple handlers. +type MultiHandler struct { + handlers []slog.Handler +} + +func NewMultiHandler(handlers ...slog.Handler) *MultiHandler { + return &MultiHandler{handlers: handlers} +} + +// Enabled reports whether any handler handles records at the given level. +func (h *MultiHandler) Enabled(ctx context.Context, level slog.Level) bool { + for _, handler := range h.handlers { + if handler.Enabled(ctx, level) { + return true + } + } + return false +} + +// Handle sends the record to all enabled handlers. +// It continues processing all handlers even if one fails, and returns +// a combined error of all failures using errors.Join. +func (h *MultiHandler) Handle(ctx context.Context, r slog.Record) error { + var errs []error + for _, handler := range h.handlers { + if handler.Enabled(ctx, r.Level) { + if err := handler.Handle(ctx, r.Clone()); err != nil { + errs = append(errs, err) + } + } + } + return errors.Join(errs...) +} + +// WithAttrs returns a new MultiHandler with the given attributes added to all handlers. +func (h *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + handlers := make([]slog.Handler, len(h.handlers)) + for i, handler := range h.handlers { + handlers[i] = handler.WithAttrs(attrs) + } + return &MultiHandler{handlers: handlers} +} + +// WithGroup returns a new MultiHandler with the given group name applied to all handlers. +func (h *MultiHandler) WithGroup(name string) slog.Handler { + handlers := make([]slog.Handler, len(h.handlers)) + for i, handler := range h.handlers { + handlers[i] = handler.WithGroup(name) + } + return &MultiHandler{handlers: handlers} +} + +var _ slog.Handler = (*MultiHandler)(nil) diff --git a/server/platform/logging/multi_handler_test.go b/server/platform/logging/multi_handler_test.go new file mode 100644 index 0000000000..33d9e21fa7 --- /dev/null +++ b/server/platform/logging/multi_handler_test.go @@ -0,0 +1,28 @@ +package logging + +import ( + "log/slog" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/fleetdm/fleet/v4/server/platform/logging/testutils" +) + +func TestMultiHandler(t *testing.T) { + t.Parallel() + + handler1 := testutils.NewTestHandler() + handler2 := testutils.NewTestHandler() + logger := slog.New(NewMultiHandler(handler1, handler2)) + + logger.InfoContext(t.Context(), "test message", "key", "value") + + record1 := handler1.LastRecord() + record2 := handler2.LastRecord() + require.NotNil(t, record1) + require.NotNil(t, record2) + assert.Equal(t, "test message", record1.Message) + assert.Equal(t, "test message", record2.Message) +} diff --git a/server/platform/mysql/common.go b/server/platform/mysql/common.go index 0492e70610..cb94d49993 100644 --- a/server/platform/mysql/common.go +++ b/server/platform/mysql/common.go @@ -64,10 +64,10 @@ func NewDB(conf *MysqlConfig, opts *DBOptions, otelDriverName string) (*sqlx.DB, driverName := "mysql" if opts.TracingConfig != nil && opts.TracingConfig.TracingEnabled { - if opts.TracingConfig.TracingType == "opentelemetry" { - driverName = otelDriverName - } else { + if opts.TracingConfig.TracingType == "elasticapm" { driverName = "apm/mysql" + } else { + driverName = otelDriverName } } if opts.Interceptor != nil { diff --git a/server/service/handler.go b/server/service/handler.go index 9816d0aff5..0262fc8044 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -138,7 +138,7 @@ func MakeHandler( r := mux.NewRouter() if config.Logging.TracingEnabled { - if config.Logging.TracingType == "opentelemetry" { + if config.OTELEnabled() { r.Use(otmiddleware.Middleware( "service", otmiddleware.WithSpanNameFormatter(func(route string, r *http.Request) string { diff --git a/server/service/middleware/otel/otel.go b/server/service/middleware/otel/otel.go index a546119b92..6705056465 100644 --- a/server/service/middleware/otel/otel.go +++ b/server/service/middleware/otel/otel.go @@ -9,8 +9,8 @@ import ( // WrapHandler wraps an HTTP handler with OpenTelemetry instrumentation for a fixed route. // It creates spans named as "{method} {route}" (e.g., "GET /healthz"). -func WrapHandler(handler http.Handler, route string, config config.FleetConfig) http.Handler { - if config.Logging.TracingEnabled && config.Logging.TracingType == "opentelemetry" { +func WrapHandler(handler http.Handler, route string, cfg config.FleetConfig) http.Handler { + if cfg.OTELEnabled() { // Wrap with OTEL handler to create properly named spans: "{method} {route}" return otelhttp.NewHandler( otelhttp.WithRouteTag(route, handler), @@ -25,8 +25,8 @@ func WrapHandler(handler http.Handler, route string, config config.FleetConfig) // WrapHandlerDynamic wraps an HTTP handler with OpenTelemetry instrumentation using dynamic routes. // It creates spans based on the actual request path (e.g., "GET /assets/app.js"). -func WrapHandlerDynamic(handler http.Handler, config config.FleetConfig) http.Handler { - if config.Logging.TracingEnabled && config.Logging.TracingType == "opentelemetry" { +func WrapHandlerDynamic(handler http.Handler, cfg config.FleetConfig) http.Handler { + if cfg.OTELEnabled() { // Create a wrapper that instruments each request with its actual path return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Use the actual request path as the route