Skip to content

Commit 0c41424

Browse files
authored
opentelemetry-appender-log: Add code attributes to logs (#2193)
1 parent d78930a commit 0c41424

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

opentelemetry-appender-log/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## vNext
44

55
- Bump MSRV to 1.70 [#2179](https://github.com/open-telemetry/opentelemetry-rust/pull/2179)
6+
- [2193](https://github.com/open-telemetry/opentelemetry-rust/pull/2193) `opentelemetry-appender-log`: Output experimental code attributes
67

78
## v0.26.0
89
Released 2024-Sep-30

opentelemetry-appender-log/Cargo.toml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,26 @@ rust-version = "1.70"
1111
edition = "2021"
1212

1313
[dependencies]
14-
opentelemetry = { version = "0.26", path = "../opentelemetry", features = ["logs"]}
15-
log = { workspace = true, features = ["kv", "std"]}
14+
opentelemetry = { version = "0.26", path = "../opentelemetry", features = [
15+
"logs",
16+
] }
17+
log = { workspace = true, features = ["kv", "std"] }
1618
serde = { workspace = true, optional = true, features = ["std"] }
19+
opentelemetry-semantic-conventions = { path = "../opentelemetry-semantic-conventions", optional = true, features = [
20+
"semconv_experimental",
21+
] }
1722

1823
[features]
1924
logs_level_enabled = ["opentelemetry/logs_level_enabled"]
2025
with-serde = ["log/kv_serde", "serde"]
26+
experimental_metadata_attributes = ["dep:opentelemetry-semantic-conventions"]
2127

2228
[dev-dependencies]
23-
opentelemetry_sdk = { path = "../opentelemetry-sdk", features = [ "testing", "logs_level_enabled" ] }
24-
opentelemetry-stdout = { path = "../opentelemetry-stdout", features = ["logs"]}
29+
opentelemetry_sdk = { path = "../opentelemetry-sdk", features = [
30+
"testing",
31+
"logs_level_enabled",
32+
] }
33+
opentelemetry-stdout = { path = "../opentelemetry-stdout", features = ["logs"] }
2534
log = { workspace = true, features = ["kv_serde"] }
2635
tokio = { workspace = true }
2736
serde = { workspace = true, features = ["std", "derive"] }

opentelemetry-appender-log/src/lib.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ use opentelemetry::{
9999
logs::{AnyValue, LogRecord, Logger, LoggerProvider, Severity},
100100
Key,
101101
};
102+
#[cfg(feature = "experimental_metadata_attributes")]
103+
use opentelemetry_semantic_conventions::trace::{CODE_FILEPATH, CODE_LINENO, CODE_NAMESPACE};
102104
use std::borrow::Cow;
103105

104106
pub struct OpenTelemetryLogBridge<P, L>
@@ -130,6 +132,28 @@ where
130132
log_record.set_severity_number(severity_of_level(record.level()));
131133
log_record.set_severity_text(record.level().as_str());
132134
log_record.set_body(AnyValue::from(record.args().to_string()));
135+
136+
#[cfg(feature = "experimental_metadata_attributes")]
137+
{
138+
if let Some(filepath) = record.file() {
139+
log_record.add_attribute(
140+
Key::new(CODE_FILEPATH),
141+
AnyValue::from(filepath.to_string()),
142+
);
143+
}
144+
145+
if let Some(line_no) = record.line() {
146+
log_record.add_attribute(Key::new(CODE_LINENO), AnyValue::from(line_no));
147+
}
148+
149+
if let Some(module) = record.module_path() {
150+
log_record.add_attribute(
151+
Key::new(CODE_NAMESPACE),
152+
AnyValue::from(module.to_string()),
153+
);
154+
}
155+
}
156+
133157
log_record.add_attributes(log_attributes(record.key_values()));
134158
log_record.set_target(record.metadata().target().to_string());
135159

@@ -1127,6 +1151,54 @@ mod tests {
11271151
}
11281152
}
11291153

1154+
#[cfg(feature = "experimental_metadata_attributes")]
1155+
#[test]
1156+
fn logbridge_code_attributes() {
1157+
use opentelemetry_semantic_conventions::trace::{
1158+
CODE_FILEPATH, CODE_LINENO, CODE_NAMESPACE,
1159+
};
1160+
1161+
let exporter = InMemoryLogsExporter::default();
1162+
1163+
let logger_provider = LoggerProvider::builder()
1164+
.with_simple_exporter(exporter.clone())
1165+
.build();
1166+
1167+
let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
1168+
1169+
otel_log_appender.log(
1170+
&log::RecordBuilder::new()
1171+
.level(log::Level::Warn)
1172+
.args(format_args!("WARN"))
1173+
.file(Some("src/main.rs"))
1174+
.module_path(Some("service"))
1175+
.line(Some(101))
1176+
.build(),
1177+
);
1178+
1179+
let logs = exporter.get_emitted_logs().unwrap();
1180+
1181+
let get = |needle: &str| -> Option<AnyValue> {
1182+
logs[0].record.attributes_iter().find_map(|(k, v)| {
1183+
if k.as_str() == needle {
1184+
Some(v.clone())
1185+
} else {
1186+
None
1187+
}
1188+
})
1189+
};
1190+
1191+
assert_eq!(
1192+
Some(AnyValue::String(StringValue::from("src/main.rs"))),
1193+
get(CODE_FILEPATH)
1194+
);
1195+
assert_eq!(
1196+
Some(AnyValue::String(StringValue::from("service"))),
1197+
get(CODE_NAMESPACE)
1198+
);
1199+
assert_eq!(Some(AnyValue::Int(101)), get(CODE_LINENO));
1200+
}
1201+
11301202
#[test]
11311203
fn test_flush() {
11321204
let exporter = InMemoryLogsExporter::default();

0 commit comments

Comments
 (0)