26 Logging Intro
I’ve build a Flutter app and use debugPrint()
to log message in all my code. Now, the app is growing fast and I’d like to use logging
package to manage app logging.
Please teach me about the logging
package. How it works and the benefits of using it.
Great to hear you’re expanding your Flutter knowledge! The logging
package is an excellent choice for managing app logs as your Flutter app grows. Let me walk you through it with your background in mind.
26.1 What is the logging
package?
The logging
package is Dart’s official hierarchical logging library. Think of it like Python’s logging
module - it provides structured, configurable logging with different severity levels and the ability to route logs to different outputs.
26.2 Key Benefits over debugPrint()
Structured Logging: - Hierarchical loggers (like myapp.network
, myapp.database
) - Severity levels (SEVERE, WARNING, INFO, CONFIG, FINE, etc.) - Rich log records with timestamps, logger names, and metadata
Production-Ready: - Can be completely disabled in release builds - Route logs to files, network endpoints, or crash reporting services - No performance impact when disabled
Flexible Configuration: - Control log levels per logger - Multiple output handlers - Custom formatting
26.3 Basic Usage
Here’s how you’d convert from debugPrint()
:
import 'dart:developer' as developer;
import 'package:logging/logging.dart';
// Create loggers (usually as static final)
final _logger = Logger('MyApp.HomePage');
final _networkLogger = Logger('MyApp.Network');
class HomePage extends StatefulWidget {
@override
=> _HomePageState();
_HomePageState createState() }
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
// Before: debugPrint('HomePage initialized');
.info('HomePage initialized');
_logger
_fetchData();}
Future<void> _fetchData() async {
try {
// Before: debugPrint('Fetching user data...');
.info('Fetching user data...');
_networkLogger
final response = await api.getUserData();
// Before: debugPrint('Data fetched successfully');
.info('Data fetched successfully', response.data);
_networkLogger
} catch (e) {
// Before: debugPrint('Error fetching data: $e');
.severe('Failed to fetch user data', e);
_networkLogger}
}
}
26.4 Logger Hierarchy
Loggers follow a hierarchical naming convention:
MyApp <- Root logger
├── MyApp.Network <- Network operations
├── MyApp.Database <- Database operations
├── MyApp.HomePage <- Specific page
└── MyApp.UserService <- Business logic
Child loggers inherit settings from parents, similar to CSS cascade.
26.5 Configuration Setup
Set this up early in your main()
function:
import 'package:logging/logging.dart';
import 'dart:developer' as developer;
void main() {
_setupLogging();
runApp(MyApp());}
void _setupLogging() {
// Set minimum log level
.root.level = kDebugMode ? Level.ALL : Level.WARNING;
Logger
// Listen to all log records
.root.onRecord.listen((record) {
Logger// Format log message
final message = '${record.level.name}: ${record.time}: '
'${record.loggerName}: ${record.message}';
if (kDebugMode) {
// In debug mode, print to console
.log(
developer.message,
record: record.time,
time: record.level.value,
level: record.loggerName,
name: record.error,
error: record.stackTrace,
stackTrace
);}
// In production, you might send to crash reporting
// crashlytics.log(message);
});
}
26.6 Log Levels (from highest to lowest severity)
SEVERE
(1000): Serious failuresWARNING
(900): Potential problems
INFO
(800): General informationCONFIG
(700): Configuration messagesFINE
(500): Tracing informationFINER
(400): Fairly detailed tracingFINEST
(300): Highly detailed tracing
26.7 Practical Migration Strategy
Start with main areas:
final _authLogger = Logger('MyApp.Auth'); final _apiLogger = Logger('MyApp.API'); final _uiLogger = Logger('MyApp.UI');
Replace
debugPrint()
gradually:// Before 'User logged in: ${user.email}'); debugPrint( // After .info('User logged in', {'email': user.email}); _authLogger
Use appropriate levels:
.fine('Button tapped'); // Debug info _logger.info('Data loaded'); // General info _logger.warning('Slow response'); // Potential issue _logger.severe('Login failed', error); // Critical error _logger
This approach gives you professional-grade logging that scales with your app. The hierarchical structure will help you debug specific areas, and you can easily adjust verbosity for different parts of your application.