Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
```

Expand Down Expand Up @@ -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
Expand Down
111 changes: 111 additions & 0 deletions example/custom_logger/main.dart
Original file line number Diff line number Diff line change
@@ -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<String> 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(),
];
}
}
15 changes: 13 additions & 2 deletions lib/src/logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ typedef LogCallback = void Function(LogEvent event);

typedef OutputCallback = void Function(OutputEvent event);

typedef EventTransformerCallback<E extends LogEvent> = E Function(
LogEvent event);

/// Use instances of logger to send log messages to the [LogPrinter].
class Logger {
/// The current logging level of the app.
Expand All @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
96 changes: 96 additions & 0 deletions test/logger_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,67 @@ class _AsyncPrinter extends LogPrinter {
List<String> 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<String> 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;
Expand Down Expand Up @@ -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;
Expand Down
Loading