Observability
Purpose and Scope
Observability in Flue is the practice of understanding what happened while agents and workflows were running: whether work completed, failed, slowed down, or consumed more model resources than expected. The guide frames two complementary views. Workflow run inspection is for bounded jobs with an invocation and a result, while observe(...) is for application-wide activity that can include workflows and continuing agents. Use this page when you need to decide where to add logs, how to retrieve run history, and how to connect runtime activity to your own monitoring pipeline.
Sources: apps/docs/src/content/docs/guide/observability.md
Flue applications can contain both structured workflows and long-lived agent sessions. That distinction matters for monitoring. A workflow invocation has a runId, a recorded history, and a completed result or error. A continuing agent session may receive direct prompts or dispatched inputs over time, so it is better understood through activity events and operation boundaries rather than as a sequence of workflow runs. The public guide explicitly separates these cases so developers do not try to force every agent interaction into workflow-style inspection.
Sources: apps/docs/src/content/docs/guide/observability.md
Relevant Source Files
apps/docs/src/content/docs/guide/observability.md— Defines the public observability guide, including run inspection, Action context logging, SDK run retrieval, live streaming,observe(...), event examples, and the timing distinction betweenstartedAtandrun_start.
Core Observability Primitives
A runId identifies a workflow invocation. When a workflow runs through a Flue application and its module exposes runs middleware, the SDK client.runs surface can retrieve the run record, inspect events, or follow a live stream. The same guide also points to the raw /runs APIs as the HTTP-level equivalent. This makes run history the right tool for bounded work: summarize a document, process a job, or perform another task where the application expects a final returned value or a captured error.
Sources: apps/docs/src/content/docs/guide/observability.md
The Action context log methods are the primary way for application code to add business meaning to runtime activity. The guide names log.info(...), log.warn(...), and log.error(...), each accepting structured attributes. Attributes should carry values that are useful later for search, aggregation, or forwarding to a monitoring system, such as input sizes, token counts, costs, provider identifiers, or application-specific IDs. Runtime events can explain that an operation happened; structured logs explain why it mattered for your product or support process.
Sources: apps/docs/src/content/docs/guide/observability.md
The observe(...) function registers an application observer from the entrypoint. Its callback receives activity handled by that running application context, including asynchronously dispatched input. The guide treats an operation as the useful finite boundary for agent activity: prompting a session, running a skill, or delegating work are examples of activity that can be timed and inspected. This is especially important for continuing agents because a single ongoing session may contain many independently useful operations without producing a workflow run record for each one.
Sources: apps/docs/src/content/docs/guide/observability.md
Inspect Workflow Runs
Add structured logs inside workflow code when runtime traces alone are not enough. The guide's summarization example records the accepted document size before prompting an agent session and then records token and cost usage after the model response returns. This pattern keeps operational facts close to the code that knows their meaning. A generic observer may know that a prompt occurred, but only the workflow knows that characters, tokens, and cost are the attributes your team will later use for debugging, alerting, or reporting.
Sources: apps/docs/src/content/docs/guide/observability.md
import { defineAgent, defineWorkflow } from '@flue/runtime';
import * as v from 'valibot';
const summarizer = defineAgent(() => ({
model: 'anthropic/claude-haiku-4-5',
instructions: 'Summarize the supplied document clearly and concisely.',
}));
export default defineWorkflow({
agent: summarizer,
input: v.object({ text: v.string() }),
async run({ harness, log, input }) {
log.info('Summarization requested', { characters: input.text.length });
const response = await (await harness.session()).prompt(input.text);
log.info('Summarization completed', {
tokens: response.usage.totalTokens,
cost: response.usage.cost.total,
});
return { summary: response.text };
},
});Run inspection has an important timing nuance. The guide states that a workflow's startedAt timestamp is captured before durable admission finishes, while live observers receive run_start after admission setup and immediately before workflow code begins. If admission takes time, these values can differ in a meaningful way. Use startedAt when you want the admitted invocation's full lifetime, and use run_start when you want to reason about live execution after the runtime is ready to call workflow code.
Sources: apps/docs/src/content/docs/guide/observability.md
Observe Application Activity
Register observe(...) in the application entrypoint when you need one callback for runtime activity across workflows and continuing agents. The guide's example imports observe from @flue/runtime, mounts Flue routing with flue() from @flue/runtime/routing, and then reacts to three event categories: failed workflow completion, slow operations, and error-level logs. This placement is intentional. Observing at the application boundary lets you see work triggered by HTTP routes, workflow invocations, and asynchronously dispatched agent input in the same running context.
Sources: apps/docs/src/content/docs/guide/observability.md
import { observe } from '@flue/runtime';
import { flue } from '@flue/runtime/routing';
import { Hono } from 'hono';
observe((event) => {
if (event.type === 'run_end' && event.isError) {
console.error('Workflow failed', event.runId, event.error);
}
if (event.type === 'operation' && event.durationMs > 5_000) {
console.warn('Slow operation', event.operationKind, event.durationMs);
}
if (event.type === 'log' && event.level === 'error') {
console.error(event.message, event.attributes);
}
});
const app = new Hono();
app.route('/', flue());
export default app;This observer pattern is intentionally small but expressive. run_end with isError is suitable for failure alerts tied to a workflow runId. operation with durationMs supports latency checks across agent activity such as prompts, skill execution, and delegation. log with an error level forwards application-authored context from the Action logging contract. In practice, your callback can write to the console during development, send events to an observability backend in production, or normalize attributes into the schema used by your existing incident and analytics tooling.
Sources: apps/docs/src/content/docs/guide/observability.md
System-to-Code Mapping
| Reader task | Flue primitive | Where it appears | Notes |
|---|---|---|---|
| Inspect a bounded workflow job | runId, run record, run events | apps/docs/src/content/docs/guide/observability.md | Use SDK client.runs or raw /runs APIs when the workflow module exposes runs middleware. |
| Add business context | log.info(...), log.warn(...), log.error(...) | apps/docs/src/content/docs/guide/observability.md | Attach structured attributes for search, aggregation, and forwarding. |
| Monitor continuing agents | observe(...) and operation events | apps/docs/src/content/docs/guide/observability.md | Use operation boundaries instead of expecting direct prompts or dispatch(...) inputs to appear as workflow runs. |
| Understand timing | startedAt and run_start | apps/docs/src/content/docs/guide/observability.md | startedAt covers the admitted invocation lifetime; run_start marks live workflow execution. |
Implementation Guidance
Start with the smallest signal that answers your operational question. If the question is whether a workflow succeeded and what it returned, inspect the workflow run. If the question is why a particular input was expensive, add Action context logs with structured attributes inside the workflow. If the question crosses workflows and agents, register observe(...) once in the application entrypoint and route events to the destination your deployment uses. Keeping those responsibilities separate prevents logs, live events, and historical run records from becoming duplicate sources of truth.
Sources: apps/docs/src/content/docs/guide/observability.md
Be deliberate about attribute names. The guide recommends attributes for values you may later search, aggregate, or forward. That means choosing stable keys such as characters, tokens, cost, operationKind, or a domain identifier rather than embedding everything in a free-form message. A message should remain human-readable, while attributes should remain machine-queryable. This pattern also makes it easier to evolve from console output during local development to production telemetry export without rewriting the workflow business logic.
Sources: apps/docs/src/content/docs/guide/observability.md
Next Steps
After adding observability, verify each path separately: invoke a workflow and confirm a runId is returned, fetch or stream the run through the SDK or /runs API, trigger a direct or dispatched agent operation and confirm the application observer sees it, and force an error path to ensure log.error(...) or run_end handling reaches your alerting destination. For adjacent concepts, read the workflow guide for durable invocation shape, the Action API for the logging contract, the streaming protocol reference for raw run streams, and the OpenTelemetry tooling page when you are ready to export telemetry from a production deployment.