Streamline Your Flutter App Debugging with Custom Loggers

Vignaraj Ravi
3 min readJul 26, 2024

--

In any robust application, effective logging is essential for tracing issues and understanding application behavior. In this blog, I’ll guide you through creating a custom logger in Flutter using the logger package and the get_it service locator for easy access throughout your app.

Step 1: Adding Dependencies

First, let’s add the necessary dependencies to your pubspec.yaml file:

dependencies:
flutter:
sdk: flutter
logger: ^1.4.0
get_it: ^7.2.0

Step 2: Creating the Custom Logger

We will create a CustomLogger class to manage logging. This logger will capture log messages, errors, and stack traces and store them in a buffer for later retrieval.

Create a new file named custom_logger.dart:

import 'package:logger/logger.dart';

class CustomLogger {
static final CustomLogger _instance = CustomLogger._internal();

factory CustomLogger() {
return _instance;
}

CustomLogger._internal() {
_logger = Logger(
printer: PrettyPrinter(
methodCount: 2,
errorMethodCount: 5,
lineLength: 120,
colors: true,
printEmojis: true,
printTime: true,
),
);
}

late final Logger _logger;
final StringBuffer _logBuffer = StringBuffer();

void _logToBuffer(String level, String message, [dynamic error, StackTrace? stackTrace]) {
_logBuffer.writeln('${DateTime.now()}: $level - $message');
if (error != null) {
_logBuffer.writeln('Error: $error');
}
if (stackTrace != null) {
_logBuffer.writeln('StackTrace: $stackTrace');
}
}

void debug(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('DEBUG', message, error, stackTrace);
_logger.d(message, error, stackTrace);
}

void info(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('INFO', message, error, stackTrace);
_logger.i(message, error, stackTrace);
}

void warning(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('WARNING', message, error, stackTrace);
_logger.w(message, error, stackTrace);
}

void error(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('ERROR', message, error, stackTrace);
_logger.e(message, error, stackTrace);
}

void verbose(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('VERBOSE', message, error, stackTrace);
_logger.v(message, error, stackTrace);
}

void wtf(String message, [dynamic error, StackTrace? stackTrace]) {
_logToBuffer('WTF', message, error, stackTrace);
_logger.wtf(message, error, stackTrace);
}

String getLogs() {
return _logBuffer.toString();
}
}

Step 3: Setting Up GetIt

Next, we set up get_it to register and provide our CustomLogger singleton. Create a new file named locator.dart:

import 'package:get_it/get_it.dart';
import 'custom_logger.dart';

final GetIt getIt = GetIt.instance;

void setupLocator() {
getIt.registerSingleton<CustomLogger>(CustomLogger());
}

CustomLogger get appLog => getIt<CustomLogger>();

Step 4: Initializing the Service Locator

We need to initialize the service locator before running the app. Update your main.dart file:

import 'package:flutter/material.dart';
import 'locator.dart';

void main() {
setupLocator();
appLog.info("App started");
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Custom Logger Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
try {
throw Exception("An example exception");
} catch (e, stackTrace) {
appLog.debug('Debug message', e, stackTrace);
appLog.info('Info message', e, stackTrace);
appLog.warning('Warning message', e, stackTrace);
appLog.error('Error message', e, stackTrace);
appLog.verbose('Verbose message', e, stackTrace);
appLog.wtf('WTF message', e, stackTrace);
}
},
child: Text('Log Messages'),
),
ElevatedButton(
onPressed: () {
final logs = appLog.getLogs();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Logs'),
content: SingleChildScrollView(
child: Text(logs),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Close'),
),
],
);
},
);
},
child: Text('Show Logs'),
),
],
),
),
),
);
}
}

Final Thoughts

By following these steps, you can set up a powerful custom logging system in your Flutter app. This approach provides a flexible and centralized way to manage logs, making it easier to debug and trace issues in your application. Happy logging!

--

--

Vignaraj Ravi

Mobile architect, tech lead, fueled by coffee. Building great apps & sharing the journey.