Skip to content

Commit 670ecc7

Browse files
Add AntithesisLayer to integrate with tracing.
1 parent 76e6d33 commit 670ecc7

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed

Cargo.lock

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Rust SDK for the Antithesis autonomous software testing platform.
1818
serde = { version = "1.0.113", features = ["derive"] }
1919
serde_json = "1.0.25"
2020
rand = "0.8"
21+
tracing-core = "0.1"
22+
tracing-subscriber = "0.3"
2123

2224
# needed only if full feature is set
2325
rustc_version_runtime = {version = "0.3", optional = true}

lib/src/lifecycle.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub fn send_event(name: &str, details: &Value) {
7171
internal::dispatch_output(&json_event)
7272
}
7373

74+
mod tracing;
75+
7476
#[cfg(test)]
7577
mod tests {
7678
use super::*;

lib/src/lifecycle/tracing.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::collections::BTreeMap;
2+
3+
use serde::Serialize;
4+
use serde_json::Value;
5+
use tracing_core::{field, span, Event, Subscriber};
6+
use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
7+
8+
pub struct AntithesisLayer;
9+
10+
struct SpanValues(BTreeMap<&'static str, Value>);
11+
12+
// TODO: Also emit events at span creation/enter/leave/drop times, or allow configuration
13+
// options to make that possible?
14+
impl<S> Layer<S> for AntithesisLayer
15+
where S: Subscriber + for<'a> LookupSpan<'a> {
16+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
17+
let span = ctx.span(id).expect("Span not found");
18+
let mut values = BTreeMap::new();
19+
attrs.record(&mut JsonVisitor::with(&mut values));
20+
span.extensions_mut().insert(SpanValues(values));
21+
}
22+
23+
fn on_record(&self, span: &span::Id, record: &span::Record<'_>, ctx: Context<'_, S>) {
24+
let span = ctx.span(span).expect("Span not found");
25+
let mut extensions = span.extensions_mut();
26+
if let Some(SpanValues(values)) = extensions.get_mut() {
27+
record.record(&mut JsonVisitor::with(values));
28+
} else {
29+
let mut values = BTreeMap::new();
30+
record.record(&mut JsonVisitor::with(&mut values));
31+
extensions.insert(SpanValues(values));
32+
}
33+
}
34+
35+
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
36+
// TODO: Use streaming JSON serialization to eliminate intermediate data structures
37+
// and cloning? Might complicates implementation.
38+
let mut values = BTreeMap::new();
39+
event.record(&mut JsonVisitor::with(&mut values));
40+
let spans = ctx.event_scope(event).map(|scope| {
41+
scope.from_root().filter_map(|span| {
42+
let extensions = span.extensions();
43+
let SpanValues(values) = extensions.get()?;
44+
Some(values.clone())
45+
}).collect::<Vec<_>>()
46+
});
47+
48+
#[derive(Serialize)]
49+
struct Event {
50+
spans: Option<Vec<BTreeMap<&'static str, Value>>>,
51+
#[serde(flatten)]
52+
values: BTreeMap<&'static str, Value>,
53+
}
54+
crate::internal::dispatch_output(&Event { values, spans });
55+
}
56+
}
57+
58+
struct JsonVisitor<'a> {
59+
values: &'a mut BTreeMap<&'static str, Value>,
60+
}
61+
62+
impl<'a> JsonVisitor<'a> {
63+
fn with(values: &'a mut BTreeMap<&'static str, Value>) -> Self {
64+
Self { values }
65+
}
66+
}
67+
68+
impl<'a> field::Visit for JsonVisitor<'a> {
69+
fn record_f64(&mut self, field: &field::Field, value: f64) {
70+
self.values.insert(field.name(), Value::from(value));
71+
}
72+
73+
fn record_i64(&mut self, field: &field::Field, value: i64) {
74+
self.values.insert(field.name(), Value::from(value));
75+
}
76+
77+
fn record_u64(&mut self, field: &field::Field, value: u64) {
78+
self.values.insert(field.name(), Value::from(value));
79+
}
80+
81+
fn record_bool(&mut self, field: &field::Field, value: bool) {
82+
self.values.insert(field.name(), Value::from(value));
83+
}
84+
85+
fn record_str(&mut self, field: &field::Field, value: &str) {
86+
self.values.insert(field.name(), Value::from(value));
87+
}
88+
89+
fn record_debug(&mut self, field: &field::Field, value: &dyn std::fmt::Debug) {
90+
self.values.insert(field.name(), Value::from(format!("{:?}", value)));
91+
}
92+
}

0 commit comments

Comments
 (0)