---
type: concept
title: Logging and Observability
description: Structured logging with evlog, Langfuse integration for tracing, and metering via Receipt primitives for billing and debugging.
tags: [logging, observability, evlog, langfuse, metering]
verified:
  - by: openwiki/0.7.2
    at: 2026-10-14T20:42:32.326Z
---
# Logging and Observability

The Company OS provides built‑in observability through three interconnected mechanisms:

1. **Structured logging** via the `evlog` library, which emits JSON logs with request‑level context.
2. **Distributed tracing** through Langfuse, correlated via the `langfuse_trace_id` stored in every Receipt.
3. **Metering for billing** captured atomically in the Receipt primitive, which records infrastructure and model costs, token usage, and outcomes.

These primitives work together to give operators visibility into request flows, cost attribution, and audit trails without requiring external instrumentation.

## evlog Integration

Both the Hono‑based server (`apps/server`) and the Next.js admin UI (`apps/admin`) initialize `evlog` at startup.

### Server (Hono)

```ts
import { initLogger } from "evlog";
import { createAuthMiddleware } from "evlog/better-auth";
import { createFsDrain } from "evlog/fs";
import { evlog } from "evlog/hono";

initLogger({
  env: { service: "os-server" },
});

const identifyUser = createAuthMiddleware(auth as BetterAuthInstance, {
  exclude: ["/api/auth/**"],
  maskEmail: true,
});

const app = new Hono<EvlogVariables>();
app.use(
  evlog({
    drain: process.env.NODE_ENV === "production" ? undefined : createFsDrain(),
  })
);
app.use("*", async (c, next) => {
  await identifyUser(c.get("log"), c.req.raw.headers, c.req.path);
  return next();
});
```
* `initLogger` sets the service name to `"os-server"`.
* The `evlog` middleware attaches a logger to each request and writes logs to the configured drain.
* In development, logs go to the filesystem via `createFsDrain`; in production, the drain is `undefined`, which defaults to standard output/error.
* The `identifyUser` middleware (built on `evlog/better-auth`) adds user‑identifying fields to the log context while masking email addresses.

### Admin UI (Next.js)

```ts
import { createFsDrain } from "evlog/fs";
import { createEvlog } from "evlog/next";
import { createInstrumentation } from "evlog/next/instrumentation/create";

export const { withEvlog, useLogger, log, createError } = createEvlog({
  drain: process.env.NODE_ENV === "production" ? undefined : createFsDrain(),
  service: "os-web",
});

export const { register, onRequestError } = createInstrumentation({
  service: "os-web",
});
```
* `createEvlog` initializes logging for the Next.js app with service name `"os-web"`.
* The returned helpers (`withEvlog`, `useLogger`, `log`, `createError`) are used throughout the admin UI to emit structured logs.
* `createInstrumentation` provides Next.js‑specific hooks for request‑level error reporting.

## Structured Logging Features

* **JSON output**: All log objects are serialized as JSON, making them easy to ingest by log‑aggregation systems (e.g., Loki, Elasticsearch).
* **Request correlation**: Each request receives a unique trace ID that is propagated to logs, enabling end‑to‑end request tracing.
* **User context**: After authentication, the log context includes user ID, email (masked in non‑production), and tenant ID.
* **Automatic error capture**: Unhandled exceptions are logged with full stack traces.
* **Selective masking**: Sensitive fields such as email addresses and authorization headers are automatically redacted in non‑secure environments.

## Langfuse Tracing Integration

While `evlog` handles logging, distributed tracing is delegated to Langfuse. The OS does not instrument Langfuse directly; instead, every Receipt generated by the OS contains a `langfuse_trace_id` field. This ID is populated by the agent runtime when it creates a trace in Langfuse and is persisted alongside the Receipt. Operators can correlate log entries (via the request ID) with Langfuse traces (via the `langfuse_trace_id`) to inspect model calls, tool invocations, and latency breakdowns.

Evidence of this integration appears in the Receipt primitive definition:

> The Receipt primitive records every atomic action or tool call executed by the OS. It contains: correlation ID (linking to the initiating task), tenant ID, principal ID, capability invoked, effect class, target, costs (model and infrastructure in cents), token usage, **Langfuse trace ID**, outcome (pending, success, failed, denied, uncertain), and timestamps.

Source: `/openwiki/concepts/primitives.md#L64`

## Metering for Billing

Metering is a core primitive of the OS, not an after‑the‑fact add‑on. Every action that traverses the OS layer produces exactly one Receipt, which captures the cost dimensions required for billing:

* `cost_model_cents` – spend on LLMs via LiteLLM.
* `cost_infra_cents` – estimated infrastructure usage (compute, storage, network).
* `tokens_in` / `tokens_out` – token counts for model calls.
* Outcome status (success, failure, etc.) for audit and retry logic.

Because the Receipt is created synchronously with the action, metering is accurate from the first line of code and enables a pay‑per‑action business model.

Source: `/docs/adr/ADR-0001-company-os-multi-tenant-metered.md#L49`

## Operational Notes

* **Log retention**: In production, logs are typically shipped to an external system (e.g., via Vector or FluentBit) from stdout/stderr. The OS itself does not retain logs beyond the process lifetime.
* **Trace sampling**: Langfuse tracing is applied at the agent level; the OS does not sample traces. All actions that produce a Receipt should have a corresponding trace if the agent runtime is instrumented.
* **Security**: Logs never contain raw API keys, passwords, or full request bodies. The `evlog` middleware and BetterAuth integration ensure that authentication tokens are stripped and PII is masked unless explicitly allowed in a secure environment.

## Relationship to Other Systems

* The logging and observability layer sits **above** the Core apps+data layer but **below** the admin UI and channel adapters. It observes all interactions that flow through the OS primitives (Principal → Capability → Task → Receipt → Policy).
* Metrics derived from Receipts feed into billing pipelines and internal dashboards, while Langfuse traces power debugging and performance analysis for agent developers.
* Because the OS enforces that every capability invocation goes through its layer, observability is guaranteed for all agent actions regardless of entry channel (MCP, REST, or internal scheduler).

--- 