๐ pretty_logger_plus
A colorful, emoji-friendly Flutter console logger with automatic debug/release detection.
No more boring print() โ make your logs pop โจ
Version:
^1.0.3(console-only logging; no file I/O by default)
๐ Features
- โ
Zero-config: just call
PrettyLoggerPlus.log(...)and go - ๐ Human-friendly timestamps (12-hour clock by default) โ configurable
- ๐ Colored output + emojis for levels (colors in debug on native terminals)
- ๐ง Smart policies: all levels in debug, errors only in release (tweakable)
- ๐ท๏ธ Clean console: emoji-only labels by default (show UPPERCASE on chosen levels)
- ๐งฉ JSON line output for machine parsing
- ๐ช
onRecordhook so you can keep a small in-memory buffer or export to temp on demand - ๐ Web-friendly (prints to console; colors disabled by browsers)
๐ฆ Installation
Add this to your pubspec.yaml:
dependencies:
pretty_logger_plus: ^1.0.3
โก Quick Start (no config)
import 'package:flutter/material.dart';
import 'package:pretty_logger_plus/pretty_logger_plus.dart';
void main() {
runApp(const MyApp());
// ๐ Logs youโll see in console (all levels in debug; only errors in release)
PrettyLoggerPlus.log("App bootingโฆ", level: LogLevel.info, module: "Startup");
PrettyLoggerPlus.log("Loaded 120 questions", level: LogLevel.success, module: "API");
PrettyLoggerPlus.log("Slow response (950ms)",level: LogLevel.warning, module: "Network");
PrettyLoggerPlus.log("Submit failed", level: LogLevel.error, module: "Submit");
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(home: Scaffold(body: Center(child: Text("Check console ๐"))));
}
Example output (debug):
[12:34:56 PM] [Startup] โน๏ธ: App bootingโฆ
[12:34:56 PM] [API] โ
: Loaded 120 questions
[12:34:56 PM] [Network] โ ๏ธ: Slow response (950ms)
[12:34:57 PM] [Submit] โ: Submit failed
๐๏ธ Optional Configuration (one-liners)
1) Time format
PrettyLoggerPlus.configureTime(
style: TimeStyle.clock, // โถ default: [12:34:56 PM]
// TimeStyle.dateClock // โถ [03 Sep 2025 12:34:56 PM]
local: true, // โถ true = local time; false = UTC
showMillis: false, // โถ set true to show .SSS
);
2) What prints in debug vs release
PrettyLoggerPlus.configureConsoleLevels(
debugLevels: { for (final l in LogLevel.values) l }, // โถ all in debug
releaseLevels: { LogLevel.warning, LogLevel.error }, // โถ show warning+error in release
);
3) Label style (emoji-only vs label+emoji)
// โถ Keep console tidy: emoji-only for low-noise levels; show UPPERCASE for warn/error
PrettyLoggerPlus.configureConsoleStyle(
shortLabelFor: { LogLevel.debug, LogLevel.info, LogLevel.success },
);
4) Catch and log framework/async errors (recommended)
FlutterError.onError = (details) {
PrettyLoggerPlus.log(details.exceptionAsString(), level: LogLevel.error, module: "FlutterError");
FlutterError.dumpErrorToConsole(details);
};
runZonedGuarded(() => runApp(const MyApp()), (error, stack) {
PrettyLoggerPlus.log("Uncaught: $error\n$stack", level: LogLevel.error, module: "Zoned");
});
๐งฉ JSON Logs (no file; printed to console)
PrettyLoggerPlus.log(
"payload accepted",
level: LogLevel.info,
module: "API",
json: true, // ๐ prints a compact JSON line (also includes prettyTs)
);
Example (console):
โน๏ธ {"ts":"2025-09-03T12:35:01.123Z","prettyTs":"12:35:01 PM","module":"API","level":"INFO","msg":"payload accepted"}
๐ช In-Memory Buffer + Temp Export (optional, still no constant disk I/O)
Use the onRecord hook to keep the last N logs in memory, then write them once to the OS temp folder when you need to share a log.
import 'dart:convert';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
final List<String> _ring = [];
const int _maxLines = 500;
void setupTempLogRing() {
PrettyLoggerPlus.onRecord = (e) {
final jsonLine = const JsonEncoder().convert(e); // e is Map<String,dynamic>
_ring.add(jsonLine);
if (_ring.length > _maxLines) _ring.removeAt(0);
};
}
Future<String> saveLogsToTempFile() async {
final dir = await getTemporaryDirectory(); // e.g., /data/user/0/<app>/cache
final path = "${dir.path}/pretty_logger_plus_temp.log";
final file = File(path);
await file.writeAsString(_ring.join('\n'));
return path;
}
โ No background writing. The app remains fast; you export only when you choose.
๐ Example with both profiles (defaults & configured)
void main() {
// Defaults are fine (clock time, all levels in debug, errors in release, emoji-only labels)
PrettyLoggerPlus.log("Defaults active", level: LogLevel.info, module: "Demo");
// Apply optional tweaks
PrettyLoggerPlus.configureTime(style: TimeStyle.dateClock, showMillis: true);
PrettyLoggerPlus.configureConsoleLevels(
debugLevels: { for (final l in LogLevel.values) l },
releaseLevels: { LogLevel.warning, LogLevel.error },
);
PrettyLoggerPlus.configureConsoleStyle(
shortLabelFor: { LogLevel.debug, LogLevel.info, LogLevel.success },
);
PrettyLoggerPlus.log("Configured mode active", level: LogLevel.info, module: "Demo");
}
๐งช Testing
- Capture prints with a
ZoneSpecificationand strip ANSI colors first:
String stripAnsi(String s) => s.replaceAll(RegExp(r'\x1B\[[0-9;]*m'), '');
Future<List<String>> capturePrints(Future<void> Function() body) async {
final lines = <String>[];
final spec = ZoneSpecification(print: (_, __, ___, String msg) => lines.add(stripAnsi(msg)));
await Zone.current.fork(specification: spec).run(body);
return lines;
}
- Then assert on plain text (timestamps, labels, JSON structure).
Run:
flutter test -r expanded
โ ๏ธ Notes
- Colors show in native debug terminals; browsers (web) ignore ANSI.
- This package does not write logs to disk by default. If you need files, implement a small sink using
onRecord(see above).
๐ License
MIT
โค๏ธ Thanks
Built to make Flutter logs easier to scan at a glance โ with colors, emojis, and sensible defaults.