crashlog 1.3.0
crashlog: ^1.3.0 copied to clipboard
A comprehensive Flutter package for capturing errors, console logs, and screenshots with cross-platform support and dev report functionality.
Crashlog #
A comprehensive Flutter package for capturing errors, console logs, and screenshots with cross-platform support and developer report functionality.
Overview #
Crashlog helps Flutter developers capture, analyze, and troubleshoot errors and issues in their applications across all platforms. With automatic error capture, automatic debugPrint interception, and screenshot capability, it provides everything needed to understand what went wrong and why. The built-in developer reporting interface makes it easy to view and share logs during development and testing.
π ZERO CODE CHANGES REQUIRED - Just initialize once and everything is captured automatically!
Features #
β
Automatic Error Capture: Captures ALL uncaught Flutter and Dart errors without any code changes
β
Enhanced Console Capture: Automatically captures ALL console output including debugPrint()
, print()
, formatted logs from custom loggers, BLoC state changes, HTTP interceptors, and native Android/iOS logs (I/flutter)
β
Screenshot Capture: Optional screenshot capture when errors occur
β
Dev Report Screen: Beautiful UI to view, share, and manage logs
β
Cross-Platform: Works on Android, iOS, Windows, macOS, Linux, and Web
β
Flexible Dependencies: Wide version ranges to avoid conflicts with your existing packages
β
Automatic Cleanup: Configurable retention policies for logs and screenshots
β
Non-Blocking: All operations are asynchronous and won't block your app
β
Zero Maintenance: No need to wrap every exception or modify existing debug prints
β‘ Quick Start (2 Steps Only!) #
1. Add dependency #
dependencies:
crashlog: ^1.1.0
2. Initialize once in main() #
import 'package:flutter/material.dart';
import 'package:crashlog/crashlog.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// One line to capture everything automatically!
await Crashlog.init();
runApp(MyApp());
}
That's it! π All errors and debug prints in your entire app will now be captured automatically.
π οΈ Optional: Advanced Configuration #
If you need custom settings, you can configure Crashlog with additional options:
await Crashlog.init(
enabled: true, // Enable/disable the package
enableScreenshots: true, // Capture screenshots on errors
autoOpenOnError: false, // Auto-open dev report on errors
maxLogs: 50, // Maximum error logs to retain
maxConsoleLogs: 1000, // Maximum console logs to retain
logRetentionDays: 7, // Delete logs older than 7 days
showDeviceInfo: true, // Include device info in logs
logFileName: "error_logs.json", // Custom error log filename
consoleLogFileName: "console_logs.json", // Custom console log filename
screenshotFolder: "screenshots", // Custom screenshot folder
);
π± Optional: Add UI Components #
Add Dev Report Button (Optional) #
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
actions: [
ErrorRecorderIconButton(), // In app bar
],
),
body: YourContent(),
floatingActionButton: ErrorRecorderButton(), // As FAB
);
}
}
Wrap App for Screenshots (Optional) #
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ErrorRecorderWrapper(
child: HomePage(),
),
);
}
}
π Integration with Existing Logging #
Automatic Support #
Crashlog automatically captures:
- β
All
debugPrint()
statements (no changes needed!) - β
All
print()
statements (no changes needed!) - β All uncaught errors (no changes needed!)
Manual Integration with Logger Package #
If you're using the popular logger
package, you can easily integrate:
import 'package:logger/logger.dart';
import 'package:crashlog/crashlog.dart';
// Option 1: Use Crashlog's logger wrapper (recommended)
final logger = Crashlog.createLogger(source: 'MyService');
logger.info('This will be captured automatically');
logger.error('Errors are captured too');
// Option 2: Integrate with existing logger package
final logger = Logger();
// Add crashlog output listener
Logger.addOutputListener((event) {
Crashlog.captureExternalLog(
message: event.message,
level: event.level.name,
source: 'logger',
);
});
// Now all logger calls are also captured by Crashlog
logger.d('Debug message');
logger.i('Info message');
logger.e('Error message');
Integration with Any Logging Package #
// Capture any log manually
Crashlog.captureExternalLog(
message: 'Custom log message',
level: 'info',
source: 'my_custom_logger',
metadata: {'userId': '123', 'action': 'login'},
);
// Use extension method on any object
'Something happened'.logTocrashlog(level: 'warning', source: 'MyClass');
Integration with Dio HTTP Logging #
import 'package:dio/dio.dart';
final dio = Dio();
// Add interceptor to capture HTTP logs
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
Crashlog.captureExternalLog(
message: 'HTTP Request: ${options.method} ${options.uri}',
level: 'info',
source: 'dio',
);
handler.next(options);
},
onError: (error, handler) {
Crashlog.captureExternalLog(
message: 'HTTP Error: ${error.message}',
level: 'error',
source: 'dio',
);
handler.next(error);
},
));
Installation #
Requirements #
- Flutter: >=3.0.0
- Dart: >=3.0.0 <4.0.0
Steps #
- Add the package to your
pubspec.yaml
:
dependencies:
crashlog: ^1.0.0
- Install the package:
flutter pub get
- Import the package:
import 'package:crashlog/crashlog.dart';
Usage #
Initialization #
Initialize Crashlog as early as possible in your app's lifecycle, ideally before runApp()
:
void main() async {
// Ensure Flutter binding is initialized
WidgetsFlutterBinding.ensureInitialized();
// Initialize Crashlog with default settings
await Crashlog.init();
// Or with custom configuration
await Crashlog.init(
enabled: true,
enableScreenshots: true,
maxLogs: 100,
logRetentionDays: 14,
);
// Run your app
runApp(MyApp());
}
Enabling Screenshots #
To enable screenshot capture when errors occur, wrap your app or specific screens with ErrorRecorderWrapper
:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
home: ErrorRecorderWrapper(
child: HomePage(),
),
);
}
}
Adding the Developer Report Button #
Add the developer report button to your app to access error logs and screenshots:
// As a floating action button
Scaffold(
appBar: AppBar(title: Text('My App')),
body: YourContent(),
floatingActionButton: ErrorRecorderButton(),
)
// Or in the app bar
AppBar(
title: Text('My App'),
actions: [
ErrorRecorderIconButton(),
],
)
// Or as a custom button anywhere
ElevatedButton(
onPressed: () => Crashlog.openDevReport(context),
child: Text('Open Dev Report'),
)
Logging Methods #
Crashlog provides several methods for custom logging:
// Log levels
await Crashlog.logInfo('User logged in successfully');
await Crashlog.logWarning('Network connectivity is slow');
await Crashlog.logError('Failed to save user data');
await Crashlog.logDebug('Processing user preferences');
// Custom level
await Crashlog.log('Custom message', level: 'trace');
// Manual error logging with stack trace
try {
// risky operation
throw Exception('Something went wrong');
} catch (e, stackTrace) {
await Crashlog.logError('Operation failed: $e', stackTrace: stackTrace);
}
Automatic Console Capture #
Crashlog automatically captures ALL types of console output without requiring any code changes:
What's Captured Automatically:
- Standard Logging: All
print()
anddebugPrint()
statements - Formatted Logs: Custom logger output with special characters (β, β, β, ββββ)
- BLoC State Changes: Logs from flutter_bloc package with π emoji
- HTTP Interceptors: Network logs with π‘ and β emojis
- Native Platform Logs: Android
I/flutter
logs and iOS console output - Error Logs: Framework warnings like
[log] Incorrect use of ParentDataWidget
- Custom Loggers: Output from logger, dio, and other logging packages
Example Log Formats Captured:
// These are ALL captured automatically - no code changes needed!
print('Simple print statement');
debugPrint('Debug print statement');
// BLoC logs
print('ββββββββββββββββββββββββββββββββββββββββββββββββββ');
print('β π State Changed: MyBloc, Change: Loading() β Loaded()');
print('ββββββββββββββββββββββββββββββββββββββββββββββββββ');
// HTTP Interceptor logs
print('β π‘ Response: 200 ---- Body: {"status":"success"}');
// Error logs
print('β β Error restoring session: Exception details');
// Framework warnings
print('[log] Incorrect use of ParentDataWidget');
// Native logs
print('I/flutter: FCM Service initialized successfully');
Advanced Integration for Custom Loggers:
If you're using a custom logging framework that doesn't use print()
, you can manually integrate:
// For the logger package
import 'package:logger/logger.dart';
import 'package:crashlog/crashlog.dart';
final logger = Logger(
output: MultiOutput([
ConsoleOutput(),
// Add custom output that forwards to Crashlog
CrashlogOutput(),
]),
);
class CrashlogOutput extends LogOutput {
@override
void output(OutputEvent event) {
for (var line in event.lines) {
LoggingAdapterService.captureLoggerOutput(
event.level.name,
line,
source: 'logger',
);
}
}
}
// For dio interceptors
class CustomInterceptor extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
final logMessage = 'Response: ${response.statusCode} ${response.data}';
print(logMessage); // This will be automatically captured
// Or manually capture:
LoggingAdapterService.captureDioLog(logMessage);
super.onResponse(response, handler);
}
}
Configuration Options #
Crashlog offers extensive configuration options to tailor its behavior to your needs:
await Crashlog.init(
// Core functionality
enabled: true, // Enable/disable entire package
enableScreenshots: true, // Capture screenshots on errors
autoOpenOnError: false, // Auto-open dev report on error (debug mode only)
// Storage limits
maxLogs: 50, // Max error logs to retain
maxConsoleLogs: 1000, // Max console logs to retain
logRetentionDays: 7, // Delete logs older than X days
// Metadata options
showDeviceInfo: true, // Include device info in error logs
// File naming
logFileName: "error_logs.json", // Custom error log file name
consoleLogFileName: "console_logs.json", // Custom console log file name
screenshotFolder: "crashlog_screenshots", // Custom screenshot folder
);
You can also use the CrashlogConfig
class for more type-safe configuration:
final config = CrashlogConfig(
enabled: true,
enableScreenshots: true,
maxLogs: 100,
logRetentionDays: 14,
);
await Crashlog.initWithConfig(config);
Managing Logs #
Crashlog provides methods to manage logs programmatically:
// Clear specific types of logs
await Crashlog.clearErrorLogs(); // Clear all error logs
await Crashlog.clearConsoleLogs(); // Clear all console logs
await Crashlog.clearScreenshots(); // Clear all screenshots
// Clear everything
await Crashlog.clearAll();
// Get log counts
final errorCount = await Crashlog.getErrorLogCount();
final consoleCount = await Crashlog.getConsoleLogCount();
final screenshotCount = await Crashlog.getScreenshotCount();
// Share logs (opens native share dialog)
await Crashlog.shareErrorLogs();
await Crashlog.shareConsoleLogs();
Button Variations #
// Floating Action Button
ErrorRecorderButton(
icon: Icons.bug_report,
color: Colors.red,
)
// App Bar Icon Button
ErrorRecorderIconButton(
icon: Icons.developer_mode,
tooltip: 'Open Dev Report',
)
// Inline Button
ErrorRecorderInlineButton(
label: 'View Logs',
icon: Icons.list,
)
// Overlay Button (draggable)
ErrorRecorderOverlayButton(
alignment: Alignment.bottomRight,
margin: EdgeInsets.all(16),
)
Manual Screenshot Capture #
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final GlobalKey _screenshotKey = GlobalKey();
Future<void> _captureScreenshot() async {
final screenshotPath = await ScreenshotService.instance
.captureWidget(_screenshotKey);
if (screenshotPath != null) {
print('Screenshot saved: $screenshotPath');
}
}
@override
Widget build(BuildContext context) {
return ScreenshotCaptureWidget(
child: RepaintBoundary(
key: _screenshotKey,
child: YourContent(),
),
onScreenshotCaptured: (path) {
print('Screenshot captured: $path');
},
);
}
}
Managing Logs #
// Clear specific log types
await Crashlog.clearErrorLogs();
await Crashlog.clearConsoleLogs();
await Crashlog.clearAllLogs();
// Get log counts
final counts = await Crashlog.getLogsCounts();
print('Errors: ${counts['errors']}, Console: ${counts['console']}');
// Check status
print('Initialized: ${Crashlog.isInitialized}');
print('Enabled: ${Crashlog.isEnabled}');
Dev Report Screen #
The Dev Report screen provides a comprehensive view of all captured logs:
Error Logs Tab #
- View all captured errors with timestamps
- Expand/collapse stack traces
- View associated screenshots
- Share individual or all error logs
- Clear error logs with confirmation
Console Logs Tab #
- View all console output with timestamps
- Color-coded by log level (error, warning, info, debug, print)
- Source information for logs
- Share console logs
- Clear console logs with confirmation
Features #
- Pull-to-refresh to reload logs
- Search and filter capabilities
- Export logs as text files
- Share logs via platform sharing
- Automatic cleanup based on retention policies
Platform Support #
Feature | Android | iOS | Web | Windows | macOS | Linux |
---|---|---|---|---|---|---|
Error Capture | β | β | β | β | β | β |
Console Logs | β | β | β | β | β | β |
Screenshots | β | β | β | β | β | β |
File Storage | β | β | β * | β | β | β |
Sharing | β | β | β | β | β | β |
*Web uses browser storage with limitations
Best Practices #
1. Initialize Early #
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Crashlog.init(/* config */);
runApp(MyApp());
}
2. Use Appropriate Log Levels #
Crashlog.logDebug('Detailed debugging info');
Crashlog.logInfo('General information');
Crashlog.logWarning('Something unusual happened');
Crashlog.logError('An error occurred');
3. Configure Retention Policies #
await Crashlog.init(
maxLogs: 100, // Keep last 100 error logs
maxConsoleLogs: 5000, // Keep last 5000 console logs
logRetentionDays: 14, // Delete logs older than 2 weeks
);
4. Production Configuration #
await Crashlog.init(
enabled: kDebugMode, // Only enable in debug mode
enableScreenshots: false, // Disable screenshots in production
showDeviceInfo: false, // Disable device info for privacy
);
Example App #
Check out the comprehensive example app in the example/
directory that demonstrates:
- Error simulation and capture
- Console logging examples
- Screenshot functionality
- Dev Report screen usage
- Different button types
- Cross-platform compatibility
Run the example:
cd example
flutter run
Advanced Usage #
Custom Configuration Object #
final config = CrashlogConfig(
enabled: true,
enableScreenshots: true,
autoOpenOnError: false,
maxLogs: 50,
maxConsoleLogs: 1000,
logRetentionDays: 7,
showDeviceInfo: true,
logFileName: "custom_errors.json",
consoleLogFileName: "custom_console.json",
screenshotFolder: "custom_screenshots",
);
await Crashlog.initWithConfig(config);
Runtime Configuration Updates #
// Update configuration at runtime
final newConfig = Crashlog.config!.copyWith(
enableScreenshots: false,
maxLogs: 100,
);
await Crashlog.updateConfig(newConfig);
Integration with Other Logging Packages #
// Use with logger package
import 'package:logger/logger.dart';
final logger = Logger(
output: ConsoleOutput(),
);
// Custom output that also logs to Crashlog
class CrashlogOutput extends LogOutput {
@override
void output(OutputEvent event) {
for (final line in event.lines) {
// Also log to Crashlog
Crashlog.log(line, level: event.level.name);
}
}
}
Platform-Specific Notes #
Android #
- Screenshot capture works in all UI contexts
- Storage uses application documents directory
- Full stack traces are available
iOS #
- Screenshot capture works in all UI contexts
- Storage uses application documents directory
- Full stack traces are available
Web #
- Screenshot capture is limited to elements wrapped with ErrorRecorderWrapper
- Storage uses IndexedDB
- Some limitations on stack trace information based on browser
Windows/macOS/Linux #
- Screenshot capture works in all UI contexts
- Storage uses application documents directory
- Full stack traces are available
Advanced Usage #
Custom Error Handling #
// Override default error handling
Crashlog.setCustomErrorHandler((error, stackTrace) {
// Your custom error handling logic
print('Custom handler: $error');
// Return true to also use the default handler, false to only use yours
return true;
});
Manual Screenshot Capture #
// Capture a screenshot programmatically
final screenshotPath = await Crashlog.captureScreenshot('custom_name');
if (screenshotPath != null) {
print('Screenshot saved to: $screenshotPath');
}
Accessing Logs Programmatically #
// Get all error logs
final errorLogs = await Crashlog.getErrorLogs();
for (final log in errorLogs) {
print('${log.timestamp}: ${log.message}');
}
// Get all console logs
final consoleLogs = await Crashlog.getConsoleLogs();
for (final log in consoleLogs) {
print('${log.timestamp} [${log.level}]: ${log.message}');
}
Direct Dev Report Navigation #
// Open the dev report screen directly
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DevReportScreen(),
),
);
},
child: Text('Open Dev Report'),
)
Troubleshooting #
Common Issues #
Screenshots Not Captured #
- Ensure
enableScreenshots
is set totrue
in configuration - Verify your UI is wrapped with
ErrorRecorderWrapper
- Check if the platform supports screenshot capture
Console Logs Not Appearing #
- Make sure
debugPrint
is used instead ofprint
in release mode - Check if
enableConsoleLogs
is set totrue
- Try using
Crashlog.log()
methods directly
Memory Usage Concerns #
- Decrease
maxLogs
andmaxConsoleLogs
values - Set a lower
logRetentionDays
value - Call
Crashlog.clearAll()
periodically if needed
-
Print statements not captured
- Use
Crashlog.log*
methods for guaranteed capture - Global print override has limitations in Dart
- Use
-
Storage permissions
- The package handles storage permissions automatically
- Uses app-specific directories that don't require additional permissions
-
Performance impact
- All operations are asynchronous and non-blocking
- Automatic cleanup prevents storage bloat
- Minimal impact on app performance
Debug Mode #
Enable debug logging to troubleshoot issues:
import 'dart:developer' as developer;
// Check Crashlog status
developer.log('Crashlog initialized: ${Crashlog.isInitialized}');
developer.log('Crashlog enabled: ${Crashlog.isEnabled}');
Contributing #
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and submission process.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
- π Documentation
- π Issue Tracker
- π¬ Discussions
- π§ Email Support
Acknowledgments #
- Built with β€οΈ for the Flutter community
- Inspired by the need for better debugging tools in mobile development
- Thanks to all contributors and users for feedback and improvements