Astute Logger
A lightweight yet powerful logging utility designed specifically for Flutter applications. Astute Logger provides comprehensive logging capabilities with console output, file persistence, remote forwarding, and built-in security features.
Features
- ๐ฑ Console Logging - Beautifully colored output with ANSI support
- ๐พ File Logging - Persistent storage of logs to disk
- ๐ Remote Logging - Forward logs to remote servers via custom callbacks
- ๐ Sensitive Data Redaction - Automatically redacts passwords, tokens, and sensitive fields
- โก Async Log Queue - Non-blocking, queue-based logging system
- ๐ฏ Log Level Filtering - Control verbosity with debug, info, warning, error levels
- ๐งช Test Support - Built-in console override for deterministic testing
- ๐ JSON Pretty Printing - Format and log JSON objects with indentation
- โฑ๏ธ Execution Time Tracking - Measure function execution time
- ๐ Log Search - Search through log files with keyword filtering
- ๐ค Log Sharing - Share log files via native share dialogs
- ๐ Caller Information - Automatically captures file, function, and line number
Installation
Add this to your package's pubspec.yaml file:
dependencies:
astute_logger: ^2.0.0
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('Debug message');
logger.info('App started successfully');
logger.warning('Network connection unstable');
logger.error('Failed to load user data');
File Logging
// Enable file logging
final logger = AstuteLogger(
'MyApp',
enableFileLogging: true,
fileNamePrefix: 'myapp_log',
);
// Initialize file logging
await logger.initFileLogging();
// Logs are now written to disk
logger.info('This will be saved to file');
Remote Logging
final logger = AstuteLogger(
'MyApp',
enableRemote: true,
remoteSender: (message, level, tag) async {
// Send to your logging service
await http.post(
Uri.parse('https://api.example.com/logs'),
body: jsonEncode({
'message': message,
'level': level.name,
'tag': tag,
}),
);
},
);
Advanced Features
JSON Logging
// Log JSON objects with pretty formatting
final userData = {'name': 'John', 'age': 30, 'role': 'developer'};
logger.logJson(userData);
// Log lists
logger.logPrettyList(
['item1', 'item2', 'item3'],
label: 'Shopping List',
);
Execution Time Tracking
// Measure how long a function takes
final result = logger.logExecutionTime('Data Processing', () {
// Your expensive operation
return processData();
});
Log File Operations
// Print all logs to console
await logger.printAllLogsToConsole();
// Print logs with pagination (prevents UI freeze)
await logger.printLogsPaginated(
chunkSize: 200,
reverse: true, // Newest first
);
// Search logs by keywords
await logger.searchLogs(
keywords: ['error', 'network'],
reverse: true,
);
// Share log file via native dialog
await logger.shareLogFile(
subject: 'App Logs',
text: 'Debug logs from MyApp',
);
Custom Colors
// Log with custom ANSI colors
logger.logWithColor(
'Custom colored message',
color: '35', // Magenta
);
Security Features
Automatic Redaction
Astute Logger automatically redacts sensitive information from logs:
// This log entry will have the password redacted
logger.info('User login: {"username": "john", "password": "secret123"}');
// Output: User login: {"username": "john", "password": "***"}
Redacted fields include:
passwordtokenaccess_token/accessTokenrefresh_token/refreshToken
Custom Redaction Patterns
// Add custom redaction patterns
AstuteLogger.globalRedactionPatterns.add(
RegExp(r'("ssn"\s*:\s*")[^"]+(")', caseSensitive: false),
);
Testing Support
Astute Logger provides a console override mechanism for deterministic testing:
// In your test file
final logBuffer = <String>[];
setUp(() {
AstuteLogger.consoleOverride = (msg) => logBuffer.add(msg);
});
tearDown(() {
AstuteLogger.consoleOverride = null;
logBuffer.clear();
});
test('should log messages', () {
final logger = AstuteLogger('Test');
logger.info('Test message');
expect(logBuffer.length, 1);
expect(logBuffer.first, contains('Test message'));
});
Mock Directory for File Testing
// Provide a custom directory for testing
AstuteLogger.debugDirectoryProvider = () async {
return Directory.systemTemp.createTemp('test_logs');
};
Log Levels
enum LogLevel {
debug, // Verbose development logs
info, // General application flow
warning, // Unexpected but non-fatal scenarios
error, // Failures and critical issues
off, // Disable logging
}
Best Practices
-
Create logger instances per module
final networkLogger = AstuteLogger('Network'); final databaseLogger = AstuteLogger('Database'); -
Initialize file logging early
void main() async { WidgetsFlutterBinding.ensureInitialized(); final logger = AstuteLogger('MyApp', enableFileLogging: true); await logger.initFileLogging(); runApp(MyApp(logger: logger)); } -
Always dispose loggers
@override void dispose() { logger.dispose(); super.dispose(); } -
Use appropriate log levels
debug: Detailed diagnostic informationinfo: Routine application eventswarning: Degraded functionality or unexpected situationserror: Failures that need immediate attention
Platform Support
- โ Android
- โ iOS
- โ Web
- โ macOS
- โ Windows
- โ Linux
Dependencies
flutter/foundation.dart- Platform detectionpath_provider- File system accesspath- Path manipulationshare_plus- Native sharing functionality
Performance Considerations
- All logging operations are asynchronous and queued
- File writes are batched to minimize I/O operations
- Logs in release mode are automatically disabled
- Pagination prevents UI freezing when reading large log files
API Reference
Constructor
AstuteLogger(
String title, {
bool enableFileLogging = false,
bool enableRemote = false,
String? fileNamePrefix = "log",
Future<void> Function(String message, LogLevel level, String? tag)? remoteSender,
})
Core Methods
debug(String message, {String? tag})- Log debug messageinfo(String message, {String? tag})- Log info messagewarning(String message, {String? tag})- Log warning messageerror(String message, {String? tag})- Log error messagewrite({required String message, required LogLevel level, String? tag})- Write raw log
File Operations
Future<void> initFileLogging({String? directoryPath})- Initialize file loggingFuture<bool> shareLogFile({String? subject, String? text})- Share log fileFuture<void> printAllLogsToConsole()- Print entire log fileFuture<void> printLogsPaginated({int chunkSize, bool reverse})- Print logs in chunksFuture<void> searchLogs({required List<String> keywords, int chunkSize, bool reverse})- Search logs
Utility Methods
logJson(dynamic jsonObject, {LogLevel level})- Pretty print JSONlogPrettyList(List list, {String? label, LogLevel level})- Pretty print listT logExecutionTime<T>(String label, T Function() fn)- Measure execution timelogWithColor(String message, {required String color, String? tag})- Custom colored log
Cleanup
Future<void> dispose()- Flush logs and close file handles
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
If you encounter any issues or have questions, please file an issue on the GitHub repository.