iii-observability
v0.12.0OpenTelemetry-based traces, metrics, logs, alerts, and sampling.
exact versions are immutable; binary and bundle artifacts are digest-pinned.
skill doc
Emit a log entry through the engine's OpenTelemetry pipeline
how-toWhen to use
The five emit functions all do the same thing — record a structured log entry in the engine's OTel log pipeline — and differ only in severity. Pick by how a downstream consumer (alerting, log routing, the log reactive trigger) should treat the message.
| Function | Severity | OTel severity_number | Typical use |
|---|---|---|---|
engine::log::trace |
TRACE | 1 | Per-step diagnostic noise; almost always sampled out. |
engine::log::debug |
DEBUG | 5 | Developer-facing detail useful during incidents. |
engine::log::info |
INFO | 9 | Normal operational events; the default for most callers. |
engine::log::warn |
WARN | 13 | Recoverable problems worth surfacing in dashboards. |
engine::log::error |
ERROR | 17 | Failures that warrant alerting or operator attention. |
Reach for these when:
- A handler should record progress, decisions, or failure detail in a way that survives in the engine's log store and propagates to OTLP exporters when configured.
- You want a
logreactive trigger (see React to ingested log entries) to fire on the message — every emit goes through the same pipeline that feeds the trigger. - You want trace correlation: pass
trace_id/span_idso the entry shows up under the right span inengine::traces::tree.
Use engine::traces::* (iii://iii-observability/engine/traces/traces) instead when the goal is to record timing/structure of an operation — spans are richer than logs for that.
Inputs
All five functions accept the same OtelLogInput:
{
"message": "request handled", // required. The log body.
"data": { "user_id": "u_1" }, // optional. Structured attributes attached to the log entry.
"trace_id": "0123456789abcdef0123456789abcdef", // optional. Correlation to a specific trace; leave unset to inherit the current OTel context.
"span_id": "0123456789abcdef", // optional. Correlation to a specific span within the trace.
"service_name": "billing" // optional. Override the service name attribute on this entry; defaults to the worker's configured `service_name`.
}message is required. All other fields are optional.
Outputs
The functions return no value (FunctionResult::NoResult); on the wire the response is null. The observable effects happen on the side: the entry is stored in memory (subject to logs_max_count and logs_retention_seconds), the log reactive trigger fires, and — when logs_console_output is on — the entry is printed to stderr.
When the worker is disabled (config.enabled: false) or logs_enabled: false, the call is silently a no-op: no storage, no trigger fan-out, no console print, no export.
Worked example
Emit an error entry with a structured payload and trace correlation:
{
"message": "payment authorization failed",
"data": { "user_id": "u_1", "amount_cents": 4200, "code": "card_declined" },
"trace_id": "0123456789abcdef0123456789abcdef",
"span_id": "0123456789abcdef",
"service_name": "billing"
}The typical pattern is one call per significant operational event: an info on success, a warn on a recoverable problem, an error on a failure that should alert. Pass data for structured fields the log query and trigger handler can branch on (level, user_id, request_id, etc.) — those land in the entry's attributes map and are queryable via engine::logs::list.
For runnable scaffolds (TypeScript, Python, Rust) plus the standard Logger wrapper that batches these calls, see the observability worker source and SDK examples in the iii main repo.
Related
- React to ingested log entries — register a
logtrigger to run a function every timeengine::log::*is called. engine::logs::list— query the stored entries this function produces.engine::traces::*— for span-shaped telemetry; pair with logs by passing the sametrace_id.