auto_logger 1.1.0
auto_logger: ^1.1.0 copied to clipboard
Automatic debug logging for Flutter apps. Zero-setup capture of HTTP, platform channels, pointer events, keyboard, navigation, lifecycle, errors, and more.
auto_logger #
Debug logging for Flutter apps with automatic capture of HTTP, platform channels, pointer events, keyboard, navigation, lifecycle, errors, and more.
This package includes all core features plus Flutter-specific logging, so you only need this one package for Flutter apps.
Goal #
100% complete logging — every single thing that happens in your app should be logged so that when something goes wrong during development in the emulator/IDE, you can debug and fix it entirely by reading the logs without needing to reproduce the issue or add more logging.
Installation #
dependencies:
auto_logger: ^1.0.0
Quick Start #
import 'package:auto_logger/auto_logger.dart';
void main() {
AutoLogger.run(() => MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [AutoLogger.navObserver],
onUnknownRoute: AutoLogger.onUnknownRoute,
home: HomeScreen(),
);
}
}
What's Logged Automatically #
| Category | Native | Web |
|---|---|---|
| HTTP (dart:io) | ✅ Auto | ❌ (use Dio) |
| HTTP (Dio) | ✅ Semi | ✅ Semi |
| File operations | ✅ Auto | ❌ N/A |
| Socket connections | ✅ Auto | ❌ N/A |
| SharedPreferences | ✅ Semi | ✅ Semi |
| Hive storage | ✅ Semi | ✅ Semi |
| Drift SQL | ✅ Semi | ✅ Semi |
| WebSocket | ✅ Semi | ✅ Semi |
| gRPC | ✅ Semi | ✅ Semi |
| Timers | ✅ Auto | ✅ Auto |
| Errors/crashes | ✅ Auto | ✅ Auto |
| Print statements | ✅ Auto | ✅ Auto |
| Pointer events | ✅ Auto | ✅ Auto |
| Keyboard & focus | ✅ Auto | ✅ Auto |
| Navigation | ✅ Semi | ✅ Semi |
| Lifecycle & accessibility | ✅ Auto | ✅ Auto |
| Scroll & layout | ✅ Auto | ✅ Auto |
| Frame drops & jank | ✅ Auto | ✅ Auto |
| Memory allocations | ✅ Auto | ✅ Auto |
| Asset loading | ✅ Auto | ✅ Auto |
| Image cache | ✅ Auto | ✅ Auto |
| Platform channels | ✅ Auto | ✅ Auto |
Auto = no setup needed, Semi = one-time wrapper/observer setup
Note: Default
minLevelisWARN. Most automatic events are logged atDEBUGlevel and won't appear unless you setlogger.minLevel = LogLevel.debug. Errors, crashes, timeouts, and stuck operations are always logged (WARN/ERROR/FATAL).
Web: Add Dio Interceptor for HTTP Logging #
IMPORTANT: On Flutter Web, HttpOverrides don't work. You MUST use Dio with our interceptor:
import 'package:dio/dio.dart';
final dio = Dio();
// Add this interceptor for HTTP logging on web
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
loggingDioInterceptor.onRequest(options);
handler.next(options);
},
onResponse: (response, handler) {
loggingDioInterceptor.onResponse(response);
handler.next(response);
},
onError: (error, handler) {
loggingDioInterceptor.onError(error);
handler.next(error);
},
));
Log Format #
[TIME] [LEVEL] [SESSION] [CID] [COMPONENT] [EVENT] | MESSAGE | {METADATA}
Problem Markers #
⚠️PROBLEM- Error with context🔴CRASH- Fatal crash⏰TIMEOUT- Operation timeout🔒LEAK- Memory leak detected❌STUCK- Operation stuck/hung🥶FREEZE- UI frozen/unresponsive
Deduplication #
Errors include:
fingerprint= hash of error type + location (same bug)trailHash= hash of user actions before error (same flow)
Optional Integrations #
// BLoC - create observer extending BlocObserver
class LoggingBlocObserver extends BlocObserver {
@override void onCreate(BlocBase b) { super.onCreate(b); blocLogging.onCreate(b); }
@override void onEvent(Bloc b, Object? e) { super.onEvent(b, e); blocLogging.onEvent(b, e); }
@override void onChange(BlocBase b, Change c) { super.onChange(b, c); blocLogging.onChange(b, c); }
@override void onTransition(Bloc b, Transition t) { super.onTransition(b, t); blocLogging.onTransition(b, t); }
@override void onError(BlocBase b, Object e, StackTrace s) { super.onError(b, e, s); blocLogging.onError(b, e, s); }
@override void onClose(BlocBase b) { super.onClose(b); blocLogging.onClose(b); }
}
Bloc.observer = LoggingBlocObserver();
// Riverpod - create observer extending ProviderObserver
class LoggingProviderObserver extends ProviderObserver {
@override void didAddProvider(...) => riverpodLogging.didAddProvider(...);
// ... other overrides calling riverpodLogging methods
}
ProviderScope(observers: [LoggingProviderObserver()], child: ...)
// State snapshot on errors
AutoLogger.setStateSnapshotCallback(() => {'bloc': myBloc.state.toString()});
// Appwrite Realtime (monitor function executions from a long-running Dart app)
appwriteLogger.subscribe(realtime.subscribe(['executions']));
// WebSocket
final channel = LoggingWebSocketChannel.connect(Uri.parse('wss://example.com/ws'));
// Drift SQL (add .interceptWith to your database setup - logs ALL queries automatically)
NativeDatabase.createInBackground(file).interceptWith(loggingQueryInterceptor)
// gRPC (uses HTTP/2, not captured by HttpOverrides)
final result = await grpcLogger.call('UserService.GetUser', () => stub.getUser(request));
// SharedPreferences (works on web where dart:io file logging doesn't work)
final prefs = await SharedPreferences.getInstance();
final loggingPrefs = LoggingSharedPreferences(prefs);
await loggingPrefs.setString('key', 'value');
final value = loggingPrefs.getString('key');
// Hive (works on all platforms including web)
final box = await Hive.openBox('myBox');
final loggingBox = LoggingHiveBox(box, name: 'myBox');
await loggingBox.put('key', 'value');
Defensive Coding Utilities #
9 Exception Types #
throw NetworkException('msg', statusCode: 500, url: url);
throw ValidationException('msg', field: 'email', invalidValue: v);
throw NotFoundException('msg', resourceType: 'User', resourceId: id);
throw StateException('msg', expectedState: 'ready', actualState: 'loading');
throw AppTimeoutException('op', Duration(seconds: 10));
throw PermissionException('msg', permission: 'camera');
throw AuthenticationException('msg');
throw StorageException('msg', path: '/file');
throw ParseException('msg', input: json, expectedFormat: 'JSON');
9 Validation Utilities #
Validate.notNull(v, 'name');
Validate.notEmpty(s, 'name');
Validate.listNotEmpty(l, 'name');
Validate.positive(n, 'name');
Validate.nonNegative(n, 'name');
Validate.inRange(n, 'name', 0, 100);
Validate.percentage(n, 'name');
Validate.email(s, 'name');
Validate.isTrue(cond, 'msg', field: 'name');
Safe Disposal (Flutter) #
class _MyState extends State<MyWidget> with SafeDisposalMixin {
void initState() {
super.initState();
trackTextController(TextEditingController());
trackAnimationController(AnimationController(vsync: this));
trackSubscription(stream.listen((d) => safeSetState(() => _d = d)));
trackTimer(Timer.periodic(Duration(seconds: 1), (_) => refresh()));
}
// All auto-disposed on widget dispose!
// Logs 🔒LEAK if Timer still active or StreamController not closed
}
Loading Monitor #
// Detects stuck operations (default 2min threshold)
final result = await loadingMonitor.track('fetchData', () => api.getData());
// Hard timeout
final result = await withTimeout('op', () => api.call(), timeout: Duration(seconds: 30));
Documentation #
- doc_ai_guide.md - Concise code-only reference for AI assistants
- auto_logger_developer_documentation.md - Full developer documentation with examples
- doc3_human_log_parsing_guide.md - How to read and interpret logs
For Appwrite/Pure Dart #
Use the dart_auto_logger package instead if you don't need Flutter-specific features.
License #
MIT