Logger for Dart

repository·main·Indexed 43 days ago

https://github.com/sourcehorizon/logger

A small, extensible logging library for Dart that produces formatted logs. It features customizable log levels, filters, printers, and outputs, including a PrettyPrinter for visual formatting and AdvancedFileOutput for buffered, rotating log files. Supports various output destinations such as ConsoleOutput, FileOutput, StreamOutput, and MemoryOutput.

Tokens
5.9K
Snippets
25
Records
26
Agent score
55%

What's inside Logger

  1. How LogPrinter works and how to implement a custom one

    main

    A LogPrinter is responsible for formatting the log event into a list of strings. This is where the visual structure of the log is defined. You can implement a custom printer by extending LogPrinter and overriding the log method.

    class MyPrinter extends LogPrinter {
      @override
      List<String> log(LogEvent event) {
        return [event.message];
      }
    }
  2. How LogOutput works and available implementations

    main

    A LogOutput takes the formatted strings from a printer and sends them to a destination. The default is ConsoleOutput.

    Available implementations:

    • ConsoleOutput: Sends to system console.
    • FileOutput / AdvancedFileOutput: Writes to files.
    • StreamOutput: Sends to a stream.

    You can implement your own by extending LogOutput and overriding the output method.

    class ConsoleOutput extends LogOutput {
      @override
      void output(OutputEvent event) {
        for (var line in event.lines) {
          print(line);
        }
      }
    }
  3. How LogFilter works and how to implement a custom one

    main

    A LogFilter determines which log events are displayed. The default DevelopmentFilter only shows logs when running in debug mode (with --enable-asserts) and suppresses all logs in release mode. You can implement your own filter by extending LogFilter and overriding shouldLog.

    class MyFilter extends LogFilter {
      @override
      bool shouldLog(LogEvent event) {
        return true; // Always show logs
      }
    }
  4. Auto-detect terminal width and colors

    main

    To ensure logs look correct in a terminal, you can auto-detect lineLength and colors using the dart:io package. This is recommended for CLI applications, though you should avoid it on the web.

    import 'dart:io' as io;
    
    var logger = Logger(
      printer: PrettyPrinter(
        lineLength: io.stdout.terminalColumns,
        colors: io.stdout.supportsAnsiEscapes,
      ),
    );
  5. Use DevelopmentFilter to restrict logging in release mode

    main

    The DevelopmentFilter is a specialized LogFilter designed for Flutter or Dart applications where you want to ensure that logs are only processed during development (debug) mode.

    It uses an assert block to determine whether a log should be emitted. Because assert statements are ignored in release mode, the shouldLog method will always return false in production, effectively silencing all logs. In development mode, it allows logs to pass through if their level is greater than or equal to the configured Logger.level.

    // Example usage when configuring a Logger
    final logger = Logger(
      filter: DevelopmentFilter(),
    );
  6. Fixing colors in macOS/iOS/Flutter

    main

    Due to potential issues with ANSI escape sequences on macOS and certain Flutter builds, you can use the PrefixPrinter decorator combined with a JetBrains IDE (like Android Studio or IntelliJ) and the 'Grep Console' plugin to achieve colored logs.

    var logger = Logger(
        printer: PrefixPrinter(PrettyPrinter(colors: false))
    );
  7. Configure Logger options and PrettyPrinter

    main

    When initializing Logger, you can provide custom filter, printer, and output. If you use PrettyPrinter, you can fine-tune the formatting including method call counts, line length, colors, emojis, and timestamp formats.

    var logger = Logger(
      filter: null, // Use default LogFilter (only logs in debug mode)
      printer: PrettyPrinter(
          methodCount: 2, // Number of method calls to be displayed
          errorMethodCount: 8, // Number of method calls if stacktrace is provided
          lineLength: 120, // Width of the output
          colors: true, // Colorful log messages
          printEmojis: true, // Print an emoji for each log message
          dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
      ),
      output: null, // Use default LogOutput (send to console)
    );
  8. Log with different levels

    main

    The Logger class provides several methods for different log levels. You can also globally control the minimum log level to show by setting Logger.level.

    logger.t("Trace log");
    logger.d("Debug log");
    logger.i("Info log");
    logger.w("Warning log");
    logger.e("Error log", error: 'Test Error');
    logger.f("What a fatal log", error: error, stackTrace: stackTrace);
    
    // To show only specific log levels (e.g., warning and above):
    Logger.level = Level.warning;
  9. Configure AdvancedFileOutput options

    main

    When instantiating AdvancedFileOutput, use the following parameters to control buffering, rotation, and file management:

    ParameterTypeDescription
    pathStringIf rotating, this is the directory. If not rotating, this is the file path.
    overrideExistingboolIf true, overwrites the existing file. If false (default), appends to it.
    encodingEncodingThe character encoding (default: utf8).
    fileHeaderString?Text added to the start of the file when opened.
    fileFooterString?Text added to the end of the file when closed.
    writeImmediatelyList<Level>?Log levels that trigger an immediate buffer flush (default: [Level.error, Level.fatal, Level.warning, Level.wtf]).
    maxDelayDurationMax time to wait before flushing the buffer (default: 2s).
    maxBufferSizeintMax number of log entries to hold in memory before flushing (default: 2000).
    maxFileSizeKBintSize limit in KB for rotation. Set to -1 to disable rotation.
    latestFileNameStringThe name of the active log file (default: 'latest.log').
    fileNameFormatterString Function(DateTime)?Custom function to name rotated files based on timestamp.
    maxRotatedFilesCountint?Number of old log files to keep. If null, no limit.
    fileSorterComparator<File>?Custom logic to sort files for deletion (default: sorts by last modified date).
    fileUpdateDurationDurationFrequency of checking if the file needs rotation (default: 1m).
  10. Configure global log level and defaults

    main

    The Logger class provides static properties to configure the default behavior for all new logger instances:

    • static Level level: Sets the global minimum logging level. Logs below this level are omitted.
    • static LogFilter Function() defaultFilter: The factory for the default filter.
    • static LogPrinter Function() defaultPrinter: The factory for the default printer.
    • static LogOutput Function() defaultOutput: The factory for the default output.
    // Set global minimum level to info
    Logger.level = Level.info;
  11. Configure PrettyPrinter log boxing

    main

    By default, PrettyPrinter wraps log messages in ASCII 'boxes' based on their Level. You can control this behavior using excludeBox and noBoxingByDefault.

    • noBoxingByDefault: If true, all levels will have boxing disabled by default. You can then selectively enable it for specific levels using excludeBox.
    • excludeBox: A map where setting a Level to true prevents that level from being boxed.

    Example: Disable boxing for all levels except Level.error:

    final printer = PrettyPrinter(
      noBoxingByDefault: true,
      excludeBox: {
        Level.error: false, // Setting to false enables boxing for this level
      },
    );
    // Example: Prevent boxing of trace and info levels
    final printer = PrettyPrinter(
      excludeBox: {
        Level.trace: true,
        Level.info: true,
      },
    );