Skip to main content

astute_logger 2.6.0 copy "astute_logger: ^2.6.0" to clipboard
astute_logger: ^2.6.0 copied to clipboard

A simple and powerful logger for Flutter apps with support for color-coding, JSON pretty-printing, and performance tracking.

๐Ÿš€ Astute Logger #

A lightweight, configurable logging package for Flutter with support for colored console output, persistent file logging, contextual logging, execution time measurement, structured and free-text sensitive data redaction, and a built-in Dio HTTP interceptor.


โœจ Features #

  • ๐Ÿ“ Multiple log levels
    • ๐ŸŸข Debug
    • ๐Ÿ”ต Info
    • ๐ŸŸก Warning
    • ๐Ÿ”ด Error
    • ๐ŸŸฃ Critical
  • ๐ŸŽจ Colored console logs
  • ๐Ÿ“‚ Persistent file logging with an async, serialized write queue
  • โš™๏ธ Configurable logger settings
  • ๐Ÿ“ Automatic caller method detection (VM + web fallback)
  • ๐ŸŒ Context-aware logging using Zones
  • ๐Ÿ†” Request ID propagation
  • ๐Ÿท๏ธ Additional contextual metadata & tags
  • ๐Ÿ“„ Pretty JSON logging
  • ๐Ÿ”’ Sensitive data redaction
    • ๐Ÿ”‘ Authorization tokens & Bearer headers
    • ๐Ÿ” Passwords, secrets & API keys
    • ๐Ÿ“ง Email addresses (opt-out via redactEmails)
    • ๐Ÿ’ณ Credit card numbers
    • ๐Ÿงฉ Key-based redaction for nested maps & lists via redactObject
  • โฑ๏ธ Execution time measurement (sync & async)
  • ๐Ÿšจ Error & stack trace logging
  • ๐ŸŒ Built-in Dio LoggingInterceptor for HTTP request/response/error traffic
  • ๐Ÿ“ฑ Cross-platform Flutter support (Android, iOS, Web, Desktop)

๐Ÿ“ฆ Installation #

Add the package to your pubspec.yaml.

dependencies:
  astute_logger: ^0.1.0

Install it:

flutter pub get

๐Ÿ“ฅ Import #

import 'package:astute_logger/astute_logger.dart';

// Optional: HTTP interceptor
import 'package:astute_logger/service/logging_interceptor.dart';

๐Ÿ—๏ธ Create a Logger #

final logger = AstuteLogger(
  'AppLogger',
  config: const LogConfig(
    enableConsoleOutput: true,
    enableFileOutput: true,
    enableColorLogging: true,
    enableRedaction: true,
    redactEmails: true,
    minimumLogLevel: LogLevel.debug,
    logFileName: 'app_logs.txt',
  ),
);

๐Ÿ“– Examples #

๐ŸŸข Debug #

await logger.debug("Debug message");

๐Ÿ”ต Info #

await logger.info("Application started");

๐ŸŸก Warning #

await logger.warning("API response is slow");

๐Ÿ”ด Error #

await logger.error(
  "Unable to load profile",
  error: Exception("HTTP 500"),
  stackTrace: StackTrace.current,
);

๐ŸŸฃ Critical #

await logger.critical(
  "Database connection lost",
  error: Exception("SocketException"),
  stackTrace: StackTrace.current,
);

โ„น๏ธ Async by default. All write methods (debug, info, warning, error, critical, json, write) return Future<void>. The returned Future completes only after the log line has been persisted to disk (if file output is enabled). Await it whenever you need a durability guarantee.


๐Ÿท๏ธ Logging with Metadata #

await logger.info(
  "User logged in",
  tag: 'AUTH',
  extra: {
    "userId": 123,
    "role": "Admin",
  },
);

๐ŸŒ Context-Aware Logging #

await LoggerContext.runWithContext(
  requestId: "REQ-1001",
  extra: {
    "screen": "Home",
    "feature": "Login",
  },
  body: () async {
    await logger.info("Inside contextual logging");
    // Every log within this Zone automatically carries:
    //   [ReqID: REQ-1001] [Ctx: {screen: Home, feature: Login}]
  },
);

โฑ๏ธ Measure Execution Time #

Synchronous #

final sorted = logger.logExecutionTime(
  "Sorting List",
  () {
    final list = List.generate(100000, (i) => 100000 - i);
    list.sort();
    return list;
  },
);

The timing log is emitted even if the callback throws โ€” the original exception is rethrown unchanged.

Asynchronous #

await logger.logExecutionTimeAsync(
  "API Request",
  () async {
    await Future.delayed(const Duration(seconds: 2));
  },
);

๐Ÿ“„ Pretty JSON Logging #

Use write(prettyPrint: true) for raw JSON strings, or json() for objects.

Pretty-print a JSON string #

await logger.write(
  message: jsonEncode({
    "name": "John",
    "age": 25,
    "roles": ["Admin", "User"]
  }),
  prettyPrint: true,
  level: LogLevel.info,
);

Log a structured object (auto pretty-printed + redacted) #

await logger.json({
  "user": "john",
  "password": "hunter2",          // โ†’ [REDACTED]
  "profile": {
    "email": "john@example.com",  // โ†’ [REDACTED] (unless redactEmails=false)
  },
});

๐Ÿ”’ Pretty-printed JSON is redacted using key-based rules, not regex. This means {"password": "p@ss!"} is reliably redacted even when the value contains special characters or when the JSON is multi-line.


๐Ÿ”’ Automatic Sensitive Data Redaction #

AstuteLogger offers two complementary redaction engines:

1. Key-based redaction (structured data) #

Applied automatically by json() and by write(prettyPrint: true) when the message decodes to a Map or List. It also powers the public redactObject() helper used by the Dio interceptor.

logger.redactObject({
  'user': 'alice',
  'credentials': {
    'password': 'hunter2',
    'token': 'abc',
  },
});
// => {
//      'user': 'alice',
//      'credentials': {
//        'password': '[REDACTED]',
//        'token': '[REDACTED]',
//      },
//    }

Manage the sensitive key set at runtime:

AstuteLogger.registerSensitiveKey('ssn');
AstuteLogger.unregisterSensitiveKey('ssn');
AstuteLogger.getSensitiveKeys(); // sorted list

Default keys: password, token, accesstoken, refreshtoken, authorization, apikey, secret, email.

2. Free-text redaction (regex) #

Applied to every message when enableRedaction: true.

Rule Example input Output
Quoted key/value "password": "p@ss!" "password": "[REDACTED]"
Bearer token Authorization: Bearer abc-123 Authorization: Bearer [REDACTED]
Bare key/value token:abc123 count:5 token:[REDACTED] count:5
Email address contact alice@example.com contact [REDACTED]
Credit card card 4111111111111111 card [REDACTED]

Disabling email redaction #

If you need to log emails for debugging:

const LogConfig(redactEmails: false)

Emails are preserved, but credit-card numbers and other rules still apply. To disable redaction entirely, set enableRedaction: false.


๐Ÿ“‚ Read Log File #

final file = await AstuteLogger.getLogFile();

if (file != null && await file.exists()) {
  final logs = await file.readAsString();
  print(logs);
}

Custom file name:

final file = await AstuteLogger.getLogFile(fileName: 'api_logs.txt');

getLogFile() caches File instances per path to avoid repeated platform channel calls and to keep a stable identity across lookups.


๐Ÿท๏ธ Filter Logs by Tag #

final authLogs = await AstuteLogger.getLogsByTag('AUTH');
authLogs.forEach(print);

Custom file name:

final apiLogs = await AstuteLogger.getLogsByTag(
  'HTTP',
  fileName: 'api_logs.txt',
);

Matching is case-insensitive and ANSI color codes are stripped from the returned lines.


๐ŸŒ Dio HTTP Logging #

Attach the built-in LoggingInterceptor to any Dio instance:

import 'package:dio/dio.dart';
import 'package:astute_logger/service/logging_interceptor.dart';

final dio = Dio()
  ..interceptors.add(LoggingInterceptor(
    logger: logger,
    logRequestHeaders: true,
    logRequestBody: true,
    logResponseHeaders: true,
    logResponseBody: true,
    maxBodyLength: 2000,       // truncate long bodies
    maxHeaderValueLength: 500, // truncate long header values
  ));

What gets logged #

  • Request: method, URL, headers (sensitive ones redacted), body
  • Response: status code + message, elapsed time, headers, body
  • Error: status code, elapsed time, URL, request/response headers, request/response bodies, error message, DioExceptionType

Redaction & safety #

  • Authorization, Cookie, Set-Cookie, X-Api-Key, X-Access-Token, Access-Token, Bearer, Token headers are replaced with [REDACTED].
  • JSON bodies are decoded and passed through redactObject() โ€” so {"password": "hunter2"} becomes {"password": "[REDACTED]"}.
  • FormData bodies log only field names and file basenames โ€” never full paths or raw bytes.
  • Long values are truncated with a ... [truncated, total N chars] suffix.

โš™๏ธ Configuration #

const LogConfig(
  enableConsoleOutput: true,
  enableFileOutput: true,
  enableColorLogging: true,
  enableRedaction: true,
  redactEmails: true,
  minimumLogLevel: LogLevel.debug,
  logFileName: 'app_logs.txt',
)
Field Default Description
enableRedaction true Master switch for all redaction
redactEmails true Redact email addresses in free text
minimumLogLevel LogLevel.debug Drop messages below this level
enableConsoleOutput true Write to dart:developer log()
enableFileOutput true Persist to the app documents directory
logFileName 'app_logs.txt' Target log file name
enableColorLogging true ANSI colorize console output

๐Ÿงญ App Modes #

Each log line includes the current Flutter build mode:

logger.appMode; // AppMode.debug | profile | release | unknown

๐Ÿงช Testing Hooks #

The following static helpers are exposed for tests and diagnostics:

// Wait for the async write queue to fully drain.
await AstuteLogger.flush();

// Clear the internal File cache (useful between tests).
AstuteLogger.resetFileCache();

These are marked @visibleForTesting โ€” prefer not to call them from production code.


๐Ÿงต Threading & Durability #

  • All log events flow through a single static queue with a bounded capacity (1024 pending events). Producers block when the queue is full.
  • Writes are serialized: events are persisted strictly in enqueue order, even across multiple logger instances writing to the same file.
  • File handles are opened and closed per write (openWrite + close()), so log files are never held open by the process โ€” safe on Windows and in tests.

โค๏ธ License #

MIT License.

4
likes
140
points
427
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A simple and powerful logger for Flutter apps with support for color-coding, JSON pretty-printing, and performance tracking.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

dio, flutter, path_provider

More

Packages that depend on astute_logger