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

Implement ColoredTextPrintStrategy #15

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
28 changes: 24 additions & 4 deletions lib/src/logging_bugfender.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<LogRecord> 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);
Expand Down Expand Up @@ -109,3 +120,12 @@ class LoggingBugfenderListener {
Future<void> 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,
}
99 changes: 95 additions & 4 deletions lib/src/print_strategy.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:developer' as dev;

import 'package:logging/logging.dart';
import 'package:logging_bugfender/src/logging_bugfender.dart';

Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -26,7 +36,7 @@ class PlainTextPrintStrategy extends PrintStrategy {

@override
String print(LogRecord record) {
final log = StringBuffer()
final logMessage = StringBuffer()
..writeAll(
<String>[
'[${record.level.name}]',
Expand All @@ -37,12 +47,93 @@ 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,
);
}
}

/// 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();

static final _levelColors = <Level, String?>{
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 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,
);
}

String _encodeColor(String text, Level level) {
final color = _levelColors[level];

if (color != null) {
return '${ansiEsc}38;5;${color}m$text$ansiDefault';
} else {
return text;
}
}
}
Loading