Logging & Tracing Conventions
SkillMonitoring & opsTracing and logging conventions for the Golem codebase. Use when adding tracing::debug!, info!, warn!, error! calls, or reviewing log statements for style.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Logging & Tracing Conventions skill
What this skill tells your AI
The instructions your AI receives, as published by golemcloud/golem in .agents/skills/logging/SKILL.md and read by ahel’s review.
Golem's service code uses the tracing crate for structured logging. Some existing code still uses
the log facade; do not copy that pattern into new service logging unless the API being integrated
specifically requires log records. Follow these conventions when adding or modifying tracing
statements.
Core Rule: Structured Attributes Over Format Interpolation
Always pass dynamic values as structured key-value attributes. Keep the format string static (no {variable} interpolation).
✅ Good — structured attributes
debug!(
shard_ids_current = shard_assignment.shard_ids.iter().join(", "),
shard_ids_to_assign = shard_ids.iter().join(", "),
"ShardService.assign_shards"
);
tracing::warn!(
plugin = %grant_id,
error = %err,
"Locality recovery: failed to check locality"
);
error!(
agent_id = owned_agent_id.to_string(),
promise_id = promise_id.to_string(),
error = %e,
"Failed to complete promise"
);
info!(
agent_id = owned_agent_id.to_string(),
"Deleting cached status of fully archived worker"
);
❌ Bad — dynamic values in format string
tracing::error!("Failed to resolve target for plugin {grant_id}: {err}");
tracing::error!("Failed to send oplog entries to plugin {grant_id}: {err}");
debug!("Not enough memory to allocate {mem32} (available: {}), trying to free some up",
self.worker_memory.available_permits());
debug!("Updating cached worker status for {owned_agent_id} to {status_value:?}");
Corrected versions
tracing::error!(
plugin = %grant_id,
error = %err,
"Failed to resolve target for plugin"
);
tracing::error!(
plugin = %grant_id,
error = %err,
"Failed to send oplog entries to plugin"
);
debug!(
requested = mem32,
available = self.worker_memory.available_permits(),
"Not enough memory to allocate, trying to free some up"
);
debug!(
agent_id = %owned_agent_id,
status = ?status_value,
"Updating cached worker status"
);
Attribute Formatting
Use tracing's field syntax for values:
| Syntax | Meaning | When to use |
|---|---|---|
key = %value | Uses Display trait | IDs, strings, user-facing values |
key = ?value | Uses Debug trait | Enums, structs, complex types |
key = value | Literal / implements tracing::Value | Integers, bools, &str |
key = value.to_string() | Explicit conversion | When Display is not implemented or you want a specific format |
Prefer % over .to_string() when Display is implemented.
Message String
- The message (last argument) should be a static string that describes what is happening.
- Use short, descriptive messages — typically
"ServiceName.method_name"or a brief human-readable description. - Record error causes as structured fields such as
error = %error; do not interpolate them into the message.
Log Levels
| Level | Use for |
|---|---|
error! | Failures an operation cannot recover from or complete, especially broken invariants and conditions requiring attention |
warn! | Recoverable issues, degraded behavior, skipped operations |
info! | Significant lifecycle events (startup, shutdown, registration, archival) |
debug! | Detailed operational info useful during development |
trace! | Very fine-grained, rarely used in this codebase |
Import Style
- Prefer importing the macros directly:
use tracing::debug;(oruse tracing::{debug, info, warn, error};) - The fully-qualified
tracing::warn!(...)form is also acceptable, especially when only one or two calls exist in a file. - Prefer
tracingoverlogfor new service code. Uselogonly at an integration boundary that specifically consumes or emitslogrecords.
Canonical Example
From golem-worker-executor/src/services/shard.rs:
debug!(
shard_ids_current = shard_assignment.shard_ids.iter().join(", "),
shard_ids_to_assign = shard_ids.iter().join(", "),
"ShardService.assign_shards"
);
This demonstrates: structured attributes with descriptive keys, a static message identifying the operation, and no dynamic interpolation in the format string.
Signals
- GitHub stars
- 2k
- Forks
- 212
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
logging-golemcloud- Source
- github.com/golemcloud/golem