Event data model
All diagnostics in emit
are represented as an Event
.
Each event is the combination of:
mdl
(Path
): The path of the component that generated the event.tpl
(Template
): A lazily-rendered, user-facing description of the event.extent
(Extent
): The point in time that the event occurred at, or the span of time for which it was active.props
(Props
): A set of key-value pairs associated with the event.
Here's an example of an event created using the emit!
macro:
#![allow(unused)] fn main() { extern crate emit; let user = "user-123"; let item = "product-456"; emit::emit!("{user} added {item} to their cart"); }
This event will have:
mdl
: The path of the module that calledemit!
, likeshop::orders::add_to_cart
.tpl
: The raw template. In this case it's"{user} added {item} to their cart"
. When rendered, the template will produce user-123 added product-456 to their cart.extent
: The time whenemit!
was called, like2024-01-02T03:04:05.678Z
. Extents may also be a range. See Extents and timestamps for details.props
: Any properties referenced in or after the template. In this case it'suser
anditem
, so the properties are{ user: "user-123", item: "product-456" }
. Property values aren't restricted to strings, they can be any primitive or complex type. See Value data model for details.
Extensions
The core event data model doesn't encode any specific diagnostic paradigm. It doesn't even include log levels. emit
uses well-known properties to support extensions to its data model. A well-known property is a reserved name and set of allowed values that consumers of diagnostic data can use to treat an event as something more specific. See the well_known
module for a complete list of well-known properties.
The two main extensions to the event data model are tracing, and metrics. You can also define your own extensions. These extensions are both based on the evt_kind
well-known property. Consumers that aren't specially aware of it will treat unknown extended events as regular ones.
Value data model
The Value
type is emit
's representation of an anonymous structured value based on the value_bag
library. Value
is a concrete type rather than a trait to make working with them in Props
easier. Internally, a value holds a direct reference or embedded primitive value for:
- Integers:
i8
-i128
,u8
-u128
. - Binary floating points:
f32
-f64
. - Booleans:
bool
. - Strings:
&'v str
.
Values can also store more complex types by embedding references implementing a trait from a serialization framework:
- Standard formatting:
std::fmt::Debug
,std::fmt::Display
. - Serde:
serde::Serialize
. - Sval:
sval::Value
.
A value can always be formatted or serialized using any of the above frameworks, regardless of whatever might be embedded in it, in the most structure-preserving way. That means if you embed an enum using serde::Serialize
you can still serialize it as an enum using the sval::Value
implementation on Value
.
Extents and timestamps
The time-oriented part of an event is its Extent
. Internally, an extent stores Timestamp
s. An extent can either store one or a pair of timestamps.
An extent that stores a single timestamp is called a point. These are used by log events and other events that represent a point-in-time observation.
An extent that stores a pair of timestamps is called a range. These are used by trace spans and other events that represent something happening over time.
Constructing events without macros
Events don't have to be constructed using macros. You can use the Event::new
constructor manually:
#![allow(unused)] fn main() { extern crate emit; let parts = [ emit::template::Part::hole("user"), emit::template::Part::text(" added "), emit::template::Part::hole("item"), emit::template::Part::text(" to their cart"), ]; let evt = emit::Event::new( // mdl emit::path!("shop::orders::add_to_cart"), // tpl emit::Template::new_ref(&parts), // extent emit::Timestamp::try_from_str("2024-01-02T03:04:05.678Z").unwrap(), // props [ ("user", "user-123"), ("item", "product-456"), ] ); }