Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(printer):create one line printer with stack trace #145

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
151 changes: 151 additions & 0 deletions lib/src/printers/one_line_printer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import 'dart:convert';
import 'package:logger/src/ansi_color.dart';
import 'package:logger/src/log_printer.dart';
import 'package:logger/src/logger.dart';

/// An implementation of [LogPrinter] that prints everything on one line,
/// including the function that called the logger function. This makes it
/// possible to use VSCode's log filter more efficiently.
/// For example:
///
/// ```
/// [D] _HomePageState.someFunction (package:my_app/pages/home/HomePage.dart:141:12) some_message
/// ```
///
/// So by filtering by 'HomePage' one can see all the logs by that page.
///
/// It is also possible to print the time. Anonymous functions have been
/// replaced by <a> to save space.
class OneLinePrinter extends LogPrinter {
@override

/// Matches a stacktrace line as generated on Android/iOS devices.
/// For example:
/// #1 Logger.log (package:logger/src/logger.dart:115:29)
static final _deviceStackTraceRegex =
RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)');

/// Matches a stacktrace line as generated by Flutter web.
/// For example:
/// packages/logger/src/printers/pretty_printer.dart 91:37
static final _webStackTraceRegex =
RegExp(r'^((packages|dart-sdk)\/[^\s]+\/)');

/// Matches a stacktrace line as generated by browser Dart.
/// For example:
/// dart:sdk_internal
/// package:logger/src/logger.dart
static final _browserStackTraceRegex =
RegExp(r'^(?:package:)?(dart:[^\s]+|[^\s]+)');

static final levelPrefixes = {
Level.verbose: '[V]',
Level.debug: '[D]',
Level.info: '[I]',
Level.warning: '[W]',
Level.error: '[E]',
Level.wtf: '[WTF]',
};

static final levelColors = {
Level.verbose: AnsiColor.fg(AnsiColor.grey(0.5)),
Level.debug: AnsiColor.none(),
Level.info: AnsiColor.fg(12),
Level.warning: AnsiColor.fg(208),
Level.error: AnsiColor.fg(196),
Level.wtf: AnsiColor.fg(199),
};

final bool printTime;
final bool colors;

OneLinePrinter({this.printTime = false, this.colors = true});

bool _discardDeviceStacktraceLine(String line) {
var match = _deviceStackTraceRegex.matchAsPrefix(line);
if (match == null) {
return false;
}
return match.group(2)!.startsWith('package:logger');
}

bool _discardWebStacktraceLine(String line) {
var match = _webStackTraceRegex.matchAsPrefix(line);
if (match == null) {
return false;
}
return match.group(1)!.startsWith('packages/logger') ||
match.group(1)!.startsWith('dart-sdk/lib');
}

bool _discardBrowserStacktraceLine(String line) {
var match = _browserStackTraceRegex.matchAsPrefix(line);
if (match == null) {
return false;
}
return match.group(1)!.startsWith('package:logger') ||
match.group(1)!.startsWith('dart:');
}

String? formatStackTrace(StackTrace? stackTrace, int methodCount) {
var lines = stackTrace.toString().split('\n');

var formatted = <String>[];
var count = 0;
for (var line in lines) {
if (_discardDeviceStacktraceLine(line) ||
_discardWebStacktraceLine(line) ||
_discardBrowserStacktraceLine(line) ||
line.isEmpty) {
continue;
}

line = line.replaceFirst(RegExp(r'#\d+\s+'), '');
//shorten anonymous closures to <a>
formatted.add('${line.replaceFirst(".<anonymous closure>", '<a>')}');
if (++count == methodCount) {
break;
}
}

if (formatted.isEmpty) {
return null;
} else {
return formatted.join('\n');
}
}

@override
List<String> log(LogEvent event) {
String? stackTraceStr;
//methodCount is 1 to save space.
if (event.stackTrace == null) {
stackTraceStr = formatStackTrace(StackTrace.current, 1);
} else {
stackTraceStr = formatStackTrace(event.stackTrace, 1);
}
var messageStr = _stringifyMessage(event.message);
var errorStr = event.error != null ? ' ERROR: ${event.error}' : '';
var timeStr = printTime ? 'TIME: ${DateTime.now().toIso8601String()}' : '';
return [
'${_labelFor(event.level)} $stackTraceStr $timeStr $messageStr$errorStr'
];
}

String _labelFor(Level level) {
var prefix = levelPrefixes[level]!;
var color = levelColors[level]!;

return colors ? color(prefix) : prefix;
}

String _stringifyMessage(dynamic message) {
final finalMessage = message is Function ? message() : message;
if (finalMessage is Map || finalMessage is Iterable) {
var encoder = JsonEncoder.withIndent(null);
return encoder.convert(finalMessage);
} else {
return finalMessage.toString();
}
}
}