Getting started
Add emit to your Cargo.toml, along with an emitter to write diagnostics to:
[dependencies.emit]
version = "2.23.0"
[dependencies.emit_term]
version = "2.23.0"
Initialize emit at the start of your main.rs using emit::setup(), and ensure any emitted diagnostics are flushed by calling blocking_flush() at the end:
extern crate emit;
extern crate emit_term;
fn main() {
// Configure `emit` to write events to the console
let rt = emit::setup()
.emit_to(emit_term::stdout())
.init();
// Your app code goes here
// Flush any remaining events before `main` returns
rt.blocking_flush(std::time::Duration::from_secs(5));
}
You can configure emit to write to OpenTelemetry, the console, rolling files, or any custom emitter you create.
Start peppering diagnostics through your application with emit’s macros.
Logging events
When something of note happens, use debug! or info! to log it:
#![allow(unused)]
fn main() {
extern crate emit;
let user = "user-123";
let item = "product-123";
emit::info!("{user} added {item} to their cart");
}
When something fails, use warn! or error!:
#![allow(unused)]
fn main() {
extern crate emit;
let user = "user-123";
let err = std::io::Error::new(
std::io::ErrorKind::Other,
"failed to connect to the remote service",
);
emit::warn!("updating {user} cart failed: {err}");
}
Macro syntax
emit’s syntax is compatible with std::fmt in simple cases, but much more capable. It uses field-value syntax to name properties captured from local variables:
#![allow(unused)]
fn main() {
extern crate emit;
emit::info!(
"{user} added {item} to their cart",
user: "user-123",
item: "product-123",
);
}
See Template syntax and rendering for details.
Structured data
emit captures properties using their Display implementation by default with special handling for booleans and numbers. It uses attribute syntax to customize how properties are captured, such as using Serialize instead:
[dependencies.emit]
version = "2.23.0"
features = ["serde"]
#![allow(unused)]
fn main() {
extern crate emit;
extern crate serde;
use serde::Serialize;
#[derive(Serialize)]
struct User<'a> {
id: &'a str,
name: &'a str,
}
let user = User {
id: "user-123",
name: "Some User",
};
emit::info!(
"{#[emit::as_serde] user} added {item} to their cart",
item: "product-123",
);
}
Errors
emit has well-known property names that are understood by components of its runtime. You can use the err well-known property to capture an error using its Error implementation:
#![allow(unused)]
fn main() {
extern crate emit;
let user = "user-123";
let err = std::io::Error::new(
std::io::ErrorKind::Other,
"failed to connect to the remote service",
);
emit::error!("something went wrong: {err}");
}
Tracing functions
Add #[span] to a significant function in your application to trace its execution:
#![allow(unused)]
fn main() {
extern crate emit;
#[emit::span("add {item} to {user} cart")]
async fn add_item(user: &str, item: &str) {
// Your code goes here
}
}
Any diagnostics emitted within a traced function will be correlated with it. Any other traced functions it calls will form a trace hierarchy.
Macro syntax
emit’s tracing attributes use the same syntax and capturing rules as log events.
Ambient context
Any properties captured in your #[span] attribute template will appear on any other events emitted in the body of the span. If a property isn’t useful ambiently, but you still want to capture it, you can include it in the span event’s properties instead:
#![allow(unused)]
fn main() {
extern crate emit;
#[emit::span(
evt_props: emit::props! {
item,
},
"add to {user} cart",
)]
async fn add_item(user: &str, item: &str) {
// Your code goes here
}
}
Sampling metrics
Use sample! to write samples of the metrics your application tracks as events:
#![allow(unused)]
fn main() {
extern crate emit;
let bytes_written = 417;
emit::sample!(value: bytes_written, agg: "count");
}
Cumulative and delta metrics
Metrics produced by sample! are assumed to be cumultive by default. You can emit deltas instead by tracking the time range the delta applies to:
#![allow(unused)]
fn main() {
extern crate emit;
use std::time::Duration;
let end = emit::clock().now();
let start = end.map(|end| end - Duration::from_secs(30));
let bytes_written = 6;
// This sample tells us that between `start` and `end`, we wrote `bytes_written` more bytes
emit::sample!(extent: start..end, value: bytes_written, agg: "count");
}
See Delta metrics for details.
Quick debugging
Use the dbg! macro to help debug code as you’re writing it:
extern crate emit;
fn main() {
let user = "user@example.com";
let id = 42;
emit::dbg!(user, id);
}
It works a lot like the standard library’s dbg! macro, and is meant to be used as a quick, temporary debugging aid.
Next steps
To learn more about configuring emit, see the Emitting events section.
To learn more about using emit, see the Producing events section.
To learn emit’s architecture and syntax in more detail, see the Reference section.
You may also want to explore: