diff --git a/CHANGELOG.md b/CHANGELOG.md index f977dee7a33..65a3f4887ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ release. ### Logs +- Add `Enabled` opt-in operation to the `LogRecordProcessor`. + ([#4439](https://github.com/open-telemetry/opentelemetry-specification/pull/4439)) + ### Baggage ### Resource diff --git a/spec-compliance-matrix.md b/spec-compliance-matrix.md index c67da6ed397..bf5eca600af 100644 --- a/spec-compliance-matrix.md +++ b/spec-compliance-matrix.md @@ -203,6 +203,7 @@ Disclaimer: this list of features is still a work in progress, please refer to t | SimpleLogRecordProcessor | | | + | | + | | | + | | + | | | | BatchLogRecordProcessor | | | + | | + | | | + | | + | | | | Can plug custom LogRecordProcessor | | | + | | + | | | + | | + | | | +| LogRecordProcessor.Enabled | X | + | | | | | | | | | | | | OTLP/gRPC exporter | | | + | | + | | | + | | + | + | | | OTLP/HTTP exporter | | | + | | + | | | + | | + | + | | | OTLP File exporter | | | - | | - | | | | | + | - | | diff --git a/specification/logs/sdk.md b/specification/logs/sdk.md index 971b9795ed7..0ee77311f96 100644 --- a/specification/logs/sdk.md +++ b/specification/logs/sdk.md @@ -24,6 +24,7 @@ - [LogRecordProcessor](#logrecordprocessor) * [LogRecordProcessor operations](#logrecordprocessor-operations) + [OnEmit](#onemit) + + [Enabled](#enabled-1) + [ShutDown](#shutdown) + [ForceFlush](#forceflush-1) * [Built-in processors](#built-in-processors) @@ -198,7 +199,9 @@ It consists of the following parameters: `Enabled` MUST return `false` when: - there are no registered [`LogRecordProcessors`](#logrecordprocessor), -- `Logger` is disabled ([`LoggerConfig.disabled`](#loggerconfig) is `true`). +- `Logger` is disabled ([`LoggerConfig.disabled`](#loggerconfig) is `true`), +- all registered `LogRecordProcessors` implement [`Enabled`](#enabled-1), + and a call to `Enabled` on each of them returns `false`. Otherwise, it SHOULD return `true`. It MAY return `false` to support additional optimizations and features. @@ -345,6 +348,46 @@ A `LogRecordProcessor` may freely modify `logRecord` for the duration of the `OnEmit` call. If `logRecord` is needed after `OnEmit` returns (i.e. for asynchronous processing) only reads are permitted. +#### Enabled + +**Status**: [Development](../document-status.md) + +`Enabled` is an operation that a `LogRecordProcessor` MAY implement +in order to support filtering via [`Logger.Enabled`](api.md#enabled). + +**Parameters:** + +* [Context](../context/README.md) explicitly passed by the caller or the current + Context +* [Instrumentation Scope](./data-model.md#field-instrumentationscope) associated + with the `Logger` +* [Severity Number](./data-model.md#field-severitynumber) passed by the caller + +**Returns:** `Boolean` + +An implementation should return `false` if a `LogRecord` (if ever created) +is supposed to be filtered out for the given parameters. +It should default to returning `true` for any indeterminate state, for example, +when awaiting configuration. + +Any modifications to parameters inside `Enabled` MUST NOT be propagated to the +caller. Parameters are immutable or passed by value. + +This operation is usually called synchronously, therefore it should not block +or throw exceptions. + +`LogRecordProcessor` implementations responsible for filtering and supporting +the `Enable` operation should ensure that [`OnEmit`](#onemit) handles filtering +independently. API users cannot be expected to call [`Enabled`](api.md#enabled) +before invoking [`Emit a LogRecord`](api.md#emit-a-logrecord). +Moreover, the filtering logic in `OnEmit` and `Enabled` may differ. + +`LogRecordProcessor` implementations that wrap other `LogRecordProcessor` +(which may perform filtering) can implement `Enabled` and delegate to +the wrapped processor’s `Enabled`, if available. However, the `OnEmit` +implementation of such processors should never call the wrapped processor’s +`Enabled`, as `OnEmit` is responsible for handling filtering independently. + #### ShutDown Shuts down the processor. Called when the SDK is shut down. This is an diff --git a/specification/logs/supplementary-guidelines.md b/specification/logs/supplementary-guidelines.md index 8779536d78c..67346488ad6 100644 --- a/specification/logs/supplementary-guidelines.md +++ b/specification/logs/supplementary-guidelines.md @@ -203,6 +203,19 @@ func (p *SeverityProcessor) OnEmit(ctx context.Context, record *sdklog.Record) e } return p.Processor.OnEmit(ctx, record) } + +// Enabled returns false if the severity is lower than p.Min. +func (p *SeverityProcessor) Enabled(ctx context.Context, param sdklog.EnabledParameters) bool { + sev := param.Severity + if sev != log.SeverityUndefined && sev < p.Min { + return false + } + if fp, ok := p.Processor.(sdklog.FilterProcessor); ok { + // The wrapped processor is also a filtering processor. + return p.Processor.Enabled(ctx, param) + } + return true +} ``` > [!NOTE] @@ -242,6 +255,29 @@ func (p *IsolatedProcessor) OnEmit(ctx context.Context, record *log.Record) erro } return rErr } + +// Enabled honors Enabled of the wrapped processors. +func (p *IsolatedProcessor) Enabled(ctx context.Context, param sdklog.EnabledParameters) bool { + fltrProcessors := make([]sdklog.FilterProcessor, len(p.Processors)) + for i, proc := range p.Processors { + fp, ok := proc.(sdklog.FilterProcessor) + if !ok { + // Processor not implementing Enabled. + // We assume it will be processed. + return true + } + fltrProcessors[i] = fp + } + + for _, proc := range fltrProcessors { + if proc.Enabled(ctx, param) { + // At least one Processor will process the Record. + return true + } + } + // No processor will process the record. + return false +} ``` #### Routing @@ -271,6 +307,29 @@ func (p *LogEventRouteProcessor) OnEmit(ctx context.Context, record *log.Record) } return p.LogProcessor.OnEmit(ctx, record) } + +// Enabled honors Enabled of the wrapped processors. +func (p *LogEventRouteProcessor) Enabled(ctx context.Context, param sdklog.EnabledParameters) bool { + fp1, ok := p.EventProcessor.(sdklog.FilterProcessor) + if !ok { + // Processor not implementing Enabled. + return true + } + fp2, ok := p.LogProcessor.(sdklog.FilterProcessor) + if !ok { + // Processor not implementing Enabled. + return true + } + + if fp1.Enabled(ctx, param) { + return true + } + if fp2.Enabled(ctx, param) { + return true + } + // No processor will process the record. + return false +} ``` #### Setup