use tracing::{Level, event};
use crate::render_impl::{
DiagnosticIr, build_ctx_and_attachments, build_diag_src_errs_val, build_display_causes,
build_origin_src_errs_val, build_stack_trace_value,
};
use crate::report::{AttachmentValue, HasSeverity, ReportTrace, TraceEvent};
use super::{EmitStats, PreparedTracingEmission, PreparedTracingLevel, TracingExporterTrait};
#[derive(Debug, Clone, Copy, Default)]
pub struct TracingExporter;
impl TracingExporterTrait for TracingExporter {
fn export_prepared(&self, emission: PreparedTracingEmission<'_>) -> EmitStats {
let stats = emission.stats();
let report_level = prepared_level_to_tracing(emission.report_level());
let ir = emission.ir();
let (context_value, system_value, attachment_items) =
build_ctx_and_attachments(ir.context, ir.system, ir.attachments);
let stack_trace_value = ir.metadata.stack_trace().map(build_stack_trace_value);
let display_causes_value = build_display_causes(ir.display_causes, ir.display_causes_state);
let origin_source_errors_value = ir
.origin_source_errors
.as_ref()
.map(build_origin_src_errs_val);
let diagnostic_source_errors_value = ir
.diagnostic_source_errors
.as_ref()
.map(build_diag_src_errs_val);
emit_report_event(
report_level,
ReportEventFields {
ir,
context: &context_value,
system: &system_value,
attachments: attachment_items.as_slice(),
stack_trace: stack_trace_value.as_ref(),
display_causes: &display_causes_value,
origin_source_errors: origin_source_errors_value.as_ref(),
diagnostic_source_errors: diagnostic_source_errors_value.as_ref(),
},
);
if !ir.trace.is_empty() {
for prepared_event in emission.trace_events() {
emit_trace_event(
prepared_level_to_tracing(prepared_event.level()),
prepared_event.index(),
prepared_event.event(),
ir.trace,
);
}
}
stats
}
}
fn prepared_level_to_tracing(level: PreparedTracingLevel) -> Level {
match level {
PreparedTracingLevel::Trace => Level::TRACE,
PreparedTracingLevel::Debug => Level::DEBUG,
PreparedTracingLevel::Info => Level::INFO,
PreparedTracingLevel::Warn => Level::WARN,
PreparedTracingLevel::Error => Level::ERROR,
}
}
macro_rules! report_event {
($level:expr, $ir:expr, $context:expr, $system:expr, $attachments:expr, $stack:expr, $display:expr, $origin_sources:expr, $diagnostic_sources:expr) => {
event!(
target: "diagweave::report",
$level,
error_message = %$ir.error.message,
error_type = %$ir.error.r#type,
error_code = ?$ir.metadata.error_code(),
error_severity = ?$ir.metadata.severity(),
error_required_severity = ?$ir.metadata.required_severity(),
error_category = ?$ir.metadata.category(),
error_retryable = ?$ir.metadata.retryable(),
trace_id = ?$ir.trace.context().and_then(|t| t.trace_id.as_ref()),
span_id = ?$ir.trace.context().and_then(|t| t.span_id.as_ref()),
parent_span_id = ?$ir.trace.context().and_then(|t| t.parent_span_id.as_ref()),
trace_sampled = ?$ir.trace.context().and_then(|t| t.sampled),
trace_state = ?$ir.trace.context().and_then(|t| t.trace_state.as_ref()),
trace_event_count = $ir.trace.events().map(|e| e.len()).unwrap_or(0),
report_context = ?$context,
report_system = ?$system,
report_attachments = ?$attachments,
report_stack_trace = ?$stack,
report_display_causes = ?$display,
report_origin_source_errors = ?$origin_sources,
report_diagnostic_source_errors = ?$diagnostic_sources,
"diagweave report emitted"
)
};
}
struct ReportEventFields<'a, 'ir> {
ir: &'a DiagnosticIr<'ir, HasSeverity>,
context: &'a AttachmentValue,
system: &'a AttachmentValue,
attachments: &'a [AttachmentValue],
stack_trace: Option<&'a AttachmentValue>,
display_causes: &'a AttachmentValue,
origin_source_errors: Option<&'a AttachmentValue>,
diagnostic_source_errors: Option<&'a AttachmentValue>,
}
fn emit_report_event(level: Level, fields: ReportEventFields<'_, '_>) {
macro_rules! emit_with_level {
($fixed:expr) => {
report_event!(
$fixed,
fields.ir,
fields.context,
fields.system,
fields.attachments,
fields.stack_trace,
fields.display_causes,
fields.origin_source_errors,
fields.diagnostic_source_errors
)
};
}
match level {
Level::TRACE => emit_with_level!(Level::TRACE),
Level::DEBUG => emit_with_level!(Level::DEBUG),
Level::INFO => emit_with_level!(Level::INFO),
Level::WARN => emit_with_level!(Level::WARN),
Level::ERROR => emit_with_level!(Level::ERROR),
}
}
macro_rules! trace_event {
($level:expr, $idx:expr, $event:expr, $trace:expr) => {
event!(
target: "diagweave::trace_event",
$level,
trace_event_index = $idx,
trace_event_name = %$event.name,
trace_event_level = ?$event.level,
trace_event_timestamp_unix_nano = ?$event.timestamp_unix_nano,
trace_event_attributes = ?$event.attributes,
trace_id = ?$trace.context().and_then(|t| t.trace_id.as_ref()),
span_id = ?$trace.context().and_then(|t| t.span_id.as_ref()),
parent_span_id = ?$trace.context().and_then(|t| t.parent_span_id.as_ref()),
trace_sampled = ?$trace.context().and_then(|t| t.sampled),
trace_state = ?$trace.context().and_then(|t| t.trace_state.as_ref()),
"diagweave trace event"
)
};
}
fn emit_trace_event(level: Level, idx: usize, event: &TraceEvent, trace: &ReportTrace) {
match level {
Level::TRACE => trace_event!(Level::TRACE, idx, event, trace),
Level::DEBUG => trace_event!(Level::DEBUG, idx, event, trace),
Level::INFO => trace_event!(Level::INFO, idx, event, trace),
Level::WARN => trace_event!(Level::WARN, idx, event, trace),
Level::ERROR => trace_event!(Level::ERROR, idx, event, trace),
}
}