/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.commons.log.logback.internal; import java.util.regex.Matcher; import java.util.regex.Pattern; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.Context; import ch.qos.logback.core.OutputStreamAppender; import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; import org.apache.sling.commons.log.logback.internal.util.SlingContextUtil; import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender; /** * The <code>LogWriter</code> class encapsulates the OSGi configuration for a * log writer and provides methods to access these to create an Appender. */ public class LogWriter { /** * Special fileName for which Console Appender would be created */ public static final String FILE_NAME_CONSOLE = "CONSOLE"; private static final long FACTOR_KB = 1024; private static final long FACTOR_MB = 1024 * FACTOR_KB; private static final long FACTOR_GB = 1024 * FACTOR_MB; /** * Regular expression matching a maximum file size specification. This * pattern case-insensitively matches a number and an optional factor * specifier of the forms k, kb, m, mb, g, or gb. */ private static final Pattern SIZE_SPEC = Pattern.compile("([\\d]+)([kmg]b?)?", Pattern.CASE_INSENSITIVE); /** * The PID of the configuration from which this instance has been * configured. If this is <code>null</code> this instance is an implicitly * created instance which is not tied to any configuration. */ private final String configurationPID; private final String fileName; private final int logNumber; private final String logRotation; private final String appenderName; private final boolean bufferedLogging; public LogWriter(String configurationPID, String appenderName, int logNumber, String logRotation, String fileName, boolean bufferedLogging) { this.appenderName = appenderName; if (fileName == null || fileName.length() == 0) { fileName = FILE_NAME_CONSOLE; } if (logNumber < 0) { logNumber = LogConfigManager.LOG_FILE_NUMBER_DEFAULT; } if (logRotation == null || logRotation.length() == 0) { logRotation = LogConfigManager.LOG_FILE_SIZE_DEFAULT; } this.configurationPID = configurationPID; this.fileName = fileName; this.logNumber = logNumber; this.logRotation = logRotation; this.bufferedLogging = bufferedLogging; } public LogWriter(String appenderName,String fileName, int logNumber, String logRotation) { this(null, appenderName, logNumber, logRotation, fileName, false); } public String getConfigurationPID() { return configurationPID; } public String getImplicitConfigPID(){ return LogConfigManager.PID; } public String getFileName() { return fileName; } public String getAppenderName() { return appenderName; } public int getLogNumber() { return logNumber; } public String getLogRotation() { return logRotation; } public boolean isImplicit() { return configurationPID == null; } public Appender<ILoggingEvent> createAppender(final Context context, final Encoder<ILoggingEvent> encoder) { SlingContextUtil ctxUtil = new SlingContextUtil(context, this); OutputStreamAppender<ILoggingEvent> appender; if (FILE_NAME_CONSOLE.equals(fileName)) { appender = new ConsoleAppender<ILoggingEvent>(); appender.setName(FILE_NAME_CONSOLE); } else { ctxUtil.addInfo("Configuring appender "+getFileName()); SlingRollingFileAppender<ILoggingEvent> rollingAppender = new SlingRollingFileAppender<ILoggingEvent>(); rollingAppender.setAppend(true); rollingAppender.setFile(getFileName()); Matcher sizeMatcher = SIZE_SPEC.matcher(getLogRotation()); if (sizeMatcher.matches()) { // group 1 is the base size and is an integer number final long baseSize = Long.parseLong(sizeMatcher.group(1)); // this will take the final size value final long maxSize; // group 2 is optional and is the size spec. If not null it is // at least one character long and the first character is enough // for use to know (the second is of no use here) final String factorString = sizeMatcher.group(2); if (factorString == null) { // no factor define, hence no multiplication maxSize = baseSize; } else { switch (factorString.charAt(0)) { case 'k': case 'K': maxSize = baseSize * FACTOR_KB; break; case 'm': case 'M': maxSize = baseSize * FACTOR_MB; break; case 'g': case 'G': maxSize = baseSize * FACTOR_GB; break; default: // we don't really expect this according to the // pattern maxSize = baseSize; } } SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>(); triggeringPolicy.setMaxFileSize(String.valueOf(maxSize)); triggeringPolicy.setContext(context); triggeringPolicy.start(); rollingAppender.setTriggeringPolicy(triggeringPolicy); FixedWindowRollingPolicy pol = new FixedWindowRollingPolicy(); pol.setMinIndex(1); pol.setMaxIndex(getLogNumber()); pol.setFileNamePattern(getFileName() + "%i"); pol.setContext(context); pol.setParent(rollingAppender); pol.start(); rollingAppender.setRollingPolicy(pol); } else { TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>(); String fileNamePattern = createFileNamePattern(getFileName(), getLogRotation()); policy.setFileNamePattern(fileNamePattern); policy.setMaxHistory(getLogNumber()); policy.setContext(context); policy.setParent(rollingAppender); policy.start(); rollingAppender.setTriggeringPolicy(policy); ctxUtil.addInfo("Configured TimeBasedRollingPolicy with pattern "+ fileNamePattern); } rollingAppender.setLogWriter(this); rollingAppender.setName(getAppenderName()); appender = rollingAppender; } if(bufferedLogging && encoder instanceof LayoutWrappingEncoder){ ((LayoutWrappingEncoder) encoder).setImmediateFlush(false); ctxUtil.addInfo("Setting immediateFlush to false"); } else{ ctxUtil.addInfo("immediateFlush property not modified. Defaults to true"); } appender.setContext(context); appender.setEncoder(encoder); appender.start(); ctxUtil.addInfo("Completed configuring appender with name "+getFileName()); return appender; } public static String createFileNamePattern(String fileName, String pattern) { // Default file name pattern "'.'yyyy-MM-dd" // http://sling.apache.org/site/logging.html#Logging-ScheduledRotation if (pattern.startsWith("'.'")) { pattern = pattern.substring(3); // 3 = '.' length pattern = ".%d{" + pattern + "}"; } // Legacy pattern which does not start with '.' Just wrap them with %d{} if (!pattern.contains("%d{")) { pattern = "%d{" + pattern + "}"; } return fileName + pattern; } @Override public String toString() { return "LogWriter{" + "configurationPID='" + configurationPID + '\'' + ", fileName='" + fileName + '\'' + ", logNumber=" + logNumber + ", logRotation='" + logRotation + '\'' + '}'; } }