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

Colors not working in Android Studio #2

Open
phpmaple opened this issue Jul 4, 2019 · 18 comments
Open

Colors not working in Android Studio #2

phpmaple opened this issue Jul 4, 2019 · 18 comments

Comments

@phpmaple
Copy link

phpmaple commented Jul 4, 2019

^[[38;5;196m⛔ - FormatException: Unexpected end of input (at character 1)
[11:10:59 AM]: <== ^

@simc
Copy link
Owner

simc commented Jul 4, 2019

Is this on Android or iOS? It may be related to #1

@Tyxz
Copy link

Tyxz commented Jul 5, 2019

I have the same issue. Could it be due to Android Studio? Maybe coloured output is not supported there and only works with Visual Studio Code.

EDIT: I worked on a custom logger myself before I found your much better work 😁. There I tried various ways out to colour the console output, but nothing worked. That's why I guess it is an issue with Android Studio.

@simc
Copy link
Owner

simc commented Jul 5, 2019

@phpmaple @Tyxz I'm working on a solution but this could take some time. In the meantime you can try out the new on device console or disable colors:

var logger = Logger(
  printer: PrettyPrinter(
    colors: false,
)

@simc simc changed the title Console Color not show well Colors not working in Android Studio Jul 5, 2019
@Creolophus
Copy link

Hey, this tool is absolutely excellent, but unfortunately i am using Android Studio. Will u fix the ansi-color problem in Android Studio?

@simc
Copy link
Owner

simc commented Jul 16, 2019

@Creolophus Yes, I'm currently working on a solution.

@NarHakobyan
Copy link

@leisim Hi, any news?)

@simc
Copy link
Owner

simc commented Sep 17, 2019

@NarHakobyan

Sorry have been busy with hive lately.

@NarHakobyan
Copy link

Congrats, awesome project 🤘

@erabti
Copy link

erabti commented Dec 11, 2019

@leisim Same problem on Ubuntu Android Studio, is it related to fonts configuration? Emojis also don't work.

@saif97
Copy link

saif97 commented Dec 17, 2019

The terminal in IntelliJ works just fine. I guess it's an issue w/ the console (the thing that appears when you hit run).

@giovannilattanzio
Copy link

giovannilattanzio commented Jan 16, 2020

While we wait for a fix, you can work around temporarily using the Grep Console plugin: you can configure it to search for the following expressions:

  • .*38;5;199m.* for WTF messages
  • .*38;5;196m.* for ERROR messages
  • .*38;5;208m.* for WARN messages
  • .*38;5;12m.* for INFO messages
  • .*38;5;244m.* for VERBOSE messages

and choose background/foreground colors of your choose.
Thus, you can continue use this awesome library and still have colors in your Android Studio console.

It's a little workaround, I know, but hey, as long as it works!

@haarts
Copy link
Collaborator

haarts commented Jan 31, 2020

That's really creative!

@dp543831577
Copy link

While we wait for a fix, you can work around temporarily using the Grep Console plugin: you can configure it to search for the following expressions:

  • .*38;5;199m.* for WTF messages
  • .*38;5;196m.* for ERROR messages
  • .*38;5;208m.* for WARN messages
  • .*38;5;12m.* for INFO messages
  • .*38;5;244m.* for VERBOSE messages

and choose background/foreground colors of your choose.
Thus, you can continue use this awesome library and still have colors in your Android Studio console.

It's a little workaround, I know, but hey, as long as it works!

tks!

@PandaProgParis
Copy link

@leisim Hello, thanks for all, it's very usefull.
@giovannilattanzio Thanks aswell.

It's a good solution but ANSI code's stay in console and it's not very beautifull.
image

So, I found a quick solution. Replace ANSI codes by strings like ERROR, FATAL, WARNING.... and add a boolean to set if we prefere to see labels or colors.
And with Grep Console plugin, colors are good.

image

pretty_printer.dart

import 'dart:convert';

import 'package:logger/src/logger.dart';
import 'package:logger/src/log_printer.dart';
import 'package:logger/src/ansi_color.dart';

/// Default implementation of [LogPrinter].
///
/// Outut looks like this:
/// ```
/// ┌──────────────────────────
/// │ Error info
/// ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
/// │ Method stack history
/// ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
/// │ Log message
/// └──────────────────────────
/// ```
class PrettyPrinter extends LogPrinter {
  static const topLeftCorner = '┌';
  static const bottomLeftCorner = '└';
  static const middleCorner = '├';
  static const verticalLine = '│';
  static const doubleDivider = "─";
  static const singleDivider = "┄";

  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),
  };

  static final levelPrefix = {
    Level.verbose: AnsiColor.txt(""),
    Level.debug: AnsiColor.txt(   "  DEBUG "),
    Level.info: AnsiColor.txt(    "   INFO "),
    Level.warning: AnsiColor.txt( "WARNING "),
    Level.error: AnsiColor.txt(   "  ERROR "),
    Level.wtf: AnsiColor.txt(     "  FATAL "),
  };

  static final levelEmojis = {
    Level.verbose: '',
    Level.debug: '🐛 ',
    Level.info: '💡 ',
    Level.warning: '⚠️ ',
    Level.error: '⛔ ',
    Level.wtf: '👾 ',
  };

  static final stackTraceRegex = RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)');

  static DateTime _startTime;

  final int methodCount;
  final int errorMethodCount;
  final int lineLength;
  final bool colors;
  final bool prefix;
  final bool printEmojis;
  final bool printTime;

  String _topBorder = '';
  String _middleBorder = '';
  String _bottomBorder = '';

  PrettyPrinter({
    this.methodCount = 2,
    this.errorMethodCount = 8,
    this.lineLength = 120,
    this.colors = true,
    this.prefix = false,
    this.printEmojis = true,
    this.printTime = false,
  }) {
    _startTime ??= DateTime.now();

    var doubleDividerLine = StringBuffer();
    var singleDividerLine = StringBuffer();
    for (int i = 0; i < lineLength - 1; i++) {
      doubleDividerLine.write(doubleDivider);
      singleDividerLine.write(singleDivider);
    }

    _topBorder = "$topLeftCorner$doubleDividerLine";
    _middleBorder = "$middleCorner$singleDividerLine";
    _bottomBorder = "$bottomLeftCorner$doubleDividerLine";
  }

  @override
  List<String> log(LogEvent event) {
    var messageStr = stringifyMessage(event.message);

    String stackTraceStr;
    if (event.stackTrace == null) {
      if (methodCount > 0) {
        stackTraceStr = formatStackTrace(StackTrace.current, methodCount);
      }
    } else if (errorMethodCount > 0) {
      stackTraceStr = formatStackTrace(event.stackTrace, errorMethodCount);
    }

    var errorStr = event.error?.toString();

    String timeStr;
    if (printTime) {
      timeStr = getTime();
    }

    return _formatAndPrint(
      event.level,
      messageStr,
      timeStr,
      errorStr,
      stackTraceStr,
    );
  }

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

    var formatted = <String>[];
    var count = 0;
    for (var line in lines) {
      var match = stackTraceRegex.matchAsPrefix(line);
      if (match != null) {
        if (match.group(2).startsWith('package:logger')) {
          continue;
        }
        var newLine = "#$count   ${match.group(1)} (${match.group(2)})";
        formatted.add(newLine.replaceAll('<anonymous closure>', '()'));
        if (++count == methodCount) {
          break;
        }
      } else {
        formatted.add(line);
      }
    }

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

  String getTime() {
    String _threeDigits(int n) {
      if (n >= 100) return "$n";
      if (n >= 10) return "0$n";
      return "00$n";
    }

    String _twoDigits(int n) {
      if (n >= 10) return "$n";
      return "0$n";
    }

    var now = DateTime.now();
    String h = _twoDigits(now.hour);
    String min = _twoDigits(now.minute);
    String sec = _twoDigits(now.second);
    String ms = _threeDigits(now.millisecond);
    var timeSinceStart = now.difference(_startTime).toString();
    return "$h:$min:$sec.$ms (+$timeSinceStart)";
  }

  String stringifyMessage(dynamic message) {
    if (message is Map || message is Iterable) {
      var encoder = JsonEncoder.withIndent('  ');
      return encoder.convert(message);
    } else {
      return message.toString();
    }
  }

  AnsiColor _getLevelColor(Level level) {
    if (colors) {
      return levelColors[level];
    } else {
      if (prefix) {
        return levelPrefix[level];
      } else {
        return AnsiColor.none();
      }
    }
  }

  AnsiColor _getErrorColor(Level level) {
    if (colors) {
      if (level == Level.wtf) {
        return levelColors[Level.wtf].toBg();
      } else {
        return levelColors[Level.error].toBg();
      }
    } else {
      if (prefix) {
        return levelPrefix[level];
      } else {
        return AnsiColor.none();
      }
    }
  }

  String _getEmoji(Level level) {
    if (printEmojis) {
      return levelEmojis[level];
    } else {
      return "";
    }
  }

  List<String> _formatAndPrint(
    Level level,
    String message,
    String time,
    String error,
    String stacktrace,
  ) {
    List<String> buffer = [];
    var color = _getLevelColor(level);
    buffer.add(color(_topBorder));

    if (error != null) {
      var errorColor = _getErrorColor(level);
      for (var line in error.split('\n')) {
        buffer.add(
          color('$verticalLine ') +
              errorColor.resetForeground +
              errorColor(line) +
              errorColor.resetBackground,
        );
      }
      buffer.add(color(_middleBorder));
    }

    if (stacktrace != null) {
      for (var line in stacktrace.split('\n')) {
        buffer.add('$color$verticalLine $line');
      }
      buffer.add(color(_middleBorder));
    }

    if (time != null) {
      buffer..add(color('$verticalLine $time'))..add(color(_middleBorder));
    }

    var emoji = _getEmoji(level);
    for (var line in message.split('\n')) {
      buffer.add(color('$verticalLine $emoji$line'));
    }
    buffer.add(color(_bottomBorder));

    return buffer;
  }
}

ansi_color.dart

class AnsiColor {
  /// ANSI Control Sequence Introducer, signals the terminal for new settings.
  static const ansiEsc = '\x1B[';

  /// Reset all colors and options for current SGRs to terminal defaults.
  static const ansiDefault = "${ansiEsc}0m";

  final int fg;
  final int bg;
  final bool color;
  final String prefix;

  AnsiColor.none()
      : fg = null,
        bg = null,
        color = false,
        prefix = "";

  AnsiColor.fg(this.fg)
      : bg = null,
        color = true,
        prefix = "";

  AnsiColor.bg(this.bg)
      : fg = null,
        color = true,
        prefix = "";

  AnsiColor.txt(this.prefix)
      : fg = null,
        bg = null,
        color = false;

  String toString() {
    if (color) {
      if (fg != null) {
        return "${ansiEsc}38;5;${fg}m";
      } else if (bg != null) {
        return "${ansiEsc}48;5;${bg}m";
      } else {
        return "";
      }
    } else {
      return prefix;
    }
  }

  String call(String msg) {
    if (color) {
      return "${this}$msg$ansiDefault";
    } else {
      return "${this}$msg";
    }
  }

  AnsiColor toFg() => AnsiColor.fg(bg);

  AnsiColor toBg() => AnsiColor.bg(fg);

  /// Defaults the terminal's foreground color without altering the background.
  String get resetForeground => color ? "${ansiEsc}39m" : "";

  /// Defaults the terminal's background color without altering the foreground.
  String get resetBackground => color ? "${ansiEsc}49m" : "";

  static int grey(double level) => 232 + (level.clamp(0.0, 1.0) * 23).round();
}

@tkutcher
Copy link
Contributor

tkutcher commented May 9, 2020

Hi all - so I came up with a slightly tweaked workaround for this as well. I wrote a PrefixPrinter class which you can use to decorate the PrettyPrinter class with colors turned off.

class PrefixPrinter extends LogPrinter {
  final LogPrinter _realPrinter;
  Map<Level, String> _prefixMap;

  PrefixPrinter(this._realPrinter,
      {debug, verbose, wtf, info, warning, error, nothing}) : super() {
    _prefixMap = {
      Level.debug: debug ?? '  DEBUG ',
      Level.verbose: verbose ?? 'VERBOSE ',
      Level.wtf: wtf ?? '    WTF ',
      Level.info: info ?? '   INFO ',
      Level.warning: warning ?? 'WARNING ',
      Level.error: error ?? '  ERROR ',
      Level.nothing: nothing ?? 'NOTHING',
    };
  }

  @override
  List<String> log(LogEvent event) {
    return _realPrinter.log(event).map((s) => '${_prefixMap[event.level]}$s').toList();
  }
}

So using that you could create a printer like:

PrefixPrinter(PrettyPrinter(colors: false));

image

and you don't have to grep (or see) the ansi strings or copy/change the existing PrettyPrinter class. Hopefully this helps! And thanks for this thread in helping me find the issue/workaround in the first place.

@mohadel92
Copy link

so do i have to customize the classes to fix this issue?

@tkutcher
Copy link
Contributor

tkutcher commented Nov 30, 2020

@mohadel92 The solution I mention included some classes that were merged and released in 0.9.2. You can check out the pull request that has some examples of how you'd use this in #44

But the idea was that you don't have to customize the classes, this additional class just decorates the existing. Here is a simple example:

final logger = Logger(
  printer:  PrefixPrinter(
    PrettyPrinter(
      methodCount: 2,
      colors: false,
      printTime: true
    )
  )
);

Then you just need to grep the prefixes (see README). Note this was specifically targeting AndroidStudio / JetBrains IDEs.

@Hakim-Allaoui
Copy link

Thanks @PandaProgParis it's working with me perfectly 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests