OpenWork Logging: How the Platform Implements Structured Observability with Pino

OpenWork implements a centralized, structured logging system built on the Pino library, using a single appLogger instance that forks into component-specific child loggers to emit JSON-formatted logs with automatic context injection.

OpenWork relies on high-performance observability to manage its enterprise features, and its logging architecture reflects this priority. The platform implements a structured logging strategy centered around the pino library, enabling machine-readable JSON output across all services. This approach ensures that logs from different subsystems can be aggregated, filtered, and analyzed without complex parsing.

Centralized Logger Configuration

The foundation of OpenWork logging resides in dev/ee/apps/den-api/src/observability/logger.ts. This file exports a singleton instance named appLogger that wraps the Pino library with platform-specific configuration.

By instantiating the logger once and importing it across modules, OpenWork maintains consistent log formatting and transport settings throughout the application. The centralization eliminates configuration drift and ensures that every subsystem emits logs with the same baseline properties, including timestamps and log levels.

Component-Scoped Child Loggers

Rather than creating new logger instances for each module, OpenWork uses Pino's child logger pattern to create specialized loggers that inherit from appLogger while injecting component-specific metadata.

In dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts, the implementation creates a child logger scoped to the plugin system:

import { appLogger } from "../../../observability/logger.js";

const logger = appLogger.child({ component: "plugin_system_store" });

Similarly, dev/ee/apps/den-api/src/routes/org/mcp-connections.ts initializes a logger for connection management:

const logger = appLogger.child({ component: "mcp_connections" });

This pattern automatically appends a component field to every log entry, enabling precise filtering in log aggregation tools like Loki or Datadog. When logger.info() or logger.error() is called, the resulting JSON includes both the message and the component identifier.

Structured JSON Output and Log Levels

OpenWork logging adheres to standard severity levels: debug, info, warn, and error. The Pino-based implementation emits these as newline-delimited JSON objects, making the logs immediately consumable by structured logging pipelines.

When an error occurs in the plugin system store, the code captures both the error message and contextual data:

logger.error("failed to update marketplace", {
  marketplaceId,
  error: err.message,
});

The resulting JSON output includes the component field from the child logger, the marketplaceId, and the error details, eliminating the need for string concatenation or manual formatting. This structured approach supports high-cardinality fields without performance penalties, as Pino optimizes JSON serialization through native fast-stringify mechanisms.

Development-Only Logging Utilities

While production code relies on the structured Pino logger, OpenWork includes lightweight console-style utilities for local development scripts. The file dev/scripts/dev-headless-web.ts implements a simple logLine helper that writes prefixed messages to a temporary log file:

const logLine = (message: string) => {
  console.log(`[dev:headless-web] ${message}`);
};

logLine("Starting services");
logLine(`Workspace: ${workspace}`);
logLine(`Web URL: ${webUrl}`);

This dual-path approach ensures that developers see human-readable output during local debugging, while production deployments capture machine-readable JSON suitable for automated alerting and analysis.

Summary

OpenWork implements a sophisticated yet straightforward logging strategy that balances developer experience with production observability:

  • Centralized Pino instance: The appLogger singleton in dev/ee/apps/den-api/src/observability/logger.ts provides consistent configuration across all services.
  • Component scoping: Child loggers inject component metadata automatically, enabling granular filtering by subsystem.
  • JSON structured output: All production logs emit as JSON, supporting direct ingestion into log aggregation platforms.
  • Standard log levels: The implementation supports debug, info, warn, and error methods throughout the codebase.
  • Development utilities: Simple console wrappers in scripts like dev/scripts/dev-headless-web.ts provide readable output for local debugging without affecting production log structure.

Frequently Asked Questions

What logging library does OpenWork use?

OpenWork uses the Pino library for its core logging infrastructure. Pino was selected for its high-performance JSON serialization and low overhead, making it suitable for production environments where log volume and parsing speed are critical concerns.

How does OpenWork distinguish logs from different components?

OpenWork distinguishes logs by creating child loggers via appLogger.child({ component: "component_name" }). Each child logger automatically includes the component identifier in every log entry, allowing operators to filter logs by specific subsystems such as plugin_system_store or mcp_connections without parsing message strings.

What format do OpenWork logs use in production?

In production, OpenWork emits JSON-formatted log lines. Each log entry is a newline-delimited JSON object containing the message, severity level, timestamp, component name, and any additional structured metadata passed by the developer. This format integrates directly with modern log aggregation services.

Does OpenWork support different log levels?

Yes, OpenWork supports standard log levels including debug, info, warn, and error. These levels are available on all child loggers and can be filtered at the transport level or within log aggregation tools to reduce noise during routine operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →