opentelemetry-collector
opentelemetry-collector copied to clipboard
[otelcol] Obtain the Collector's effective configuration from `otelcol.Config`
Description
The current ConfmapProvider interface accurately reports the config provided to the Collector by the user, but fails to effectively report the Collector's effective configuration. In particular, it misses:
- Default values for fields in Config structs.
- Transformations done to Config structs by their
UnmarshalorValidatemethods. - Custom marshaling of types after we know the type of the config. This is most obvious with
configopaque.String, where we want these values to always be redacted when sent out of the Collector.
As a result, I think we should attempt to get the Collector's effective configuration from otelcol.Config instead of using the map compiled by the confmap.Resolver. I initially intended to generate a confmap.Conf from otelcol.Config and call yaml.Marshal on that, but this encounters errors such as being unable to marshal Prometheus config which has invalid zero values. These errors don't occur when calling yaml.Marshal on otelcol.Config directly. I've updated and tested updating the ConfigWatcher interface to just accept an opaque any-typed data object and marshal that, and the end-to-end flow works. I haven't dug into this far enough to fully understand the differences between unmarshaling each type.
Two related PRs:
- https://github.com/open-telemetry/opentelemetry-collector/pull/10137
- https://github.com/open-telemetry/opentelemetry-collector/pull/10136
To demonstrate the issue I'm seeing, this will fail:
cfg, _ := col.configProvider.Get(ctx, factories)
conf := confmap.New()
_ = conf.Marshal(cfg)
// This fails
bytes, err := yaml.Marshal(conf.ToStringMap())
This will succeed:
cfg, _ := col.configProvider.Get(ctx, factories)
// This succeeds
bytes, err := yaml.Marshal(cfg)
I'm fine just letting ConfigWatcher.NotifyConfig take an opaque any-typed parameter and telling the user to just marshal it with whatever format they want, but I don't fully understand why the first one fails. The second one also appears to make us more dependent on yaml since we don't have mapstructure as a buffer (e.g. we need to add yaml:"-" to confighttp.ClientConfig for the second snippet to work).
As a result, I think we should attempt to get the Collector's effective configuration from otelcol.Config instead of using the map compiled by the confmap.Resolver. I initially intended to generate a confmap.Conf from otelcol.Config and call yaml.Marshal on that, but this encounters errors such as being unable to marshal Prometheus config which has invalid zero values. These errors don't occur when calling yaml.Marshal on otelcol.Config directly. I've updated and tested updating the ConfigWatcher interface to just accept an opaque any-typed data object and marshal that, and the end-to-end flow works. I haven't dug into this far enough to fully understand the differences between unmarshaling each type.
I think you should look into fixing marshal. /cc @atoulme
I think you should look into fixing marshal.
Thanks for the tip, I think that's also the best path forward. I found that the issue is that we don't do anything with yaml tags, which is what most third-party structs annotate on their fields. I will issue a PR to find a solution to that problem then update this PR afterward.
Codecov Report
Attention: Patch coverage is 33.33333% with 2 lines in your changes missing coverage. Please review.
Project coverage is 92.26%. Comparing base (
08b0be7) to head (a708ac8). Report is 1 commits behind head on main.
| Files | Patch % | Lines |
|---|---|---|
| otelcol/collector.go | 33.33% | 1 Missing and 1 partial :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## main #10139 +/- ##
==========================================
- Coverage 92.35% 92.26% -0.09%
==========================================
Files 395 395
Lines 18706 18703 -3
==========================================
- Hits 17275 17257 -18
- Misses 1070 1086 +16
+ Partials 361 360 -1
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
I think you should look into fixing marshal.
Thanks for the tip, I think that's also the best path forward. I found that the issue is that we don't do anything with
yamltags, which is what most third-party structs annotate on their fields. I will issue a PR to find a solution to that problem then update this PR afterward.
I have a possible solution in https://github.com/open-telemetry/opentelemetry-collector/pull/10282.
Basically it adds a hook which looks for structs that have yaml tags but no mapstructure tags. For such structs, it uses the yaml package to "natively" convert the struct into map[string]any, which can then be marshaled by mapstructure in a generic way.
This PR was marked stale due to lack of activity. It will be closed in 14 days.
It seems like the ask to:
generate a confmap.Conf from otelcol.Config and call yaml.Marshal on that
cfg, _ := col.configProvider.Get(ctx, factories)
conf := confmap.New()
_ = conf.Marshal(cfg)
// This fails
bytes, err := yaml.Marshal(conf.ToStringMap())
is now supported following:
- https://github.com/open-telemetry/opentelemetry-collector/pull/10310
- https://github.com/open-telemetry/opentelemetry-collector/pull/10282
Are there any other scenarios where the code snippet above will fail ?
@mackjmr You're right, this will work now; thanks for keeping an eye on it. I haven't been able to get back to this, but I have more time now and will push an update this week.
is this purely internal change, or can the end user print the config now?
This only affects the Collector APIs, but it is a step towards allowing the user to print the config.