app_factory_logging 0.0.1
app_factory_logging: ^0.0.1 copied to clipboard
App Factory debug logging baseline (AppLogger abstraction + Riverpod bindings).
app_factory_logging #
Riverpod-native debug logging baseline for Flutter apps.
This package gives you a small AppLogger abstraction, a console implementation
backed by Dart's official package:logging, generated Riverpod providers, and a
test fake. Logging stays separate from telemetry (Analytics / Crashlytics): this
package never depends on Firebase.
Features #
- Four public levels only:
debug/info/warn/error - Default
ConsoleAppLogger(internalpackage:logging+dart:developerlog) - Per-instance
minLevelgating (debug is silent in release by default) - Message truncation (default 2000 chars) and
debugChunkedfor large payloads - Generated keepAlive providers:
appLoggerProvider/appLogSinkProvider FakeAppLoggerfor unit tests- Optional debug-only
AppLoggerProviderObserver AppLogSinkinterface for breadcrumb bridges (e.g. Crashlytics in another package)
What This Package Does Not Do #
- Production analytics or crash reporting
- Log UI, local persistence, or remote log shipping
- Third-party logging ecosystems (Talker, etc.)
Missing console output in release builds is expected. dart:developer log
depends on the VM service, so release builds usually show nothing. That matches
the "debug diagnostics" role of this package. In release, the useful path for
warn / error is an optional AppLogSink (breadcrumbs). This package will
not fall back to print.
Installation #
dependencies:
app_factory_logging: ^0.0.1
flutter pub get
Quick Start #
Create one logger instance, override the provider with it, and optionally attach the provider observer in debug. Sharing the same instance avoids the chicken-and-egg problem of reading the logger from a container that is still being constructed:
import 'package:app_factory_logging/app_factory_logging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
final appLogger = ConsoleAppLogger(
// sink: MyCrashlyticsBreadcrumbSink(...),
);
final container = ProviderContainer(
overrides: [
appLoggerProvider.overrideWithValue(appLogger),
],
observers: [
if (kDebugMode)
AppLoggerProviderObserver(
appLogger,
include: {
// Codegen names look like authControllerProvider
'authControllerProvider',
'bootstrapControllerProvider',
},
),
],
);
runApp(
UncontrolledProviderScope(
container: container,
child: const MyApp(),
),
);
}
Custom minimum level:
appLoggerProvider.overrideWithValue(
ConsoleAppLogger(minLevel: AppLogLevel.info),
);
Using the logger in features #
Depend on the abstraction only. Prefer a shared tag constants file in your app
(for example core/logging/log_tags.dart) instead of string literals:
final log = ref.read(appLoggerProvider);
log.info('asr', 'transcription started');
log.error(
'rtc',
'join channel failed',
error: e,
stackTrace: st,
);
// Large payloads (network bodies, etc.): default chunk size is 800
log.debugChunked('net', responseBody);
Do not put tokens, passwords, emails, or full URLs in log messages.
Provider Observer #
AppLoggerProviderObserver is off by default. Mount it only in debug:
| Callback | Level | Filtered by include? |
|---|---|---|
didAddProvider |
debug | No |
didDisposeProvider |
debug | No |
didUpdateProvider |
debug | Yes (null = all updates; noisy) |
providerDidFail |
error | No |
include matches context.provider.name. Codegen providers already have names
such as authControllerProvider. Hand-written providers without name: never
match a whitelist.
Failures observed here are breadcrumbs only (see sink contract below). Your app still owns explicit error reporting.
AppLogSink contract #
AppLogSink is a breadcrumb channel. Implementations may do lightweight
logging (for example FirebaseCrashlytics.instance.log) and must never call
error-reporting APIs such as recordError. Error reporting stays at the
business boundary so each failure is recorded once.
Override the sink from bootstrap:
final container = ProviderContainer(
overrides: [
appLogSinkProvider.overrideWithValue(MyCrashlyticsBreadcrumbSink()),
// Or pass sink into ConsoleAppLogger(...) and override appLoggerProvider.
],
);
This package ships no Firebase (or other) sink implementation.
Testing #
final fake = FakeAppLogger();
final container = ProviderContainer(
overrides: [
appLoggerProvider.overrideWithValue(fake),
],
);
container.read(appLoggerProvider).info('rtc', 'hello');
expect(fake.entries, hasLength(1));
When testing ConsoleAppLogger itself, call
ConsoleAppLogger.debugResetForTest() between tests to reset the process-wide
format handler.
Suggested lint gates #
Keep feature code on AppLogger instead of ad-hoc prints:
# Ban print/debugPrint in lib (allow your bootstrap path if needed)
rg -n "\bdebugPrint\(|\bprint\(" lib --glob "*.dart"
# Ban direct package:logging / dart:developer imports in app code
rg -n "package:logging|import 'dart:developer'" lib --glob "*.dart"
Enable avoid_print in analysis_options.yaml (flutter_lints includes it).
License #
See LICENSE.