import ch.qos.logback.classic.Level; | import ch.qos.logback.classic.Level; | ||||
import ch.qos.logback.classic.Logger; | import ch.qos.logback.classic.Logger; | ||||
import ch.qos.logback.classic.LoggerContext; | import ch.qos.logback.classic.LoggerContext; | ||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||||
import ch.qos.logback.classic.spi.ILoggingEvent; | import ch.qos.logback.classic.spi.ILoggingEvent; | ||||
import ch.qos.logback.core.ConsoleAppender; | import ch.qos.logback.core.ConsoleAppender; | ||||
import ch.qos.logback.core.FileAppender; | import ch.qos.logback.core.FileAppender; | ||||
import ch.qos.logback.core.encoder.Encoder; | |||||
import org.sonar.application.config.AppSettings; | import org.sonar.application.config.AppSettings; | ||||
import org.sonar.application.process.StreamGobbler; | import org.sonar.application.process.StreamGobbler; | ||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
* It creates a dedicated appender to the System.out which applies no formatting the logs it receives. | * It creates a dedicated appender to the System.out which applies no formatting the logs it receives. | ||||
*/ | */ | ||||
private void configureConsole(LoggerContext loggerContext) { | private void configureConsole(LoggerContext loggerContext) { | ||||
ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(loggerContext, CONSOLE_PLAIN_APPENDER, "%msg%n"); | |||||
Encoder<ILoggingEvent> encoder = newPatternLayoutEncoder(loggerContext, "%msg%n"); | |||||
ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(loggerContext, CONSOLE_PLAIN_APPENDER, encoder); | |||||
Logger consoleLogger = loggerContext.getLogger(CONSOLE_LOGGER); | Logger consoleLogger = loggerContext.getLogger(CONSOLE_LOGGER); | ||||
consoleLogger.setAdditive(false); | consoleLogger.setAdditive(false); | ||||
// logs are written to the console because we want them to be in sonar.log and the wrapper will write any log | // logs are written to the console because we want them to be in sonar.log and the wrapper will write any log | ||||
// from APP's System.out and System.err to sonar.log | // from APP's System.out and System.err to sonar.log | ||||
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | ||||
rootLogger.addAppender(createAppConsoleAppender(ctx, helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG))); | |||||
Encoder<ILoggingEvent> encoder = newPatternLayoutEncoder(ctx, helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG)); | |||||
rootLogger.addAppender(createAppConsoleAppender(ctx, encoder)); | |||||
// in regular configuration, sub processes are not copying their logs to their System.out, so, the only logs to be | // in regular configuration, sub processes are not copying their logs to their System.out, so, the only logs to be | ||||
// expected in LOGGER_GOBBLER are those before logback is setup in subprocesses or when JVM crashes | // expected in LOGGER_GOBBLER are those before logback is setup in subprocesses or when JVM crashes | ||||
private void configureRootWithLogbackWritingToFile(LoggerContext ctx) { | private void configureRootWithLogbackWritingToFile(LoggerContext ctx) { | ||||
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | ||||
String appLogPattern = helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG); | |||||
FileAppender<ILoggingEvent> fileAppender = helper.newFileAppender(ctx, appSettings.getProps(), APP_ROOT_LOGGER_CONFIG, appLogPattern); | |||||
Encoder<ILoggingEvent> encoder = newPatternLayoutEncoder(ctx, helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG)); | |||||
FileAppender<ILoggingEvent> fileAppender = helper.newFileAppender(ctx, appSettings.getProps(), APP_ROOT_LOGGER_CONFIG, encoder); | |||||
rootLogger.addAppender(fileAppender); | rootLogger.addAppender(fileAppender); | ||||
rootLogger.addAppender(createAppConsoleAppender(ctx, appLogPattern)); | |||||
rootLogger.addAppender(createAppConsoleAppender(ctx, encoder)); | |||||
} | } | ||||
/** | /** | ||||
private void configureGobbler(LoggerContext ctx) { | private void configureGobbler(LoggerContext ctx) { | ||||
Logger gobblerLogger = ctx.getLogger(LOGGER_GOBBLER); | Logger gobblerLogger = ctx.getLogger(LOGGER_GOBBLER); | ||||
gobblerLogger.setAdditive(false); | gobblerLogger.setAdditive(false); | ||||
gobblerLogger.addAppender(helper.newConsoleAppender(ctx, GOBBLER_PLAIN_CONSOLE, "%msg%n")); | |||||
Encoder<ILoggingEvent> encoder = newPatternLayoutEncoder(ctx, "%msg%n"); | |||||
gobblerLogger.addAppender(helper.newConsoleAppender(ctx, GOBBLER_PLAIN_CONSOLE, encoder)); | |||||
} | } | ||||
private ConsoleAppender<ILoggingEvent> createAppConsoleAppender(LoggerContext ctx, String appLogPattern) { | |||||
return helper.newConsoleAppender(ctx, APP_CONSOLE_APPENDER, appLogPattern); | |||||
private ConsoleAppender<ILoggingEvent> createAppConsoleAppender(LoggerContext ctx, Encoder<ILoggingEvent> encoder) { | |||||
return helper.newConsoleAppender(ctx, APP_CONSOLE_APPENDER, encoder); | |||||
} | } | ||||
private static Encoder<ILoggingEvent> newPatternLayoutEncoder(LoggerContext context, String pattern) { | |||||
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |||||
encoder.setContext(context); | |||||
encoder.setPattern(pattern); | |||||
encoder.start(); | |||||
return encoder; | |||||
} | |||||
} | } |
import ch.qos.logback.classic.Level; | import ch.qos.logback.classic.Level; | ||||
import ch.qos.logback.classic.Logger; | import ch.qos.logback.classic.Logger; | ||||
import ch.qos.logback.classic.LoggerContext; | import ch.qos.logback.classic.LoggerContext; | ||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||||
import ch.qos.logback.classic.joran.JoranConfigurator; | import ch.qos.logback.classic.joran.JoranConfigurator; | ||||
import ch.qos.logback.classic.jul.LevelChangePropagator; | import ch.qos.logback.classic.jul.LevelChangePropagator; | ||||
import ch.qos.logback.classic.spi.ILoggingEvent; | import ch.qos.logback.classic.spi.ILoggingEvent; | ||||
import ch.qos.logback.core.ConsoleAppender; | import ch.qos.logback.core.ConsoleAppender; | ||||
import ch.qos.logback.core.Context; | import ch.qos.logback.core.Context; | ||||
import ch.qos.logback.core.FileAppender; | import ch.qos.logback.core.FileAppender; | ||||
import ch.qos.logback.core.encoder.Encoder; | |||||
import ch.qos.logback.core.joran.spi.JoranException; | import ch.qos.logback.core.joran.spi.JoranException; | ||||
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; | import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; | ||||
import ch.qos.logback.core.rolling.RollingFileAppender; | import ch.qos.logback.core.rolling.RollingFileAppender; | ||||
} | } | ||||
/** | /** | ||||
* Creates a new {@link ConsoleAppender} to {@code System.out} with the specified name and log pattern. | |||||
* | |||||
* @see #buildLogPattern(RootLoggerConfig) | |||||
* Creates a new {@link ConsoleAppender} to {@code System.out} with the specified name and log encoder. | |||||
*/ | */ | ||||
public ConsoleAppender<ILoggingEvent> newConsoleAppender(Context loggerContext, String name, String logPattern) { | |||||
PatternLayoutEncoder consoleEncoder = new PatternLayoutEncoder(); | |||||
consoleEncoder.setContext(loggerContext); | |||||
consoleEncoder.setPattern(logPattern); | |||||
consoleEncoder.start(); | |||||
public ConsoleAppender<ILoggingEvent> newConsoleAppender(Context loggerContext, String name, Encoder<ILoggingEvent> encoder) { | |||||
ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>(); | ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>(); | ||||
consoleAppender.setContext(loggerContext); | consoleAppender.setContext(loggerContext); | ||||
consoleAppender.setEncoder(consoleEncoder); | |||||
consoleAppender.setEncoder(encoder); | |||||
consoleAppender.setName(name); | consoleAppender.setName(name); | ||||
consoleAppender.setTarget("System.out"); | consoleAppender.setTarget("System.out"); | ||||
consoleAppender.start(); | consoleAppender.start(); | ||||
* <li>the file's name will use the prefix defined in {@link RootLoggerConfig#getProcessId()#getLogFilenamePrefix()}.</li> | * <li>the file's name will use the prefix defined in {@link RootLoggerConfig#getProcessId()#getLogFilenamePrefix()}.</li> | ||||
* <li>the file will follow the rotation policy defined in property {@link #ROLLING_POLICY_PROPERTY} and | * <li>the file will follow the rotation policy defined in property {@link #ROLLING_POLICY_PROPERTY} and | ||||
* the max number of files defined in property {@link #MAX_FILES_PROPERTY}</li> | * the max number of files defined in property {@link #MAX_FILES_PROPERTY}</li> | ||||
* <li>the logs will follow the specified log pattern</li> | |||||
* <li>the logs will follow the specified log encoder</li> | |||||
* </ul> | * </ul> | ||||
* </p> | * </p> | ||||
* | |||||
* @see #buildLogPattern(RootLoggerConfig) | |||||
*/ | */ | ||||
public FileAppender<ILoggingEvent> configureGlobalFileLog(Props props, RootLoggerConfig config, String logPattern) { | |||||
public void configureGlobalFileLog(Props props, RootLoggerConfig config, Encoder<ILoggingEvent> encoder) { | |||||
LoggerContext ctx = getRootContext(); | LoggerContext ctx = getRootContext(); | ||||
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); | ||||
FileAppender<ILoggingEvent> fileAppender = newFileAppender(ctx, props, config, logPattern); | |||||
FileAppender<ILoggingEvent> fileAppender = newFileAppender(ctx, props, config, encoder); | |||||
rootLogger.addAppender(fileAppender); | rootLogger.addAppender(fileAppender); | ||||
return fileAppender; | |||||
} | } | ||||
public FileAppender<ILoggingEvent> newFileAppender(LoggerContext ctx, Props props, RootLoggerConfig config, String logPattern) { | |||||
public FileAppender<ILoggingEvent> newFileAppender(LoggerContext ctx, Props props, RootLoggerConfig config, Encoder<ILoggingEvent> encoder) { | |||||
RollingPolicy rollingPolicy = createRollingPolicy(ctx, props, config.getProcessId().getLogFilenamePrefix()); | RollingPolicy rollingPolicy = createRollingPolicy(ctx, props, config.getProcessId().getLogFilenamePrefix()); | ||||
FileAppender<ILoggingEvent> fileAppender = rollingPolicy.createAppender("file_" + config.getProcessId().getLogFilenamePrefix()); | FileAppender<ILoggingEvent> fileAppender = rollingPolicy.createAppender("file_" + config.getProcessId().getLogFilenamePrefix()); | ||||
fileAppender.setContext(ctx); | fileAppender.setContext(ctx); | ||||
PatternLayoutEncoder fileEncoder = new PatternLayoutEncoder(); | |||||
fileEncoder.setContext(ctx); | |||||
fileEncoder.setPattern(logPattern); | |||||
fileEncoder.start(); | |||||
fileAppender.setEncoder(fileEncoder); | |||||
fileAppender.setEncoder(encoder); | |||||
fileAppender.start(); | fileAppender.start(); | ||||
return fileAppender; | return fileAppender; | ||||
} | } | ||||
/** | /** | ||||
* Make the logback configuration for a sub process to correctly push all its logs to be read by a stream gobbler | * Make the logback configuration for a sub process to correctly push all its logs to be read by a stream gobbler | ||||
* on the sub process's System.out. | * on the sub process's System.out. | ||||
* | |||||
* @see #buildLogPattern(RootLoggerConfig) | |||||
*/ | */ | ||||
public void configureForSubprocessGobbler(Props props, String logPattern) { | |||||
public void configureForSubprocessGobbler(Props props, Encoder<ILoggingEvent> encoder) { | |||||
if (isAllLogsToConsoleEnabled(props)) { | if (isAllLogsToConsoleEnabled(props)) { | ||||
LoggerContext ctx = getRootContext(); | LoggerContext ctx = getRootContext(); | ||||
ctx.getLogger(ROOT_LOGGER_NAME).addAppender(newConsoleAppender(ctx, "root_console", logPattern)); | |||||
ctx.getLogger(ROOT_LOGGER_NAME).addAppender(newConsoleAppender(ctx, "root_console", encoder)); | |||||
} | } | ||||
} | } | ||||
@Test | @Test | ||||
public void newConsoleAppender() { | public void newConsoleAppender() { | ||||
LoggerContext ctx = underTest.getRootContext(); | LoggerContext ctx = underTest.getRootContext(); | ||||
ConsoleAppender<?> appender = underTest.newConsoleAppender(ctx, "MY_APPENDER", "%msg%n"); | |||||
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |||||
encoder.setContext(ctx); | |||||
encoder.setPattern("%msg%n"); | |||||
encoder.start(); | |||||
ConsoleAppender<?> appender = underTest.newConsoleAppender(ctx, "MY_APPENDER", encoder); | |||||
assertThat(appender.getName()).isEqualTo("MY_APPENDER"); | assertThat(appender.getName()).isEqualTo("MY_APPENDER"); | ||||
assertThat(appender.getContext()).isSameAs(ctx); | assertThat(appender.getContext()).isSameAs(ctx); |
import ch.qos.logback.classic.Level; | import ch.qos.logback.classic.Level; | ||||
import ch.qos.logback.classic.Logger; | import ch.qos.logback.classic.Logger; | ||||
import ch.qos.logback.classic.LoggerContext; | import ch.qos.logback.classic.LoggerContext; | ||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||||
import ch.qos.logback.classic.spi.ILoggingEvent; | import ch.qos.logback.classic.spi.ILoggingEvent; | ||||
import ch.qos.logback.core.ConsoleAppender; | import ch.qos.logback.core.ConsoleAppender; | ||||
import com.google.common.collect.ImmutableSet; | import com.google.common.collect.ImmutableSet; | ||||
.setProcessId(processId) | .setProcessId(processId) | ||||
.setThreadIdFieldPattern(threadIdFieldPattern) | .setThreadIdFieldPattern(threadIdFieldPattern) | ||||
.build(); | .build(); | ||||
String logPattern = helper.buildLogPattern(config); | |||||
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |||||
encoder.setContext(helper.getRootContext()); | |||||
encoder.setPattern(helper.buildLogPattern(config)); | |||||
encoder.start(); | |||||
helper.configureGlobalFileLog(props, config, logPattern); | |||||
helper.configureForSubprocessGobbler(props, logPattern); | |||||
helper.configureGlobalFileLog(props, config, encoder); | |||||
helper.configureForSubprocessGobbler(props, encoder); | |||||
} | } | ||||
/** | /** | ||||
.setProcessId(ProcessId.APP) | .setProcessId(ProcessId.APP) | ||||
.setThreadIdFieldPattern("") | .setThreadIdFieldPattern("") | ||||
.build(); | .build(); | ||||
String logPattern = helper.buildLogPattern(config); | |||||
ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(context, "CONSOLE", logPattern); | |||||
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |||||
encoder.setContext(context); | |||||
encoder.setPattern(helper.buildLogPattern(config)); | |||||
encoder.start(); | |||||
ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(context, "CONSOLE", encoder); | |||||
for (String loggerName : loggerNames) { | for (String loggerName : loggerNames) { | ||||
Logger consoleLogger = context.getLogger(loggerName); | Logger consoleLogger = context.getLogger(loggerName); |