From 2e0f19a1c9110138fadcb38384d3f6f5228dcf6f Mon Sep 17 00:00:00 2001 From: HazemSayad <94578938+HazemSayad@users.noreply.github.com> Date: Wed, 2 Sep 2026 00:03:35 +0300 Subject: [PATCH] add eventTransformer to logger --- README.md | 25 +++++++ example/custom_logger/main.dart | 111 ++++++++++++++++++++++++++++++++ lib/src/logger.dart | 15 ++++- test/logger_test.dart | 96 +++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 example/custom_logger/main.dart diff --git a/README.md b/README.md index ab885dc..d791131 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ var logger = Logger( filter: null, // Use the default LogFilter (-> only log in debug mode) printer: PrettyPrinter(), // Use the PrettyPrinter to format and print log output: null, // Use the default LogOutput (-> send everything to console) + eventTransformer: null // Keep events unchanged (LogEvent) ); ``` @@ -183,6 +184,30 @@ Other provided `LogOutput`s are: Possible future `LogOutput`s could send to Firebase or to Logcat. Feel free to open pull requests. +## EventTransformer + +`eventTransformer` field in the `Logger()` constructor can be used to transform the event into a subclass of the original `LogEvent` event type + +This property by itself is not that useful. It shows its powers when you pair it with any extended custom implementation of: + - `LogFilter` + - `LogPrinter` + - `LogOutput` + +And pairing it with `message` object (your custom message object) + +```dart + void log( + Level level, + dynamic message, // pass whatever you want here + { + DateTime? time, + Object? error, + StackTrace? stackTrace, + }) +``` + +Check out `example/custom_logger.dart` for a more advanced implementation of `logger` + # Acknowledgments This package was originally created by [Simon Choi](https://github.com/simc), with further diff --git a/example/custom_logger/main.dart b/example/custom_logger/main.dart new file mode 100644 index 0000000..6f76cb2 --- /dev/null +++ b/example/custom_logger/main.dart @@ -0,0 +1,111 @@ +import 'dart:math'; +import 'package:logger/logger.dart'; + +/// Custom logger implementation that supports tags and randomly masked messages to demo +/// tightened custom subtype messages and event transformers when paired with custom +/// printer or output extended implementations +var logger = CustomLogger( + printer: _ExtendedPrinter(), + eventTransformer: (event) { + final e = event.message as CustomMessage; + return _ExtendedLogEvent.fromLogEvent( + event: event, + tag: e.tag, + shouldMask: Random().nextBool(), + ); + }, +); + +void main() { + logger.d(CustomMessage( + tag: "CustomTag", message: "custom log message implementation")); +} + +/// [CustomLogger] enforces [d] to have a [CustomMessage] type +class CustomLogger extends Logger { + CustomLogger({ + LogFilter? filter, + LogPrinter? printer, + LogOutput? output, + Level? level, + EventTransformerCallback? eventTransformer, + }) : super( + filter: filter, + printer: printer, + output: output, + level: level, + eventTransformer: eventTransformer, + ); + + @override + void d(covariant CustomMessage message, + {DateTime? time, Object? error, StackTrace? stackTrace}) { + super.d(message, time: time, error: error, stackTrace: stackTrace); + } +} + +/// [CustomMessage] to enforce a consistent message type in your [CustomLogger] calls +class CustomMessage { + final String tag; + final String message; + + CustomMessage({required this.tag, required this.message}); +} + +/// Custom [LogEvent] +class _ExtendedLogEvent extends LogEvent { + final String tag; + final bool shouldMask; + + String _message; + + @override + String get message => _message; + + _ExtendedLogEvent( + Level level, + String message, { + super.error, + super.stackTrace, + super.time, + required this.tag, + required this.shouldMask, + }) : _message = message, + super(level, message); + + factory _ExtendedLogEvent.fromLogEvent({ + required LogEvent event, + required String tag, + required bool shouldMask, + }) { + final m = event.message as CustomMessage; + return _ExtendedLogEvent( + event.level, + m.message, + error: event.error, + stackTrace: event.stackTrace, + time: event.time, + tag: tag, + shouldMask: shouldMask, + ); + } +} + +/// Custom [LogPrinter] +class _ExtendedPrinter extends LogPrinter { + String? lastTagField; + bool? lastShouldMaskField; + + @override + List log(LogEvent event) { + final _ExtendedLogEvent e = event as _ExtendedLogEvent; + lastTagField = e.tag; + lastShouldMaskField = e.shouldMask; + return [ + "TAG: [${e.tag.toString()}]", + e.shouldMask + ? "*** MESSAGE IS MASKED RANDOMLY ***" + : e.message.toString(), + ]; + } +} diff --git a/lib/src/logger.dart b/lib/src/logger.dart index bcfc0b3..79024ba 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -12,6 +12,9 @@ typedef LogCallback = void Function(LogEvent event); typedef OutputCallback = void Function(OutputEvent event); +typedef EventTransformerCallback = E Function( + LogEvent event); + /// Use instances of logger to send log messages to the [LogPrinter]. class Logger { /// The current logging level of the app. @@ -36,6 +39,7 @@ class Logger { final LogFilter _filter; final LogPrinter _printer; final LogOutput _output; + final EventTransformerCallback? _eventTransformer; bool _active = true; /// Create a new instance of Logger. @@ -48,9 +52,11 @@ class Logger { LogPrinter? printer, LogOutput? output, Level? level, + EventTransformerCallback? eventTransformer, }) : _filter = filter ?? defaultFilter(), _printer = printer ?? defaultPrinter(), - _output = output ?? defaultOutput() { + _output = output ?? defaultOutput(), + _eventTransformer = eventTransformer { var filterInit = _filter.init(); if (level != null) { _filter.level = level; @@ -171,13 +177,18 @@ class Logger { throw ArgumentError('Log events cannot have Level.off'); } - var logEvent = LogEvent( + LogEvent logEvent = LogEvent( level, message, time: time, error: error, stackTrace: stackTrace, ); + + if (_eventTransformer != null) { + logEvent = _eventTransformer!.call(logEvent); + } + for (var callback in _logCallbacks) { callback(logEvent); } diff --git a/test/logger_test.dart b/test/logger_test.dart index 9b0aca3..50a45d4 100644 --- a/test/logger_test.dart +++ b/test/logger_test.dart @@ -69,6 +69,67 @@ class _AsyncPrinter extends LogPrinter { List log(LogEvent event) => [event.message.toString()]; } +class _ExtendedLogEvent extends LogEvent { + final String customField; + final bool shouldDoField; + + _ExtendedLogEvent( + super.level, + super.message, { + super.error, + super.stackTrace, + super.time, + required this.customField, + required this.shouldDoField, + }); + + factory _ExtendedLogEvent.fromLogEvent({ + required LogEvent event, + required String customField, + required bool shouldDoField, + }) { + return _ExtendedLogEvent( + event.level, + event.message, + error: event.error, + stackTrace: event.stackTrace, + time: event.time, + customField: customField, + shouldDoField: shouldDoField, + ); + } +} + +class _ExtendedPrinter extends LogPrinter { + String? lastLoggedCustomField; + bool? lastLoggedShouldDoField; + + @override + List log(LogEvent event) { + final _ExtendedLogEvent e = event as _ExtendedLogEvent; + lastLoggedCustomField = e.customField; + lastLoggedShouldDoField = e.shouldDoField; + return [ + if (e.shouldDoField) e.shouldDoField.toString(), + e.customField.toString(), + e.message.toString() + ]; + } +} + +class _ExtendedOutput extends LogOutput { + _ExtendedLogEvent? lastEventOrigin; + int? lastLinesCount; + + @override + void output(OutputEvent event) { + final _ExtendedLogEvent e = event.origin as _ExtendedLogEvent; + lastLinesCount = event.lines.length; + lastEventOrigin = e; + // No-op + } +} + class _AsyncOutput extends LogOutput { final Duration delay; bool initialized = false; @@ -181,6 +242,41 @@ void main() { expect(printedStackTrace, null); }); + test( + 'Logger.eventTransformer', + () { + const customField = "customField"; + + bool shouldDoField = true; + final extendedPrinter = _ExtendedPrinter(); + final extendedOutput = _ExtendedOutput(); + final logger = Logger( + printer: extendedPrinter, + output: extendedOutput, + eventTransformer: (event) { + final shouldDo = shouldDoField; + shouldDoField = !shouldDoField; + return _ExtendedLogEvent.fromLogEvent( + event: event, + customField: customField, + shouldDoField: shouldDo, + ); + }, + ); + logger.log(Level.info, "message"); + expect(extendedPrinter.lastLoggedCustomField, customField); + expect(extendedPrinter.lastLoggedShouldDoField, !shouldDoField); + expect(extendedOutput.lastEventOrigin, isNotNull); + expect(extendedOutput.lastLinesCount, !shouldDoField ? 3 : 2); + + logger.log(Level.info, "message"); + expect(extendedPrinter.lastLoggedCustomField, customField); + expect(extendedPrinter.lastLoggedShouldDoField, !shouldDoField); + expect(extendedOutput.lastEventOrigin, isNotNull); + expect(extendedOutput.lastLinesCount, !shouldDoField ? 3 : 2); + }, + ); + test('Logger.t', () { var logger = Logger(filter: _AlwaysFilter(), printer: callbackPrinter); var stackTrace = StackTrace.current;