use crate::formatter::format_immediate;
use crate::processor::Processor;
use crate::tag::{NoTag, Tag, TagData, TagParser};
use crate::{cfg_chrono, cfg_json, cfg_uuid, fail};
#[cfg(feature = "smallvec")]
use smallvec::SmallVec;
use std::time::{Duration, Instant};
use std::{borrow::Cow, fmt};
use tracing::field::{Field, Visit};
use tracing::span::Attributes;
use tracing::{Event, Id, Level, Subscriber};
use tracing_subscriber::layer::Layered;
use tracing_subscriber::Registry;
use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
cfg_json! {
use crate::ser;
use serde::Serialize;
}
cfg_chrono! {
use chrono::{DateTime, Utc};
}
cfg_uuid! {
use uuid::Uuid;
const DEFAULT_EVENT_UUID: Uuid = Uuid::nil();
}
#[cfg(feature = "smallvec")]
pub(crate) type Fields = SmallVec<[KeyValue; 3]>;
#[cfg(not(feature = "smallvec"))]
pub(crate) type Fields = Vec<KeyValue>;
#[doc(hidden)]
#[derive(Debug)]
pub struct KeyValue {
pub key: &'static str,
pub value: String,
}
pub(crate) const TAG_KEY: &str = "__event_tag";
pub struct TreeLayer<P> {
processor: P,
tag_parser: TagParser,
}
impl<P: Processor> TreeLayer<P> {
pub fn new(processor: P) -> Self {
TreeLayer {
processor,
tag_parser: NoTag::from_field,
}
}
pub fn into_subscriber(self) -> Layered<Self, Registry> {
self.with_subscriber(Registry::default())
}
pub fn tag<T: Tag>(mut self) -> Self {
self.tag_parser = T::from_field;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "json", derive(Serialize))]
pub struct Tree {
#[cfg_attr(feature = "json", serde(flatten))]
pub attrs: TreeAttrs,
pub kind: TreeKind,
}
impl Tree {
fn new(attrs: TreeAttrs, kind: impl Into<TreeKind>) -> Self {
Tree {
attrs,
kind: kind.into(),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "json", derive(Serialize))]
pub struct TreeAttrs {
#[cfg(feature = "uuid")]
pub uuid: Uuid,
#[cfg(feature = "chrono")]
#[cfg_attr(feature = "json", serde(serialize_with = "ser::timestamp"))]
pub timestamp: DateTime<Utc>,
#[cfg_attr(feature = "json", serde(serialize_with = "ser::level"))]
pub level: Level,
}
#[derive(Debug)]
#[cfg_attr(feature = "json", derive(Serialize))]
pub enum TreeKind {
Event(TreeEvent),
Span(TreeSpan),
}
#[derive(Debug)]
#[cfg_attr(feature = "json", derive(Serialize))]
pub struct TreeEvent {
pub tag: Option<TagData>,
pub message: Cow<'static, str>,
#[cfg_attr(feature = "json", serde(serialize_with = "ser::fields"))]
pub fields: Fields,
}
impl From<TreeEvent> for TreeKind {
fn from(event: TreeEvent) -> Self {
TreeKind::Event(event)
}
}
impl From<TreeSpan> for TreeKind {
fn from(span: TreeSpan) -> Self {
TreeKind::Span(span)
}
}
impl TreeKind {
pub fn into_event(self) -> Option<TreeEvent> {
match self {
TreeKind::Event(event) => Some(event),
TreeKind::Span(_) => None,
}
}
pub fn into_span(self) -> Option<TreeSpan> {
match self {
TreeKind::Event(_) => None,
TreeKind::Span(span) => Some(span),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "json", derive(Serialize))]
pub struct TreeSpan {
pub name: &'static str,
#[cfg_attr(
feature = "json",
serde(rename = "nanos_total", serialize_with = "ser::nanos")
)]
pub duration_total: Duration,
#[cfg_attr(
feature = "json",
serde(rename = "nanos_nested", serialize_with = "ser::nanos")
)]
pub duration_nested: Duration,
pub children: Vec<Tree>,
}
pub(crate) struct TreeSpanOpened {
attrs: TreeAttrs,
span: TreeSpan,
start: Instant,
}
impl TreeSpanOpened {
fn open<S>(attrs: &Attributes, ctx: &Context<S>) -> Self
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
#[cfg(not(feature = "uuid"))]
let _ = ctx;
struct SpanVisitor {
#[cfg(feature = "uuid")]
uuid_lsb: Option<u64>,
#[cfg(feature = "uuid")]
uuid_msb: Option<u64>,
}
impl SpanVisitor {
fn new() -> Self {
SpanVisitor {
#[cfg(feature = "uuid")]
uuid_lsb: None,
#[cfg(feature = "uuid")]
uuid_msb: None,
}
}
cfg_uuid! {
fn get_uuid(&self) -> Option<Uuid> {
match (self.uuid_msb, self.uuid_lsb) {
(Some(msb), Some(lsb)) => Some(crate::uuid::from_u64_pair(msb, lsb)),
(None, None) => None,
_ => {
None
}
}
}
}
}
impl Visit for SpanVisitor {
fn record_u64(&mut self, field: &Field, value: u64) {
match field.name() {
#[cfg(feature = "uuid")]
"__uuid_lsb" => self.uuid_lsb = Some(value),
#[cfg(feature = "uuid")]
"__uuid_msb" => self.uuid_msb = Some(value),
_ => self.record_debug(field, &value),
}
}
fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {
}
}
let mut visitor = SpanVisitor::new();
attrs.record(&mut visitor);
#[cfg(feature = "uuid")]
let uuid = match visitor.get_uuid() {
Some(uuid) => uuid,
None => match ctx.lookup_current() {
Some(parent) => parent
.extensions()
.get::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.uuid(),
None => Uuid::new_v4(),
},
};
TreeSpanOpened {
attrs: TreeAttrs {
#[cfg(feature = "chrono")]
timestamp: Utc::now(),
#[cfg(feature = "uuid")]
uuid,
level: *attrs.metadata().level(),
},
span: TreeSpan {
name: attrs.metadata().name(),
children: Vec::new(),
duration_nested: Duration::ZERO,
duration_total: Duration::ZERO,
},
start: Instant::now(),
}
}
fn enter(&mut self) {
self.start = Instant::now();
}
fn exit(&mut self) {
self.span.duration_total += self.start.elapsed();
}
fn close(self) -> (TreeAttrs, TreeSpan) {
(self.attrs, self.span)
}
fn log_event(&mut self, attrs: TreeAttrs, event: TreeEvent) {
#[cfg(feature = "uuid")]
let attrs = TreeAttrs {
uuid: self.uuid(),
..attrs
};
self.span.children.push(Tree::new(attrs, event));
}
fn log_span(&mut self, attrs: TreeAttrs, span: TreeSpan) {
self.span.duration_nested += span.duration_total;
self.span.children.push(Tree::new(attrs, span));
}
cfg_uuid! {
pub fn uuid(&self) -> Uuid {
self.attrs.uuid
}
}
}
impl<P: Processor> TreeLayer<P> {
fn parse_event(&self, event: &Event) -> (TreeAttrs, TreeEvent, bool) {
struct EventVisitor {
immediate: bool,
tag: Option<TagData>,
message: Cow<'static, str>,
fields: Fields,
tag_parser: TagParser,
}
impl EventVisitor {
fn new(tag_parser: TagParser) -> Self {
EventVisitor {
immediate: false,
tag: None,
message: Cow::from("<no message>"),
fields: Fields::new(),
tag_parser,
}
}
}
impl Visit for EventVisitor {
fn record_bool(&mut self, field: &Field, value: bool) {
match field.name() {
"immediate" => self.immediate = value,
_ => self.record_debug(field, &value),
}
}
fn record_u64(&mut self, field: &Field, value: u64) {
match field.name() {
TAG_KEY => {
if self.tag.is_some() {
fail::multiple_tags_on_event();
}
self.tag = Some((self.tag_parser)(value));
}
_ => self.record_debug(field, &value),
}
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
let value = format!("{:?}", value);
match field.name() {
"message" if matches!(self.message, Cow::Borrowed(_)) => {
self.message = Cow::from(value)
}
key => self.fields.push(KeyValue { key, value }),
}
}
}
let mut visitor = EventVisitor::new(self.tag_parser);
event.record(&mut visitor);
let tree_event = TreeEvent {
tag: visitor.tag,
message: visitor.message,
fields: visitor.fields,
};
let tree_attrs = TreeAttrs {
#[cfg(feature = "uuid")]
uuid: DEFAULT_EVENT_UUID,
#[cfg(feature = "chrono")]
timestamp: Utc::now(),
level: *event.metadata().level(),
};
(tree_attrs, tree_event, visitor.immediate)
}
}
impl<P, S> Layer<S> for TreeLayer<P>
where
P: Processor,
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
let span = ctx.span(id).unwrap_or_else(fail::span_not_in_context);
let opened = TreeSpanOpened::open(attrs, &ctx);
let mut extensions = span.extensions_mut();
extensions.insert(opened);
}
fn on_event(&self, event: &Event, ctx: Context<S>) {
let (tree_attrs, tree_event, immediate) = self.parse_event(event);
if immediate {
#[allow(clippy::expect_used)]
format_immediate(&tree_attrs, &tree_event, ctx.event_span(event))
.expect("formatting immediate event failed");
}
match ctx.event_span(event) {
Some(parent) => {
parent
.extensions_mut()
.get_mut::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.log_event(tree_attrs, tree_event);
}
None => {
self.processor.process(Tree::new(tree_attrs, tree_event));
}
}
}
fn on_enter(&self, id: &Id, ctx: Context<S>) {
ctx.span(id)
.unwrap_or_else(fail::span_not_in_context)
.extensions_mut()
.get_mut::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.enter();
}
fn on_exit(&self, id: &Id, ctx: Context<S>) {
ctx.span(id)
.unwrap_or_else(fail::span_not_in_context)
.extensions_mut()
.get_mut::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.exit();
}
fn on_close(&self, id: Id, ctx: Context<S>) {
let span = ctx.span(&id).unwrap_or_else(fail::span_not_in_context);
let (tree_attrs, tree_span) = span
.extensions_mut()
.remove::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.close();
match span.parent() {
Some(parent) => parent
.extensions_mut()
.get_mut::<TreeSpanOpened>()
.unwrap_or_else(fail::tree_span_opened_not_in_extensions)
.log_span(tree_attrs, tree_span),
None => self.processor.process(Tree::new(tree_attrs, tree_span)),
}
}
}