-
Notifications
You must be signed in to change notification settings - Fork 660
Expand file tree
/
Copy pathmain.rs
More file actions
121 lines (105 loc) · 5.24 KB
/
main.rs
File metadata and controls
121 lines (105 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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 resource = Resource::builder()
.with_service_name("log-appender-tracing-example")
.build();
// Publish resource attributes via a memory-mapped region so external readers
// (e.g. the OpenTelemetry eBPF Profiler) can discover this process.
// This is a no-op on non-Linux platforms.
opentelemetry_proto::process_context::publish(&resource);
let exporter = opentelemetry_stdout::LogExporter::default();
let enriching_processor = EnrichmentLogProcessor::new(SimpleLogProcessor::new(exporter));
let provider: SdkLoggerProvider = SdkLoggerProvider::builder()
.with_resource(resource)
.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 = "otel@opentelemetry.io", message = "This is an example message");
error!(name: "my-event-name", target: "my-system", event_id = 50, user_name = "otel", user_email = "otel@opentelemetry.io", message = "This is an example message");
let _ = provider.shutdown();
opentelemetry_proto::process_context::unpublish();
}
/// 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()
}
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)
}
}