001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * SonarQube is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * SonarQube is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.process; 021 022import ch.qos.logback.classic.Level; 023import ch.qos.logback.classic.Logger; 024import ch.qos.logback.classic.LoggerContext; 025import ch.qos.logback.classic.encoder.PatternLayoutEncoder; 026import ch.qos.logback.classic.joran.JoranConfigurator; 027import ch.qos.logback.classic.jul.LevelChangePropagator; 028import ch.qos.logback.classic.spi.LoggerContextListener; 029import ch.qos.logback.core.ConsoleAppender; 030import ch.qos.logback.core.Context; 031import ch.qos.logback.core.FileAppender; 032import ch.qos.logback.core.joran.spi.JoranException; 033import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; 034import ch.qos.logback.core.rolling.RollingFileAppender; 035import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; 036import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; 037import org.apache.commons.lang.StringUtils; 038import org.slf4j.LoggerFactory; 039 040import java.io.File; 041 042/** 043 * Helps to configure Logback in a programmatic way, without using XML. 044 */ 045public class LogbackHelper { 046 047 public static final String ROLLING_POLICY_PROPERTY = "sonar.log.rollingPolicy"; 048 public static final String MAX_FILES_PROPERTY = "sonar.log.maxFiles"; 049 050 public LoggerContext getRootContext() { 051 Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 052 return rootLogger.getLoggerContext(); 053 } 054 055 public LoggerContextListener enableJulChangePropagation(LoggerContext loggerContext) { 056 LevelChangePropagator propagator = new LevelChangePropagator(); 057 propagator.setContext(loggerContext); 058 propagator.start(); 059 loggerContext.addListener(propagator); 060 return propagator; 061 } 062 063 public ConsoleAppender newConsoleAppender(Context loggerContext, String name, String pattern) { 064 PatternLayoutEncoder consoleEncoder = new PatternLayoutEncoder(); 065 consoleEncoder.setContext(loggerContext); 066 consoleEncoder.setPattern(pattern); 067 consoleEncoder.start(); 068 ConsoleAppender consoleAppender = new ConsoleAppender(); 069 consoleAppender.setContext(loggerContext); 070 consoleAppender.setEncoder(consoleEncoder); 071 consoleAppender.setName(name); 072 consoleAppender.setTarget("System.out"); 073 consoleAppender.start(); 074 return consoleAppender; 075 } 076 077 public Logger configureLogger(LoggerContext loggerContext, String loggerName, Level level) { 078 Logger logger = loggerContext.getLogger(loggerName); 079 logger.setLevel(level); 080 return logger; 081 } 082 083 /** 084 * Generally used to reset logback in logging tests 085 */ 086 public void resetFromXml(String xmlResourcePath) throws JoranException { 087 LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); 088 JoranConfigurator configurator = new JoranConfigurator(); 089 configurator.setContext(context); 090 context.reset(); 091 configurator.doConfigure(LogbackHelper.class.getResource(xmlResourcePath)); 092 } 093 094 public RollingPolicy createRollingPolicy(Context ctx, Props props, String filenamePrefix) { 095 String rollingPolicy = props.value(ROLLING_POLICY_PROPERTY, "time:yyyy-MM-dd"); 096 int maxFiles = props.valueAsInt(MAX_FILES_PROPERTY, 7); 097 File logsDir = props.nonNullValueAsFile(ProcessConstants.PATH_LOGS); 098 099 if (rollingPolicy.startsWith("time:")) { 100 return new TimeRollingPolicy(ctx, filenamePrefix, logsDir, maxFiles, StringUtils.substringAfter(rollingPolicy, "time:")); 101 102 } else if (rollingPolicy.startsWith("size:")) { 103 return new SizeRollingPolicy(ctx, filenamePrefix, logsDir, maxFiles, StringUtils.substringAfter(rollingPolicy, "size:")); 104 105 } else if ("none".equals(rollingPolicy)) { 106 return new NoRollingPolicy(ctx, filenamePrefix, logsDir, maxFiles); 107 108 } else { 109 throw new MessageException(String.format("Unsupported value for property %s: %s", ROLLING_POLICY_PROPERTY, rollingPolicy)); 110 } 111 } 112 113 public abstract static class RollingPolicy { 114 protected final Context context; 115 protected final String filenamePrefix; 116 protected final File logsDir; 117 protected final int maxFiles; 118 119 public RollingPolicy(Context context, String filenamePrefix, File logsDir, int maxFiles) { 120 this.context = context; 121 this.filenamePrefix = filenamePrefix; 122 this.logsDir = logsDir; 123 this.maxFiles = maxFiles; 124 } 125 126 public abstract FileAppender createAppender(String appenderName); 127 } 128 129 /** 130 * Log files are not rotated, for example for unix command logrotate is in place. 131 */ 132 static class NoRollingPolicy extends RollingPolicy { 133 NoRollingPolicy(Context context, String filenamePrefix, File logsDir, int maxFiles) { 134 super(context, filenamePrefix, logsDir, maxFiles); 135 } 136 137 @Override 138 public FileAppender createAppender(String appenderName) { 139 FileAppender appender = new FileAppender<>(); 140 appender.setContext(context); 141 appender.setFile(new File(logsDir, filenamePrefix + ".log").getAbsolutePath()); 142 appender.setName(appenderName); 143 return appender; 144 } 145 } 146 147 /** 148 * Log files are rotated according to time (one file per day, month or year). 149 * See http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy 150 */ 151 static class TimeRollingPolicy extends RollingPolicy { 152 private final String datePattern; 153 154 public TimeRollingPolicy(Context context, String filenamePrefix, File logsDir, int maxFiles, String datePattern) { 155 super(context, filenamePrefix, logsDir, maxFiles); 156 this.datePattern = datePattern; 157 } 158 159 @Override 160 public FileAppender createAppender(String appenderName) { 161 RollingFileAppender appender = new RollingFileAppender(); 162 appender.setContext(context); 163 appender.setName(appenderName); 164 String filePath = new File(logsDir, filenamePrefix + ".log").getAbsolutePath(); 165 appender.setFile(filePath); 166 167 TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy(); 168 rollingPolicy.setContext(context); 169 rollingPolicy.setFileNamePattern(StringUtils.replace(filePath, filenamePrefix + ".log", filenamePrefix + ".%d{" + datePattern + "}.log")); 170 rollingPolicy.setMaxHistory(maxFiles); 171 rollingPolicy.setParent(appender); 172 rollingPolicy.start(); 173 appender.setRollingPolicy(rollingPolicy); 174 175 return appender; 176 } 177 } 178 179 /** 180 * Log files are rotated according to their size. 181 * See http://logback.qos.ch/manual/appenders.html#FixedWindowRollingPolicy 182 */ 183 static class SizeRollingPolicy extends RollingPolicy { 184 private final String size; 185 186 SizeRollingPolicy(Context context, String filenamePrefix, File logsDir, int maxFiles, String parameter) { 187 super(context, filenamePrefix, logsDir, maxFiles); 188 this.size = parameter; 189 } 190 191 @Override 192 public FileAppender createAppender(String appenderName) { 193 RollingFileAppender appender = new RollingFileAppender(); 194 appender.setContext(context); 195 appender.setName(appenderName); 196 String filePath = new File(logsDir, filenamePrefix + ".log").getAbsolutePath(); 197 appender.setFile(filePath); 198 199 SizeBasedTriggeringPolicy trigger = new SizeBasedTriggeringPolicy(size); 200 trigger.setContext(context); 201 trigger.start(); 202 appender.setTriggeringPolicy(trigger); 203 204 FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); 205 rollingPolicy.setContext(context); 206 rollingPolicy.setFileNamePattern(StringUtils.replace(filePath, filenamePrefix + ".log", filenamePrefix + ".%i.log")); 207 rollingPolicy.setMinIndex(1); 208 rollingPolicy.setMaxIndex(maxFiles); 209 rollingPolicy.setParent(appender); 210 rollingPolicy.start(); 211 appender.setRollingPolicy(rollingPolicy); 212 213 return appender; 214 } 215 } 216}