Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a695b6b
Print command with redaction support
jmacd Jun 28, 2025
a8f51b0
naming
jmacd Jun 28, 2025
e7d56a8
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Aug 11, 2025
fd712ba
mode behavior
jmacd Aug 12, 2025
59862c3
wip
jmacd Aug 19, 2025
fd51709
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Aug 19, 2025
37c96e5
tidy
jmacd Aug 19, 2025
aa9f100
cleanup
jmacd Aug 20, 2025
e4f7784
ok ok
jmacd Aug 20, 2025
7cb165b
wip
jmacd Aug 20, 2025
e51e5a6
doc
jmacd Aug 20, 2025
4b4018e
lint
jmacd Aug 20, 2025
b1c4a51
linter
jmacd Aug 20, 2025
68fbede
issue
jmacd Aug 20, 2025
23e6030
spelling
jmacd Aug 20, 2025
e22d731
crosslink/tidy
jmacd Aug 20, 2025
b33d1b3
empty
jmacd Aug 20, 2025
94ce86c
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Aug 20, 2025
ab3b122
Merge branch 'main' into jmacd/print_command
jmacd Aug 22, 2025
44d9a20
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Aug 22, 2025
83c8d86
Merge branch 'jmacd/print_command' of github.com:jmacd/opentelemetry-…
jmacd Aug 22, 2025
2a6a23e
well done
jmacd Aug 22, 2025
bf81c9d
test featuregate)
jmacd Aug 22, 2025
e6bf157
lint
jmacd Aug 22, 2025
860c008
fmt
jmacd Aug 22, 2025
7f553c9
Merge branch 'main' into jmacd/print_command
codeboten Aug 22, 2025
e67e61a
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Aug 28, 2025
2d514ab
remove stderr, add helper
jmacd Aug 28, 2025
0ebb858
print
jmacd Aug 28, 2025
6d297c9
Merge branch 'jmacd/print_command' of github.com:jmacd/opentelemetry-…
jmacd Aug 28, 2025
99faebd
thanks bogdan
jmacd Aug 28, 2025
2366866
Merge remote-tracking branch 'upstream/main' into jmacd/print_command
jmacd Sep 8, 2025
8a1586b
gotidy
jmacd Sep 8, 2025
f64eb0d
more chlog
jmacd Sep 8, 2025
3766a77
use evans version
jmacd Sep 8, 2025
3f572f1
breaking
jmacd Sep 9, 2025
e608d0e
Update otelcol/command_print.go
jmacd Sep 9, 2025
9c6c835
rm binary
jmacd Sep 9, 2025
418bffd
Update service/README.md
jmacd Sep 9, 2025
96b7040
Merge branch 'jmacd/print_command' of github.com:jmacd/opentelemetry-…
jmacd Sep 9, 2025
ac45777
crosslink
jmacd Sep 9, 2025
44af337
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
jmacd Sep 9, 2025
1fe7c32
Merge branch 'main' into jmacd/print_command
jmacd Sep 9, 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
25 changes: 25 additions & 0 deletions .chloggen/print-config-redaction.yaml
Original file line number Diff line number Diff line change
@@ -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: breaking

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

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add unified print-config command with mode support (redacted, unredacted), json support (unstable), and validation support.

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

# (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: This replaces the `print-initial-config` command. See the `service` package README for more details. The original command name `print-initial-config` remains an alias, to be retired with the feature flag.

# 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: [user]
1 change: 1 addition & 0 deletions .github/workflows/utils/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@
"unmarshal",
"unmarshalling",
"unmarshalls",
"unredacted",
"unshallow",
"unstarted",
"userfriendly",
Expand Down
2 changes: 0 additions & 2 deletions cmd/mdatagen/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension

replace go.opentelemetry.io/collector/service/hostcapabilities => ../../service/hostcapabilities

replace go.opentelemetry.io/collector/confmap/provider/yamlprovider => ../../confmap/provider/yamlprovider

replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls

replace go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression
Expand Down
2 changes: 2 additions & 0 deletions otelcol/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ func (s State) String() string {
// CollectorSettings holds configuration for creating a new Collector.
type CollectorSettings struct {
// Factories service factories.
// TODO(13263) This is a dangerous "bare" function value, should define an interface
// following style guidelines.
Factories func() (Factories, error)

// BuildInfo provides collector start information.
Expand Down
195 changes: 168 additions & 27 deletions otelcol/command_print.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,196 @@
package otelcol // import "go.opentelemetry.io/collector/otelcol"

import (
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"strings"

"github.com/spf13/cobra"
yaml "go.yaml.in/yaml/v3"

"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/featuregate"
)

const featureGateName = "otelcol.printInitialConfig"

var printCommandFeatureFlag = featuregate.GlobalRegistry().MustRegister(
"otelcol.printInitialConfig",
featureGateName,
featuregate.StageAlpha,
featuregate.WithRegisterFromVersion("v0.120.0"),
featuregate.WithRegisterDescription("if set to true, turns on the print-initial-config command"),
featuregate.WithRegisterDescription("if set to true, enable the print-config command"),
)

// newConfigPrintSubCommand constructs a new config print sub command using the given CollectorSettings.
// newConfigPrintSubCommand constructs a new print-config command using the given CollectorSettings.
func newConfigPrintSubCommand(set CollectorSettings, flagSet *flag.FlagSet) *cobra.Command {
var outputFormat string
var mode string
var validate bool

cmd := &cobra.Command{
Use: "print-initial-config",
Short: "Prints the Collector's configuration in YAML format after all config sources are resolved and merged",
Long: `Note: This command prints the final yaml configuration before it is unmarshaled into config structs, which may contain sensitive values.`,
Args: cobra.ExactArgs(0),
Use: "print-config",
Aliases: []string{"print-initial-config"},
Short: "Prints the Collector's configuration in the specified mode",
Long: `Prints the Collector's configuration with different levels of processing:

- redacted: Shows the resolved configuration with sensitive data redacted (default)
- unredacted: Shows the resolved configuration with all sensitive data visible

The output prints in YAML by default. To print JSON use --format=json,
however this is considered unstable.

Validation is enabled by default, as a safety measure.

All modes are experimental, requiring the otelcol.printInitialConfig feature gate.`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, _ []string) error {
if !printCommandFeatureFlag.IsEnabled() {
return errors.New("print-initial-config is currently experimental, use the otelcol.printInitialConfig feature gate to enable this command")
}
err := updateSettingsUsingFlags(&set, flagSet)
if err != nil {
return err
pc := printContext{
cmd: cmd,
stdout: cmd.OutOrStdout(),
set: set,
outputFormat: outputFormat,
validate: validate,
}
resolver, err := confmap.NewResolver(set.ConfigProviderSettings.ResolverSettings)
if err != nil {
return fmt.Errorf("failed to create new resolver: %w", err)
}
conf, err := resolver.Resolve(cmd.Context())
if err != nil {
return fmt.Errorf("error while resolving config: %w", err)
}
b, err := yaml.Marshal(conf.ToStringMap())
if err != nil {
return fmt.Errorf("error while marshaling to YAML: %w", err)
}
fmt.Printf("%s\n", b)
return nil
return pc.configPrintSubCommand(flagSet, mode)
},
}

formatHelp := "Output format: yaml (default), json (unstable))"
cmd.Flags().StringVar(&outputFormat, "format", "yaml", formatHelp)

modeHelp := "Operating mode: redacted (default), unredacted"
cmd.Flags().StringVar(&mode, "mode", "redacted", modeHelp)

validateHelp := "Validation mode: true (default), false"
cmd.Flags().BoolVar(&validate, "validate", true, validateHelp)

cmd.Flags().AddGoFlagSet(flagSet)
return cmd
}

type printContext struct {
cmd *cobra.Command
stdout io.Writer
set CollectorSettings
outputFormat string
validate bool
}

func (pctx *printContext) configPrintSubCommand(flagSet *flag.FlagSet, mode string) error {
if !printCommandFeatureFlag.IsEnabled() {
return errors.New("print-config is currently experimental, use the otelcol.printInitialConfig feature gate to enable this command")
}
err := updateSettingsUsingFlags(&pctx.set, flagSet)
if err != nil {
return err
}

switch strings.ToLower(mode) {
case "redacted":
return pctx.printRedactedConfig()
case "unredacted":
return pctx.printUnredactedConfig()
default:
return fmt.Errorf("invalid mode %q: modes are: redacted, unredacted", mode)
}
}

// printConfigData formats and prints configuration data in yaml or json format.
func (pctx *printContext) printConfigData(data map[string]any) error {
format := pctx.outputFormat
if format == "" {
format = "yaml"
}

switch {
case strings.EqualFold(format, "yaml"):
b, err := yaml.Marshal(data)
if err != nil {
return err
}
fmt.Fprintf(pctx.stdout, "%s\n", b)
return nil

case strings.EqualFold(format, "json"):
encoder := json.NewEncoder(pctx.stdout)
encoder.SetIndent("", " ")
return encoder.Encode(data)
}

return fmt.Errorf("unrecognized print format: %s", format)
}

func (pctx *printContext) getPrintableConfig() (any, error) {
var factories Factories
if pctx.set.Factories != nil {
var err error
factories, err = pctx.set.Factories()
if err != nil {
return nil, fmt.Errorf("failed to get factories: %w", err)
}
}

configProvider, err := NewConfigProvider(pctx.set.ConfigProviderSettings)
if err != nil {
return nil, fmt.Errorf("failed to create config provider: %w", err)
}

cfg, err := configProvider.Get(pctx.cmd.Context(), factories)
if err != nil {
return nil, fmt.Errorf("failed to get config: %w", err)
}
return cfg, nil
}

// printUnredactedConfig prints resolved configuration before interpreting
// with the intended types for each component, thus it shows the full
// configuration without considering configuopaque. Use with caution.
func (pctx *printContext) printUnredactedConfig() error {
if pctx.validate {
// Validation serves prevent revealing invalid data.
cfg, err := pctx.getPrintableConfig()
if err != nil {
return err
}
if err = xconfmap.Validate(cfg); err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}

// Note: we discard the validated configuration.
}
resolver, err := confmap.NewResolver(pctx.set.ConfigProviderSettings.ResolverSettings)
if err != nil {
return fmt.Errorf("failed to create new resolver: %w", err)
}
conf, err := resolver.Resolve(pctx.cmd.Context())
if err != nil {
return fmt.Errorf("error while resolving config: %w", err)
}
return pctx.printConfigData(conf.ToStringMap())
}

// printRedactedConfig prints resolved configuration with its assigned
// types, but without validation. Notably, configopaque strings are printed
// as "[redacted]". This is the default.
func (pctx *printContext) printRedactedConfig() error {
cfg, err := pctx.getPrintableConfig()
if err != nil {
return err
}

if pctx.validate {
if err = xconfmap.Validate(cfg); err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}
}

confMap := confmap.New()
if err := confMap.Marshal(cfg); err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
return pctx.printConfigData(confMap.ToStringMap())
}
Loading