live_log_care 2.3.1
live_log_care: ^2.3.1 copied to clipboard
Redaction-safe logging for Flutter: build-mode gated (silent in release) and auto-scrubs passwords, tokens, cookies, OTPs and PII from logs, Dio and Bloc.
live_log_care #
Redaction-safe logging for Flutter. A drop-in logging facade that is build-mode gated (silent in release) and automatically scrubs secrets and PII — passwords, tokens, cookies, OTPs, national IDs, bearer/JWT tokens, card numbers — from your log messages, your Dio traffic and your Bloc state, before anything is written.
Most apps leak credentials to device logs without realizing it: a PrettyDioLogger
left on in release, a cookie printed by an auth interceptor, a Bloc state containing
a password. live_log_care makes the safe path the default one.
Features #
- 🔒 Automatic redaction — secrets are masked everywhere, including nested maps, lists, headers and request/response bodies.
- 🌗 True build-mode gating — uses
ProductionFilter, so in release onlywarning/errorare emitted (not theloggerdefault, which silently drops all release logs). - 🛰️ Redaction-safe Dio interceptor — replaces
PrettyDioLogger; debug-only by default. - 🧊 Bloc observer — routes Bloc/Cubit lifecycle + errors through the same gate.
- 🪝 Pluggable crash reporting — forward release errors to Crashlytics/Sentry via a tiny interface; the package itself pulls in neither.
- ⚙️ Configurable — add your own sensitive keys/patterns, change the mask, set per-build levels, swap the printer.
Install #
flutter pub add live_log_care
Fast config (recommended) #
Drop this into main.dart — same pattern used in production Mdarj apps.
Two lines at startup; Dio wiring goes where you create your client.
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:live_log_care/live_log_care.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Use LiveLog.d/i/w/e(...) everywhere instead of print/debugPrint.
// Every message is redaction-scrubbed and gated by build mode
// (silent below `warning` in release).
//
// clean(): borderless, copy-friendly output — each log is one
// dart:developer event, so multi-line JSON stays a single selectable
// block with no `I/flutter (PID): │` prefix in the VS Code Debug Console.
// Secrets show in DEBUG only; RELEASE is always redacted.
LiveLog.configure(LiveLogConfig.clean());
Bloc.observer = LiveLogBlocObserver();
// ... DI, Firebase, runApp, etc.
}
On your Dio instance (factory / DI module):
dio.interceptors.add(RedactingDioInterceptor());
// prettyJson: true is the default — indented, copyable body JSON.
Then log as usual:
LiveLog.d('dev-only diagnostic');
LiveLog.i('user opened loan #$id');
LiveLog.w('retrying request');
LiveLog.e('payment failed', error: e, stackTrace: s);
// Secrets are masked automatically (release always; debug shows real values
// unless you pass revealSecretsInDebug: false):
LiveLog.d({'login': 'a@b.com', 'password': 'hunter2'});
HTTP traffic renders as labeled boxes; Body JSON has no │ prefix so you
can copy it straight into a formatter:
[D] ┌─ Response (200) ──────────────────────────────────
│ GET https://api.example.com/items
├─ Body ────────────────────────────────────────────
{
"success": true,
"data": []
}
└───────────────────────────────────────────────────
Optional knobs (only when you need them):
LiveLog.configure(LiveLogConfig.clean(
releaseLevel: LogLevel.error, // quieter release
revealSecretsInDebug: false, // redact in debug too
decorateHttp: false, // flat `→ METHOD url` instead of boxes
));
LogRedactor.addSensitiveKeys(['iban', 'card_holder']);
Crash reporting (optional) #
class CrashlyticsSink implements CrashSink {
@override
void recordError(Object error, StackTrace? st, Object? context) =>
FirebaseCrashlytics.instance.recordError(error, st, reason: context);
}
LiveLog.crashSink = CrashlyticsSink(); // only release `error`s are forwarded
Configuration #
Everything has a secure default; configure only what you need, once at startup:
LiveLog.configure(
const LiveLogConfig(
releaseLevel: LogLevel.error, // even quieter in release
// enabled: false, // kill switch
// printer: CleanPrinter(), // or BoxedPrinter() (default), or your own
// output: DevLogOutput(), // or ConsoleOutput() (default), MultiOutput, FileOutput
// filter: MyFilter(), // custom gating; default is a ThresholdFilter
// redactionEnabled: true, // (default) never turn this off in production
// revealSecretsInDebug: false,// redact in debug too (defaults to kDebugMode; release always redacted)
),
);
// Teach the redactor about your domain's secrets:
LogRedactor.addSensitiveKeys(['iban', 'card_holder']);
LogRedactor.addValuePatterns([RegExp(r'\b\d{16}\b')]); // bare 16-digit numbers
LogRedactor.mask = '[hidden]';
The logging engine is self-contained — no third-party logger dependency.
Compose your own pipeline from the bundled building blocks, or implement
LogPrinter / LogOutput / LogFilter yourself:
// Console + a rotating file, both fed the same redacted, gated stream:
LiveLog.configure(
LiveLogConfig(
output: MultiOutput([const ConsoleOutput(), FileOutput('app.log')]),
),
);
What gets redacted by default #
Keys (case-insensitive, substring): password, passwd, pwd, token,
access_token, refresh_token, id_token, auth_token, authorization,
bearer, cookie, set-cookie, session_id, api_session, national_id,
ssn, otp, secret, client_secret, api_key, x-api-key, private_key,
credit_card, card_number, cvv, cvc.
Values: Bearer <token> and JWTs anywhere in a string. Add your own with
addValuePatterns.
Redaction reduces risk; it is not a guarantee for arbitrarily-shaped data. Keep debug network logging out of release for anything highly sensitive (the default).
Real secret values are shown while debugging locally by default —
revealSecretsInDebug defaults to kDebugMode, so values are un-masked in
debug builds only; release builds are always redacted regardless, so there's
no production-leak risk. To redact in debug too — recommended if your debug logs
may be screenshotted, screen-shared, or captured in CI — set
revealSecretsInDebug: false.
License #
MIT