A shared Flutter logging package for internal use across all packages and applications. Built on top of logger with support for environment-aware logging and temporary release-mode debugging.
- Overview
- Features
- Installation
- Quick Start
- Log Levels
- Release Mode Debugging
- API Reference
- Architecture
- Versioning
- Security Guidelines
- Contributing
- Changelog
mawaqit_core_logger is a centralized logging solution designed to:
- β Silence all logs in release mode by default
- β Allow temporary logging in release for troubleshooting (with auto-expiry)
- β Provide a consistent API across all internal packages and apps
- β Act as a single place to plug in crash reporters (Crashlytics, Sentry) in the future
Why not just use
print()?print()always logs β even in release mode β potentially exposing sensitive data to anyone with device log access.mawaqit_core_loggeris environment-aware and safe by default.
| Feature | Details |
|---|---|
| Environment-aware | Logs in debug/profile, silent in release by default |
| Log levels | trace β debug β info β warn β error β fatal |
| Pretty output | Colored, emoji-tagged, timestamped logs in console |
| Temporary release logging | Enable with auto-expiry timeout (default: 2 hours) |
| Status inspection | Check if release logging is active and time remaining |
| Future-ready | Hook point for Crashlytics / Sentry already in place |
Add mawaqit_core_logger to your package's pubspec.yaml:
dependencies:
core_logger:
git:
url: https://github.com/mawaqit/mawaqit_core_logger.git
ref: v1.1.0 # always pin to a specific tagThen run:
flutter pub get
β οΈ Always pin to a version tag (e.g.v1.1.0), never useref: mainin production. This ensures your builds are reproducible and not broken by upstream changes.
import 'package:mawaqit_core_logger/mawaqit_core_logger.dart';
class AuthService {
Future<void> login(String email, String password) async {
try {
Log.i('Login attempt for: $email');
await _api.login(email, password);
Log.i('Login successful');
} catch (e, s) {
Log.e('Login failed', error: e, stackTrace: s);
}
}
}That's it. No setup, no initialization needed for standard usage.
Use the appropriate level to make logs meaningful and filterable:
Log.t('Verbose detail β socket frames, raw JSON'); // π trace
Log.d('General debug info β variable values'); // π debug
Log.i('Key app events β user login, screen open'); // βΉοΈ info
Log.w('Non-critical issues β retry attempt, fallback used'); // β οΈ warning
Log.e('Errors β failed API call, caught exception', // β error
error: e,
stackTrace: s,
);
Log.f('Fatal β unrecoverable state, app about to crash', // π fatal
error: e,
stackTrace: s,
);| Level | When to use | Example |
|---|---|---|
trace |
Very verbose, low-level detail | Raw HTTP request/response body |
debug |
Development-time info | Parsed model values, state changes |
info |
Important business events | User logged in, order placed |
warn |
Something unexpected but recoverable | Token expiring, cache miss |
error |
Caught exceptions, failures | API error, file not found |
fatal |
Unrecoverable failures | DB corruption, critical crash |
Rule of thumb: If it would be noise in production, use
debug. If it matters for understanding user flows, useinfo.
By default, all logs are silenced in release builds. For troubleshooting bugs that only reproduce in release mode, you can temporarily enable logging with an auto-expiry timeout.
β οΈ Security warning: Only enable release logging when actively troubleshooting. It auto-disables after the timeout, but you should also callLog.disableReleaseLogging()when done.
Option A: Hidden Gesture Trigger
Suitable for quick, on-device debugging without a backend dependency:
// Typically placed on a logo or version text in Settings / About screen
int _tapCount = 0;
GestureDetector(
onTap: () {
_tapCount++;
if (_tapCount >= 7) {
Log.enableReleaseLogging(timeout: const Duration(hours: 1));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('β οΈ Debug logging enabled for 1 hour'),
backgroundColor: Colors.orange,
),
);
_tapCount = 0;
}
},
child: const AppVersionText(),
)Best for teams β toggle logging remotely per user or environment without a new build:
// In app startup (e.g. main.dart or AppProvider init)
final remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.fetchAndActivate();
final enableLogs = remoteConfig.getBool('force_enable_logs');
if (enableLogs) {
Log.enableReleaseLogging(timeout: const Duration(hours: 2));
}Set force_enable_logs = true in your Firebase Remote Config console, then set it back to false when done.
// Default is 2 hours
Log.enableReleaseLogging(timeout: const Duration(minutes: 30));if (LogConfig.isForceEnabled) {
final remaining = LogConfig.timeRemaining;
Log.i('Release logging active β expires in ${remaining?.inMinutes} min');
}// Call this as soon as troubleshooting is complete
Log.disableReleaseLogging();// Log methods
Log.t(String msg)
Log.d(String msg)
Log.i(String msg)
Log.w(String msg)
Log.e(String msg, {Object? error, StackTrace? stackTrace})
Log.f(String msg, {Object? error, StackTrace? stackTrace})
// Release logging control
Log.enableReleaseLogging({Duration? timeout}) // default: 2 hours
Log.disableReleaseLogging()LogConfig.shouldLog // bool β is logging active right now?
LogConfig.isForceEnabled // bool β is release logging force-enabled?
LogConfig.timeRemaining // Duration? β time left before auto-disablemawaqit_core_logger/
lib/
src/
log_config.dart # Controls when logging is allowed (env + release override)
app_logger.dart # Logger instance configuration (PrettyPrinter, levels)
log.dart # Public Log class β the only API consumers use
mawaqit_core_logger.dart # Public exports: Log + LogConfig
app_logger.dart is not exported.
The Logger instance is an internal detail. Consumers should never interact with it directly β only through Log.*.
Log.enableReleaseLogging() calls refreshLogger() internally.
The underlying logger package captures log level at construction time. Rebuilding the instance after a config change ensures the new level takes effect immediately.
LogConfig is exported for status inspection only.
Consumers can read LogConfig.isForceEnabled and LogConfig.timeRemaining but all mutations should go through Log.enableReleaseLogging() / Log.disableReleaseLogging() to keep logger state in sync.
This package follows Semantic Versioning:
| Change | Version bump | Example |
|---|---|---|
| Bug fix, internal improvement | patch | v1.0.0 β v1.0.1 |
| New feature, backwards compatible | minor | v1.0.0 β v1.1.0 |
| Breaking API change | major | v1.0.0 β v2.0.0 |
# pubspec.yaml
dependencies:
mawaqit_core_logger:
git:
url: https://github.com/mawaqit/mawaqit_core_logger.git
ref: v1.1.0 # bump this when updatingThese apply to all engineers using this package:
- π« Never log passwords, tokens, or PII (emails, phone numbers, IDs) at any log level
- π« Never leave release logging enabled after troubleshooting β call
Log.disableReleaseLogging()or let the timeout expire - π« Never log raw API responses that may contain sensitive user data
- β
Use
Log.ewith stackTrace for all caught exceptions β this is where Crashlytics will hook in - β
Prefer
Log.ifor user-facing events (login, checkout) β these are most useful for debugging flows
This is an internal package maintained by the core platform team.
- Open an issue describing the problem or feature
- Branch from
main:git checkout -b feat/your-feature - Make changes, update tests
- Bump version in
pubspec.yamlfollowing semver - Update Changelog
- Open a pull request β tag
@platform-teamfor review
flutter test- Added
LogConfigfor temporary release-mode logging - Added
Log.enableReleaseLogging()andLog.disableReleaseLogging() - Added
LogConfig.isForceEnabledandLogConfig.timeRemainingfor status inspection - Added
refreshLogger()to rebuild logger instance after config changes
- Initial release
Log.t / d / i / w / e / fmethods- Environment-aware logging (silent in release by default)
- PrettyPrinter configuration with colors, emojis, timestamps