aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-application
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-02-13 14:56:28 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-02-19 12:01:20 +0100
commitc063afcb3a908e4205f4f3b605f5137489ae2eba (patch)
tree57192e99495589cb415e451c250124a4a77ce1e5 /sonar-application
parent776b67bdfafe3602802e3c76f14b0a5fd3871d12 (diff)
downloadsonarqube-c063afcb3a908e4205f4f3b605f5137489ae2eba.tar.gz
sonarqube-c063afcb3a908e4205f4f3b605f5137489ae2eba.zip
SONAR-6057 Make log rotation more configurable for production
Diffstat (limited to 'sonar-application')
-rw-r--r--sonar-application/src/main/assembly/conf/sonar.properties26
-rw-r--r--sonar-application/src/main/java/org/sonar/application/App.java8
-rw-r--r--sonar-application/src/main/java/org/sonar/application/AppLogging.java102
-rw-r--r--sonar-application/src/main/resources/org/sonar/application/logback.xml48
-rw-r--r--sonar-application/src/test/java/org/sonar/application/AppLoggingTest.java94
-rw-r--r--sonar-application/src/test/resources/logback-test.xml20
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