logd 0.9.4 copy "logd: ^0.9.4" to clipboard
logd: ^0.9.4 copied to clipboard

Modular hierarchical logging for Dart and Flutter. Features modular pipeline processing, O(1) configuration resolution, and low‑overhead performance.

example/main.dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:logd/logd.dart';
import 'package:meta/meta.dart';

import '../../../scripts/servers/network_test_utils.dart';

/// This example serves as a comprehensive tutorial for [logd].
/// It covers everything from basic usage to advanced hierarchical configuration,
/// custom pipelines, and network logging.
void main() async {
  print('================================================');
  print('          LOGD: COMPREHENSIVE SHOWCASE          ');
  print('================================================');
  print('Welcome to the logd tutorial. Follow the console');
  print('output to see how to master the logging engine.');
  print('');

  await runZonedGuarded(
    () async {
      _showcaseBasics();
      _showcaseHierarchy();
      await _showcaseTargetHandlers();
      await _showcaseStructuredContext();
      _showcasePipelines();
      _showcaseTimeAndLocalization();
      _showcaseAdvancedLayouts();
      _showcaseGridLayouts();
      await _showcaseNetwork();

      print('\n\x1B[1mTutorial Complete!\x1B[0m');
      print('Check the source code in `example/main.dart` to learn more.');
    },
    (final error, final stack) {
      Logger.get().error(
        'Caught unexpected error in demo zone',
        error: error,
        stackTrace: stack,
      );
    },
  );
}

/// 1. The Basics: Getting Started
/// logd uses a hierarchical naming system. The root logger is accessed via
/// Logger.get(), or you can specify a name.
void _showcaseBasics() {
  _section('1. The Basics');

  // By default, logd is configured with a ConsoleSink and StructuredFormatter.
  final logger = Logger.get('app')
    ..info('Welcome to Logd!')
    ..debug('Debug messages are hidden by default if level is higher.')
    ..warning('This is a warning message.')
    ..trace('Trace messages are not visible, right now.');

  // You can check and change the log level globally or per logger.
  Logger.configure('app', logLevel: LogLevel.trace);
  logger.trace('Now trace messages are visible.');
}

/// 2. Hierarchical Logging
/// Loggers inherit configuration from their parents. Overriding a parent's
/// config allows for fine-grained control over specific modules.
void _showcaseHierarchy() {
  _section('2. Hierarchical Logging');

  // We'll configure a decorator to visually show the depth.
  final hierarchicalHandler = Handler(
    formatter: const StructuredFormatter(),
    decorators: [
      const StyleDecorator(),
      const HierarchyDepthPrefixDecorator(indent: '  │ '),
    ],
    sink: const ConsoleSink(),
  );

  Logger.configure('app.services', handlers: [hierarchicalHandler]);

  final authService = Logger.get('app.services.auth');
  final database = Logger.get('app.services.database');

  authService.info('User "admin" logged in.');
  database.debug('Starting transaction...');

  // Overriding a sub-logger's level doesn't affect the parent.
  Logger.configure('app.services.database', logLevel: LogLevel.error);
  database.info('This will NOT be printed.');
  database.error('Critical database failure!');

  // --- Dynamic Wildcard Pattern Matching (v0.8.7+) ---
  // You can also match loggers dynamically by pattern.
  // Overwrite any *.database loggers with a warning level threshold.
  Logger.configurePattern('*.database', logLevel: LogLevel.warning);
  database.info('Pattern Override: Database info log (will not print).');
  database.warning('Pattern Override: Database warning log (WILL print!).');

  // Clean up pattern rule to restore default inheritance
  Logger.removePattern('*.database');
}

/// 3. Pre-Wired Target Handlers & Dual-Mode .async() (v0.9.3+)
/// logd provides zero-config TargetHandlers for common output destinations,
/// with .async() factories for unblocked background isolate offloading.
Future<void> _showcaseTargetHandlers() async {
  _section('3. Pre-Wired Target Handlers (v0.9.3+)');

  // ConsoleHandler with customized theme
  final console = ConsoleHandler(
    theme: const LogTheme.dark(),
  );

  // In-memory ring-buffer with direct programmatic inspection
  final memory = MemoryHandler(capacity: 10);

  Logger.configure('app.target_demo', handlers: [console, memory]);

  final logger = Logger.get('app.target_demo');
  logger.info('Event dispatched to ConsoleHandler and MemoryHandler');
  logger.warning('Second event captured in memory ring-buffer');

  // Yield to allow non-blocking async pipeline dispatch to complete
  await Future<void>.delayed(Duration.zero);

  print(
    'Inspect MemoryHandler entries: ${memory.entries.length} logs captured.',
  );
}

/// 4. Ambient Structured Context (MDC) (v0.9.4+)
/// LogContext.run allows attaching request, user, or trace metadata to all logs
/// in an asynchronous execution scope without passing maps through function parameters.
Future<void> _showcaseStructuredContext() async {
  _section('4. Ambient Structured Context (v0.9.4+)');

  final logger = Logger.get('app.order');

  await LogContext.run({'requestId': 'req-9812', 'tenant': 'eu-central'},
      () async {
    logger.info('Processing incoming order submission');

    await Future<void>.delayed(const Duration(milliseconds: 10));
    logger.info(
      'Payment verified',
      context: {'amount': 120.50, 'currency': 'EUR'},
    );
  });
}

/// 5. Pipeline Architecture: Handlers, Formatters, & Decorators
/// logd pipelines are modular. A Handler composes a Formatter (what it says),
/// a sequence of Decorators (how it looks), and a Sink (where it goes).
void _showcasePipelines() {
  _section('5. Pipeline Architecture');

  const prettyHandler = Handler(
    formatter: JsonPrettyFormatter(),
    decorators: [
      BoxDecorator(borderStyle: BorderStyle.double),
      StyleDecorator(),
    ],
    sink: ConsoleSink(
      lineLength: 60,
    ),
  );

  Logger.configure('app.api', handlers: [prettyHandler]);
  Logger.get('app.api').info('Received GET /users/123');

  // Multi-Sink: Send logs to multiple places (e.g., Console + File)
  final multiHandler = Handler(
    formatter: const PlainFormatter(),
    sink: MultiSink([
      const ConsoleSink(),
      FileSink('logs/demo.log'),
    ]),
  );

  Logger.configure('app.audit', handlers: [multiHandler]);
  Logger.get('app.audit').info('Audit log saved to file (logs/) and console.');
}

/// 6. Time & Localization
/// You can configure how timestamps appear globally or per handler.
/// Timezones are fully supported (UTC, Local, or Fixed offsets).
void _showcaseTimeAndLocalization() {
  _section('6. Time & Localization');

  // Global Timestamp configuration via Logger.configure
  Logger.configure(
    'global',
    timestamp: Timestamp(
      formatter: 'yyyy-MM-dd HH:mm:ss.SSS',
      timezone: Timezone.utc(),
    ),
  );
  Logger.get('app').info('This log is in UTC.');

  // Reset to Local time
  Logger.configure(
    'global',
    timestamp: Timestamp(
      formatter: 'HH:mm:ss',
      timezone: Timezone.local(),
    ),
  );
  Logger.get('app').info('Now we are back to Local time.');

  // Named Timezone (e.g., Asia/Tehran)
  Logger.configure(
    'global',
    timestamp: Timestamp(
      formatter: 'HH:mm:ss ZZZ',
      timezone: Timezone.named('Asia/Tehran'),
    ),
  );
  Logger.get('app').info('Log from a specific named timezone.');

  // Reset to default for the rest of the demo
  Logger.configure(
    'global',
    timestamp: Timestamp(
      formatter: 'HH:mm:ss',
      timezone: Timezone.local(),
    ),
  );
}

/// 7. Advanced Layouts: Toon & JSON
/// Specialized formatters provide unique visual styles for different use cases.
void _showcaseAdvancedLayouts() {
  _section('7. Advanced Layouts');

  // Comic-style structured logs (with explicit schema for machine parsing)
  Logger.configure('app.toon', handlers: [
    const Handler(
      formatter: ToonFormatter(explicitSchema: true),
      sink: ConsoleSink(),
    )
  ]);
  Logger.get('app.toon').warning('Something unusual happened in the story!');

  // Vibrant JSON for deep inspection
  Logger.configure('app.vibrant', handlers: [
    const Handler(
      formatter: JsonPrettyFormatter(),
      sink: ConsoleSink(),
    )
  ]);
  Logger.get('app.vibrant').info('Inspection of complex data.');
}

/// 8. Grid Layouts: Tables & Columns
/// One of the most powerful features of logd is its ability to render true
/// multi-column grids in the terminal.
void _showcaseGridLayouts() {
  _section('8. Grid Layouts');

  final gridHandler = Handler(
    formatter: const TableExampleFormatter(),
    sink: const ConsoleSink(),
  );

  Logger.configure('app.grid', handlers: [gridHandler]);
  Logger.get('app.grid').info('Rendering a professional report...');
}

@immutable
class TableExampleFormatter implements LogFormatter {
  const TableExampleFormatter();

  @override
  Set<LogMetadata> get metadata => {};

  @override
  void format(
    final LogEntry entry,
    final LogDocument document,
    final LogPipelineFactory factory,
  ) {
    document.startBox(tags: LogTag.header);
    document.startAlignment(LogAlignment.center);
    document.text('SYSTEM HEALTH REPORT', style: const LogStyle(bold: true));
    document.endAlignment();
    document.endBox();

    document.startTable(columnWidths: [16, 12, 12]);

    document.startRow();
    document.startCell();
    document.text('SUBSYSTEM', style: const LogStyle(bold: true));
    document.endCell();
    document.startCell();
    document.text('STATUS', style: const LogStyle(bold: true));
    document.endCell();
    document.startCell();
    document.text('LATENCY', style: const LogStyle(bold: true));
    document.endCell();
    document.endRow();

    document.startRow();
    document.startCell();
    document.text('Authentication');
    document.endCell();
    document.startCell();
    document.text('ONLINE', style: const LogStyle(color: LogColor.green));
    document.endCell();
    document.startCell();
    document.text('12ms');
    document.endCell();
    document.endRow();

    document.startRow();
    document.startCell();
    document.text('Payment Gateway');
    document.endCell();
    document.startCell();
    document.text('DEGRADED', style: const LogStyle(color: LogColor.yellow));
    document.endCell();
    document.startCell();
    document.text('185ms');
    document.endCell();
    document.endRow();

    document.startRow();
    document.startCell();
    document.text('Database Pool');
    document.endCell();
    document.startCell();
    document.text('ONLINE', style: const LogStyle(color: LogColor.green));
    document.endCell();
    document.startCell();
    document.text('3ms');
    document.endCell();
    document.endRow();

    document.endTable();
  }
}

/// 9. Network Logging: HTTP & WebSockets
/// Send logs over the wire using HttpSink and SocketSink.
Future<void> _showcaseNetwork() async {
  _section('9. Network Logging');

  Process? socketServer;
  Process? httpServer;

  try {
    // Dynamically find script paths
    final scriptFile = File(Platform.script.toFilePath());
    final projectRoot = scriptFile.parent.parent.parent.parent.path;
    final socketDir = '$projectRoot/scripts/servers/socket';
    final httpDir = '$projectRoot/scripts/servers/http';

    final socketPort = await NetworkTestUtils.findAvailablePort(12347);
    final httpPort = await NetworkTestUtils.findAvailablePort(8081);

    print('Starting local test servers...');
    final pythonPath = Platform.isWindows
        ? '.\\.venv\\Scripts\\python.exe'
        : './.venv/bin/python';

    socketServer = await Process.start(
      pythonPath,
      ['main.py', '--port', socketPort.toString()],
      workingDirectory: socketDir,
      environment: {'PYTHONUNBUFFERED': '1'},
    );

    httpServer = await Process.start(
      pythonPath,
      ['main.py', '--port', httpPort.toString()],
      workingDirectory: httpDir,
      environment: {'PYTHONUNBUFFERED': '1'},
    );

    // Give servers a moment to bind
    await Future.delayed(const Duration(seconds: 1));

    // Show server output in console
    socketServer.stdout.transform(utf8.decoder).listen((final data) {
      if (data.contains('ENTRY') || data.contains('Connection')) {
        stdout.write('\x1B[34m[WS] $data\x1B[0m');
      }
    });
    httpServer.stdout.transform(utf8.decoder).listen((final data) {
      if (data.contains('BATCH') || data.contains('Received')) {
        stdout.write('\x1B[35m[HTTP] $data\x1B[0m');
      }
    });

    final networkHandler = Handler(
      formatter: const JsonFormatter(),
      sink: HttpSink(
        url: 'http://127.0.0.1:$httpPort/logs',
        batchSize: 2, // Flush every 2 logs
        flushInterval: const Duration(seconds: 1),
      ),
      timeout: const Duration(seconds: 5), // v0.8.7+ Handler timeout safeguard
    );

    Logger.configure('app.network', handlers: [networkHandler]);
    final logger = Logger.get('app.network');

    logger.info('Shipping log #1...');
    logger.info('Shipping log #2 (Triggers HTTP Batch)...');

    // Socket Sink Example (Manual disposal for flush)
    final wsHandler = Handler(
      formatter: const JsonFormatter(),
      sink: SocketSink(url: 'ws://127.0.0.1:$socketPort'),
    );
    Logger.configure('app.ws', handlers: [wsHandler]);
    Logger.get('app.ws').info('Log via WebSocket');

    // Wait for network activity to settle
    await Future.delayed(const Duration(seconds: 2));
    await wsHandler.sink.dispose();
    await networkHandler.sink.dispose();
  } catch (e) {
    print('\x1B[31mNetwork showcase failed (is Python venv setup?): $e\x1B[0m');
  } finally {
    socketServer?.kill();
    httpServer?.kill();
    print('\n[Cleanup] Network servers terminated.');
  }
}

/// Helper to print section headers
void _section(final String title) {
  print('\n\x1B[1m--- $title ---\x1B[0m');
}
7
likes
0
points
850
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

Modular hierarchical logging for Dart and Flutter. Features modular pipeline processing, O(1) configuration resolution, and low‑overhead performance.

Homepage
Repository (GitHub)
View/report issues

Topics

#logging #hierarchical #performance #structured-logging

License

unknown (license)

Dependencies

characters, ffi, http, matcher, meta, source_maps, source_span, timezone, web_socket_channel

More

Packages that depend on logd