System Overview

This package implements a layered diagnostic output system with clean separation between message dispatch, content transformation, and output rendering. The architecture enables flexible configuration, library-friendly registration, and optional Rich integration without tight coupling.

Component Layers

The system is organized into three primary layers:

┌─────────────────────────────────────────────────────────────┐
│  Dispatcher Layer                                           │
│  ┌───────────┐  Manages routing and activation control    │
│  │ Dispatcher├──────────────────────────────────────────┐  │
│  └─────┬─────┘                                          │  │
└────────┼────────────────────────────────────────────────┼──┘
         │ creates/caches                         invokes │
         ↓                                                 ↓
┌─────────────────────────────────────────────────────────────┐
│  Reporter Layer                                             │
│  ┌────────┐  Coordinates textualization and printing      │
│  │Reporter├────────────────────────────────────────────┐  │
│  └───┬────┘                                            │  │
└──────┼─────────────────────────────────────────────────┼──┘
       │ uses                                      uses  │
       ↓                                                 ↓
┌──────────────────────────┐   ┌──────────────────────────┐
│   Compositor Layer       │   │    Printer Layer         │
│  ┌─────────────┐         │   │  ┌────────┐             │
│  │ Compositor  │ Formats │   │  │Printer │ Outputs     │
│  │  + Introducer content │   │  └────────┘ to targets  │
│  │  + Linearizers        │   │                          │
│  └─────────────┘         │   │                          │
└──────────────────────────┘   └──────────────────────────┘

Dispatcher Layer: Entry point for diagnostic calls. Routes messages to appropriate reporters based on module address and flavor. Manages activation control (which flavors/trace levels are active per module).

Reporter Layer: Bridges dispatcher and output. Each reporter instance binds a specific address + flavor combination to a compositor and printer. Active/inactive state controls whether messages are processed.

Compositor Layer: Transforms structured records into formatted text lines. Composed of introducer (prefix generation), linearizers (content to lines), and layout logic. Supports both plain and Rich rendering.

Printer Layer: Abstracts output targets. Default prints to stderr, but custom printers can route to files, logging, or other destinations. Provides column constraints to compositors for proper line wrapping.

Data Flow

Typical message flow through the system:

User Code
   │
   │ ctrl.note( "summary", detail1, detail2 )
   ↓
Dispatcher.__getattr__( "note" )
   │
   │ returns cached or creates Reporter
   ↓
Reporter.__call__( "summary", detail1, detail2 )
   │
   │ packages MessageContent → Record
   ↓
Compositor( control, record )
   │
   │ 1. Introducer generates prefix lines
   │ 2. Linearizers convert content to text lines
   │ 3. Layout combines introduction + content
   ↓
Printer( formatted_lines )
   │
   └→ Output Target (stderr, file, logging, etc.)

Configuration Hierarchy

Configuration follows Python package structure with inheritance:

Global Configuration (all modules)
   ├─→ Package Configuration (myapp.*)
   │      ├─→ Subpackage Configuration (myapp.subsystem.*)
   │      └─→ Module Configuration (myapp.module)
   └─→ Package Configuration (library.*)

Each level can specify:

  • Active flavors: Which message categories produce output

  • Max trace level: Deepest trace level to render

  • Printer factory: How to create output targets

  • Compositor factory: How to format messages

  • Per-flavor overrides: Flavor-specific configuration

Libraries register configurations without affecting application or other library settings. Applications can selectively enable library diagnostics by updating active flavors for specific addresses.

Standard Flavor Recipes

The ictr.standard subpackage provides ready-made configurations:

Message Flavors: note, monition, error, errorx, abort, abortx, future, success, advice with semantic labels, colors, and emoji.

Trace Levels: 0-9 with automatic hierarchical indentation (2 spaces per level) for visualizing call depth.

Rich Integration: Automatic detection and graceful fallback. When Rich is available, enables colorized output with styled prefixes. When absent, uses plain text formatting.

These recipes implement common patterns but all components are replaceable via configuration factories.

Extension Points

The architecture provides multiple extension points:

Custom Compositors: Implement Compositor protocol to control formatting logic. Factory pattern allows different compositors per address or flavor.

Custom Printers: Implement Printer protocol to route output to custom targets (files, logging, remote services, etc.).

Custom Introducers: Replace prefix generation logic while reusing standard linearizers and layout.

Custom Linearizers: Replace content-to-lines conversion for specific object types (structured data, custom exceptions, etc.).

All extensions integrate through factory configuration, preserving the layered architecture and avoiding tight coupling.

Threading and Concurrency

The system is designed for concurrent use:

  • Dispatcher registration uses mutex-protected initialization

  • Reporters are immutable once created

  • Compositors and printers are stateless protocols

  • Configuration objects are immutable dataclasses

No global mutable state exists after initialization, enabling safe concurrent message emission from multiple threads.

Deployment Patterns

Application Development: Use global installation (install_builtin()) for convenient access without imports. Configure global settings and per-module overrides as needed.

Library Development: Register library-specific configuration during import. Respect application’s activation control. Avoid global installation from libraries.

Testing: Direct instantiation without builtins. Override printer factories to capture output for assertions. Mock auxiliaries (time, thread discovery) for deterministic tests.