diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-02-13 14:56:28 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-02-19 12:01:20 +0100 |
commit | c063afcb3a908e4205f4f3b605f5137489ae2eba (patch) | |
tree | 57192e99495589cb415e451c250124a4a77ce1e5 /sonar-application | |
parent | 776b67bdfafe3602802e3c76f14b0a5fd3871d12 (diff) | |
download | sonarqube-c063afcb3a908e4205f4f3b605f5137489ae2eba.tar.gz sonarqube-c063afcb3a908e4205f4f3b605f5137489ae2eba.zip |
SONAR-6057 Make log rotation more configurable for production
Diffstat (limited to 'sonar-application')
6 files changed, 219 insertions, 79 deletions
diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index 53fb2cfb207..c2a1b29cf57 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -189,11 +189,6 @@ #sonar.web.http.acceptCount=25 #sonar.web.https.acceptCount=25 -# Access logs are generated in the file logs/access.log. This file is rolled over when it's 5Mb. -# An archive of 3 files is kept in the same directory. -# Access logs are enabled by default. -#sonar.web.accessLogs.enable=true - # TCP port for incoming AJP connections. Disabled if value is -1. Disabled by default. #sonar.ajp.port=-1 @@ -258,6 +253,27 @@ # Default is <installation home>/logs #sonar.path.logs=logs +# Rolling policy of log files +# - based on time if value starts with "time:", for example by day ("time:yyyy-MM-dd") +# or by month ("time:yyyy-MM") +# - based on size if value starts with "size:", for example "size:10MB" +# - disabled if value is "none". That needs logs to be managed by an external system like logrotate. +#sonar.log.rollingPolicy=time:yyyy-MM-dd + +# Maximum number of files to keep if a rolling policy is enabled +#sonar.log.maxFiles=7 + +# Access log is the list of all the HTTP requests received by server. If enabled, it is stored +# in the file {sonar.path.logs}/access.log. This file follows the same rolling policy as for +# sonar.log (see sonar.log.rollingPolicy and sonar.log.maxFiles). +#sonar.web.accessLogs.enable=true + +# Format of access log. It is ignored if sonar.web.accessLogs.enable=false. Value is: +# - "common" is the Common Log Format (shortcut for: %h %l %u %user %date "%r" %s %b) +# - "combined" is another format widely recognized (shortcut for: %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}") +# - else a custom pattern. See http://logback.qos.ch/manual/layouts.html#AccessPatternLayout +#sonar.web.accessLogs.pattern=combined + #-------------------------------------------------------------------------------------------------- # OTHERS diff --git a/sonar-application/src/main/java/org/sonar/application/App.java b/sonar-application/src/main/java/org/sonar/application/App.java index af325f1d370..e70c34df4fe 100644 --- a/sonar-application/src/main/java/org/sonar/application/App.java +++ b/sonar-application/src/main/java/org/sonar/application/App.java @@ -24,7 +24,6 @@ import org.apache.commons.lang.StringUtils; import org.sonar.process.MinimumViableSystem; import org.sonar.process.ProcessCommands; import org.sonar.process.ProcessConstants; -import org.sonar.process.ProcessLogging; import org.sonar.process.Props; import org.sonar.process.StopWatcher; import org.sonar.process.Stoppable; @@ -114,11 +113,8 @@ public class App implements Stoppable { CommandLineParser cli = new CommandLineParser(); Properties rawProperties = cli.parseArguments(args); Props props = new PropsBuilder(rawProperties, new JdbcSettings()).build(); - ProcessLogging logging = new ProcessLogging(); - logging.configure(props, "/org/sonar/application/logback.xml"); - if (props.valueAsBoolean("sonar.log.console", false)) { - logging.addConsoleAppender(); - } + AppLogging logging = new AppLogging(); + logging.configure(props); App app = new App(); app.start(props); diff --git a/sonar-application/src/main/java/org/sonar/application/AppLogging.java b/sonar-application/src/main/java/org/sonar/application/AppLogging.java new file mode 100644 index 00000000000..6c8e56553da --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/AppLogging.java @@ -0,0 +1,102 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.encoder.PatternLayoutEncoder; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.FileAppender; +import org.slf4j.LoggerFactory; +import org.sonar.process.LogbackHelper; +import org.sonar.process.Props; + +/** + * Configure logback for the master process + */ +class AppLogging { + + static final String CONSOLE_LOGGER = "console"; + static final String CONSOLE_APPENDER = "CONSOLE"; + static final String GOBBLER_LOGGER = "gobbler"; + static final String GOBBLER_APPENDER = "GOBBLER"; + static final String APP_PATTERN = "%d{yyyy.MM.dd HH:mm:ss} %-5level app[%logger{20}] %msg%n"; + + private final LogbackHelper helper = new LogbackHelper(); + + LoggerContext configure(Props props) { + LoggerContext ctx = helper.getRootContext(); + ctx.reset(); + helper.enableJulChangePropagation(ctx); + configureConsole(ctx); + configureGobbler(props, ctx); + configureRoot(ctx); + if (props.valueAsBoolean("sonar.log.console", false)) { + // used by SonarSource testing environment + copyGobblerToConsole(); + } + return ctx; + } + + /** + * Enable the copy in console of the logs written in logs/sonar.log + */ + private void copyGobblerToConsole() { + Logger consoleLogger = (Logger) LoggerFactory.getLogger(CONSOLE_LOGGER); + Appender consoleAppender = consoleLogger.getAppender(CONSOLE_APPENDER); + + Logger gobblerLogger = (Logger) LoggerFactory.getLogger(GOBBLER_LOGGER); + gobblerLogger.addAppender(consoleAppender); + } + + private void configureConsole(LoggerContext loggerContext) { + ConsoleAppender consoleAppender = helper.newConsoleAppender(loggerContext, CONSOLE_APPENDER, "%msg%n"); + Logger consoleLogger = loggerContext.getLogger(CONSOLE_LOGGER); + consoleLogger.setAdditive(false); + consoleLogger.addAppender(consoleAppender); + } + + private void configureGobbler(Props props, LoggerContext ctx) { + // configure appender + LogbackHelper.RollingPolicy rollingPolicy = helper.createRollingPolicy(ctx, props, "sonar"); + FileAppender fileAppender = rollingPolicy.createAppender(GOBBLER_APPENDER); + fileAppender.setContext(ctx); + PatternLayoutEncoder fileEncoder = new PatternLayoutEncoder(); + fileEncoder.setContext(ctx); + fileEncoder.setPattern("%msg%n"); + fileEncoder.start(); + fileAppender.setEncoder(fileEncoder); + fileAppender.start(); + + // configure logger + Logger gobblerLogger = ctx.getLogger(GOBBLER_LOGGER); + gobblerLogger.setAdditive(false); + gobblerLogger.addAppender(fileAppender); + } + + private void configureRoot(LoggerContext loggerContext) { + ConsoleAppender consoleAppender = helper.newConsoleAppender(loggerContext, "ROOT_CONSOLE", APP_PATTERN); + Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.setLevel(Level.INFO); + rootLogger.addAppender(consoleAppender); + } +} diff --git a/sonar-application/src/main/resources/org/sonar/application/logback.xml b/sonar-application/src/main/resources/org/sonar/application/logback.xml deleted file mode 100644 index cbcd991ded9..00000000000 --- a/sonar-application/src/main/resources/org/sonar/application/logback.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<configuration debug="false"> - <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/> - - <appender name="LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> - <File>${sonar.path.logs}/sonar.log</File> - <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> - <param name="FileNamePattern" value="${sonar.path.logs}/sonar.%i.log"/> - <param name="MinIndex" value="1"/> - <param name="MaxIndex" value="5"/> - </rollingPolicy> - <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> - <param name="MaxFileSize" value="10MB"/> - </triggeringPolicy> - <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> - <pattern>%msg%n</pattern> - </encoder> - </appender> - - <appender name="APP" class="ch.qos.logback.core.ConsoleAppender"> - <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> - <pattern>%d{yyyy.MM.dd HH:mm:ss} %-5level app[%logger{20}] %msg%n</pattern> - </encoder> - </appender> - - <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> - <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> - <pattern> - %msg%n - </pattern> - </encoder> - </appender> - - <logger name="console" additivity="false"> - <appender-ref ref="CONSOLE"/> - </logger> - - <logger name="gobbler" additivity="false"> - <appender-ref ref="LOGFILE"/> - </logger> - - <root> - <level value="INFO"/> - <appender-ref ref="APP"/> - </root> - -</configuration> diff --git a/sonar-application/src/test/java/org/sonar/application/AppLoggingTest.java b/sonar-application/src/test/java/org/sonar/application/AppLoggingTest.java new file mode 100644 index 00000000000..e2c902aaf39 --- /dev/null +++ b/sonar-application/src/test/java/org/sonar/application/AppLoggingTest.java @@ -0,0 +1,94 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.rolling.RollingFileAppender; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.process.LogbackHelper; +import org.sonar.process.ProcessConstants; +import org.sonar.process.Props; + +import java.io.File; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AppLoggingTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + Props props = new Props(new Properties()); + AppLogging sut = new AppLogging(); + + @Before + public void setUp() throws Exception { + File dir = temp.newFolder(); + props.set(ProcessConstants.PATH_LOGS, dir.getAbsolutePath()); + } + + @AfterClass + public static void resetLogback() throws Exception { + new LogbackHelper().resetFromXml("/logback-test.xml"); + } + + @Test + public void configure_defaults() throws Exception { + LoggerContext ctx = sut.configure(props); + + Logger gobbler = ctx.getLogger(AppLogging.GOBBLER_LOGGER); + Appender<ILoggingEvent> appender = gobbler.getAppender(AppLogging.GOBBLER_APPENDER); + assertThat(appender).isInstanceOf(RollingFileAppender.class); + + // gobbler is not copied to console + assertThat(gobbler.getAppender(AppLogging.GOBBLER_APPENDER)).isNotNull(); + assertThat(gobbler.getAppender(AppLogging.CONSOLE_APPENDER)).isNull(); + } + + @Test + public void configure_no_rotation() throws Exception { + props.set("sonar.log.rollingPolicy", "none"); + + LoggerContext ctx = sut.configure(props); + + Logger gobbler = ctx.getLogger(AppLogging.GOBBLER_LOGGER); + Appender<ILoggingEvent> appender = gobbler.getAppender(AppLogging.GOBBLER_APPENDER); + assertThat(appender).isNotInstanceOf(RollingFileAppender.class).isInstanceOf(FileAppender.class); + } + + @Test + public void copyGobblerToConsole() throws Exception { + props.set("sonar.log.console", "true"); + + LoggerContext ctx = sut.configure(props); + Logger gobbler = ctx.getLogger(AppLogging.GOBBLER_LOGGER); + assertThat(gobbler.getAppender(AppLogging.GOBBLER_APPENDER)).isNotNull(); + assertThat(gobbler.getAppender(AppLogging.CONSOLE_APPENDER)).isNotNull(); + } +} diff --git a/sonar-application/src/test/resources/logback-test.xml b/sonar-application/src/test/resources/logback-test.xml index ff2270cc122..f62e5c2b41d 100644 --- a/sonar-application/src/test/resources/logback-test.xml +++ b/sonar-application/src/test/resources/logback-test.xml @@ -1,28 +1,8 @@ <?xml version="1.0" encoding="UTF-8" ?> - -<!-- - Configuration for default logger. Only used while embedded server is starting, - before proper logging configuration is loaded. - - See http://logback.qos.ch/manual/configuration.html ---> <configuration debug="false"> <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> - <filter class="ch.qos.logback.classic.filter.LevelFilter"> - <level>INFO</level> - <onMatch>ACCEPT</onMatch> - <onMismatch>DENY</onMismatch> - </filter> - <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> - <pattern> - %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n - </pattern> - </encoder> - </appender> - - <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern> %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n |