Files
gridpilot.gg/docs/architecture/shared/LOGGING.md
2026-01-11 13:04:33 +01:00

4.0 KiB

Logging & Correlation ID Design Guide (Clean Architecture)

This document defines a clean, strict, and production-ready logging architecture with correlation IDs.

It removes ambiguity around: • where logging belongs • how context is attached • how logs stay machine-readable • how Clean Architecture boundaries are preserved

The rules below are non-negotiable.

Core Principles

Logs are data, not text. Context is injected, never constructed in the Core.

Logging must: • be machine-readable • be framework-agnostic in the Core • support correlation across requests • be safe for parallel execution

Architectural Responsibilities

Core • describes intent • never knows about correlation IDs • never knows about log destinations

App Layer (API) • defines runtime context (request, user) • binds correlation IDs

Adapters • implement concrete loggers (console, file, structured) • decide formatting and transport

  1. Logger Port (Core)

Purpose

Defines what logging means, without defining how logging works.

Rules • exactly one logging interface • no framework imports • no correlation or runtime context

Location

core/shared/application/LoggerPort.ts

Contract • debug(message, meta?) • info(message, meta?) • warn(message, meta?) • error(message, meta?)

Messages are semantic. Metadata is optional and structured.

  1. Request Context (App Layer)

Purpose

Represents runtime execution context.

Contains • correlationId • optional userId

Rules • never visible to Core • created per request

Location

apps/api/context/RequestContext.ts

  1. Logger Implementations (Adapters)

Purpose

Provide concrete logging behavior.

Rules • implement LoggerPort • accept context via constructor • produce structured logs • no business logic

Examples • ConsoleLogger • FileLogger • PinoLogger • LokiLogger

Location

adapters/logging/

  1. Logger Factory

Purpose

Creates context-bound logger instances.

Rules • factory is injected • logger instances are short-lived • component name is bound here

Location

adapters/logging/LoggerFactory.ts adapters/logging/LoggerFactoryImpl.ts

  1. Correlation ID Handling

Where it lives • API middleware • message envelopes • background job contexts

Rules • generated once per request • propagated across async boundaries • never generated in the Core

  1. Usage Rules by Layer

Layer Logging Allowed Notes Domain No Throw domain errors instead Use Cases ⚠️ Minimal Business milestones only API Services Yes Main logging location Adapters Yes IO, integration, failures Frontend ⚠️ Limited Errors + analytics only

  1. Forbidden Patterns

Manual string prefixes ([ServiceName]) Global/singleton loggers with mutable state any in logger abstractions Correlation IDs in Core Logging inside domain entities

  1. File Structure (Final)

core/ └── shared/ └── application/ └── LoggerPort.ts # * required

apps/api/ ├── context/ │ └── RequestContext.ts # * required ├── middleware/ │ └── CorrelationMiddleware.ts └── modules/ └── */ └── *Service.ts

adapters/ └── logging/ ├── LoggerFactory.ts # * required ├── LoggerFactoryImpl.ts # * required ├── ConsoleLogger.ts # optional ├── FileLogger.ts # optional └── PinoLogger.ts # optional

Mental Model (Final)

The Core describes events. The App provides context. Adapters deliver telemetry.

If any layer violates this, the architecture is broken.

Summary • one LoggerPort in the Core • context bound outside the Core • adapters implement logging destinations • correlation IDs are runtime concerns • logs are structured, searchable, and safe

This setup is: • Clean Architecture compliant • production-ready • scalable • refactor-safe