21

I have the following logback pattern:

<pattern>
    {"hostname": "${HOSTNAME}", 
     "level": "%p", 
     "method": "%M", 
     "process_id": "${process}", 
     "thread_id": "%t", 
     "timestamp": "%d{Y-M-d}T%d{H:M:S.s}", 
     "mesg":"%msg"}%n
</pattern>

Unfortunately when the log messages are actually generated I see: "process_id": "process_IS_UNDEFINED"

Is there any automatically set variable for process id, such as there is for hostname? I am having a lot of trouble finding a documented list of such automatically set variables in the logback documentation, does anyone know of a better documentation source?

Edit: I am aware of Mapped Diagnostic Contexts, but was hoping for a builtin solution that does not need such setup, much like how hostname works.

vsminkov
  • 10,912
  • 2
  • 38
  • 50
qwwqwwq
  • 6,999
  • 2
  • 26
  • 49

5 Answers5

18

You can solve your problem with Mapped Diagnostic Context:

import org.slf4j.MDC;

public class Main {
    public static void main(String... args) {
        // put process ID early
        MDC.put("process_id", 
            ManagementFactory.getRuntimeMXBean().getName());
    }
}

After that all you need is to re-define your pattern as follows:

<pattern>{..., "process_id": "%X{process_id}"}</pattern>

EDITED

Also you can create your own encoder and converter and use them in logback.xml:

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;

public class ExtendedPatternLayoutEncoder extends PatternLayoutEncoder {
    @Override
    public void start() {
        // put your converter
        PatternLayout.defaultConverterMap.put(
            "process_id", ProcessIdConverter.class.getName());
        super.start();
    }
}
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;

import java.lang.management.ManagementFactory;

public class ProcessIdConverter extends ClassicConverter {
    private static final String PROCESS_ID =
            ManagementFactory.getRuntimeMXBean().getName();

    @Override
    public String convert(final ILoggingEvent event) {
        // for every logging event return processId from mx bean
        // (or better alternative)
        return PROCESS_ID;
    }
}
<encoder class="some.package.ExtendedPatternLayoutEncoder">
    <pattern>{..., "process_id": "%process_id"}</pattern>
</encoder>

Full example:

    <encoder class="some.package.ExtendedPatternLayoutEncoder">
        <pattern>%d{dd.MM.yyyy HH:mm:ss.SSS} PID:%process_id [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
LukaszTaraszka
  • 801
  • 1
  • 10
  • 26
vsminkov
  • 10,912
  • 2
  • 38
  • 50
  • This is a solution I was aware of, but it is annoying because it requires modifying our actual code. Is there anyway to setup the MDC outside of modifying the application code? Maybe by writing a separate class that modifies the MDC and putting that on the class path ? – qwwqwwq May 03 '16 at 21:29
  • @qwwqwwq I'm not sure if there is some documented way to do it. I guess you can override `ch.qos.logback.classic.PatternLayoutBase` class and put your converter in `defaultConverterMap`. But this is a dirty hack :) – vsminkov May 03 '16 at 21:46
  • @qwwqwwq There is another way. You can actually inherit from `ch.qos.logback.classic.PatternLayout` and use it in your logback.xml like [here](http://logback.qos.ch/manual/layouts.html). – vsminkov May 03 '16 at 22:00
  • 1
    @qwwqwwq also I think it is a good idea to implement your own encoder/layout for rendering `json` output so you can properly escape message in `"mesg"` – vsminkov May 03 '16 at 22:35
  • 1
    This seems a lot nicer! And yes that is a good point about the JSON, thanks for bringing that up. – qwwqwwq May 04 '16 at 01:14
  • Going to wait a bit longer for other people, otherwise you get the check btw :) – qwwqwwq May 04 '16 at 01:16
  • Beware MDC.put will impact a ThreadLocal variable, meaning %X{process_id} will be resolved only for logs produced by the main thread – blacelle Jan 11 '17 at 08:44
  • to geti it working in other threads for any thead started as a Runnable or equivalent, one should repeat the call to MDC.put() in the run() method. Also I use: MDC.put("pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]) to get rid of the hostname – user1708042 Nov 16 '20 at 11:45
9

There is a better solution than that of @vsminkov. I found it here: Layouts, were it says Creating a custom conversion specifier. Basically you create your converter, but instead of extending PatternLayoutEncoder you add a conversion rule to your configuration:

<configuration>

  <conversionRule conversionWord="pid" 
                  converterClass="my.custom.converter.ProcessIdConverter" />
  <conversionRule conversionWord="processId" 
                  converterClass="my.custom.converter.ProcessIdConverter" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%-6pid [%thread] - %msg%n</pattern>
    </encoder>
  </appender>
  ...
</configuration>

That way you get rid of the encoder

Triqui
  • 261
  • 3
  • 4
2

The default pattern for console logging defined in DefaultLogbackConfiguration is:

"${CONSOLE_LOG_PATTERN:-"
            + "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) "
            + "%clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "
            + "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"

As @Hlulani has pointed out, and as you can see in this default pattern, you can use ${PID} and logback will replace it with the process id.

Faraz
  • 189
  • 3
  • 9
  • 2
    The logging pattern you are linking to is from `spring-boot`, a project which uses logback as a third-party dependency. They have specified PID as a custom logging property here: https://github.com/spring-projects/spring-boot/blob/6a353c60216aeb51aa3d6c3ce947c61603af46aa/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java#L139 If you are only using logback as a dependency, you wont be able to use a logging pattern like this. – qwwqwwq Nov 05 '21 at 12:36
0

I tried using ${PID} which worked for me

<pattern>
    {"hostname": "${HOSTNAME}", 
     "level": "%p", 
     "method": "%M", 
     "process_id": "${PID}", 
     "thread_id": "%t", 
     "timestamp": "%d{Y-M-d}T%d{H:M:S.s}", 
     "mesg":"%msg"}%n
</pattern>
Hlulani
  • 419
  • 4
  • 12
  • I don't think this works "out of the box" with logback, somewhere in your project you must be specifying a custom logging property `PID`, maybe a third party library is doing it? If you search for the string `PID` in the logback source code, it does not seem to appear anywhere in the code. – qwwqwwq Nov 05 '21 at 12:35
-3

May be you may try %thread instead of process.

Andrei_N
  • 437
  • 2
  • 15