use std::sync::{Arc, Mutex, MutexGuard};
use std::{fmt, io};
use tracing::{Dispatch, Instrument};
use tracing_subscriber::fmt::MakeWriter;
use tracing_subscriber::FmtSubscriber;
use xtra::prelude::*;
#[tokio::test]
async fn assert_send_is_child_of_span() {
let (subscriber, buf) = get_subscriber("instrumentation=trace,xtra=trace");
let _g = tracing::dispatcher::set_default(&subscriber);
let addr = xtra::spawn_tokio(Tracer, Mailbox::unbounded());
let _ = addr
.send(Hello("world"))
.instrument(tracing::info_span!("user_span"))
.await;
assert_eq!(
buf,
[" INFO user_span:xtra_actor_request\
{actor_type=instrumentation::Tracer message_type=instrumentation::Hello}:\
xtra_message_handler: instrumentation: Hello world"]
);
}
#[tokio::test]
async fn assert_handler_span_is_child_of_caller_span_with_min_level_info() {
let (subscriber, buf) = get_subscriber("instrumentation=info,xtra=info");
let _g = tracing::dispatcher::set_default(&subscriber);
let addr = xtra::spawn_tokio(Tracer, Mailbox::unbounded());
let _ = addr
.send(CreateInfoSpan)
.instrument(tracing::info_span!("sender_span"))
.await;
assert_eq!(buf, [" INFO sender_span:info_span: instrumentation: Test!"]);
}
#[derive(xtra::Actor)]
struct Tracer;
struct Hello(&'static str);
impl Handler<Hello> for Tracer {
type Return = ();
async fn handle(&mut self, message: Hello, _ctx: &mut Context<Self>) {
tracing::info!("Hello {}", message.0)
}
}
struct CreateInfoSpan;
impl Handler<CreateInfoSpan> for Tracer {
type Return = ();
async fn handle(&mut self, _msg: CreateInfoSpan, _ctx: &mut Context<Self>) {
tracing::info_span!("info_span").in_scope(|| tracing::info!("Test!"));
}
}
#[derive(Debug, Default)]
struct BufferWriter {
buf: Buffer,
}
#[derive(Default, Clone)]
struct Buffer(Arc<Mutex<Vec<u8>>>);
impl Buffer {
fn as_str(&self) -> String {
let buf = self.0.lock().unwrap().clone();
String::from_utf8(buf).expect("Logs contain invalid UTF8")
}
}
impl<const N: usize> PartialEq<[&str; N]> for Buffer {
fn eq(&self, other: &[&str; N]) -> bool {
self.as_str().lines().collect::<Vec<_>>().eq(other)
}
}
impl fmt::Debug for Buffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().lines().collect::<Vec<_>>().fmt(f)
}
}
impl BufferWriter {
fn buf(&self) -> io::Result<MutexGuard<Vec<u8>>> {
self.buf
.0
.lock()
.map_err(|_| io::Error::from(io::ErrorKind::Other))
}
}
impl io::Write for BufferWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut target = self.buf()?;
print!("{}", String::from_utf8(buf.to_vec()).unwrap());
target.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.buf()?.flush()
}
}
impl MakeWriter<'_> for BufferWriter {
type Writer = Self;
fn make_writer(&self) -> Self::Writer {
BufferWriter {
buf: self.buf.clone(),
}
}
}
fn get_subscriber(env_filter: &str) -> (Dispatch, Buffer) {
let writer = BufferWriter::default();
let buffer = writer.buf.clone();
let dispatch = FmtSubscriber::builder()
.with_env_filter(env_filter)
.with_writer(writer)
.with_level(true)
.with_ansi(false)
.without_time()
.into();
(dispatch, buffer)
}