logd 0.7.1 copy "logd: ^0.7.1" to clipboard
logd: ^0.7.1 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 '../../../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();
      _showcasePipelines();
      _showcaseTimeAndLocalization();
      _showcaseAdvancedLayouts();
      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!');
}

/// 3. 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('3. 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.');
}

/// 4. Time & Localization
/// You can configure how timestamps appear globally or per handler.
/// Timezones are fully supported (UTC, Local, or Fixed offsets).
void _showcaseTimeAndLocalization() {
  _section('4. 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(),
    ),
  );
}

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

  // Comic-style structured logs
  Logger.configure('app.toon', handlers: [
    const Handler(
      formatter: ToonFormatter(),
      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.');
}

/// 6. High-Performance Network Logging
/// logd can ship logs to remote servers via WebSocket or HTTP with batching.
Future<void> _showcaseNetwork() async {
  _section('6. 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...');
    socketServer = await Process.start(
      './.venv/bin/python',
      ['main.py', '--port', socketPort.toString()],
      workingDirectory: socketDir,
      environment: {'PYTHONUNBUFFERED': '1'},
    );

    httpServer = await Process.start(
      './.venv/bin/python',
      ['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),
      ),
    );

    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');
}
6
likes
160
points
238
downloads

Documentation

Documentation
API reference

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
Contributing

Topics

#logging #hierarchical #performance #structured-logging

License

BSD-3-Clause (license)

Dependencies

characters, flutter, http, meta, timezone, web_socket_channel

More

Packages that depend on logd