Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d1b8a24
add ability to set output_paths
ishaish103 Nov 15, 2025
fbabe6d
Update exporter/debugexporter/README.md
ishaish103 Nov 17, 2025
374c202
fix pr comments
ishaish103 Nov 17, 2025
1e9b18b
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 17, 2025
d8feeba
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 18, 2025
5a3232b
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 19, 2025
b4a6b0e
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 22, 2025
c08384d
add tests
ishaish103 Nov 25, 2025
30c8ec3
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
ishaish103 Nov 25, 2025
4b136fd
Merge branch '10472-debug-exporter-output-paths-ishai' of github.com:…
ishaish103 Nov 25, 2025
8bb3be0
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 25, 2025
d725b74
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Nov 30, 2025
c3ee29e
Address PR review comments: add validation and remove redundant fallback
ishaish103 Dec 3, 2025
fc1885f
Resolve merge conflicts with upstream/main
ishaish103 Dec 3, 2025
15349f4
Fix formatting and regenerate code
ishaish103 Dec 3, 2025
071b0e3
Add Makefile to local module and update generated files
ishaish103 Dec 3, 2025
22f3245
format
ishaish103 Dec 3, 2025
3908483
Fix linter errors
ishaish103 Dec 3, 2025
58b0cad
Fix Windows file handle cleanup in tests
ishaish103 Dec 3, 2025
a88eacb
Fix remaining linter error: use require for error assertion
ishaish103 Dec 3, 2025
a2ee4d0
Exclude local module from test runs
ishaish103 Dec 3, 2025
890e9ba
trigger ci
ishaish103 Dec 3, 2025
e2d1942
make sure to delete file
ishaish103 Dec 3, 2025
a48ff56
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Dec 5, 2025
1c1353a
remove makefile commit
ishaish103 Dec 5, 2025
709a284
Merge branch '10472-debug-exporter-output-paths-ishai' of github.com:…
ishaish103 Dec 5, 2025
b8433b6
add default
ishaish103 Dec 5, 2025
a288646
remove validation
ishaish103 Dec 6, 2025
743e1db
remove validation
ishaish103 Dec 6, 2025
72905b9
fix test
ishaish103 Dec 8, 2025
5b31428
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Dec 9, 2025
435c097
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Dec 10, 2025
be1255f
Merge branch 'main' into 10472-debug-exporter-output-paths-ishai
ishaish103 Dec 12, 2025
2e01494
improve lock to avoid failing tests
ishaish103 Dec 14, 2025
5038a92
Merge branch '10472-debug-exporter-output-paths-ishai' of github.com:…
ishaish103 Dec 14, 2025
1725f7e
fix cleanup
ishaish103 Dec 14, 2025
83fe581
handle windows cleanup
ishaish103 Dec 14, 2025
25e0e95
windows cleanup fix
ishaish103 Dec 14, 2025
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
29 changes: 29 additions & 0 deletions .chloggen/debug-exporter-output-paths.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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: exporter/debug

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add `output_paths` configuration option to control output destination when `use_internal_logger` is false

# One or more tracking issues or pull requests related to the change
issues: [10472]

# (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: |
When `use_internal_logger` is set to `false`, the debug exporter now supports configuring the output destination via the `output_paths` option.
This allows users to send debug exporter output to `stdout`, `stderr`, or a file path.
The default value is `["stdout"]` to maintain backward compatibility.

# 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: []

1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ALL_MODULES := $(shell find . -mindepth 2 \
-type f \
-name "go.mod" \
-not -path "./internal/tools/*" \
-not -path "./local/*" \
-exec dirname {} \; | sort )

CMD?=
Expand Down
14 changes: 13 additions & 1 deletion exporter/debugexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The following settings are optional:
Refer to [Zap docs](https://godoc.org/go.uber.org/zap/zapcore#NewSampler) for more details
on how sampling parameters impact number of messages.
- `use_internal_logger` (default = `true`): uses the collector's internal logger for output. See [below](#using-the-collectors-internal-logger) for description.
- `output_paths` (default = `["stdout"]`): a list of file paths to write logging output to. This option is only used when `use_internal_logger` is `false`. Special strings "stdout" and "stderr" are interpreted as os.Stdout and os.Stderr respectively. All other values are treated as file paths.
- `sending_queue` (disabled by default): see [Sending Queue](../exporterhelper/README.md#sending-queue) for the full set of available options.

Example configuration:
Expand All @@ -50,6 +51,16 @@ exporters:
sampling_thereafter: 200
```

Example configuration with custom output path:

```yaml
exporters:
debug:
use_internal_logger: false
output_paths:
- stderr
```

## Verbosity levels

The following subsections describe the output from the exporter depending on the configured verbosity level - `basic`, `normal` and `detailed`.
Expand Down Expand Up @@ -138,7 +149,8 @@ This comes with the following consequences:

When `use_internal_logger` is set to `false`, the exporter does not use the collector's internal logger.
Changing the values in `service::telemetry::logs` has no effect on the exporter's output.
The exporter's output is sent to `stdout`.
The exporter's output is sent to the paths specified in `output_paths` (default: `["stdout"]`).
You can configure `output_paths` to send output to `stderr`, a file, or multiple destinations.

[internal_telemetry]: https://opentelemetry.io/docs/collector/internal-telemetry/
[internal_logs_config]: https://opentelemetry.io/docs/collector/internal-telemetry/#configure-internal-logs
Expand Down
18 changes: 18 additions & 0 deletions exporter/debugexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package debugexporter // import "go.opentelemetry.io/collector/exporter/debugexporter"

import (
"errors"
"fmt"

"go.opentelemetry.io/collector/component"
Expand Down Expand Up @@ -34,6 +35,13 @@ type Config struct {
// UseInternalLogger defines whether the exporter sends the output to the collector's internal logger.
UseInternalLogger bool `mapstructure:"use_internal_logger"`

// OutputPaths is a list of file paths to write logging output to.
// This option is only used when use_internal_logger is false.
// Special strings "stdout" and "stderr" are interpreted as os.Stdout and os.Stderr respectively.
// All other values are treated as file paths.
// (default = ["stdout"])
OutputPaths []string `mapstructure:"output_paths"`

QueueConfig configoptional.Optional[exporterhelper.QueueBatchConfig] `mapstructure:"sending_queue"`

// prevent unkeyed literal initialization
Expand All @@ -48,5 +56,15 @@ func (cfg *Config) Validate() error {
return fmt.Errorf("verbosity level %q is not supported", cfg.Verbosity)
}

// If use_internal_logger is false, output_paths must be specified and non-empty
if !cfg.UseInternalLogger && len(cfg.OutputPaths) == 0 {
return errors.New("output_paths must be specified and non-empty when use_internal_logger is false")
}

// If use_internal_logger is true, output_paths should not be specified (it's ignored)
if cfg.UseInternalLogger && len(cfg.OutputPaths) > 0 {
return errors.New("output_paths should not be specified when use_internal_logger is true")
}

return nil
}
75 changes: 68 additions & 7 deletions exporter/debugexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func TestUnmarshalDefaultConfig(t *testing.T) {
}

func TestUnmarshalConfig(t *testing.T) {
queueCfg := exporterhelper.NewDefaultQueueConfig()
tests := []struct {
filename string
cfg *Config
Expand All @@ -37,9 +36,26 @@ func TestUnmarshalConfig(t *testing.T) {
Verbosity: configtelemetry.LevelDetailed,
SamplingInitial: 10,
SamplingThereafter: 50,
QueueConfig: configoptional.Default(queueCfg),
UseInternalLogger: false,
OutputPaths: []string{"stdout"},
QueueConfig: configoptional.None[exporterhelper.QueueBatchConfig](),
},
},
{
filename: "config_output_paths.yaml",
cfg: &Config{
Verbosity: configtelemetry.LevelBasic,
SamplingInitial: 2,
SamplingThereafter: 1,
UseInternalLogger: false,
OutputPaths: []string{"stderr"},
QueueConfig: configoptional.None[exporterhelper.QueueBatchConfig](),
},
},
{
filename: "config_output_paths_empty.yaml",
expectedErr: "output_paths must be specified and non-empty when use_internal_logger is false",
},
{
filename: "config_verbosity_typo.yaml",
expectedErr: "'' has invalid keys: verBosity",
Expand All @@ -53,10 +69,24 @@ func TestUnmarshalConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
err = cm.Unmarshal(&cfg)
if tt.expectedErr != "" {
assert.ErrorContains(t, err, tt.expectedErr)
} else {
if err != nil {
if tt.expectedErr != "" {
require.ErrorContains(t, err, tt.expectedErr)
} else {
require.NoError(t, err)
}
return
}
// Validate the config (validation errors are separate from unmarshal errors)
if cfgCasted, ok := cfg.(*Config); ok {
err = cfgCasted.Validate()
if tt.expectedErr != "" {
assert.ErrorContains(t, err, tt.expectedErr)
return
}
require.NoError(t, err)
}
if tt.expectedErr == "" {
assert.Equal(t, tt.cfg, cfg)
}
})
Expand Down Expand Up @@ -122,7 +152,38 @@ func TestValidate(t *testing.T) {
{
name: "verbosity detailed",
cfg: &Config{
Verbosity: configtelemetry.LevelDetailed,
Verbosity: configtelemetry.LevelDetailed,
UseInternalLogger: true, // Default behavior
},
},
{
name: "empty output_paths when use_internal_logger is false",
cfg: &Config{
UseInternalLogger: false,
OutputPaths: []string{},
},
expectedErr: "output_paths must be specified and non-empty when use_internal_logger is false",
},
{
name: "valid output_paths when use_internal_logger is false",
cfg: &Config{
UseInternalLogger: false,
OutputPaths: []string{"stdout"},
},
},
{
name: "output_paths specified when use_internal_logger is true (error)",
cfg: &Config{
UseInternalLogger: true,
OutputPaths: []string{"stdout"},
},
expectedErr: "output_paths should not be specified when use_internal_logger is true",
},
{
name: "empty output_paths when use_internal_logger is true (allowed)",
cfg: &Config{
UseInternalLogger: true,
OutputPaths: []string{},
},
},
}
Expand All @@ -131,7 +192,7 @@ func TestValidate(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
err := tt.cfg.Validate()
if tt.expectedErr != "" {
assert.EqualError(t, err, tt.expectedErr)
assert.ErrorContains(t, err, tt.expectedErr)
} else {
assert.NoError(t, err)
}
Expand Down
12 changes: 12 additions & 0 deletions exporter/debugexporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ func createTestCases() []testCase {
return cfg
}(),
},
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test case for when user sets output_paths but does not set use_internal_logger to false.

What should happen then? Shouldn't such config be invalid?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically the same thing:
throw an error if out_paths is null and set_intenral_logger is false

name: "custom output paths",
config: func() *Config {
cfg := createDefaultConfig().(*Config)
queueCfg := exporterhelper.NewDefaultQueueConfig()
queueCfg.QueueSize = 10
cfg.QueueConfig = configoptional.Some(queueCfg)
cfg.UseInternalLogger = false
cfg.OutputPaths = []string{"stderr"}
return cfg
}(),
},
}
}

Expand Down
6 changes: 3 additions & 3 deletions exporter/debugexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ func createDefaultConfig() component.Config {
SamplingInitial: defaultSamplingInitial,
SamplingThereafter: defaultSamplingThereafter,
UseInternalLogger: true,
QueueConfig: configoptional.Default(exporterhelper.NewDefaultQueueConfig()),
OutputPaths: []string{},
QueueConfig: configoptional.None[exporterhelper.QueueBatchConfig](),
}
}

Expand Down Expand Up @@ -135,8 +136,7 @@ func createCustomLogger(exporterConfig *Config) *zap.Logger {
},
Encoding: "console",
EncoderConfig: encoderConfig,
// Send exporter's output to stdout. This should be made configurable.
OutputPaths: []string{"stdout"},
OutputPaths: exporterConfig.OutputPaths,
}
return zap.Must(zapConfig.Build())
}
Loading
Loading