| /* |
| * 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 ch.qos.logback.core.util.FileSize; |
| 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(FileSize.valueOf(String.valueOf(maxSize))); |
| triggeringPolicy.setContext(context); |
| triggeringPolicy.start(); |
| rollingAppender.setTriggeringPolicy(triggeringPolicy); |
| |
| FixedWindowRollingPolicy pol = new FixedWindowRollingPolicy() { |
| @Override |
| protected int getMaxWindowSize() { |
| return Integer.MAX_VALUE; |
| } |
| }; |
| 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 + '\'' + '}'; |
| } |
| } |