Logger Flutter Setup: Implementing debugPrint and package:logging for Effective Debugging
Combine Flutter's built-in debugPrint function with the package:logging framework to create a hierarchical, throttled logging system that works across development, testing, and production environments.
Implementing a robust logger Flutter setup is essential for debugging complex applications across multiple platforms. The flutter/flutter repository provides several built-in utilities in foundation/debug.dart and integrates seamlessly with Dart's package:logging framework. This guide demonstrates how to wire these components together using actual source implementations from the Flutter SDK.
Core Logging APIs in the Flutter SDK
Before configuring your logger, understand the three primary APIs available in the Flutter ecosystem.
debugPrint from foundation/debug.dart
The debugPrint function in packages/flutter/lib/src/foundation/debug.dart serves as the standard output mechanism for Flutter applications. Unlike raw print() statements, debugPrint implements throttling logic controlled by debugPrintThrottlePeriod (defined in packages/flutter/lib/src/foundation/constants.dart) to prevent console flooding that could block the UI thread.
dart:developer log API
For structured logging with metadata, the log function from dart:developer sends entries directly to the Dart VM and attached IDEs. This API supports log levels, error objects, stack traces, and named loggers, making it ideal for tracing execution flow in DevTools.
package:logging Integration
Flutter wraps the external package:logging framework in packages/flutter/lib/src/foundation/logging.dart, providing a hierarchical logger system. This allows you to define named loggers (e.g., Logger('AuthService')), configure multiple handlers, and filter output by severity levels (FINE, INFO, WARNING, SEVERE).
Step-by-Step Logger Flutter Setup
Basic Implementation with debugPrint
For simple debugging, use the built-in debugPrint function:
import 'package:flutter/foundation.dart';
void main() {
debugPrint('Application startup initiated');
debugPrint('Environment: ${kDebugMode ? "debug" : "production"}');
}
Structured Logging with dart:developer
When you need metadata and stack traces:
import 'dart:developer' as developer;
class NetworkService {
void fetchData() {
try {
// Network logic here
developer.log(
'Data fetched successfully',
name: 'NetworkService',
level: 800, // INFO level
);
} catch (e, stack) {
developer.log(
'Network request failed',
name: 'NetworkService',
error: e,
stackTrace: stack,
level: 1000, // SEVERE level
);
}
}
}
Advanced Setup with package:logging
For production applications, configure the hierarchical logging system:
- Add the dependency to
pubspec.yaml:
dependencies:
logging: ^1.2.0
- Initialize the logger in your main entry point:
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
void initLogger({Level level = Level.INFO}) {
Logger.root.level = level;
Logger.root.onRecord.listen((LogRecord record) {
final buffer = StringBuffer()
..write('[${record.level.name}] ')
..write('${record.loggerName}: ')
..write(record.message);
if (record.error != null) {
buffer.write(' (${record.error})');
}
debugPrint(buffer.toString());
});
}
void main() {
initLogger(level: Level.FINE);
runApp(const MyApp());
}
- Create named loggers for specific components:
final Logger _authLogger = Logger('AuthService');
final Logger _dbLogger = Logger('DatabaseService');
void authenticateUser(String username) {
_authLogger.fine('Authentication attempt for $username');
try {
// Authentication logic
_authLogger.info('User $username authenticated successfully');
} catch (e, stack) {
_authLogger.severe('Authentication failed', e, stack);
}
}
Production-Ready Configuration
Environment-Based Log Level Switching
Configure different verbosity levels for development versus production builds:
void main() {
const bool isProduction = bool.fromEnvironment('dart.vm.product');
initLogger(
level: isProduction ? Level.WARNING : Level.FINE,
);
runApp(const MyApp());
}
Persistent File Logging
For crash reporting, add a file handler alongside debugPrint:
import 'dart:io';
import 'package:path_provider/path_provider.dart';
Future<void> addFileHandler() async {
final directory = await getApplicationDocumentsDirectory();
final logFile = File('${directory.path}/app.log');
Logger.root.onRecord.listen((LogRecord record) async {
final timestamp = DateTime.now().toIso8601String();
final line = '$timestamp [${record.level.name}] '
'${record.loggerName}: ${record.message}\n';
await logFile.writeAsString(line, mode: FileMode.append);
});
}
Summary
- Use debugPrint from
packages/flutter/lib/src/foundation/debug.dartfor throttled console output that prevents UI blocking. - Leverage dart:developer.log for structured entries with stack traces and metadata visible in DevTools.
- Implement package:logging for hierarchical loggers with configurable levels and handlers, integrated via
packages/flutter/lib/src/foundation/logging.dart. - Configure environment-based log levels to silence verbose output in production builds.
- Add file handlers for persistent crash logs when debugging field issues.
Frequently Asked Questions
What is the difference between debugPrint and print in Flutter?
The debugPrint function in packages/flutter/lib/src/foundation/debug.dart implements throttling logic using debugPrintThrottlePeriod to prevent console flooding that could block the UI thread, whereas standard print statements output immediately without rate limiting and may cause performance issues in high-frequency logging scenarios.
How do I view logs from dart:developer in Flutter DevTools?
Logs sent via dart:developer.log automatically appear in the Logging tab of Flutter DevTools, displaying the logger name, severity level, message, and any attached error objects or stack traces without requiring additional configuration.
Should I use package:logging or debugPrint for a small Flutter app?
For small applications, debugPrint provides sufficient functionality with minimal setup, but package:logging becomes essential as your app scales, offering hierarchical logger names, configurable log levels, and multiple handlers that allow you to filter output differently for development and production environments.
How do I prevent sensitive information from appearing in production logs?
Configure your logger initialization to use Level.WARNING or higher in production builds by checking const bool.fromEnvironment('dart.vm.product'), and ensure your logging handlers filter out records below the threshold before they reach debugPrint or file outputs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →