Debugging after an app hits production isnβt just frustrating β itβs expensive. It drains time, money, and your teamβs morale. But what if you could catch most issues before they ever reach your users?
Thatβs the power of smart, debug-only logging in Flutter β a small practice that delivers huge returns.
In Flutter, logs can be useful, but without structure, they become noise. My approach? A custom
DebugUtils utility that adds:
And best of all β it only runs when assert statements are active. That means:
import 'dart:developer' as dev;
enum LogLevel { success, info, warning, error, debug }
class DebugUtils {
static void debugLog(
String message, {
LogLevel level = LogLevel.debug,
bool breakPoint = false,
String? tag,
}) {
assert(() {
if (breakPoint) dev.debugger();
_logMessage(message, level: level, tag: tag);
return true;
}());
}
static void _logMessage(
String message, {
required LogLevel level,
String? tag,
}) {
final timestamp = DateTime.now().toIso8601String();
final levelStr = level.toString().split('.').last.toUpperCase();
final effectiveTag = tag ?? 'DebugUtils';
final formattedMessage = '[$timestamp][$levelStr] $message';
dev.log(
formattedMessage,
name: '$effectiveTag @rohan-165',
level: _mapLogLevelToInt(level),
);
}
static int _mapLogLevelToInt(LogLevel level) {
switch (level) {
case LogLevel.success:
return 850;
case LogLevel.info:
return 800;
case LogLevel.warning:
return 900;
case LogLevel.error:
return 1000;
case LogLevel.debug:
return 700;
}
}
}
This small utility has made a big difference in my projects like Nagarik App and Ambition Guru:
void main() {
DebugUtils.debugLog(
"App Init successfully",
level: LogLevel.info,
);
DebugUtils.debugLog("No Data Found");
DebugUtils.debugLog(
"Failed to load user data",
level: LogLevel.error,
tag: 'UserModel',
);
}
Using tags (like 'UserModel', 'LoginBloc', etc.) lets you filter and track logs by
feature. This makes it easier to diagnose bugs fast β even in complex apps.