fix(sdk-node): fail-fast on LoggerProvider creation from config#6785
Conversation
This PR includes a few changes to declarative config "create()" handling in startNodeSDK(). I've started with just the config handling of `logger_provider` to have a smaller start and smaller diff for review. First, this *starts* a move to "fail fast" semantics: If there is a problem creating SDK components from the parsed config, then `create()` throws, and startNodeSDK() warns and returns a no-op SDK. This matches OTel Java behaviour and the spec. From https://opentelemetry.io/docs/specs/otel/configuration/sdk/#create > This SHOULD return an error if it encounters an error in configuration (i.e. fail fast) in accordance with initialization error handling principles. Second, I'm proposing a naming pattern: create<SDK thing>FromConfig() # e.g., createLogRecordExporterFromConfig for the suite of functions that create an SDK thing from configuration data. "create" because these are all used by the SDK configuration "create()" step (https://opentelemetry.io/docs/specs/otel/configuration/sdk/#create). "FromConfig" because it is shorter than "FromConfiguration", and these functions don't always take a full `ConfigurationModel`. Currently we have these patterns: get<SDK thing> # getLogRecordExporter get<SDK thing>FromConfiguration # getSpanProcessorsFromConfiguration build<SDK thing>FromConfig # buildSamplerFromConfig Third, this fixes missing handling of `logger_provider.limits`, which were previously not being passed to the new LoggerProvider(...). Last, I've added a `checkConfigUse(...)` internal function that can help warn about unhandled config properties in `create<SDK thing>FromConfig()` functions. It is pretty low tech. With this config: ```yaml logger_provider: logger_configurator/development: default_config: enabled: true processors: - simple: exporter: console: ``` Before: this would silently ignore "logger_configurator/development". After: it diag.warn's ``` Config warning: some specified LoggerProvider configuration properties were not handled by SDK setup: ["logger_configurator/development"] ```
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6785 +/- ##
==========================================
- Coverage 94.97% 94.93% -0.04%
==========================================
Files 383 383
Lines 12968 13007 +39
Branches 2961 2974 +13
==========================================
+ Hits 12316 12348 +32
- Misses 652 659 +7
🚀 New features to boost your workflow:
|
Arguably this could be made to throw per the "fail fast" policy. |
| readers: meterReaders, | ||
| views: meterViews ?? [], | ||
| }); | ||
| components.meterProvider = meterProvider; |
There was a problem hiding this comment.
to confirm (as per your PR description), do you plan on opening following PRs to update here to createMeterProviderFromConfig and below a createTracerProviderFromConfig?
if your plan is to do this right after, then fine, otherwise make sure you open an issue so is not forgotten :)
There was a problem hiding this comment.
Yes, definitely. I wanted to start with just one in case discussion/review suggested significant changes.
|
|
||
| export interface SDKComponents { | ||
| contextManager: ContextManager; | ||
| contextManager?: ContextManager; |
There was a problem hiding this comment.
on the existing sdk start parameters (NodeSDKConfiguration) the contextManager is required, so I followed that. Any reason why now should be optional?
There was a problem hiding this comment.
Making all the SDKComponents properties optional was necessary (without some coding pain) to support this change where components = {} is created outside the try { ... } so it can be used inside the catch { ... }:
const components: SDKComponents = {};
try {
components.contextManager = new AsyncLocalStorageContextManager();
components.contextManager.enable();
...
} catch (err) {
// ... use `components` here for cleanup
}Interface SDKComponents is only used for the return type of the internal create() function, so there is no breaking change for the user here.
MikeGoldsmith
left a comment
There was a problem hiding this comment.
Looking good, thanks @trentm.
| return new BatchLogRecordProcessor(exporter, { | ||
| maxQueueSize: props.max_queue_size ?? undefined, | ||
| maxExportBatchSize: props.max_export_batch_size ?? undefined, | ||
| scheduledDelayMillis: props.schedule_delay ?? undefined, |
There was a problem hiding this comment.
We've lost the default of 1000ms here.
| * configuration during `create()` and watches for untouched properties | ||
| * might be nice.) | ||
| */ | ||
| function checkConfigUse( |
There was a problem hiding this comment.
Should this also throw like the mustSingleEntry func?
There was a problem hiding this comment.
Thanks for the review. I went back and forth on this and settled on not throwing, at least for now.
Say a future v1.2.0 of the configuration spec allows a new property for attribute_limits, and assuming the guard on exact file_format values changes to allow future v1.x versions. Do we want declarative config file handing to completely fail if that new attribute limit isn't handled yet by the current sdk-node package? I realize this is theoretical because the current configuration handling doesn't do semver-based handling of the file_format.
Happy to change to throwing here.
Remove the EXPERIMENTAL_SAMPLER_KEYS list and the dedicated warn branch. Unrecognized sampler types (including known experimentals like jaeger_remote/development) now fall through to a single diag.warn + ParentBased(AlwaysOn) default. Saves a maintenance touch point against the upstream schema and aligns with open-telemetry#6785's direction.
This PR includes a few changes to declarative config "create()" handling in startNodeSDK().
I've started with just the config handling of
logger_providerto have a smaller start and smaller diff for review.create()throws, and startNodeSDK() warns and returns a no-op SDK. This matches OTel Java behaviour and the spec. From https://opentelemetry.io/docs/specs/otel/configuration/sdk/#createfor the suite of functions that create an SDK thing from configuration data.
"create" because these are all used by the SDK configuration "create()" step (https://opentelemetry.io/docs/specs/otel/configuration/sdk/#create).
"FromConfig" because it is shorter than "FromConfiguration", and these functions don't always take a full
ConfigurationModel.Currently we have these patterns:
This fixes missing handling of
logger_provider.limits, which were previously not being passed to the new LoggerProvider(...).I've added a
checkConfigUse(...)internal function that can help warn about unhandled config properties increate<SDK thing>FromConfig()functions. It is pretty low tech.With this config:
Before: this would silently ignore "logger_configurator/development".
After: it diag.warn's