diff --git a/exporter/debugexporter/exporter.go b/exporter/debugexporter/exporter.go new file mode 100644 index 00000000000..191ac562563 --- /dev/null +++ b/exporter/debugexporter/exporter.go @@ -0,0 +1,87 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// NOTE: If you are making changes to this file, consider whether you want to make similar changes +// to the Logging exporter in /exporter/internal/common/logging_exporter.go, which has similar logic. +// This is especially important for security issues. + +package debugexporter // import "go.opentelemetry.io/collector/exporter/debugexporter" + +import ( + "context" + + "go.uber.org/zap" + + "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/exporter/internal/otlptext" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +type debugExporter struct { + verbosity configtelemetry.Level + logger *zap.Logger + logsMarshaler plog.Marshaler + metricsMarshaler pmetric.Marshaler + tracesMarshaler ptrace.Marshaler +} + +func newDebugExporter(logger *zap.Logger, verbosity configtelemetry.Level) *debugExporter { + return &debugExporter{ + verbosity: verbosity, + logger: logger, + logsMarshaler: otlptext.NewTextLogsMarshaler(), + metricsMarshaler: otlptext.NewTextMetricsMarshaler(), + tracesMarshaler: otlptext.NewTextTracesMarshaler(), + } +} + +func (s *debugExporter) pushTraces(_ context.Context, td ptrace.Traces) error { + s.logger.Info("TracesExporter", + zap.Int("resource spans", td.ResourceSpans().Len()), + zap.Int("spans", td.SpanCount())) + if s.verbosity != configtelemetry.LevelDetailed { + return nil + } + + buf, err := s.tracesMarshaler.MarshalTraces(td) + if err != nil { + return err + } + s.logger.Info(string(buf)) + return nil +} + +func (s *debugExporter) pushMetrics(_ context.Context, md pmetric.Metrics) error { + s.logger.Info("MetricsExporter", + zap.Int("resource metrics", md.ResourceMetrics().Len()), + zap.Int("metrics", md.MetricCount()), + zap.Int("data points", md.DataPointCount())) + if s.verbosity != configtelemetry.LevelDetailed { + return nil + } + + buf, err := s.metricsMarshaler.MarshalMetrics(md) + if err != nil { + return err + } + s.logger.Info(string(buf)) + return nil +} + +func (s *debugExporter) pushLogs(_ context.Context, ld plog.Logs) error { + s.logger.Info("LogsExporter", + zap.Int("resource logs", ld.ResourceLogs().Len()), + zap.Int("log records", ld.LogRecordCount())) + if s.verbosity != configtelemetry.LevelDetailed { + return nil + } + + buf, err := s.logsMarshaler.MarshalLogs(ld) + if err != nil { + return err + } + s.logger.Info(string(buf)) + return nil +} diff --git a/exporter/debugexporter/factory.go b/exporter/debugexporter/factory.go index e9e75130dd6..b91b1950f7e 100644 --- a/exporter/debugexporter/factory.go +++ b/exporter/debugexporter/factory.go @@ -5,12 +5,18 @@ package debugexporter // import "go.opentelemetry.io/collector/exporter/debugexp import ( "context" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/debugexporter/internal/metadata" - "go.opentelemetry.io/collector/exporter/internal/common" + "go.opentelemetry.io/collector/exporter/exporterhelper" + "go.opentelemetry.io/collector/exporter/internal/otlptext" ) // The value of "type" key in configuration. @@ -42,27 +48,47 @@ func createDefaultConfig() component.Config { func createTracesExporter(ctx context.Context, set exporter.CreateSettings, config component.Config) (exporter.Traces, error) { cfg := config.(*Config) - return common.CreateTracesExporter(ctx, set, config, &common.Common{ - Verbosity: cfg.Verbosity, - SamplingInitial: cfg.SamplingInitial, - SamplingThereafter: cfg.SamplingThereafter, - }) + exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger) + debugExporter := newDebugExporter(exporterLogger, cfg.Verbosity) + return exporterhelper.NewTracesExporter(ctx, set, config, + debugExporter.pushTraces, + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithShutdown(otlptext.LoggerSync(exporterLogger)), + ) } func createMetricsExporter(ctx context.Context, set exporter.CreateSettings, config component.Config) (exporter.Metrics, error) { cfg := config.(*Config) - return common.CreateMetricsExporter(ctx, set, config, &common.Common{ - Verbosity: cfg.Verbosity, - SamplingInitial: cfg.SamplingInitial, - SamplingThereafter: cfg.SamplingThereafter, - }) + exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger) + debugExporter := newDebugExporter(exporterLogger, cfg.Verbosity) + return exporterhelper.NewMetricsExporter(ctx, set, config, + debugExporter.pushMetrics, + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithShutdown(otlptext.LoggerSync(exporterLogger)), + ) } func createLogsExporter(ctx context.Context, set exporter.CreateSettings, config component.Config) (exporter.Logs, error) { cfg := config.(*Config) - return common.CreateLogsExporter(ctx, set, config, &common.Common{ - Verbosity: cfg.Verbosity, - SamplingInitial: cfg.SamplingInitial, - SamplingThereafter: cfg.SamplingThereafter, - }) + exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger) + debugExporter := newDebugExporter(exporterLogger, cfg.Verbosity) + return exporterhelper.NewLogsExporter(ctx, set, config, + debugExporter.pushLogs, + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithShutdown(otlptext.LoggerSync(exporterLogger)), + ) +} + +func createLogger(cfg *Config, logger *zap.Logger) *zap.Logger { + core := zapcore.NewSamplerWithOptions( + logger.Core(), + 1*time.Second, + cfg.SamplingInitial, + cfg.SamplingThereafter, + ) + + return zap.New(core) } diff --git a/exporter/debugexporter/go.mod b/exporter/debugexporter/go.mod index 7e21568d01c..93ac5bdc107 100644 --- a/exporter/debugexporter/go.mod +++ b/exporter/debugexporter/go.mod @@ -7,11 +7,13 @@ require ( go.opentelemetry.io/collector/component v0.98.0 go.opentelemetry.io/collector/config/configtelemetry v0.98.0 go.opentelemetry.io/collector/confmap v0.98.0 + go.opentelemetry.io/collector/consumer v0.98.0 go.opentelemetry.io/collector/exporter v0.98.0 go.opentelemetry.io/collector/pdata v1.5.0 go.opentelemetry.io/otel/metric v1.25.0 go.opentelemetry.io/otel/trace v1.25.0 go.uber.org/goleak v1.3.0 + go.uber.org/zap v1.27.0 ) require ( @@ -39,7 +41,6 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect go.opentelemetry.io/collector v0.98.0 // indirect go.opentelemetry.io/collector/config/configretry v0.98.0 // indirect - go.opentelemetry.io/collector/consumer v0.98.0 // indirect go.opentelemetry.io/collector/extension v0.98.0 // indirect go.opentelemetry.io/collector/receiver v0.98.0 // indirect go.opentelemetry.io/otel v1.25.0 // indirect @@ -47,7 +48,6 @@ require ( go.opentelemetry.io/otel/sdk v1.25.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/exporter/internal/common/logging_exporter.go b/exporter/internal/common/logging_exporter.go index 026018465f4..1d34f1f5620 100644 --- a/exporter/internal/common/logging_exporter.go +++ b/exporter/internal/common/logging_exporter.go @@ -1,6 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +// NOTE: If you are making changes to this file, consider whether you want to make similar changes +// to the Debug exporter in /exporter/debugexporter/exporter.go, which has similar logic. +// This is especially important for security issues. + package common // import "go.opentelemetry.io/collector/exporter/internal/common" import (