any_logger 1.1.0
any_logger: ^1.1.0 copied to clipboard
A powerful, flexible, and intuitive logging library for Dart and Flutter with progressive complexity - from one-line setup to enterprise configurations.
Any Logger #
A powerful, flexible, and intuitive logging library for Dart and Flutter applications with automatic device/session tracking and progressive complexity - from one-line setup to enterprise-grade configurations.
Logs can be sent to console, file, JSON HTTP endpoints, email, MySQL databases, or any custom appender extension you create. Start with simple console logging and progressively add capabilities as your application grows.
✨ Why Any Logger? #
- 🚀 Zero Configuration - Start logging with literally one line of code
- 📱 Flutter-First - Built for mobile apps with proper app directory support
- 🔍 Automatic User Tracking - Built-in anonymous device/session/version identification
- 📈 Progressive Complexity - Simple for beginners, powerful for experts
- ⚡ Performance First - Optimized with early exits, caching, and lazy evaluation
- 🎯 Production Ready - Battle-tested with file rotation, batching, and error handling
- 🚨 Fail-Fast Design - Clear errors instead of silent failures
- 📦 Zero Dependencies - Core library has no dependencies
📦 Installation #
dependencies:
any_logger: ^x.y.z // See "Installing"
That's it! No other dependencies needed to start logging.
🚀 Quick Start #
Flutter Apps #
import 'package:any_logger/any_logger.dart';
void main() async {
await LoggerFactory.initConsole();
Logger.info("Flutter app started!");
runApp(MyApp());
}
Dart Console Apps #
import 'package:any_logger/any_logger.dart';
void main() {
Logger.info("I'm logging!"); // That's it! Auto-configures everything
}
No initialization needed for simple cases. The logger auto-configures on first use.
One Line with Options #
// Dart or Flutter
void main() {
LoggerFactory.initSimpleConsole(level: Level.DEBUG);
Logger.debug("Debug mode enabled");
Logger.info("Application started");
Logger.error("An error occurred");
}
📖 Configuration Examples #
Basic Console Logging #
// Simple console with custom format
LoggerFactory.initConsole
(
format: '🚀 %l: %m',
level: Level.DEBUG,
);
// Professional console with file location
LoggerFactory.initProConsole(
level: Level.
DEBUG
,
);
// Output: [10:30:45][ROOT_LOGGER][INFO][main:42] User logged in [lib/main.dart(42:5)]
File Logging #
// Simple file logging
await
LoggerFactory.initFile
(
filePattern: 'myapp',
fileLevel: Level.DEBUG,
consoleLevel: Level.INFO, // Optional console output
);
// Creates: myapp_2025-01-20.log
// Professional file setup
await LoggerFactory.initProFile(
filePattern: 'myapp',
fileLevel: Level.DEBUG,
consoleLevel: Level.INFO
,
);
Using Presets #
// Development - verbose with full stack traces
await
LoggerFactory.initWithPreset
(
LoggerPresets.development);
// Production - optimized with essential info only
await LoggerFactory.initWithPreset(LoggerPresets.
production
);
Builder Pattern #
// Console and file logging
await
LoggerFactory.builder
().console
(
level: Level.INFO)
.file(
filePattern: 'app',
level: Level.DEBUG,
path: 'logs/',
)
.build();
Using the AnyLogger Mixin #
class PaymentService with AnyLogger {
@override
String get loggerName => 'PaymentService';
void processPayment(String userId, double amount) {
logInfo('Processing payment for $userId: \$$amount');
if (isDebugEnabled) {
logDebug('Payment details: ${_getExpensiveDetails()}');
}
logInfo('Payment successful');
}
}
📝 Format Patterns #
Pattern | Description | Example Output |
---|---|---|
%d |
Date/time | 2025-01-20 10:30:45 |
%l |
Log level | INFO |
%m |
Message | User logged in |
%c |
Class.method:line | UserService.login:42 |
%f |
File location | lib/user.dart(42:5) |
%i |
Logger name | UserService |
%t |
Tag | AUTH |
Example Formats #
// Minimal
'%l: %m'
// Output: INFO: User logged in
// With timestamp
'%d [%l] %m'
// Output: 10:30:45 [INFO] User logged in
// With location
'[%l][%c] %m'
// Output: [INFO][UserService.login:42] User logged in
// More complete
'[%d][%did][%sid][%i][%l][%c] %m [%f]'
// Output: [11:50:43.399][lw8aqkjl][2xny54b4][ROOT_LOGGER][INFO][ServiceFactory.initializeCoreServices:326] Core services initialized successfully [package:my_app/service/service_factory.dart(326:7)]
🔍 Automatic User Tracking #
Any Logger can automatically generate and persist anonymous IDs to help you understand user behavior without compromising privacy:
- Device ID (
%did
) - Persists across app restarts, unique per device - Session ID (
%sid
) - New for each app launch, tracks individual sessions - App Version (
%app
) - Your application version for tracking deployments
Basic Usage (Dart Console/Server) #
// Just add IDs to your format - works automatically on Dart console/server
LoggerFactory.initConsole
(
format
:
'
[%did][%sid] %l: %m
'
,
);
// Output: [a3f5c8d2][e7b9f1a4] INFO: User clicked button
Flutter Setup for Device/Session IDs #
Flutter apps need additional setup for persistent device IDs:
# Add to pubspec.yaml
dependencies:
any_logger: ^x.y.z // See "Installing"
path_provider: ^2.1.5 # Required for %did on Flutter
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Connect path_provider to AnyLogger (one line!)
LoggerFactory.setGetAppDocumentsDirectoryFnc(getApplicationDocumentsDirectory);
await LoggerFactory.initConsole(
format: '[%app][%did][%sid] %l: %m',
);
LoggerFactory.setAppVersion('1.2.3');
Logger.info('Device ID persists across app restarts!');
// Output: [1.2.3][a3f5c8d2][e7b9f1a4] INFO: Device ID persists...
runApp(MyApp());
}
Alternative: Memory-Only IDs (No path_provider needed) #
void main() async {
// Use MemoryIdProvider when persistence isn't needed
LoggerFactory.setIdProvider(MemoryIdProvider());
await LoggerFactory.initConsole(
format: '[%did][%sid] %l: %m', // IDs work but don't persist
);
runApp(MyApp());
}
This tracking helps you:
- Debug user-reported issues by asking for their logs
- Track which app versions have specific issues
- Understand user journeys without collecting personal data
- Maintain GDPR compliance with anonymous identifiers
🏷️ MDC - Mapped Diagnostic Context #
Track context across all your logs - perfect for request tracking, user sessions, or feature flags:
// Set global context
LoggerFactory.setMdcValue
('userId
'
,
'
user-123
'
);LoggerFactory.setMdcValue('feature', 'new-checkout');
// Use in format with %X{key}
LoggerFactory.initConsole(
format: '[%X{userId}][%X{feature}] %l: %m',
);
// All logs now include context
Logger.info('Checkout started');
// Output: [user-123][new-checkout] INFO: Checkout started
// Clean up when done
LoggerFactory.removeMdcValue('userId');
Request Tracking Example #
class ApiServer {
void handleRequest(Request request) {
final requestId = Uuid().v4();
// Set request context
LoggerFactory.setMdcValue('requestId', requestId);
LoggerFactory.setMdcValue('endpoint', request.uri.path);
Logger.info('Request started');
// Process request...
Logger.info('Request completed');
// Clean up
LoggerFactory.clearMdc();
}
}
🧩 Extension Packages #
The core any_logger
library is intentionally kept lightweight. Additional appenders are available through optional
extension packages:
Available Extensions #
Package | Description | When to Use |
---|---|---|
any_logger_json_http |
JSON over HTTP logging | When sending logs to REST APIs, Logstash, centralized logging services |
any_logger_email |
Email notifications | For critical alerts, error notifications, and daily digests |
any_logger_mysql |
MySQL database logging | For structured, queryable log storage and audit trails |
Installation #
dependencies:
any_logger: ^x.y.z // See "Installing"
any_logger_json_http: ^x.y.z # Only if needed
any_logger_email: ^x.y.z # Only if needed
any_logger_mysql: ^x.y.z # Only if needed
Usage Example #
import 'package:any_logger/any_logger.dart';
import 'package:any_logger_json_http/any_logger_json_http.dart';
await
LoggerFactory.builder
().console
() // Core package
.file
() // Core package
.jsonHttp
( // Extension package
url: 'https://api.example.com/logs',
level: Level.ERROR,
bufferSize: 100,
)
.build();
⚡ Performance Optimization #
Early Exit Pattern #
// ❌ Bad - always computes expensive operation
logger.logDebug
(
expensiveComputation());
// ✅ Good - only computes if debug is enabled
if (logger.isDebugEnabled) {
logger.logDebug(expensiveComputation());
}
// ✅ Better - use supplier for lazy evaluation
logger.logDebugSupplier(() => expensiveComputation());
🔍 Troubleshooting #
Enable Self-Debugging #
Having issues? Enable self-debugging to see what the logger is doing:
// See internal logger operations
LoggerFactory.builder
().console
(
level: Level.INFO)
.withSelfDebug(Level.DEBUG
) // Shows platform detection, ID provider selection, etc.
.
build
(
);
// Output:
// [LoggerFactory.DEBUG] Platform: Dart | IDs: %did+%sid | Provider: FileIdProvider
// [LoggerFactory.DEBUG] Self-debugging enabled
// [LoggerFactory.DEBUG] Logger initialized with 1 appender
Common Flutter Issues #
"path_provider Not Configured for Device ID (%did)"
The logger will show a clear error message with instructions. Either:
- Add
path_provider
and configure it (see User Tracking section) - Use
MemoryIdProvider
for non-persistent IDs - Remove
%did
from your format
"No appender registered for type 'JSON_HTTP'"
Add and import the required extension package:
dependencies:
any_logger_json_http: ^x.y.z
and call the corresponding class to register the appender, like e.g.:
AnyLoggerJsonHttpExtension.register
();
"Permission denied" on Mobile
Use MemoryIdProvider instead of file-based storage:
LoggerFactory.setIdProvider
(
MemoryIdProvider
(
)
);
Performance Tips #
- If you don't use
%did
or%sid
, the ID provider never runs - Use
MemoryIdProvider
if persistence isn't needed - Enable batching for network appenders (extension packages)
- Only import extension packages you actually use
🏆 Best Practices #
- Start simple - Use basic console logging, add features as needed
- Use self-debugging when troubleshooting logger configuration
- Set appropriate log levels - DEBUG for development, INFO/WARN for production
- Use named loggers via the mixin for better organization
- Add tracking IDs (
%did
/%sid
) only when you need user journey tracking - Use MDC for request/transaction tracking
- Configure rotation for file appenders to manage disk space
- Flush logs before app termination:
await LoggerFactory.flushAll()
📄 License #
MIT License - see LICENSE file for details.
🙏 Acknowledgments #
This library is a fork of Log4Dart2 by Ephenodrom, enhanced with modern features, performance optimizations, automatic ID tracking, and a simplified API.
📮 Support #
- 📧 Email: hello@raoulsson.com
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
💚 Funding #
Happy Logging! 🎉