Astute Logger

A lightweight yet powerful logging utility designed specifically for Flutter applications. Astute Logger provides beautiful console output, persistent file logging, remote log forwarding, and sensitive data redactionβ€”all with a simple, intuitive API.

Pub Version License

✨ Features

  • 🎨 Color-Coded Console Logs - Beautiful, level-based color formatting for easy debugging
  • πŸ“ Persistent File Logging - Save logs to disk for later analysis
  • 🌐 Remote Logging - Forward logs to your backend/monitoring service
  • πŸ”’ Automatic Sensitive Data Redaction - Hide passwords, tokens, and sensitive fields
  • ⚑ Async Log Queue - Non-blocking logging that won't slow down your app
  • πŸ” Caller Location Tracking - See exactly where each log originated
  • πŸ§ͺ Test-Friendly - Override console output for deterministic unit tests
  • πŸ“Š Advanced Log Analysis - Search, paginate, and filter logs
  • πŸ“€ Share Logs - Export log files via native share dialogs
  • 🎯 Zero Dependencies in Release Mode - Logging automatically disabled

πŸ“¦ Installation

Add to your pubspec.yaml:

dependencies:
  astute_logger: ^1.0.0
  path_provider: ^2.0.0  # Required for file logging
  share_plus: ^12.0.0     # Required for log sharing

Then run:

flutter pub get

πŸš€ Quick Start

Basic Usage

import 'package:astute_logger/astute_logger.dart';

// Create a logger instance
final logger = AstuteLogger('MyApp');

// Log at different levels
logger.debug('Verbose debugging information');
logger.info('General application flow');
logger.warning('Something unexpected happened');
logger.error('Critical error occurred');

With File Logging

final logger = AstuteLogger(
  'MyApp',
  enableFileLogging: true,
  fileNamePrefix: 'app_logs',
);

// Initialize file logging
await logger.initFileLogging();

// Now all logs are saved to disk AND printed to console
logger.info('This will be saved to file');

With Custom Directory

final logger = AstuteLogger(
  'MyApp',
  enableFileLogging: true,
);

// Use a custom directory
await logger.initFileLogging(
  directoryPath: '/path/to/custom/logs',
);

πŸ“š Advanced Features

Remote Logging

Forward logs to your backend or monitoring service:

final logger = AstuteLogger(
  'MyApp',
  enableRemote: true,
  remoteSender: (message, level, tag) async {
    await http.post(
      Uri.parse('https://api.example.com/logs'),
      body: jsonEncode({
        'message': message,
        'level': level.toString(),
        'tag': tag,
        'timestamp': DateTime.now().toIso8601String(),
      }),
    );
  },
);

logger.info('This will be sent to your server');

Pretty-Print JSON

final user = {'name': 'John', 'age': 30, 'email': 'john@example.com'};

logger.logJson(user);
// Output:
// {
//   "name": "John",
//   "age": 30,
//   "email": "john@example.com"
// }

Measure Execution Time

final result = logger.logExecutionTime('Database Query', () {
  return database.query('SELECT * FROM users');
});
// Output: Database Query executed in 45 ms

Search Logs

// Search for specific keywords
await logger.searchLogs(
  keywords: ['error', 'network'],
  reverse: true,  // Show newest first
);

Paginated Log Viewing

// Print logs in chunks (prevents UI freeze)
await logger.printLogsPaginated(
  chunkSize: 200,
  reverse: true,
);

Share Log Files

// Share logs via native dialogs (email, messaging, etc.)
final success = await logger.shareLogFile(
  subject: 'App Debug Logs',
  text: 'Please find the attached log file',
);

πŸ”’ Sensitive Data Redaction

Astute Logger automatically redacts sensitive information from your logs:

logger.info('User login: {"password": "secret123", "token": "abc-xyz"}');
// Output: User login: {"password": "***", "token": "***"}

Default patterns redact:

  • password
  • token
  • access_token / accessToken
  • refresh_token / refreshToken

Custom Redaction Patterns

AstuteLogger.globalRedactionPatterns.add(
  RegExp(r'("api_key"\s*:\s*")[^"]+(")', caseSensitive: false),
);

πŸ§ͺ Testing Support

Capture logs during unit tests without depending on dart:developer:

test('should log error on failure', () {
  final logBuffer = <String>[];
  
  // Override console output
  AstuteLogger.consoleOverride = (msg) => logBuffer.add(msg);
  
  final logger = AstuteLogger('Test');
  logger.error('Test error');
  
  expect(logBuffer, contains(contains('Test error')));
  
  // Clean up
  AstuteLogger.consoleOverride = null;
});

Mock File System for Tests

test('file logging in tests', () async {
  final tempDir = Directory.systemTemp.createTempSync();
  
  AstuteLogger.debugDirectoryProvider = () async => tempDir;
  
  final logger = AstuteLogger(
    'Test',
    enableFileLogging: true,
  );
  
  await logger.initFileLogging();
  logger.info('Test message');
  await logger.dispose();
  
  final logFile = File('${tempDir.path}/log.log');
  final content = await logFile.readAsString();
  
  expect(content, contains('Test message'));
});

🎨 Log Output Format

Logs include comprehensive metadata for easy debugging:

[2024-12-10 14:32:15] [INFO] [myFunction @ main.dart:42] User logged in successfully

Each log shows:

  • Timestamp - Local time when log was created
  • Level - DEBUG, INFO, WARNING, or ERROR
  • Caller - Function name, file, and line number
  • Message - Your log content

πŸ”§ Configuration

Log Levels

enum LogLevel {
  debug,    // Verbose development logs
  info,     // General application flow
  warning,  // Unexpected but non-fatal scenarios
  error,    // Failures, exceptions, or critical issues
  off,      // Disable logging entirely
}

Color Coding

Logs are automatically color-coded in the console:

  • πŸ”΅ DEBUG - Blue
  • 🟒 INFO - Green
  • 🟑 WARNING - Yellow
  • πŸ”΄ ERROR - Red

Colors are automatically stripped from file and remote logs.

πŸ“‹ API Reference

Core Methods

Method Description
debug(message, {tag}) Log debug information
info(message, {tag}) Log general information
warning(message, {tag}) Log warnings
error(message, {tag}) Log errors
write(message, level, tag) Low-level logging method

Utility Methods

Method Description
logJson(jsonObject, {level}) Pretty-print JSON
logPrettyList(list, {label, level}) Pretty-print lists
logExecutionTime(label, fn) Measure execution time
logWithColor(message, {color, tag}) Custom color logging

File Management

Method Description
initFileLogging({directoryPath}) Initialize file logging
shareLogFile({subject, text}) Share via native dialogs
printAllLogsToConsole() Print entire log file
printLogsPaginated({chunkSize, reverse}) Paginated log viewing
searchLogs({keywords, chunkSize, reverse}) Search log contents

Lifecycle

Method Description
dispose() Close file handles and clean up resources

Properties

Property Type Description
logFilePath String? Current log file path
logFile File? Log file object
enableFileLogging bool Enable/disable file logging
enableRemote bool Enable/disable remote forwarding

⚠️ Important Notes

  • Release Mode: All logging is automatically disabled in release mode to avoid performance impact
  • Async Queue: Logs are processed asynchronously to prevent blocking your app
  • Disposal: Always call dispose() during app shutdown to ensure all logs are flushed
  • File Permissions: Ensure your app has appropriate permissions for file logging on Android/iOS

πŸ“± Platform Support

Platform Console Logging File Logging Remote Logging Share Logs
Android βœ… βœ… βœ… βœ…
iOS βœ… βœ… βœ… βœ…
Web βœ… ❌ βœ… ❌
macOS βœ… βœ… βœ… βœ…
Windows βœ… βœ… βœ… ❌
Linux βœ… βœ… βœ… ❌

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

Built with ❀️ for the Flutter community.

πŸ“ž Support


Made with Flutter πŸ’™

Libraries

astute_logger