All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.application.AppLogging Maven / Gradle / Ivy

/*
 * SonarQube
 * Copyright (C) 2009-2018 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.application;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import org.sonar.application.config.AppSettings;
import org.sonar.application.process.StreamGobbler;
import org.sonar.process.ProcessId;
import org.sonar.process.logging.LogLevelConfig;
import org.sonar.process.logging.LogbackHelper;
import org.sonar.process.logging.RootLoggerConfig;

import static org.slf4j.Logger.ROOT_LOGGER_NAME;
import static org.sonar.application.process.StreamGobbler.LOGGER_GOBBLER;
import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;

/**
 * Configure logback for the APP process.
 *
 * 

* SonarQube's logging use cases: *

    *
  1. * SQ started as a background process (with {@code sonar.sh start}): *
      *
    • * logs produced by the JVM before logback is setup in the APP JVM or which can't be caught by logback * (such as JVM crash) must be written to sonar.log *
    • *
    • * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught * by logback (such as JVM crash) must be written to sonar.log *
    • *
    • each JVM writes its own logs into its dedicated file
    • *
    *
  2. *
  3. * SQ started in console with wrapper (ie. with {@code sonar.sh console}): *
      *
    • * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback * (such as JVM crash) must be written to sonar.log *
    • *
    • * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught * by logback (such as JVM crash) must be written to sonar.log *
    • *
    • each JVM writes its own logs into its dedicated file
    • *
    • APP JVM logs are written to the APP JVM {@code System.out}
    • *
    *
  4. *
  5. * SQ started from command line (ie. {@code java -jar sonar-application-X.Y.jar}): *
      *
    • * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback * (such as JVM crash) are the responsibility of the user to be dealt with *
    • *
    • * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught * by logback (such as JVM crash) must be written to APP's {@code System.out} *
    • *
    • each JVM writes its own logs into its dedicated file
    • *
    • APP JVM logs are written to the APP JVM {@code System.out}
    • *
    *
  6. *
  7. * SQ started from an IT (ie. from command line with {@code option -Dsonar.log.console=true}): *
      *
    • * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback * (such as JVM crash) are the responsibility of the developer or maven to be dealt with *
    • *
    • * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught * by logback (such as JVM crash) must be written to APP's {@code System.out} and are the responsibility of the * developer or maven to be dealt with *
    • *
    • each JVM writes its own logs into its dedicated file
    • *
    • logs of all 4 JVMs are also written to the APP JVM {@code System.out}
    • *
    *
  8. *
*

* */ public class AppLogging { private static final String CONSOLE_LOGGER = "console"; private static final String CONSOLE_PLAIN_APPENDER = "CONSOLE"; private static final String APP_CONSOLE_APPENDER = "APP_CONSOLE"; private static final String GOBBLER_PLAIN_CONSOLE = "GOBBLER_CONSOLE"; private static final RootLoggerConfig APP_ROOT_LOGGER_CONFIG = newRootLoggerConfigBuilder() .setProcessId(ProcessId.APP) .build(); private final LogbackHelper helper = new LogbackHelper(); private final AppSettings appSettings; public AppLogging(AppSettings appSettings) { this.appSettings = appSettings; } public LoggerContext configure() { LoggerContext ctx = helper.getRootContext(); ctx.reset(); helper.enableJulChangePropagation(ctx); configureConsole(ctx); if (helper.isAllLogsToConsoleEnabled(appSettings.getProps()) || !appSettings.getProps().valueAsBoolean("sonar.wrapped", false)) { configureWithLogbackWritingToFile(ctx); } else { configureWithWrapperWritingToFile(ctx); } helper.apply( LogLevelConfig.newBuilder(helper.getRootLoggerName()) .rootLevelFor(ProcessId.APP) .immutableLevel("com.hazelcast", Level.toLevel("WARN")) .build(), appSettings.getProps()); return ctx; } /** * Creates a non additive logger dedicated to printing message as is (ie. assuming they are already formatted). * * It creates a dedicated appender to the System.out which applies no formatting the logs it receives. */ private void configureConsole(LoggerContext loggerContext) { ConsoleAppender consoleAppender = helper.newConsoleAppender(loggerContext, CONSOLE_PLAIN_APPENDER, "%msg%n"); Logger consoleLogger = loggerContext.getLogger(CONSOLE_LOGGER); consoleLogger.setAdditive(false); consoleLogger.addAppender(consoleAppender); } /** * The process has been started by orchestrator (ie. via {@code java -jar} and optionally passing the option {@code -Dsonar.log.console=true}). * Therefor, APP's System.out (and System.err) are not copied to sonar.log by the wrapper and * printing to sonar.log must be done at logback level. */ private void configureWithLogbackWritingToFile(LoggerContext ctx) { // configure all logs (ie. root logger) to be written to sonar.log and also to the console with formatting // in practice, this will be only APP's own logs as logs from sub processes LOGGER_GOBBLER and LOGGER_GOBBLER // is configured below to be detached from root // so, this will make all APP's log to be both written to sonar.log and visible in the console configureRootWithLogbackWritingToFile(ctx); // if option -Dsonar.log.console=true has been set, sub processes will write their logs to their own files but also // copy them to their System.out. // otherwise, the only logs to be expected in LOGGER_GOBBLER are those before logback is setup in subprocesses or // when their JVM crashes // they must be printed to App's System.out as is (as they are already formatted) // logger is configured to be non additive as we don't want these logs to be written to sonar.log and duplicated in // the console (with an incorrect formatting) configureGobbler(ctx); } /** * SQ has been started by the wrapper (ie. with sonar.sh) therefor, APP's System.out (and System.err) are written to * sonar.log by the wrapper. */ private void configureWithWrapperWritingToFile(LoggerContext ctx) { // configure all logs (ie. root logger) to be written to console with formatting // in practice, this will be only APP's own logs as logs from sub processes are written to LOGGER_GOBBLER and // LOGGER_GOBBLER is configured below to be detached from root // 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 Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); rootLogger.addAppender(createAppConsoleAppender(ctx, helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG))); // 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 // so, they must be printed to App's System.out as is (as they are already formatted) and the wrapper will write // them to sonar.log // logger is configured to be non additive as we don't want these logs written to sonar.log and duplicated in the // console with an incorrect formatting configureGobbler(ctx); } private void configureRootWithLogbackWritingToFile(LoggerContext ctx) { Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME); String appLogPattern = helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG); FileAppender fileAppender = helper.newFileAppender(ctx, appSettings.getProps(), APP_ROOT_LOGGER_CONFIG, appLogPattern); rootLogger.addAppender(fileAppender); rootLogger.addAppender(createAppConsoleAppender(ctx, appLogPattern)); } /** * Configure the logger to which logs from sub processes are written to * (called {@link StreamGobbler#LOGGER_GOBBLER}) by {@link StreamGobbler}, * to be: *
    *
  1. non additive (ie. these logs will be output by the appender of {@link StreamGobbler#LOGGER_GOBBLER} and only this one)
  2. *
  3. write logs as is (ie. without any extra formatting)
  4. *
  5. write exclusively to App's System.out
  6. *
*/ private void configureGobbler(LoggerContext ctx) { Logger gobblerLogger = ctx.getLogger(LOGGER_GOBBLER); gobblerLogger.setAdditive(false); gobblerLogger.addAppender(helper.newConsoleAppender(ctx, GOBBLER_PLAIN_CONSOLE, "%msg%n")); } private ConsoleAppender createAppConsoleAppender(LoggerContext ctx, String appLogPattern) { return helper.newConsoleAppender(ctx, APP_CONSOLE_APPENDER, appLogPattern); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy