Skip to content

Commit 50c6e5a

Browse files
authored
Add logs pipeline example (#110)
The example shows how to add a custom log processor before the exporter, which enriches the log record with attributes. In this case, it adds exception attributes for error logs, so the exporter exports them as exceptions to app insights.
1 parent 13e56e7 commit 50c6e5a

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ required-features = ["live-metrics", "opentelemetry_sdk/rt-tokio", "opentelemetr
106106
name = "logs"
107107
required-features = ["logs", "opentelemetry-http/reqwest"]
108108

109+
[[example]]
110+
name = "logs_pipeline"
111+
required-features = ["logs", "opentelemetry-http/reqwest"]
112+
109113
[[example]]
110114
name = "metrics"
111115
required-features = ["metrics", "opentelemetry_sdk/rt-tokio"]

examples/logs_pipeline.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use opentelemetry::logs::LogRecord;
2+
use opentelemetry_semantic_conventions as semcov;
3+
use std::error::Error;
4+
5+
#[derive(Debug)]
6+
struct ErrorAsExceptionLogProcessor;
7+
8+
impl opentelemetry_sdk::logs::LogProcessor for ErrorAsExceptionLogProcessor {
9+
fn emit(
10+
&self,
11+
data: &mut opentelemetry_sdk::logs::SdkLogRecord,
12+
_instrumentation: &opentelemetry::InstrumentationScope,
13+
) {
14+
if let Some(severity) = data.severity_number() {
15+
if severity >= opentelemetry::logs::Severity::Error {
16+
// TODO: Check if exception attributes are already present
17+
data.add_attribute(semcov::attribute::EXCEPTION_TYPE, "error");
18+
if let Some(body) = data.body() {
19+
data.add_attribute(
20+
semcov::attribute::EXCEPTION_MESSAGE,
21+
any_value_to_string(body),
22+
);
23+
}
24+
}
25+
}
26+
}
27+
28+
fn force_flush(&self) -> opentelemetry_sdk::error::OTelSdkResult {
29+
Ok(())
30+
}
31+
}
32+
33+
fn any_value_to_string(v: &opentelemetry::logs::AnyValue) -> String {
34+
match v {
35+
opentelemetry::logs::AnyValue::String(v) => v.to_string(),
36+
_ => format!("{:?}", v).into(),
37+
}
38+
}
39+
40+
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
41+
let client = reqwest::blocking::Client::new();
42+
43+
let exporter = opentelemetry_application_insights::Exporter::new_from_env(client)?;
44+
45+
let logger_provider = opentelemetry_sdk::logs::SdkLoggerProvider::builder()
46+
.with_log_processor(ErrorAsExceptionLogProcessor)
47+
.with_batch_exporter(exporter)
48+
.build();
49+
let otel_log_appender =
50+
opentelemetry_appender_log::OpenTelemetryLogBridge::new(&logger_provider);
51+
log::set_boxed_logger(Box::new(otel_log_appender))?;
52+
log::set_max_level(log::Level::Info.to_level_filter());
53+
54+
// Log via `log` crate.
55+
let fruit = "apple";
56+
let price = 2.99;
57+
let colors = ("red", "green");
58+
log::info!(fruit, price, colors:sval; "info! {fruit} is {price}");
59+
log::warn!("warn!");
60+
log::error!("error!");
61+
62+
// Force export before exit.
63+
logger_provider.shutdown()?;
64+
65+
Ok(())
66+
}

0 commit comments

Comments
 (0)