Logback: use colored output only when logging to a real terminal
Asked Answered
C

4

21

In my Logback configuration I have the following lines:

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>%highlight(...) %msg%n</pattern>
  </encoder>
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>WARN</level>
  </filter>
</appender>

This makes warnings and errors show up in terminal, colored, while main log file can contain much more information, e.g. INFO and DEBUG levels.

Generally, this works fine. But, when I run it from Emacs or any other "not really a terminal" program, coloring commands show out as ASCII escape sequences, e.g. ^[[31m for warning highlighting. Is it somehow possible to make Logback only use ANSI coloring when connected to a real terminal?

Colner answered 25/6, 2015 at 9:44 Comment(0)
Q
37

You have two problems here:

how to detect if you should use colors or not

This is not trivial. As this answer suggests you could use JNI call to isatty to detect if you're connected to a terminal, but it's a lot of work for fairly low-priority feature.

how to conditionally use colors in logback

That is actually quite easy (official docs), remember that you need janino for this to work:

<configuration>
    <appender name="COLOR" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%date] %highlight([%level]) [%logger{10} %file:%line] %msg%n</pattern>
            <!--             ^^^^^^^^^^ -->
        </encoder>
    </appender>
    <appender name="NOCOLOR" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%date] [%level] [%logger{10} %file:%line] %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <!-- to use enable this mode pass -Dcolor to jvm -->
        <if condition='isDefined("color")'>
            <then>
                    <appender-ref ref="COLOR"/>
            </then>
            <else>
                    <appender-ref ref="NOCOLOR"/>
            </else>
        </if>
    </root>
</configuration>
Query answered 22/4, 2016 at 9:36 Comment(1)
Thanks. if apparently isn't allowed inside root tag, so your configuration should probably be updated.Slushy
B
6

Actually filtering out ANSI escape sequences if the underlying terminal is not compatible with it is quite easy. You do not need to write any code for this.

According to the Logback documentation here you only need to set the withJansi property to true this way:

<configuration debug="true">
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <withJansi>true</withJansi>
    <encoder>
      <pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
    </encoder>
  </appender>
  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

That is it.

Bala answered 18/2, 2022 at 23:51 Comment(0)
J
2

On my side, I found a workaround by creating a custom PatternLayout. Its job is to replace default color converters by converters doing nothing when the terminal doesn't support color.

As :

  • I also use Picocli
  • and in my experience, the Picocli ANSI color support detection heuristic is pretty good.

I use it to know when the color converter should be removed. (but you could use any other heuristic or using an environmental variable like in https://mcmap.net/q/597553/-logback-use-colored-output-only-when-logging-to-a-real-terminal without the need of janino dependency)

The code looks like this :

/**
 * A Logback Pattern Layout that uses Picocli ANSI color
 * heuristic to apply ANSI color only on the terminal which
 * supports it.
 */
public class ColorAwarePatternLayout extends PatternLayout {
   static {
      if (!Ansi.AUTO.enabled()) { // Usage of Picocli heuristic
          DEFAULT_CONVERTER_MAP.put("black", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("red", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("green", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("yellow", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("blue", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("magenta", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("cyan", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("white", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("gray", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldRed", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldGreen", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldYellow", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldBlue", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldMagenta", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldCyan", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("boldWhite", NoColorConverter.class.getName());
          DEFAULT_CONVERTER_MAP.put("highlight", NoColorConverter.class.getName());
      }
   }
}
public class NoColorConverter<E> extends CompositeConverter<E> {
   @Override
   protected String transform(E event, String in) {
      return in;
   }
}
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="your.package.ColorAwarePatternLayout">
                <pattern>%gray(%30.30logger{0}) %gray(%d) [%highlight(%p)] %m%n</pattern>
            </layout>
        </encoder>
    </appender>

    <root level="WARN">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

(See https://github.com/eclipse/leshan/pull/1068)

Jester answered 3/8, 2021 at 9:5 Comment(0)
L
1

Here an example for logback console appender;

<pattern>%magenta(%d{HH:mm:ss.SSS}) %highlight([%thread]) %-5level [userId: %X{userId}]  %cyan(%logger{36}) - %blue(%msg%n)</pattern>
Lannie answered 24/2, 2023 at 15:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.