use std::io::Write;
use std::sync::{Arc, Mutex};
use vgi_rpc::access_log::{clear_trace_context_provider, set_trace_context_provider};
use vgi_rpc::{AccessLogHook, CallStatistics, DispatchHook, DispatchInfo};
struct BufSink(Arc<Mutex<Vec<u8>>>);
impl Write for BufSink {
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(b);
Ok(b.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
fn emit_one() -> serde_json::Value {
let buf: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
let hook: Arc<dyn DispatchHook> = AccessLogHook::new(BufSink(buf.clone()), "v");
let info = DispatchInfo {
method: "echo_string".into(),
method_type: "unary",
server_id: "srv".into(),
protocol: "Test".into(),
transport_metadata: Arc::new(Default::default()),
..Default::default()
};
let token = hook.on_dispatch_start(&info);
hook.on_dispatch_end(token, &info, None, &CallStatistics::default());
let text = String::from_utf8(buf.lock().unwrap().clone()).unwrap();
serde_json::from_str(text.trim()).unwrap()
}
const TRACE: &str = "4bf92f3577b34da6a3ce929d0e0e4736";
const SPAN: &str = "00f067aa0ba902b7";
#[test]
fn trace_ids_are_emitted_together_and_only_when_valid() {
clear_trace_context_provider();
let rec = emit_one();
assert!(rec.get("trace_id").is_none());
assert!(rec.get("span_id").is_none());
set_trace_context_provider(Arc::new(|| Some((TRACE.to_string(), SPAN.to_string()))));
let rec = emit_one();
assert_eq!(rec["trace_id"], TRACE);
assert_eq!(rec["span_id"], SPAN);
set_trace_context_provider(Arc::new(|| {
Some((
"4bf92f35-77b3-4da6-a3ce-929d0e0e4736".to_string(),
SPAN.to_string(),
))
}));
let rec = emit_one();
assert!(rec.get("trace_id").is_none());
assert!(rec.get("span_id").is_none());
set_trace_context_provider(Arc::new(|| Some(("0".repeat(32), SPAN.to_string()))));
let rec = emit_one();
assert!(rec.get("trace_id").is_none());
set_trace_context_provider(Arc::new(|| Some((TRACE.to_string(), String::new()))));
let rec = emit_one();
assert!(rec.get("trace_id").is_none());
assert!(rec.get("span_id").is_none());
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
set_trace_context_provider(Arc::new(|| panic!("tracer is wedged")));
let rec = emit_one();
std::panic::set_hook(previous);
assert_eq!(rec["status"], "ok");
assert!(rec.get("trace_id").is_none());
clear_trace_context_provider();
let rec = emit_one();
assert!(rec.get("trace_id").is_none());
}