diff --git a/.chloggen/codeboten_add-log-record-processor-config.yaml b/.chloggen/codeboten_add-log-record-processor-config.yaml new file mode 100644 index 00000000000..e194ce8e911 --- /dev/null +++ b/.chloggen/codeboten_add-log-record-processor-config.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: service + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add support for using the otelzap bridge and emit logs using the OTel Go SDK + +# One or more tracking issues or pull requests related to the change +issues: [10544] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 9970c1fb567..44490b8cc92 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -117,6 +117,7 @@ require ( go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 // indirect go.opentelemetry.io/collector/semconv v0.112.0 // indirect go.opentelemetry.io/collector/service v0.112.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 // indirect go.opentelemetry.io/contrib/config v0.10.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect diff --git a/cmd/otelcorecol/go.sum b/cmd/otelcorecol/go.sum index 9779d4dece0..5ebf230eaad 100644 --- a/cmd/otelcorecol/go.sum +++ b/cmd/otelcorecol/go.sum @@ -109,6 +109,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index eedeb4aacba..4146cac5c18 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -96,6 +96,7 @@ require ( go.opentelemetry.io/collector/processor/processortest v0.112.0 // indirect go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 // indirect go.opentelemetry.io/collector/semconv v0.112.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 // indirect go.opentelemetry.io/contrib/config v0.10.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum index bf723e761bd..ae0305e8300 100644 --- a/internal/e2e/go.sum +++ b/internal/e2e/go.sum @@ -101,6 +101,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= diff --git a/otelcol/go.mod b/otelcol/go.mod index 439d26e2a48..378ed38087c 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -82,6 +82,7 @@ require ( go.opentelemetry.io/collector/processor/processorprofiles v0.112.0 // indirect go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 // indirect go.opentelemetry.io/collector/semconv v0.112.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.31.0 // indirect go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.7.0 // indirect diff --git a/otelcol/go.sum b/otelcol/go.sum index e6581163b83..d2c482061e8 100644 --- a/otelcol/go.sum +++ b/otelcol/go.sum @@ -107,6 +107,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index 33dfbf29b3c..36e6a1c6f0b 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -80,6 +80,7 @@ require ( go.opentelemetry.io/collector/processor/processorprofiles v0.112.0 // indirect go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 // indirect go.opentelemetry.io/collector/semconv v0.112.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 // indirect go.opentelemetry.io/contrib/config v0.10.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.31.0 // indirect go.opentelemetry.io/otel v1.31.0 // indirect diff --git a/otelcol/otelcoltest/go.sum b/otelcol/otelcoltest/go.sum index e6581163b83..d2c482061e8 100644 --- a/otelcol/otelcoltest/go.sum +++ b/otelcol/otelcoltest/go.sum @@ -107,6 +107,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= diff --git a/service/go.mod b/service/go.mod index 742a9b912fc..b73de532061 100644 --- a/service/go.mod +++ b/service/go.mod @@ -39,6 +39,7 @@ require ( go.opentelemetry.io/collector/receiver v0.112.0 go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 go.opentelemetry.io/collector/semconv v0.112.0 + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 go.opentelemetry.io/contrib/config v0.10.0 go.opentelemetry.io/contrib/propagators/b3 v1.31.0 go.opentelemetry.io/otel v1.31.0 @@ -46,6 +47,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 go.opentelemetry.io/otel/exporters/prometheus v0.53.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.31.0 + go.opentelemetry.io/otel/log v0.7.0 go.opentelemetry.io/otel/metric v1.31.0 go.opentelemetry.io/otel/sdk v1.31.0 go.opentelemetry.io/otel/sdk/metric v1.31.0 @@ -107,7 +109,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.7.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect - go.opentelemetry.io/otel/log v0.7.0 // indirect go.opentelemetry.io/otel/sdk/log v0.7.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/net v0.30.0 // indirect diff --git a/service/go.sum b/service/go.sum index d1ebfb66b56..de8ad1301fb 100644 --- a/service/go.sum +++ b/service/go.sum @@ -99,6 +99,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= diff --git a/service/service.go b/service/service.go index 1c4d9a7dccf..e5215414597 100644 --- a/service/service.go +++ b/service/service.go @@ -11,6 +11,7 @@ import ( "fmt" "runtime" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/noop" sdkresource "go.opentelemetry.io/otel/sdk/resource" @@ -92,6 +93,7 @@ type Service struct { telemetrySettings component.TelemetrySettings host *graph.Host collectorConf *confmap.Conf + loggerProvider log.LoggerProvider } // New creates a new Service, its telemetry, and Components. @@ -122,10 +124,11 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { ZapOptions: set.LoggingOptions, } - logger, err := telFactory.CreateLogger(ctx, telset, &cfg.Telemetry) + logger, lp, err := telFactory.CreateLogger(ctx, telset, &cfg.Telemetry) if err != nil { return nil, fmt.Errorf("failed to create logger: %w", err) } + srv.loggerProvider = lp tracerProvider, err := telFactory.CreateTracerProvider(ctx, telset, &cfg.Telemetry) if err != nil { @@ -250,6 +253,12 @@ func (srv *Service) shutdownTelemetry(ctx context.Context) error { err = multierr.Append(err, fmt.Errorf("failed to shutdown tracer provider: %w", shutdownErr)) } } + + if prov, ok := srv.loggerProvider.(shutdownable); ok { + if shutdownErr := prov.Shutdown(ctx); shutdownErr != nil { + err = multierr.Append(err, fmt.Errorf("failed to shutdown logger provider: %w", shutdownErr)) + } + } return err } diff --git a/service/telemetry/config.go b/service/telemetry/config.go index 6cdb12bf6db..15f647386c9 100644 --- a/service/telemetry/config.go +++ b/service/telemetry/config.go @@ -105,6 +105,10 @@ type LogsConfig struct { // // By default, there is no initial field. InitialFields map[string]any `mapstructure:"initial_fields"` + + // Processors allow configuration of log record processors to emit logs to + // any number of suported backends. + Processors []config.LogRecordProcessor `mapstructure:"processors"` } // LogsSamplingConfig sets a sampling strategy for the logger. Sampling caps the diff --git a/service/telemetry/factory.go b/service/telemetry/factory.go index 9f25402adbe..fde93bba2c2 100644 --- a/service/telemetry/factory.go +++ b/service/telemetry/factory.go @@ -8,6 +8,7 @@ import ( "time" "go.opentelemetry.io/contrib/config" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" @@ -50,7 +51,7 @@ type Factory interface { CreateDefaultConfig() component.Config // CreateLogger creates a logger. - CreateLogger(ctx context.Context, set Settings, cfg component.Config) (*zap.Logger, error) + CreateLogger(ctx context.Context, set Settings, cfg component.Config) (*zap.Logger, log.LoggerProvider, error) // CreateTracerProvider creates a TracerProvider. CreateTracerProvider(ctx context.Context, set Settings, cfg component.Config) (trace.TracerProvider, error) @@ -65,9 +66,9 @@ type Factory interface { // NewFactory creates a new Factory. func NewFactory() Factory { return newFactory(createDefaultConfig, - withLogger(func(_ context.Context, set Settings, cfg component.Config) (*zap.Logger, error) { + withLogger(func(ctx context.Context, set Settings, cfg component.Config) (*zap.Logger, log.LoggerProvider, error) { c := *cfg.(*Config) - return newLogger(c.Logs, set.ZapOptions) + return newLogger(ctx, set, c) }), withTracerProvider(func(ctx context.Context, set Settings, cfg component.Config) (trace.TracerProvider, error) { c := *cfg.(*Config) diff --git a/service/telemetry/factory_impl.go b/service/telemetry/factory_impl.go index 439012723a0..364afbde9d6 100644 --- a/service/telemetry/factory_impl.go +++ b/service/telemetry/factory_impl.go @@ -6,6 +6,8 @@ package telemetry // import "go.opentelemetry.io/collector/service/telemetry" import ( "context" + "go.opentelemetry.io/otel/log" + lognoop "go.opentelemetry.io/otel/log/noop" "go.opentelemetry.io/otel/metric" metricnoop "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/trace" @@ -45,7 +47,7 @@ func (f *factory) CreateDefaultConfig() component.Config { } // createLoggerFunc is the equivalent of Factory.CreateLogger. -type createLoggerFunc func(context.Context, Settings, component.Config) (*zap.Logger, error) +type createLoggerFunc func(context.Context, Settings, component.Config) (*zap.Logger, log.LoggerProvider, error) // withLogger overrides the default no-op logger. func withLogger(createLogger createLoggerFunc) factoryOption { @@ -54,9 +56,9 @@ func withLogger(createLogger createLoggerFunc) factoryOption { }) } -func (f *factory) CreateLogger(ctx context.Context, set Settings, cfg component.Config) (*zap.Logger, error) { +func (f *factory) CreateLogger(ctx context.Context, set Settings, cfg component.Config) (*zap.Logger, log.LoggerProvider, error) { if f.createLoggerFunc == nil { - return zap.NewNop(), nil + return zap.NewNop(), lognoop.NewLoggerProvider(), nil } return f.createLoggerFunc(ctx, set, cfg) } diff --git a/service/telemetry/factory_test.go b/service/telemetry/factory_test.go index 86ba38cd460..09cda6b7861 100644 --- a/service/telemetry/factory_test.go +++ b/service/telemetry/factory_test.go @@ -99,7 +99,7 @@ func TestTelemetryConfiguration(t *testing.T) { t.Run(tt.name, func(t *testing.T) { f := NewFactory() set := Settings{ZapOptions: []zap.Option{}} - logger, err := f.CreateLogger(context.Background(), set, tt.cfg) + logger, _, err := f.CreateLogger(context.Background(), set, tt.cfg) if tt.success { require.NoError(t, err) assert.NotNil(t, logger) @@ -158,7 +158,7 @@ func TestSampledLogger(t *testing.T) { f := NewFactory() ctx := context.Background() set := Settings{ZapOptions: []zap.Option{}} - logger, err := f.CreateLogger(ctx, set, tt.cfg) + logger, _, err := f.CreateLogger(ctx, set, tt.cfg) require.NoError(t, err) assert.NotNil(t, logger) }) diff --git a/service/telemetry/logger.go b/service/telemetry/logger.go index eb675bc459f..904c5084e8f 100644 --- a/service/telemetry/logger.go +++ b/service/telemetry/logger.go @@ -4,22 +4,28 @@ package telemetry // import "go.opentelemetry.io/collector/service/telemetry" import ( + "context" + + "go.opentelemetry.io/contrib/bridges/otelzap" + "go.opentelemetry.io/contrib/config" + "go.opentelemetry.io/otel/log" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -func newLogger(cfg LogsConfig, options []zap.Option) (*zap.Logger, error) { +func newLogger(ctx context.Context, set Settings, cfg Config) (*zap.Logger, log.LoggerProvider, error) { // Copied from NewProductionConfig. zapCfg := &zap.Config{ - Level: zap.NewAtomicLevelAt(cfg.Level), - Development: cfg.Development, - Encoding: cfg.Encoding, + Level: zap.NewAtomicLevelAt(cfg.Logs.Level), + Development: cfg.Logs.Development, + Encoding: cfg.Logs.Encoding, EncoderConfig: zap.NewProductionEncoderConfig(), - OutputPaths: cfg.OutputPaths, - ErrorOutputPaths: cfg.ErrorOutputPaths, - DisableCaller: cfg.DisableCaller, - DisableStacktrace: cfg.DisableStacktrace, - InitialFields: cfg.InitialFields, + OutputPaths: cfg.Logs.OutputPaths, + ErrorOutputPaths: cfg.Logs.ErrorOutputPaths, + DisableCaller: cfg.Logs.DisableCaller, + DisableStacktrace: cfg.Logs.DisableStacktrace, + InitialFields: cfg.Logs.InitialFields, } if zapCfg.Encoding == "console" { @@ -27,15 +33,54 @@ func newLogger(cfg LogsConfig, options []zap.Option) (*zap.Logger, error) { zapCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder } - logger, err := zapCfg.Build(options...) + logger, err := zapCfg.Build(set.ZapOptions...) + if err != nil { - return nil, err + return nil, nil, err } - if cfg.Sampling != nil && cfg.Sampling.Enabled { - logger = newSampledLogger(logger, cfg.Sampling) + + var lp log.LoggerProvider + + if len(cfg.Logs.Processors) > 0 { + sch := semconv.SchemaURL + res := config.Resource{ + SchemaUrl: &sch, + Attributes: attributes(set, cfg), + } + sdk, err := config.NewSDK( + config.WithContext(ctx), + config.WithOpenTelemetryConfiguration( + config.OpenTelemetryConfiguration{ + LoggerProvider: &config.LoggerProvider{ + Processors: cfg.Logs.Processors, + }, + Resource: &res, + }, + ), + ) + + if err != nil { + return nil, nil, err + } + + lp = sdk.LoggerProvider() + + logger = logger.WithOptions(zap.WrapCore(func(c zapcore.Core) zapcore.Core { + return zapcore.NewTee( + c, + otelzap.NewCore("go.opentelemetry.io/collector/service/telemetry", + otelzap.WithLoggerProvider(lp), + ), + ) + })) + + } + + if cfg.Logs.Sampling != nil && cfg.Logs.Sampling.Enabled { + logger = newSampledLogger(logger, cfg.Logs.Sampling) } - return logger, nil + return logger, lp, nil } func newSampledLogger(logger *zap.Logger, sc *LogsSamplingConfig) *zap.Logger { diff --git a/service/telemetry/logger_test.go b/service/telemetry/logger_test.go new file mode 100644 index 00000000000..de901d5142c --- /dev/null +++ b/service/telemetry/logger_test.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/collector/service/telemetry" + +import ( + "context" + "errors" + "reflect" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/contrib/config" + "go.uber.org/zap/zapcore" +) + +func TestNewLogger(t *testing.T) { + tests := []struct { + name string + wantCoreType any + wantErr error + cfg Config + }{ + { + name: "no log config", + cfg: Config{}, + wantErr: errors.New("no encoder name specified"), + wantCoreType: nil, + }, + { + name: "log config with no processors", + cfg: Config{ + Logs: LogsConfig{ + Level: zapcore.DebugLevel, + Development: true, + Encoding: "console", + DisableCaller: true, + DisableStacktrace: true, + InitialFields: map[string]any{"fieldKey": "filed-value"}, + }, + }, + wantCoreType: "*zapcore.ioCore", + }, + { + name: "log config with processors and invalid config", + cfg: Config{ + Logs: LogsConfig{ + Encoding: "console", + Processors: []config.LogRecordProcessor{ + { + Batch: &config.BatchLogRecordProcessor{ + Exporter: config.LogRecordExporter{ + OTLP: &config.OTLP{}, + }, + }, + }, + }, + }, + }, + wantErr: errors.New("unsupported protocol \"\""), + }, + { + name: "log config with processors", + cfg: Config{ + Logs: LogsConfig{ + Level: zapcore.DebugLevel, + Development: true, + Encoding: "console", + DisableCaller: true, + DisableStacktrace: true, + InitialFields: map[string]any{"fieldKey": "filed-value"}, + Processors: []config.LogRecordProcessor{ + { + Batch: &config.BatchLogRecordProcessor{ + Exporter: config.LogRecordExporter{ + Console: config.Console{}, + }, + }, + }, + }, + }, + }, + wantCoreType: "zapcore.multiCore", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + l, lp, err := newLogger(context.Background(), Settings{}, tt.cfg) + if tt.wantErr != nil { + require.ErrorContains(t, err, tt.wantErr.Error()) + require.Nil(t, tt.wantCoreType) + } else { + require.NoError(t, err) + gotType := reflect.TypeOf(l.Core()).String() + require.Equal(t, tt.wantCoreType, gotType) + type shutdownable interface { + Shutdown(context.Context) error + } + if prov, ok := lp.(shutdownable); ok { + require.NoError(t, prov.Shutdown(context.Background())) + } + } + }) + } +}