-
Notifications
You must be signed in to change notification settings - Fork 602
docs: logs enrichment #3266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
cijothomas
merged 19 commits into
open-telemetry:main
from
stefanobaghino:stefanobaghino/2759-logs-enrichment-example
Dec 5, 2025
+160
−1
Merged
docs: logs enrichment #3266
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
b40bd10
First draft of logs enrichment example
stefanobaghino 565d640
Add required feature flag https://github.com/open-telemetry/opentelem…
stefanobaghino 0f3545a
Remove unnecessary debug print
stefanobaghino 542786d
Remove unnecessary comment
stefanobaghino a446807
Run formatter https://github.com/open-telemetry/opentelemetry-rust/pu…
stefanobaghino dfc718a
`logs-enrichment` -> `logs-advanced` https://github.com/open-telemetr…
stefanobaghino e23c21b
Highlight performance reason to implement `event_enabled` https://git…
stefanobaghino 757a802
`SlowEnrichmentLogProcessor` -> `EnrichmentLogProcessor` https://gith…
stefanobaghino efc20c6
Address linting errors
stefanobaghino 50dba6d
Remove unnecessary artificial slowness https://github.com/open-teleme…
stefanobaghino 3c7623f
Remove filtering log processor https://github.com/open-telemetry/open…
stefanobaghino 90b9752
Addresses https://github.com/open-telemetry/opentelemetry-rust/pull/3…
stefanobaghino 28f3cec
Addresses https://github.com/open-telemetry/opentelemetry-rust/pull/3…
stefanobaghino e95656e
Addresses https://github.com/open-telemetry/opentelemetry-rust/pull/3…
stefanobaghino 00c0897
Delegate all trait methods https://github.com/open-telemetry/opentele…
stefanobaghino 8da1410
Document `EnrichmentLogProcessor` more comprehensively https://github…
stefanobaghino 013c15f
Merge remote-tracking branch 'upstream/main' into stefanobaghino/2759…
stefanobaghino 4ee58a3
Remove unnecessary feature flag https://github.com/open-telemetry/ope…
stefanobaghino 42d0340
Remove spurious dash in `README.md`
stefanobaghino File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| [package] | ||
| name = "logs-advanced" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| license = "Apache-2.0" | ||
| rust-version = "1.75.0" | ||
| publish = false | ||
| autobenches = false | ||
|
|
||
| [[bin]] | ||
| name = "logs-advanced" | ||
| path = "src/main.rs" | ||
| bench = false | ||
|
|
||
| [dependencies] | ||
| opentelemetry_sdk = { workspace = true } | ||
| opentelemetry-stdout = { workspace = true, features = ["logs"] } | ||
| opentelemetry-appender-tracing = { workspace = true } | ||
| tracing = { workspace = true, features = ["std"]} | ||
| tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std", "fmt"] } | ||
| opentelemetry = { workspace = true } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # OpenTelemetry Log Processor Implementation and Composition - Example | ||
|
|
||
| This example builds on top of the `logs-basic`, showing how to implement `LogProcessor`s correctly. | ||
|
|
||
| The `EnrichmentProcessor` simulates a processor adding information | ||
| to the log captured by the OpenTelemetry SDK, which correctly ensures that the | ||
| downstream processor's filtering is captured, avoiding unnecessary work. | ||
|
|
||
| ## Usage | ||
|
|
||
| ```shell | ||
| cargo run | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| use opentelemetry::logs::{LogRecord, Severity}; | ||
| use opentelemetry::InstrumentationScope; | ||
| use opentelemetry_appender_tracing::layer; | ||
| use opentelemetry_sdk::error::OTelSdkResult; | ||
| use opentelemetry_sdk::logs::{LogProcessor, SdkLogRecord, SdkLoggerProvider, SimpleLogProcessor}; | ||
| use opentelemetry_sdk::Resource; | ||
| use tracing::{error, info}; | ||
| use tracing_subscriber::{prelude::*, EnvFilter}; | ||
|
|
||
| fn main() { | ||
| let exporter = opentelemetry_stdout::LogExporter::default(); | ||
| let enriching_processor = EnrichmentLogProcessor::new(SimpleLogProcessor::new(exporter)); | ||
| let provider: SdkLoggerProvider = SdkLoggerProvider::builder() | ||
| .with_resource( | ||
| Resource::builder() | ||
| .with_service_name("log-appender-tracing-example") | ||
| .build(), | ||
| ) | ||
| .with_log_processor(enriching_processor) | ||
| .build(); | ||
|
|
||
| // To prevent a telemetry-induced-telemetry loop, OpenTelemetry's own internal | ||
| // logging is properly suppressed. However, logs emitted by external components | ||
| // (such as reqwest, tonic, etc.) are not suppressed as they do not propagate | ||
| // OpenTelemetry context. Until this issue is addressed | ||
| // (https://github.com/open-telemetry/opentelemetry-rust/issues/2877), | ||
| // filtering like this is the best way to suppress such logs. | ||
| // | ||
| // The filter levels are set as follows: | ||
| // - Allow `info` level and above by default. | ||
| // - Completely restrict logs from `hyper`, `tonic`, `h2`, and `reqwest`. | ||
| // | ||
| // Note: This filtering will also drop logs from these components even when | ||
| // they are used outside of the OTLP Exporter. | ||
| let filter_otel = EnvFilter::new("info") | ||
| .add_directive("hyper=off".parse().unwrap()) | ||
| .add_directive("tonic=off".parse().unwrap()) | ||
| .add_directive("h2=off".parse().unwrap()) | ||
| .add_directive("reqwest=off".parse().unwrap()); | ||
| let otel_layer = layer::OpenTelemetryTracingBridge::new(&provider).with_filter(filter_otel); | ||
|
|
||
| // Create a new tracing::Fmt layer to print the logs to stdout. It has a | ||
| // default filter of `info` level and above, and `debug` and above for logs | ||
| // from OpenTelemetry crates. The filter levels can be customized as needed. | ||
| let filter_fmt = EnvFilter::new("error").add_directive("opentelemetry=debug".parse().unwrap()); | ||
| let fmt_layer = tracing_subscriber::fmt::layer() | ||
| .with_thread_names(true) | ||
| .with_filter(filter_fmt); | ||
|
|
||
| tracing_subscriber::registry() | ||
| .with(otel_layer) | ||
| .with(fmt_layer) | ||
| .init(); | ||
|
|
||
| info!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "[email protected]", message = "This is an example message"); | ||
| error!(name: "my-event-name", target: "my-system", event_id = 50, user_name = "otel", user_email = "[email protected]", message = "This is an example message"); | ||
| let _ = provider.shutdown(); | ||
| } | ||
|
|
||
| /// A log processor that enriches log records with additional attributes before | ||
| /// delegating to an underlying processor. | ||
| /// | ||
| /// If this were implemented as a standalone processor in a chain (e.g., | ||
| /// EnrichmentProcessor -> SimpleLogProcessor), the performance benefits of the | ||
| /// `event_enabled` check would be nullified. Here's why: | ||
| /// | ||
| /// - The `event_enabled` method is crucial for performance - it allows processors | ||
| /// to skip expensive operations for logs that will ultimately be filtered out | ||
| /// - A standalone EnrichmentProcessor would need to implement `event_enabled`, | ||
| /// but it has no knowledge of downstream filtering logic | ||
| /// - It would have to return `true` by default, causing unnecessary enrichment | ||
| /// work even for logs that the downstream processor will discard | ||
| /// | ||
| /// Because this processor wraps another, it must delegate all trait methods | ||
| /// to the underlying processor. This ensures the underlying processor receives | ||
| /// all necessary lifecycle events. | ||
| #[derive(Debug)] | ||
| pub struct EnrichmentLogProcessor<P: LogProcessor> { | ||
| /// The wrapped processor that will receive enriched log records | ||
| delegate: P, | ||
| } | ||
|
|
||
| impl<P: LogProcessor> EnrichmentLogProcessor<P> { | ||
| pub fn new(delegate: P) -> EnrichmentLogProcessor<P> { | ||
| EnrichmentLogProcessor { delegate } | ||
| } | ||
| } | ||
|
|
||
| impl<P: LogProcessor> LogProcessor for EnrichmentLogProcessor<P> { | ||
| fn emit(&self, data: &mut SdkLogRecord, instrumentation: &InstrumentationScope) { | ||
| data.add_attribute("enriched", true); | ||
| self.delegate.emit(data, instrumentation); | ||
| } | ||
|
|
||
| fn force_flush(&self) -> OTelSdkResult { | ||
| self.delegate.force_flush() | ||
stefanobaghino marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| fn shutdown_with_timeout(&self, timeout: std::time::Duration) -> OTelSdkResult { | ||
| self.delegate.shutdown_with_timeout(timeout) | ||
| } | ||
|
|
||
| fn shutdown(&self) -> OTelSdkResult { | ||
| self.delegate.shutdown() | ||
| } | ||
|
|
||
| fn set_resource(&mut self, resource: &Resource) { | ||
| self.delegate.set_resource(resource); | ||
| } | ||
|
|
||
| fn event_enabled(&self, level: Severity, target: &str, name: Option<&str>) -> bool { | ||
| self.delegate.event_enabled(level, target, name) | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.