logd 0.8.1
logd: ^0.8.1 copied to clipboard
Modular hierarchical logging for Dart and Flutter. Features modular pipeline processing, O(1) configuration resolution, and low‑overhead performance.
logd

A modular hierarchical logger for Dart and Flutter. Build structured logs, control output destinations, and keep overhead minimal.
Why logd? #
- Hierarchical configuration – Loggers are named with dot‑separated paths (
app.network.http). Settings propagate from parents to children unless overridden. - Zero‑boilerplate – Simple
Logger.get('app')gives a fully‑configured logger. - Performance‑first – Lazy resolution, aggressive caching, and optional inheritance freezing keep the cost of a disabled logger essentially zero.
- Flexible output – Choose between console, file, network, HTML, or any custom sink; format logs as text, structured JSON, HTML, Markdown or LLM‑optimized TOON.
- Layout Sovereignty – A centralized engine guarantees structural integrity (e.g., perfect boxes) across all terminal widths.
- Platform‑agnostic styling – Decouple visual intent from representation using the semantic
LogThemesystem.
Getting Started #
Add logd to your project:
dependencies:
logd: ^latest_version
Then run:
dart pub get # or flutter pub get
Quick Example #
import 'package:logd/logd.dart';
void main() {
final logger = Logger.get('app');
logger.info('Application started');
logger.debug('Debug message');
logger.warning('Low disk space');
logger.error('Connection failed',
error: exception,
stackTrace: stack,
);
}
Typical console output
[app][INFO] 2025-01-23 05:30:12.456
--example/main.dart:12 (main)
----Application started
Tip: Use
Logger.configureto set global log‑levels, handlers, or timestamps.logduses Deep Equality to ensure that re-configuring with identical values results in zero performance overhead.
Core Concepts #
Hierarchical Loggers #
Loggers inherit configuration from their ancestors.
// Configure the entire app
Logger.configure('app', logLevel: LogLevel.warning);
// Override a subsystem
Logger.configure('app.network', logLevel: LogLevel.debug);
// Create a logger deep in the tree
final uiLogger = Logger.get('app.ui.button'); // inherits WARNING
final httpLogger = Logger.get('app.network.http'); // inherits DEBUG
Log levels #
| Level | Description |
|---|---|
trace |
Diagnostic noise |
debug |
Developer debugging |
info |
Informational |
warning |
Potential issue |
error |
Failure |
Advanced Features #
Custom Handlers #
Create complex pipelines of formatters and sinks:
final jsonHandler = Handler(
formatter: const JsonFormatter(
metadata: {LogMetadata.timestamp, LogMetadata.logger},
),
sink: FileSink(
'logs/app.log',
fileRotation: TimeRotation(
interval: Duration(days: 1),
timestamp: Timestamp(formatter: 'yyyy-MM-dd'),
backupCount: 7,
compress: true,
),
),
filters: [LevelFilter(LogLevel.info)],
);
Logger.configure('app', handlers: [jsonHandler]);
Result: JSON logs written to logs/app.log, rotated daily, keeping 7 compressed backups.
Note
Modern formatters (v0.6.1+) automatically include mandatory data like level, message, error, and stackTrace. The metadata parameter is used only for additional context like timestamps or logger names.
Atomic multi‑line logs #
Prevent interleaving in concurrent environments:
final buffer = logger.infoBuffer;
buffer?.writeln('=== User Session ===');
buffer?.writeln('User ID: ${user.id}');
buffer?.writeln('Login time: ${DateTime.now()}');
buffer?.writeln('IP: ${request.ip}');
buffer?.sink(); // writes atomically
Multiple Outputs #
You can either use multiple handlers:
final consoleHandler = Handler(
formatter: const StructuredFormatter(),
decorators: const [
BoxDecorator(),
StyleDecorator(),
SuffixDecorator(
label: '[v1.0.2]',
align: true,
),
],
sink: const ConsoleSink(lineLength: 80),
);
final fileHandler = Handler(
formatter: const PlainFormatter(),
sink: FileSink('logs/app.log'),
);
Logger.configure('global', handlers: [consoleHandler, fileHandler]);
Or use a multi-sink in a handler:
final multiSinkHandler = Handler(
formatter: PlainFormatter(),
sink: MultiSink(sinks: [
ConsoleSink(),
FileSink('logs/app.log'),
],
),
);
Filtering #
Control which logs reach which handlers:
// Level-based filtering
final errorHandler = Handler(
formatter: JsonFormatter(),
sink: FileSink('logs/errors.log'),
filters: [LevelFilter(LogLevel.error)], // Errors only
);
// Regex-based filtering (exclude sensitive data)
final publicHandler = Handler(
formatter: PlainFormatter(),
sink: FileSink('logs/public.log'),
filters: [
RegexFilter(r'password|secret|token', exclude: true),
],
);
Timezone & Timestamp #
final timestamp = Timestamp(
formatter: 'yyyy-MM-dd HH:mm:ss.SSS Z',
timezone: Timezone.named('America/New_York'),
);
Logger.configure('app', timestamp: timestamp);
File Rotation #
| Strategy | Example |
|---|---|
| Size | FileSink('logs/app.log', fileRotation: SizeRotation(maxSize: '10 MB', backupCount: 5, compress: true)) |
| Time | FileSink('logs/app.log', fileRotation: TimeRotation(interval: Duration(hours: 1), timestamp: Timestamp(formatter: 'yyyy-MM-dd_HH'), backupCount: 24)) |
High-Performance Execution (v0.8.0+) #
logd features a modular engine architecture to match your performance requirements:
- StandardEngine (Default): A reliable, platform-agnostic engine running on the Dart GC heap. Fully compatible with Web, Desktop, Mobile, and VM.
- ArenaEngine: Uses isolate-local LIFO object pooling to eliminate GC pressure. Ideal for complex logs with many decorators.
- NativeEngine (Opt-in): Leverages
dart:ffiand a Binary IR (B-IR) instruction stream for native VM platforms. Fully stabilized in v0.8.1 with 100% verified layout parity.
// StandardEngine is used by default.
// You can explicitly swap to ArenaEngine or NativeEngine in your Handler setup.
// To optimize performance further, you can freeze inheritance:
Logger.get('app').freezeInheritance();
For a detailed walkthrough of each engine, including real-world benchmarks and isolate-offloading configuration, see the Execution Engines Guide.
Use Cases #
Development Console #
Logger.configure('global', handlers: [
const Handler(
formatter: StructuredFormatter(),
decorators: [
HierarchyDepthPrefixDecorator(indent: '│ '),
BoxDecorator(borderStyle: BorderStyle.rounded),
StyleDecorator(theme: LogTheme(colorScheme: LogColorScheme.darkScheme)),
],
sink: const ConsoleSink(lineLength: 80),
),
]);
Production JSON #
Logger.configure('global', handlers: [
Handler(
formatter: JsonFormatter(),
sink: FileSink('logs/production.log'),
),
]);
LLM-Native Logging (TOON) #
Optimize logs for consumption by AI agents by using the Token-Oriented Object Notation:
Logger.configure('ai.agent', handlers: [
Handler(
formatter: const ToonFormatter(
arrayName: 'context',
metadata: {LogMetadata.timestamp},
),
sink: FileSink('logs/ai_feed.toon'),
),
]);
Result: A highly token-efficient, flat format that LLMs can parse with minimal overhead.
Schema Maturity
v0.7.1+ introduces Explicit Schemas for TOON. By setting explicitSchema: true, the formatter generates a typed, aligned header block that describes every column (including enum values for levels):
logs[]{
timestamp : iso8601;
level : enum(TRACE,DEBUG,INFO,WARNING,ERROR);
message : markdown;
}:
This provides zero-shot precision for machine-consumption without prior out-of-band configuration.
Network Logging #
Ship logs to remote servers with built-in resilience:
const httpSink = HttpSink(
url: 'https://logs.api.com',
batchSize: 50,
flushInterval: Duration(seconds: 10),
dropPolicy: DropPolicy.discardOldest,
);
Logger.configure('app', handlers: [
Handler(formatter: JsonFormatter(), sink: httpSink),
]);
Supported sinks: HttpSink (batching & retries), SocketSink (real-time streaming).
// For real-time streaming to a WebSocket server:
const socketSink = SocketSink(
url: 'wss://monitor.example.com/logs',
);
Microservice Logging #
Logger.configure('api', handlers: [
Handler(formatter: JsonFormatter(), sink: FileSink('logs/api.log')),
]);
Logger.configure('database', handlers: [
Handler(formatter: JsonFormatter(), sink: FileSink('logs/db.log')),
]);
Logger.configure('auth', handlers: [
Handler(
formatter: JsonFormatter(),
sink: FileSink('logs/security.log'),
filters: [LevelFilter(LogLevel.warning)],
),
]);
Flutter integration #
Capture framework errors and async errors:
void main() {
Logger.attachToFlutterErrors(); // listens to all uncaught Flutter errors
runZonedGuarded(
() => runApp(MyApp()),
(error, stack) {
Logger.get('app.crash').error(
'Uncaught error',
error: error,
stackTrace: stack,
);
},
);
}
Documentation #
- Documentation Index - Overview and navigation
- Logger Philosophy - Design principles and rationale
- Logger Architecture - Implementation details
- Handler Guide - Pipeline and sink customization
- Execution Engines Guide - Standard, Arena, and Native engines guide
- Engine Stability Report - Engine profiling & memory lifecycle report
- Migration Guide - Upgrading from legacy components
- Decorator Composition - Execution priority and flow
- Time Module - Timestamp and timezone handling
- Roadmap - Planned features and vision
Contributing #
- Report bugs or suggest features via GitHub Issues.
- Share ideas in Discussions.
All contributions should follow the guidelines in CONTRIBUTING.md and, for docs, doc/CONTRIBUTING_DOCS.md.
License #
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.
Support #
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Package page: logd on pub.dev