Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[exporter/logging] Deprecate 'loglevel' in favor of 'verbosity' #6334

Merged
merged 7 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .chloggen/mx-psi_logging-verbosity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: deprecation

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: exporter/logging

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Deprecate 'loglevel' in favor of 'verbosity' option

# One or more tracking issues or pull requests related to the change
issues: [5878]
63 changes: 63 additions & 0 deletions exporter/loggingexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,91 @@
package loggingexporter // import "go.opentelemetry.io/collector/exporter/loggingexporter"

import (
"fmt"

"go.uber.org/zap/zapcore"

"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/confmap"
)

// Config defines configuration for logging exporter.
type Config struct {
config.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct

// LogLevel defines log level of the logging exporter; options are debug, info, warn, error.
// Deprecated: Use `Verbosity` instead.
LogLevel zapcore.Level `mapstructure:"loglevel"`

// Verbosity defines the logging exporter verbosity.
Verbosity configtelemetry.Level `mapstructure:"verbosity"`

// SamplingInitial defines how many samples are initially logged during each second.
SamplingInitial int `mapstructure:"sampling_initial"`

// SamplingThereafter defines the sampling rate after the initial samples are logged.
SamplingThereafter int `mapstructure:"sampling_thereafter"`

// warnLogLevel is set on unmarshaling to warn users about `loglevel` usage.
warnLogLevel bool
bogdandrutu marked this conversation as resolved.
Show resolved Hide resolved
}

var _ config.Exporter = (*Config)(nil)
var _ confmap.Unmarshaler = (*Config)(nil)

func mapLevel(level zapcore.Level) (configtelemetry.Level, error) {
switch level {
case zapcore.DebugLevel:
return configtelemetry.LevelDetailed, nil
case zapcore.InfoLevel:
return configtelemetry.LevelNormal, nil
case zapcore.WarnLevel, zapcore.ErrorLevel,
zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel:
// Anything above info is mapped to 'basic' level.
return configtelemetry.LevelBasic, nil
default:
return configtelemetry.LevelNone, fmt.Errorf("log level %q is not supported", level)
}
}

func (cfg *Config) Unmarshal(conf *confmap.Conf) error {
if conf.IsSet("loglevel") && conf.IsSet("verbosity") {
return fmt.Errorf("'loglevel' and 'verbosity' are incompatible. Use only 'verbosity' instead")
}

if err := conf.Unmarshal(cfg, confmap.WithErrorUnused()); err != nil {
return err
}

if conf.IsSet("loglevel") {
verbosity, err := mapLevel(cfg.LogLevel)
if err != nil {
return fmt.Errorf("failed to map 'loglevel': %w", err)
}

// 'verbosity' is unset but 'loglevel' is set.
// Override default verbosity.
cfg.Verbosity = verbosity
cfg.warnLogLevel = true
}

return nil
}

// Validate checks if the exporter configuration is valid
func (cfg *Config) Validate() error {
// Supported configtelemetry.Level values for this exporter.
// configtelemetry.LevelNone and future values are not supported.
supportedLevels := map[configtelemetry.Level]struct{}{
mx-psi marked this conversation as resolved.
Show resolved Hide resolved
configtelemetry.LevelBasic: {},
configtelemetry.LevelNormal: {},
configtelemetry.LevelDetailed: {},
}

if _, ok := supportedLevels[cfg.Verbosity]; !ok {
return fmt.Errorf("verbosity level %q is not supported", cfg.Verbosity)
}

return nil
}
104 changes: 92 additions & 12 deletions exporter/loggingexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"go.uber.org/zap/zapcore"

"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/confmaptest"
)
Expand All @@ -35,16 +36,95 @@ func TestUnmarshalDefaultConfig(t *testing.T) {
}

func TestUnmarshalConfig(t *testing.T) {
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NoError(t, config.UnmarshalExporter(cm, cfg))
assert.Equal(t,
&Config{
ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)),
LogLevel: zapcore.DebugLevel,
SamplingInitial: 10,
SamplingThereafter: 50,
}, cfg)
tests := []struct {
filename string
cfg *Config
expectedErr string
}{
{
filename: "config_loglevel.yaml",
cfg: &Config{
ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)),
LogLevel: zapcore.DebugLevel,
Verbosity: configtelemetry.LevelDetailed,
SamplingInitial: 10,
SamplingThereafter: 50,
warnLogLevel: true,
},
},
{
filename: "config_verbosity.yaml",
cfg: &Config{
ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)),
LogLevel: zapcore.InfoLevel,
Verbosity: configtelemetry.LevelDetailed,
SamplingInitial: 10,
SamplingThereafter: 50,
},
},
{
filename: "loglevel_info.yaml",
cfg: &Config{
ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)),
LogLevel: zapcore.InfoLevel,
Verbosity: configtelemetry.LevelNormal,
SamplingInitial: 2,
SamplingThereafter: 500,
warnLogLevel: true,
},
},
{
filename: "invalid_verbosity_loglevel.yaml",
expectedErr: "'loglevel' and 'verbosity' are incompatible. Use only 'verbosity' instead",
},
}

for _, tt := range tests {
t.Run(tt.filename, func(t *testing.T) {
cm, err := confmaptest.LoadConf(filepath.Join("testdata", tt.filename))
require.NoError(t, err)
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
err = config.UnmarshalExporter(cm, cfg)
if tt.expectedErr != "" {
assert.EqualError(t, err, tt.expectedErr)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.cfg, cfg)
}
})
}
}

func TestValidate(t *testing.T) {
tests := []struct {
name string
cfg *Config
expectedErr string
}{
{
name: "verbosity none",
cfg: &Config{
Verbosity: configtelemetry.LevelNone,
},
expectedErr: "verbosity level \"none\" is not supported",
},
{
name: "verbosity detailed",
cfg: &Config{
Verbosity: configtelemetry.LevelDetailed,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.cfg.Validate()
if tt.expectedErr != "" {
assert.EqualError(t, err, tt.expectedErr)
} else {
assert.NoError(t, err)
}
})
}
}
21 changes: 18 additions & 3 deletions exporter/loggingexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ package loggingexporter // import "go.opentelemetry.io/collector/exporter/loggin

import (
"context"
"sync"
"time"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)
Expand All @@ -34,6 +36,8 @@ const (
defaultSamplingThereafter = 500
)

var onceWarnLogLevel sync.Once

// NewFactory creates a factory for Logging exporter
func NewFactory() component.ExporterFactory {
return component.NewExporterFactory(
Expand All @@ -49,6 +53,7 @@ func createDefaultConfig() config.Exporter {
return &Config{
ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)),
LogLevel: zapcore.InfoLevel,
Verbosity: configtelemetry.LevelNormal,
SamplingInitial: defaultSamplingInitial,
SamplingThereafter: defaultSamplingThereafter,
}
Expand All @@ -57,7 +62,7 @@ func createDefaultConfig() config.Exporter {
func createTracesExporter(ctx context.Context, set component.ExporterCreateSettings, config config.Exporter) (component.TracesExporter, error) {
cfg := config.(*Config)
exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger)
s := newLoggingExporter(exporterLogger, cfg.LogLevel)
s := newLoggingExporter(exporterLogger, cfg.Verbosity)
return exporterhelper.NewTracesExporter(ctx, set, cfg,
s.pushTraces,
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
Expand All @@ -72,7 +77,7 @@ func createTracesExporter(ctx context.Context, set component.ExporterCreateSetti
func createMetricsExporter(ctx context.Context, set component.ExporterCreateSettings, config config.Exporter) (component.MetricsExporter, error) {
cfg := config.(*Config)
exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger)
s := newLoggingExporter(exporterLogger, cfg.LogLevel)
s := newLoggingExporter(exporterLogger, cfg.Verbosity)
return exporterhelper.NewMetricsExporter(ctx, set, cfg,
s.pushMetrics,
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
Expand All @@ -87,7 +92,7 @@ func createMetricsExporter(ctx context.Context, set component.ExporterCreateSett
func createLogsExporter(ctx context.Context, set component.ExporterCreateSettings, config config.Exporter) (component.LogsExporter, error) {
cfg := config.(*Config)
exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger)
s := newLoggingExporter(exporterLogger, cfg.LogLevel)
s := newLoggingExporter(exporterLogger, cfg.Verbosity)
return exporterhelper.NewLogsExporter(ctx, set, cfg,
s.pushLogs,
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
Expand All @@ -100,6 +105,16 @@ func createLogsExporter(ctx context.Context, set component.ExporterCreateSetting
}

func createLogger(cfg *Config, logger *zap.Logger) *zap.Logger {
if cfg.warnLogLevel {
onceWarnLogLevel.Do(func() {
logger.Warn(
"'loglevel' option is deprecated in favor of 'verbosity'. Set 'verbosity' to equivalent value to preserve behavior.",
zap.Stringer("loglevel", cfg.LogLevel),
zap.Stringer("equivalent verbosity level", cfg.Verbosity),
)
})
}

core := zapcore.NewSamplerWithOptions(
logger.Core(),
1*time.Second,
Expand Down
16 changes: 7 additions & 9 deletions exporter/loggingexporter/logging_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ import (
"os"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/exporter/loggingexporter/internal/otlptext"
"go.opentelemetry.io/collector/pdata/plog"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/ptrace"
)

type loggingExporter struct {
logLevel zapcore.Level
verbosity configtelemetry.Level
logger *zap.Logger
logsMarshaler plog.Marshaler
metricsMarshaler pmetric.Marshaler
Expand All @@ -38,7 +38,7 @@ type loggingExporter struct {

func (s *loggingExporter) pushTraces(_ context.Context, td ptrace.Traces) error {
s.logger.Info("TracesExporter", zap.Int("#spans", td.SpanCount()))
if s.logLevel != zapcore.DebugLevel {
if s.verbosity != configtelemetry.LevelDetailed {
return nil
}

Expand All @@ -52,8 +52,7 @@ func (s *loggingExporter) pushTraces(_ context.Context, td ptrace.Traces) error

func (s *loggingExporter) pushMetrics(_ context.Context, md pmetric.Metrics) error {
s.logger.Info("MetricsExporter", zap.Int("#metrics", md.MetricCount()))

if s.logLevel != zapcore.DebugLevel {
if s.verbosity != configtelemetry.LevelDetailed {
return nil
}

Expand All @@ -67,8 +66,7 @@ func (s *loggingExporter) pushMetrics(_ context.Context, md pmetric.Metrics) err

func (s *loggingExporter) pushLogs(_ context.Context, ld plog.Logs) error {
s.logger.Info("LogsExporter", zap.Int("#logs", ld.LogRecordCount()))

if s.logLevel != zapcore.DebugLevel {
if s.verbosity != configtelemetry.LevelDetailed {
return nil
}

Expand All @@ -80,9 +78,9 @@ func (s *loggingExporter) pushLogs(_ context.Context, ld plog.Logs) error {
return nil
}

func newLoggingExporter(logger *zap.Logger, logLevel zapcore.Level) *loggingExporter {
func newLoggingExporter(logger *zap.Logger, verbosity configtelemetry.Level) *loggingExporter {
return &loggingExporter{
logLevel: logLevel,
verbosity: verbosity,
logger: logger,
logsMarshaler: otlptext.NewTextLogsMarshaler(),
metricsMarshaler: otlptext.NewTextMetricsMarshaler(),
Expand Down
4 changes: 2 additions & 2 deletions exporter/loggingexporter/logging_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest"

"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/internal/testdata"
"go.opentelemetry.io/collector/pdata/plog"
"go.opentelemetry.io/collector/pdata/pmetric"
Expand Down Expand Up @@ -70,7 +70,7 @@ func TestLoggingLogsExporterNoErrors(t *testing.T) {
}

func TestLoggingExporterErrors(t *testing.T) {
le := newLoggingExporter(zaptest.NewLogger(t), zapcore.DebugLevel)
le := newLoggingExporter(zaptest.NewLogger(t), configtelemetry.LevelDetailed)
require.NotNil(t, le)

errWant := errors.New("my error")
Expand Down
3 changes: 3 additions & 0 deletions exporter/loggingexporter/testdata/config_verbosity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
verbosity: detailed
sampling_initial: 10
sampling_thereafter: 50
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
loglevel: info
verbosity: detailed
1 change: 1 addition & 0 deletions exporter/loggingexporter/testdata/loglevel_info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
loglevel: info