Logging
Structured logging helps you understand your application
Encore.rs logs through the standard log crate. Install Encore's logger once at startup, and every log record is printed as a structured line, with its key-value pairs as fields and the IDs of the current trace and span, so it's linked to the trace it was written in.
Setup
Add the log crate with its kv feature, for key-value pairs:
Cargo.toml[dependencies]
encore-dev = { path = "/path/to/encore-dev" }
log = { version = "0.4", features = ["kv"] }
Call encore::log::init() at the start of main:
src/main.rs#[tokio::main]
async fn main() {
encore::initialize();
encore::log::init();
// ...
}
init() installs Encore's logger as the log crate's global logger. It's safe to call more than once, but it panics if another global logger is already installed, such as env_logger.
Usage
Use the log macros: error!, warn!, info!, debug!, and trace!. Key-value pairs go before the message, separated from it by a semicolon:
log::info!(
order_id = order.id.as_str(),
customer_id = customer.id.as_str(),
total = order.total;
"order created"
);
Each pair becomes a field of the log record. Pass variable data as fields rather than interpolating it into the message:
// Prefer
log::info!(order_id = order_id.as_str(); "order created");
// Avoid
log::info!("order {order_id} created");
Interpolated values have to be parsed back out before they can be queried, and they make every message unique, so equivalent events can no longer be grouped or counted.
Logging errors
Log an error as a field. The kv feature captures values that implement Display with the % sigil:
if let Err(err) = charge_customer(&customer_id, amount).await {
log::error!(
err:% = err,
customer_id = customer_id.as_str(),
amount = amount;
"payment authorization failed"
);
return Err(err);
}
What's in a record
Each record is printed to stderr in Encore's structured log format, where encore run and Encore's log collection pick it up. A record has:
- The level, message, and time.
- The key-value pairs as fields.
- The record's target as its logger name: by default the module path, such as
my_app::orders. - The source file and line, as
code.file.pathandcode.line.number. - The trace ID and span ID of the current OpenTelemetry context, if there is one.
The span IDs come from encore::opentelemetry::Context::current(). Inside a Pub/Sub subscription handler, Encore sets that context for you. In an HTTP handler, attach your request span's context to the handler's future, as Tracing shows, and records written while handling the request are linked to its trace.
Levels
By default, records at info, warn, and error are printed, and debug and trace records are dropped.
The SDK's own log lines (from the encore crate and the runtime) are logged under the encore.runtime logger. Their level is controlled with the RUST_LOG environment variable, using the same syntax as env_logger.
Using your own logger
If your app installs its own global logger, compose Encore's into it instead of calling init(). encore::log::logger() returns Encore's logger as a &'static impl log::Log, which you can forward records to.
log and tracing
Encore's logger reads records from the log crate. Events from the tracing crate's macros aren't log records, so they aren't printed by it. Use tracing for spans (see Tracing) and log for log lines, or enable the tracing crate's log-always feature, which also emits its events as log records when a tracing subscriber is installed.
What to log
- A business state transition, such as a subscription activating or an order shipping.
- A decision the application made, together with the inputs that drove it.
- A fallback or retry that allowed an operation to complete.
- A failure, at the boundary that owns the recovery decision.
- A security-relevant or administrative action.
Encore already traces database queries, Redis commands, object storage operations, and Pub/Sub messages. Logs that narrate the same steps ("querying database", "publishing event") duplicate the trace. Use a metric rather than a log line per event when you only need to know how often something happens.
Viewing logs
Logs are shown in encore run's output. To read them afterwards, use encore logs, which reads the app's local telemetry store, so the app doesn't need to be running:
$ encore logs # the latest session$ encore logs --errors # only entries in which something failed$ encore logs --svc orders --since 10m