Programmably generate Logger with logback

This article is the 5th day of Recruit Lifestyle Advent Calendar 2017. I am a loose engineer at Hot Pepper Beauty. It's a shame.

Let's write about dynamic Logger generation under the title of Programmable Logger Generation with logback. </ strong> Well, it's a small logback story that doesn't seem to be in the documentation.

Introduction

Normally, when using logback, I think that the following logback.xml is prepared.

logback.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

	<appender name="APP"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>/var/log/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
			<maxHistory>14</maxHistory>
		</rollingPolicy>
		<encoder>
            <pattern>time:%d{yyyy-MMM-dd HH:mm:ss.SSS}  level:%level  marker:%marker thread:%thread  logger:%logger  file:%file  line:%line  message:%msg%n</pattern>
			<charset>UTF-8</charset>
		</encoder>
	</appender>

   	<root level="INFO">
		<appender-ref ref="APP" />
	</root>

</configuration>

Thing you want to do

However, there were cases where this XML notation was a problem. Specifically, it was at the time of this job.

What I wanted to do was dynamically set the log file PATH </ strong>. What does that mean ...

  • Conditions
  • The log transferred from host1 is /tmp/host1/app-yyyyMMdd.log.
  • The log transferred from hostN is /tmp/hostN/app-yyyyMMdd.log.
  • host is variable.

That is. Well, I don't want to maintain xml as the number of transferred hosts increases or decreases.

I think there are various ways (including not using logback), but this time I tried using logback to programmatically generate Logger </ strong>.

Below is the code of the operation sample. I will also give it to github. https://github.com/shase/logback-dynamic-logger-sample

Main.java


import java.util.stream.Stream;
import ch.qos.logback.classic.Logger;

public class Main {
	
	public static void main(String...args) {
		//Imagine that the host name comes in from the outside here.
		String[] path = {"/tmp/host1/app-%d{yyyyMMdd}.log","/tmp/host2/app-%d{yyyyMMdd}.log","/tmp/host3/app-%d{yyyyMMdd}.log"};
	
		Stream.of(path)
			.forEach(p -> {
				Logger logger = new SimpleLoggerFactory().getLogger("sample", p);
				logger.info("dynamic path");
			});
	}

}

SimpleLoggerFactory.java


import java.nio.charset.StandardCharsets;

import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;

public class SimpleLoggerFactory {
	public Logger getLogger(String loggerName, String path) {

		LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

		PatternLayoutEncoder ple = new PatternLayoutEncoder();
		ple.setPattern("%msg%n");
		ple.setContext(lc);
		ple.setCharset(StandardCharsets.UTF_8);
		ple.start();

		RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
		TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
		rollingPolicy.setFileNamePattern(path);
		rollingPolicy.setMaxHistory(14);
		rollingPolicy.setParent(fileAppender);
		rollingPolicy.setContext(lc);
		rollingPolicy.start();

		fileAppender.setAppend(true);
		fileAppender.setEncoder(ple);
		fileAppender.setRollingPolicy(rollingPolicy);
		fileAppender.setContext(lc);
		fileAppender.start();

		Logger logger = (Logger) LoggerFactory.getLogger(loggerName);
		logger.addAppender(fileAppender);
		logger.setAdditive(false);

		return logger;
	}
}
  • This allowed the output path to be dynamic.
  • As you can see from people who usually use logback, it's just writing what is usually defined in xml.
  • No xml is needed in this example.

in conclusion

  • When actually using it, it is not necessary to generate the same logger every time (since it only needs to be generated for the first time), so it is better to make it a singleton by some means.
  • After writing this, I noticed that logback also has Groovy Configuration </ strong>, so maybe I could have done it there as well? I will try it if I have a chance.
  • Then. Merry Xmas !!!