From 5f106855dfe3a93c84cac6fd036d6a91d6dcbfb6 Mon Sep 17 00:00:00 2001 From: Mateusz Pietras Date: Thu, 7 Nov 2024 13:31:24 +0100 Subject: [PATCH 1/3] Implement ColoredTextPrintStrategy --- lib/src/print_strategy.dart | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/src/print_strategy.dart b/lib/src/print_strategy.dart index 3dc8b14..9f94b42 100644 --- a/lib/src/print_strategy.dart +++ b/lib/src/print_strategy.dart @@ -46,3 +46,43 @@ class PlainTextPrintStrategy extends PrintStrategy { return log.toString(); } } + +/// Instructs [LoggingBugfenderListener] to print colored text. +/// The color is based on the log level. +/// The colored text works only if the output terminal supports ANSI escape +/// sequences. In most cases you can check if ANSI escapes are supported, using +/// `kIsWeb || io.stdout.supportsAnsiEscapes`. +class ColoredTextPrintStrategy extends PlainTextPrintStrategy { + /// Creates a strategy that prints colored plain text. + const ColoredTextPrintStrategy(); + + static final _levelColors = { + Level.FINEST: '8', + Level.FINER: '8', + Level.FINE: '8', + Level.CONFIG: null, + Level.INFO: '12', + Level.WARNING: '208', + Level.SEVERE: '196', + Level.SHOUT: '199', + }; + + /// ANSI Control Sequence Introducer, signals the terminal for new settings. + static const ansiEsc = '\x1B['; + + /// Reset all colors and options to terminal defaults. + static const ansiDefault = '${ansiEsc}0m'; + + @override + String print(LogRecord record) { + final log = super.print(record); + + final color = _levelColors[record.level]; + + if (color != null) { + return '${ansiEsc}38;5;${color}m$log$ansiDefault'; + } else { + return log; + } + } +} From d5abfe910d2918e4c97a7cb0fb2c5593d1246d6f Mon Sep 17 00:00:00 2001 From: Mateusz Pietras Date: Thu, 7 Nov 2024 14:13:48 +0100 Subject: [PATCH 2/3] Update README for ColoredTextPrintStrategy --- README.md | 41 +++++++++++++++++++++++++++++++++++++ lib/src/print_strategy.dart | 15 +++++++++----- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 40ca5dc..6c82a31 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,47 @@ void setupLogger(bool debugMode) { } ``` +### Using `ColoredTextPrintStrategy` + +To enable log level-based text coloring in console output, consider using +[ColoredTextPrintStrategy]. Note that this strategy may not function +correctly depending on the output terminal and target device. + +For optimal results, test this print strategy in your chosen IDE and +output device combination. Consider enabling this feature conditionally, +for example, using dart defines. + +1. Prepare code setup + +```dart +// ... +void setupLogger(bool debugMode) { + if (debugMode) { + // During debugging, you'll usually want to log everything + Logger.root.level = Level.ALL; + + const useColoredText = bool.fromEnvironment("USE_COLORED_TEXT_LOGGING"); + + LoggingBugfenderListener( + config.bugfenderKey, + consolePrintStrategy: useColoredText + ? const ColoredTextPrintStrategy() + : const PlainTextPrintStrategy(), + ).listen(Logger.root); + } else { + // ... + } +} +``` + +2. Pass the flag using `--dart-define` + +``` +flutter run --dart-define="USE_COLORED_TEXT_LOGGING=true" +``` + +Note: You can also use --dart-define-from-file which is introduced in Flutter 3.7. + ### Custom data You can also add and remove custom data. diff --git a/lib/src/print_strategy.dart b/lib/src/print_strategy.dart index 9f94b42..d935224 100644 --- a/lib/src/print_strategy.dart +++ b/lib/src/print_strategy.dart @@ -47,11 +47,16 @@ class PlainTextPrintStrategy extends PrintStrategy { } } -/// Instructs [LoggingBugfenderListener] to print colored text. -/// The color is based on the log level. -/// The colored text works only if the output terminal supports ANSI escape -/// sequences. In most cases you can check if ANSI escapes are supported, using -/// `kIsWeb || io.stdout.supportsAnsiEscapes`. +/// Instructs [LoggingBugfenderListener] to print color-coded text based +/// on log levels. Color display depends on the terminal’s support for ANSI +/// escape sequences, which theoretically can be verified using +/// `io.stdout.supportsAnsiEscapes`. However, this check often fails to return +/// `true` even when ANSI escapes are supported by the terminal. +/// +/// For optimal results, test this print strategy in your chosen +/// IDE and output device combination. Consider enabling this feature +/// conditionally, such as through an environment variable. + class ColoredTextPrintStrategy extends PlainTextPrintStrategy { /// Creates a strategy that prints colored plain text. const ColoredTextPrintStrategy(); From f743fc2954720d108271b99e8ef8884df309b65a Mon Sep 17 00:00:00 2001 From: Mateusz Pietras Date: Tue, 12 Nov 2024 00:33:56 +0100 Subject: [PATCH 3/3] wip --- lib/src/logging_bugfender.dart | 28 ++++++++++++--- lib/src/print_strategy.dart | 62 +++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/lib/src/logging_bugfender.dart b/lib/src/logging_bugfender.dart index 3f072c3..f608025 100644 --- a/lib/src/logging_bugfender.dart +++ b/lib/src/logging_bugfender.dart @@ -42,6 +42,7 @@ class LoggingBugfenderListener { int? maximumLocalStorageSize, this.consolePrintStrategy = const NeverPrintStrategy(), this.bugfenderPrintStrategy = const PlainTextPrintStrategy(), + this.consoleLoggingMethod = ConsoleLoggingMethod.print, bool enableUIEventLogging = true, bool enableCrashReporting = true, bool enableAndroidLogcatLogging = true, @@ -63,16 +64,26 @@ class LoggingBugfenderListener { /// Defines if and how logs should be created and printed to the console. final PrintStrategy consolePrintStrategy; + /// Defines the preferred method for printing logs to the console. By using + /// the `log` function, additional information can be included in the console + /// output. Depending on the output console type, this approach may also help + /// display colored text correctly when using [ColoredTextPrintStrategy]. + final ConsoleLoggingMethod consoleLoggingMethod; + /// Defines if and how logs should be created and sent to Bugfender. final PrintStrategy bugfenderPrintStrategy; /// Starts listening to logs emitted by [logger]. StreamSubscription listen(Logger logger) { return logger.onRecord.listen((logRecord) { - final consoleLog = consolePrintStrategy.print(logRecord); - if (consoleLog != null) { - // ignore: avoid_print - print(consoleLog); + if (consoleLoggingMethod == ConsoleLoggingMethod.log) { + consolePrintStrategy.log(logRecord); + } else { + final consoleLog = consolePrintStrategy.print(logRecord); + if (consoleLog != null) { + // ignore: avoid_print + print(consoleLog); + } } final bugfenderLog = bugfenderPrintStrategy.print(logRecord); @@ -109,3 +120,12 @@ class LoggingBugfenderListener { Future removeCustomData(String key) => FlutterBugfender.removeDeviceKey(key); } + +/// Defines prefered method of printing the logs to the console +enum ConsoleLoggingMethod { + /// Logs will be printed using the `print` function + print, + + /// Logs will be printed using the `log` function from `dart:developer` + log, +} diff --git a/lib/src/print_strategy.dart b/lib/src/print_strategy.dart index d935224..df6fa4f 100644 --- a/lib/src/print_strategy.dart +++ b/lib/src/print_strategy.dart @@ -1,3 +1,5 @@ +import 'dart:developer' as dev; + import 'package:logging/logging.dart'; import 'package:logging_bugfender/src/logging_bugfender.dart'; @@ -8,6 +10,11 @@ abstract class PrintStrategy { /// Creates a log message. String? print(LogRecord record); + + /// Creates a log message and prints it using the `log` function from + /// `dart:developer`. + + void log(LogRecord record); } /// Instructs [LoggingBugfenderListener] to never print. @@ -17,6 +24,9 @@ class NeverPrintStrategy extends PrintStrategy { @override String? print(LogRecord record) => null; + + @override + void log(LogRecord record) {} } /// Instructs [LoggingBugfenderListener] to print plain text. @@ -26,7 +36,7 @@ class PlainTextPrintStrategy extends PrintStrategy { @override String print(LogRecord record) { - final log = StringBuffer() + final logMessage = StringBuffer() ..writeAll( [ '[${record.level.name}]', @@ -37,13 +47,29 @@ class PlainTextPrintStrategy extends PrintStrategy { ); if (record.error != null) { - log.write('\n${record.error}'); + logMessage.write('\n${record.error}'); } if (record.stackTrace != null) { - log.write('\n${record.stackTrace}'); + logMessage.write('\n${record.stackTrace}'); } - return log.toString(); + return logMessage.toString(); + } + + @override + void log(LogRecord record) { + final logMessage = '[${record.level.name}] ${record.message}'; + + dev.log( + logMessage, + name: record.loggerName, + error: record.error, + stackTrace: record.stackTrace, + level: record.level.value, + time: record.time, + sequenceNumber: record.sequenceNumber, + zone: record.zone, + ); } } @@ -80,14 +106,34 @@ class ColoredTextPrintStrategy extends PlainTextPrintStrategy { @override String print(LogRecord record) { - final log = super.print(record); + final logMessage = super.print(record); + + return _encodeColor(logMessage, record.level); + } + + @override + void log(LogRecord record) { + final logMessage = '[${record.level.name}] ${record.message}'; + + dev.log( + _encodeColor(logMessage.toString(), record.level), + name: record.loggerName, + error: record.error, + stackTrace: record.stackTrace, + level: record.level.value, + time: record.time, + sequenceNumber: record.sequenceNumber, + zone: record.zone, + ); + } - final color = _levelColors[record.level]; + String _encodeColor(String text, Level level) { + final color = _levelColors[level]; if (color != null) { - return '${ansiEsc}38;5;${color}m$log$ansiDefault'; + return '${ansiEsc}38;5;${color}m$text$ansiDefault'; } else { - return log; + return text; } } }