mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Added OTEL log export support (#39279)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
5a2a656257
commit
a10f05486f
15 changed files with 280 additions and 123 deletions
1
changes/38607-otel-logs
Normal file
1
changes/38607-otel-logs
Normal file
|
|
@ -0,0 +1 @@
|
|||
Added `logging.otel_logs_enabled` contributor config option to export server logs to OpenTelemetry.
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}()
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
48
go.mod
48
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
|
||||
|
|
|
|||
98
go.sum
98
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=
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
61
server/platform/logging/multi_handler.go
Normal file
61
server/platform/logging/multi_handler.go
Normal file
|
|
@ -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)
|
||||
28
server/platform/logging/multi_handler_test.go
Normal file
28
server/platform/logging/multi_handler_test.go
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue