Skip to content

Add tests for tracing appender #1415

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
merged 4 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opentelemetry-appender-tracing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ opentelemetry-stdout = { path = "../opentelemetry-stdout", features = ["logs"] }
[features]
logs_level_enabled = ["opentelemetry/logs_level_enabled", "opentelemetry_sdk/logs_level_enabled"]
default = ["logs_level_enabled"]
testing = ["opentelemetry_sdk/testing"]
142 changes: 142 additions & 0 deletions opentelemetry-appender-tracing/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,145 @@ const fn severity_of_level(level: &Level) -> Severity {
Level::ERROR => Severity::Error,
}
}

#[cfg(test)]
mod tests {
use crate::layer;
use opentelemetry::logs::Severity;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry::trace::{TraceContextExt, TraceFlags, Tracer};
use opentelemetry::{logs::AnyValue, Key};
use opentelemetry_sdk::logs::LoggerProvider;
use opentelemetry_sdk::testing::logs::InMemoryLogsExporter;
use opentelemetry_sdk::trace::{config, Sampler, TracerProvider};
use tracing::error;
use tracing_subscriber::layer::SubscriberExt;

// cargo test --features=testing
#[test]
fn tracing_appender_standalone() {
// Arrange
let exporter: InMemoryLogsExporter = InMemoryLogsExporter::default();
let logger_provider = LoggerProvider::builder()
.with_simple_exporter(exporter.clone())
.build();

let layer = layer::OpenTelemetryTracingBridge::new(&logger_provider);
let subscriber = tracing_subscriber::registry().with(layer);

// avoiding setting tracing subscriber as global as that does not
// play well with unit tests.
let _guard = tracing::subscriber::set_default(subscriber);

// Act
error!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "[email protected]");
logger_provider.force_flush();

// Assert TODO: move to helper methods
let exported_logs = exporter
.get_emitted_logs()
.expect("Logs are expected to be exported.");
assert_eq!(exported_logs.len(), 1);
let log = exported_logs
.get(0)
.expect("Atleast one log is expected to be present.");

// Validate common fields
assert_eq!(log.instrumentation.name, "opentelemetry-appender-tracing");
assert_eq!(log.record.severity_number, Some(Severity::Error));

// Validate trace context is none.
assert!(log.record.trace_context.is_none());

// Validate attributes
let attributes: Vec<(Key, AnyValue)> = log
.record
.attributes
.clone()
.expect("Attributes are expected");
assert_eq!(attributes.len(), 4);
assert!(attributes.contains(&(Key::new("name"), "my-event-name".into())));
assert!(attributes.contains(&(Key::new("event_id"), 20.into())));
assert!(attributes.contains(&(Key::new("user_name"), "otel".into())));
assert!(attributes.contains(&(Key::new("user_email"), "[email protected]".into())));
}

#[test]
fn tracing_appender_inside_tracing_context() {
// Arrange
let exporter: InMemoryLogsExporter = InMemoryLogsExporter::default();
let logger_provider = LoggerProvider::builder()
.with_simple_exporter(exporter.clone())
.build();

let layer = layer::OpenTelemetryTracingBridge::new(&logger_provider);
let subscriber = tracing_subscriber::registry().with(layer);

// avoiding setting tracing subscriber as global as that does not
// play well with unit tests.
let _guard = tracing::subscriber::set_default(subscriber);

// setup tracing as well.
let tracer_provider = TracerProvider::builder()
.with_config(config().with_sampler(Sampler::AlwaysOn))
.build();
let tracer = tracer_provider.tracer("test-tracer");

// Act
let (trace_id_expected, span_id_expected) = tracer.in_span("test-span", |cx| {
let trace_id = cx.span().span_context().trace_id();
let span_id = cx.span().span_context().span_id();

// logging is done inside span context.
error!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "[email protected]");
(trace_id, span_id)
});

logger_provider.force_flush();

// Assert TODO: move to helper methods
let exported_logs = exporter
.get_emitted_logs()
.expect("Logs are expected to be exported.");
assert_eq!(exported_logs.len(), 1);
let log = exported_logs
.get(0)
.expect("Atleast one log is expected to be present.");

// validate common fields.
assert_eq!(log.instrumentation.name, "opentelemetry-appender-tracing");
assert_eq!(log.record.severity_number, Some(Severity::Error));

// validate trace context.
assert!(log.record.trace_context.is_some());
assert_eq!(
log.record.trace_context.as_ref().unwrap().trace_id,
trace_id_expected
);
assert_eq!(
log.record.trace_context.as_ref().unwrap().span_id,
span_id_expected
);
assert_eq!(
log.record
.trace_context
.as_ref()
.unwrap()
.trace_flags
.unwrap(),
TraceFlags::SAMPLED
);

// validate attributes.
let attributes: Vec<(Key, AnyValue)> = log
.record
.attributes
.clone()
.expect("Attributes are expected");
assert_eq!(attributes.len(), 4);
assert!(attributes.contains(&(Key::new("name"), "my-event-name".into())));
assert!(attributes.contains(&(Key::new("event_id"), 20.into())));
assert!(attributes.contains(&(Key::new("user_name"), "otel".into())));
assert!(attributes.contains(&(Key::new("user_email"), "[email protected]".into())));
}
}
2 changes: 1 addition & 1 deletion opentelemetry/src/logs/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl From<&SpanContext> for TraceContext {
}

/// Value types for representing arbitrary values in a log record.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum AnyValue {
/// An integer value
Int(i64),
Expand Down