blob: 8bb560a3eb1ec2c195fdf7660e8cacb42e7adac3 [file] [log] [blame]
/*
* 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.io.File;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.joran.action.ActionConst;
import ch.qos.logback.core.util.ContextUtil;
import org.apache.sling.commons.log.logback.internal.config.ConfigAdminSupport;
import org.apache.sling.commons.log.logback.internal.config.ConfigurationException;
import org.apache.sling.commons.log.logback.internal.stacktrace.OSGiAwareExceptionHandling;
import org.apache.sling.commons.log.logback.internal.util.LoggerSpecificEncoder;
import org.apache.sling.commons.log.logback.internal.util.Util;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogConfigManager implements LogbackResetListener, LogConfig.LogWriterProvider {
public static final String LOG_LEVEL = "org.apache.sling.commons.log.level";
public static final String LOG_FILE = "org.apache.sling.commons.log.file";
public static final String LOGBACK_FILE = "org.apache.sling.commons.log.configurationFile";
public static final String LOG_FILE_NUMBER = "org.apache.sling.commons.log.file.number";
public static final String LOG_FILE_SIZE = "org.apache.sling.commons.log.file.size";
public static final String LOG_FILE_BUFFERED = "org.apache.sling.commons.log.file.buffered";
public static final String LOG_PATTERN = "org.apache.sling.commons.log.pattern";
public static final String LOG_PATTERN_DEFAULT = "%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n";
public static final String LOG_LOGGERS = "org.apache.sling.commons.log.names";
public static final String LOG_ADDITIV = "org.apache.sling.commons.log.additiv";
public static final String LOG_PACKAGING_DATA = "org.apache.sling.commons.log.packagingDataEnabled";
public static final String LOG_MAX_CLALLER_DEPTH = "org.apache.sling.commons.log.maxCallerDataDepth";
public static final String PRINTER_MAX_INCLUDED_FILES = "org.apache.sling.commons.log.maxOldFileCountInDump";
public static final int PRINTER_MAX_INCLUDED_FILES_DEFAULT = 3;
public static final String PRINTER_NUM_OF_LINES = "org.apache.sling.commons.log.numOfLines";
public static final int PRINTER_NUM_OF_LINES_DEFAULT = 1000;
public static final String LOG_LEVEL_DEFAULT = "INFO";
public static final String LOG_LEVEL_RESET_TO_DEFAULT = "DEFAULT";
public static final int LOG_FILE_NUMBER_DEFAULT = 5;
public static final String LOG_FILE_SIZE_DEFAULT = "'.'yyyy-MM-dd";
public static final String PID = "org.apache.sling.commons.log.LogManager";
public static final String FACTORY_PID_WRITERS = PID + ".factory.writer";
public static final String FACTORY_PID_CONFIGS = PID + ".factory.config";
public static final String DEFAULT_CONSOLE_APPENDER_NAME = "org.apache.sling.commons.log.CONSOLE";
private static final String CONFIG_PID_SET = "org.apache.sling.commons.log.ConfigPids";
private final LoggerContext loggerContext;
private final ContextUtil contextUtil;
// map of log writers indexed by configuration PID
private final Map<String, LogWriter> writerByPid;
// map of log writers indexed by (absolute) file name. This map does
// not contain writers writing to standard out
private final Map<String, LogWriter> writerByFileName;
// map of appenders indexed by LogWriter filename and LogConfig pattern
// private final Map<AppenderKey, Appender<ILoggingEvent>> appenderByKey;
// map of log configurations by configuration PID
private final Map<String, LogConfig> configByPid;
// map of log configurations by the categories they are configured with
private final Map<String, LogConfig> configByCategory;
// the root folder to make relative writer paths absolute
private final File rootDir;
// global default configuration (from BundleContext properties)
private Dictionary<String, String> defaultConfiguration;
private final ConfigAdminSupport configAdminSupport;
private final LogbackManager logbackManager;
private final Logger log = LoggerFactory.getLogger(getClass());
private final Object configLock = new Object();
private File logbackConfigFile;
private boolean packagingDataEnabled;
private int maxCallerDataDepth;
private int maxOldFileCount;
private int numOfLines;
/**
* Logs a message an optional stack trace to error output. This method is
* used by the logging system in case of errors writing to the correct
* logging output.
*/
public void internalFailure(String message, Throwable t) {
if (t != null) {
contextUtil.addError(message, t);
} else {
contextUtil.addError(message);
}
// log the message to error stream also
System.err.println(message);
if (t != null) {
t.printStackTrace(System.err);
}
}
/**
* Sets up this log configuration manager by creating the default writers
* and logger configuration
*/
public LogConfigManager(LoggerContext loggerContext, BundleContext bundleContext, String rootDir,
LogbackManager logbackManager) {
this.logbackManager = logbackManager;
this.loggerContext = loggerContext;
contextUtil = new ContextUtil(loggerContext);
writerByPid = new ConcurrentHashMap<String, LogWriter>();
writerByFileName = new ConcurrentHashMap<String, LogWriter>();
configByPid = new ConcurrentHashMap<String, LogConfig>();
configByCategory = new ConcurrentHashMap<String, LogConfig>();
this.rootDir = new File(rootDir);
setDefaultConfiguration(getBundleConfiguration(bundleContext));
this.configAdminSupport = new ConfigAdminSupport(bundleContext, this);
}
/**
* Sets and applies the default configuration used by the
* {@link #updateGlobalConfiguration(java.util.Dictionary)} method if no
* configuration is supplied.
*/
public void setDefaultConfiguration(Dictionary<String, String> defaultConfiguration) {
this.defaultConfiguration = defaultConfiguration;
try {
updateGlobalConfiguration(defaultConfiguration);
} catch (ConfigurationException ce) {
internalFailure(ce.getMessage(), ce);
}
}
/**
* Shuts this configuration manager down by dropping all references to
* existing configurations, dropping all stored loggers and closing all log
* writers.
* <p>
* After this methods is called, this instance should not be used again.
*/
public void close() {
configAdminSupport.shutdown();
writerByPid.clear();
writerByFileName.clear();
configByPid.clear();
configByCategory.clear();
this.defaultConfiguration = null;
}
// ---------- SlingLogPanel support
@Override
public LogWriter getLogWriter(String logWriterName) {
LogWriter lw = writerByFileName.get(logWriterName);
if (lw == null) {
lw = createImplicitWriter(logWriterName);
}
return lw;
}
public File getLogbackConfigFile() {
return logbackConfigFile;
}
public Iterable<LogConfig> getLogConfigs() {
return configByPid.values();
}
public Appender<ILoggingEvent> getDefaultAppender() {
OutputStreamAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
appender.setName(DEFAULT_CONSOLE_APPENDER_NAME);
appender.setContext(loggerContext);
appender.setEncoder(MaskingMessageUtil.getDefaultEncoder(loggerContext));
appender.start();
return appender;
}
// ---------- Logback reset listener
@Override
public void onResetStart(LoggerContext context) {
}
@SuppressWarnings("unchecked")
@Override
public void onResetComplete(LoggerContext context) {
//The OSGi config based appenders are attached on reset complete as by that time Logback config
// would have been parsed and various appenders and logger configured. Now we use the OSGi config
// 1. If an appender with same name as one defined by OSGi config is found then it takes precedence
// 2. If no existing appender is found then we create one use that
Map<String, Appender<ILoggingEvent>> appendersByName = new HashMap<String, Appender<ILoggingEvent>>();
Map<String, Appender<ILoggingEvent>> configuredAppenders =
(Map<String, Appender<ILoggingEvent>>) context.getObject(ActionConst.APPENDER_BAG);
if(configuredAppenders == null){
configuredAppenders = Collections.emptyMap();
}
final Map<Appender<ILoggingEvent>, LoggerSpecificEncoder> encoders = new HashMap<>();
Set<String> configPids = new HashSet<>();
for (LogConfig config : getLogConfigs()) {
configPids.add(config.getConfigPid());
Appender<ILoggingEvent> appender = null;
if (config.isAppenderDefined()) {
LogWriter lw = config.getLogWriter();
final String appenderName = lw.getAppenderName();
appender = appendersByName.get(appenderName);
if(appender == null){
appender = configuredAppenders.get(appenderName);
if(appender != null){
contextUtil.addInfo("Found overriding configuration for appender "+ appenderName
+ " in Logback config. OSGi config would be ignored");
}
}
if (appender == null) {
LoggerSpecificEncoder encoder = new LoggerSpecificEncoder(getDefaultLayout());
appender = lw.createAppender(loggerContext, encoder);
encoders.put(appender, encoder);
appendersByName.put(appenderName, appender);
}
if(encoders.containsKey(appender)){
encoders.get(appender).addLogConfig(config);
}
}
for (String category : config.getCategories()) {
ch.qos.logback.classic.Logger logger = loggerContext.getLogger(category);
if (config.isResetToDefault()){
logger.setLevel(null);
logger.setAdditive(true); //Reset additivity
} else {
logger.setLevel(config.getLogLevel());
if (appender != null) {
logger.setAdditive(config.isAdditive());
logger.addAppender(appender);
contextUtil.addInfo("Registering appender " + appender.getName() + "(" + appender.getClass() +
") with logger " + logger.getName());
}
}
}
}
//Record the config pids which have been picked up in this reset cycle
context.putObject(CONFIG_PID_SET, configPids);
}
// ---------- Configuration support
public void updateGlobalConfiguration(Dictionary<String, String> configuration) throws ConfigurationException {
// fallback to start default settings when the config is deleted
if (configuration == null) {
configuration = defaultConfiguration;
}
processGlobalConfig(configuration);
// set the logger name to a special value to indicate the global
// (ROOT) logger setting (SLING-529)
configuration.put(LogConfigManager.LOG_LOGGERS, Logger.ROOT_LOGGER_NAME);
// normalize logger file (might be console
final String logFile = configuration.get(LOG_FILE);
if (logFile == null || logFile.trim().length() == 0) {
configuration.put(LOG_FILE, LogWriter.FILE_NAME_CONSOLE);
}
// update the default log writer and logger configuration
updateLogWriter(LogConfigManager.PID, configuration, false);
updateLoggerConfiguration(LogConfigManager.PID, configuration, false);
logbackManager.configChanged();
}
/**
* Updates or removes the log writer configuration identified by the
* <code>pid</code>. In case of log writer removal, any logger configuration
* referring to the removed log writer is modified to now log to the default
* log writer.
* <p>
* The configuration object is expected to contain the following properties:
* <dl>
* <dt>{@link LogConfigManager#LOG_FILE}</dt>
* <dd>The relative of absolute path/name of the file to log to. If this
* property is missing or an empty string, the writer writes to standard
* output</dd>
* <dt>{@link LogConfigManager#LOG_FILE_SIZE}</dt>
* <dd>The maximum size of the log file to write before rotating the log
* file. This property must be a number of be convertible to a number. The
* actual value may also be suffixed by a size indicator <code>k</code>,
* <code>kb</code>, <code>m</code>, <code>mb</code>, <code>g</code> or
* <code>gb</code> representing the respective factors of kilo, mega and
* giga.If this property is missing or cannot be converted to a number, the
* default value {@link LogConfigManager#LOG_FILE_SIZE_DEFAULT}
* is assumed. If the writer writes standard output this property is
* ignored.</dd>
* <dt>{@link LogConfigManager#LOG_FILE_NUMBER}</dt>
* <dd>The maximum number of rotated log files to keep. This property must
* be a number of be convertible to a number. If this property is missing or
* cannot be converted to a number, the default value
* {@link LogConfigManager#LOG_FILE_NUMBER_DEFAULT} is assumed.
* If the writer writes standard output this property is ignored.</dd>
* </dl>
*
* @param pid The identifier of the log writer to update or remove
* @param configuration New configuration setting for the log writer or
* <code>null</code> to indicate to remove the log writer.
* @throws ConfigurationException If another log writer already exists for
* the same file as configured for the given log writer or if
* configuring the log writer fails.
*/
public void updateLogWriter(String pid, Dictionary<?, ?> configuration, boolean performRefresh)
throws ConfigurationException {
if (configuration != null) {
LogWriter oldWriter = writerByPid.get(pid);
// get the log file parameter and normalize empty string to null
String logFileName = (String) configuration.get(LogConfigManager.LOG_FILE);
// Null logFileName is treated as Console Appender
if (logFileName != null && logFileName.trim().length() == 0) {
logFileName = LogWriter.FILE_NAME_CONSOLE;
}
// if we have a file name, make it absolute and correct for our
// environment and verify there is no other writer already existing
// for the same file
if (logFileName != null && !isConsole(logFileName)) {
// ensure absolute path
logFileName = getAbsoluteFilePath(logFileName);
// ensure unique configuration of the log writer
LogWriter existingWriterByFileName = writerByFileName.get(logFileName);
if (existingWriterByFileName != null
&& (oldWriter != null && !existingWriterByFileName.getConfigurationPID().equals(pid))) {
// this file is already configured by another LOG_PID
throw new ConfigurationException(LogConfigManager.LOG_FILE, "LogFile " + logFileName
+ " already configured by configuration " + existingWriterByFileName.getConfigurationPID());
}
}
// get number of files and ensure minimum and default
Object fileNumProp = configuration.get(LogConfigManager.LOG_FILE_NUMBER);
int fileNum = -1;
if (fileNumProp instanceof Number) {
fileNum = ((Number) fileNumProp).intValue();
} else if (fileNumProp != null) {
try {
fileNum = Integer.parseInt(fileNumProp.toString());
} catch (NumberFormatException nfe) {
// don't care
}
}
// get the log file size
Object fileSizeProp = configuration.get(LogConfigManager.LOG_FILE_SIZE);
String fileSize = null;
if (fileSizeProp != null) {
fileSize = fileSizeProp.toString();
}
boolean bufferedLogging = Util.toBoolean(configuration.get(LogConfigManager.LOG_FILE_BUFFERED), false);
LogWriter newWriter = new LogWriter(pid, getAppnderName(logFileName), fileNum,
fileSize, logFileName, bufferedLogging);
if (oldWriter != null) {
writerByFileName.remove(oldWriter.getFileName());
}
writerByFileName.put(newWriter.getFileName(), newWriter);
writerByPid.put(newWriter.getConfigurationPID(), newWriter);
} else {
final LogWriter logWriter = writerByPid.remove(pid);
if (logWriter != null) {
writerByFileName.remove(logWriter.getFileName());
}
}
if (performRefresh) {
logbackManager.configChanged();
}
}
/**
* Updates or removes the logger configuration indicated by the given
* <code>pid</code>. If the case of modified categories or removal of the
* logger configuration, existing loggers will be modified to reflect the
* correct logger configurations available.
* <p>
* The configuration object is expected to contain the following properties:
* <dl>
* <dt>{@link LogConfigManager#LOG_PATTERN}</dt>
* <dd>The <code>MessageFormat</code> pattern to apply to format the log
* message before writing it to the log writer. If this property is missing
* or the empty string the default pattern
* {@link LogConfigManager#LOG_PATTERN_DEFAULT} is used.</dd>
* <dt>{@link LogConfigManager#LOG_LEVEL}</dt>
* <dd>The log level to use for log message limitation. The supported values
* are <code>off</code>, <code>trace</code>, <code>debug</code>, <code>info</code>,
* <code>warn</code> and <code>error</code>. Case does not matter. If this
* property is missing a <code>ConfigurationException</code> is thrown and
* this logger configuration is not used.</dd>
* <dt>{@link LogConfigManager#LOG_LOGGERS}</dt>
* <dd>The logger names to which this configuration applies. As logger names
* form a hierarchy like Java packages, the listed names also apply to
* "child names" unless more specific configuration applies for such
* children. This property may be a single string, an array of strings or a
* collection of strings. Each string may itself be a comma-separated list
* of logger names. If this property is missing a
* <code>ConfigurationException</code> is thrown.</dd>
* <dt>{@link LogConfigManager#LOG_FILE}</dt>
* <dd>The name of the log writer to use. This may be the name of a log file
* configured for any log writer or it may be the configuration PID of such
* a writer. If this property is missing or empty or does not refer to an
* existing log writer configuration, the default log writer is used.</dd>
*</dl>
* @param pid The name of the configuration to update or remove.
* @param configuration The configuration object.
* @throws ConfigurationException If the log level and logger names
* properties are not configured for the given configuration.
*/
public void updateLoggerConfiguration(final String pid, final Dictionary<?, ?> configuration, final boolean performRefresh)
throws ConfigurationException {
if (configuration != null) {
String pattern = (String) configuration.get(LogConfigManager.LOG_PATTERN);
final String level = (String) configuration.get(LogConfigManager.LOG_LEVEL);
String fileName = (String) configuration.get(LogConfigManager.LOG_FILE);
final Set<String> categories = toCategoryList(configuration.get(LogConfigManager.LOG_LOGGERS));
final boolean additiv;
if ( configuration.get(LogConfigManager.LOG_ADDITIV) != null ) {
additiv = Boolean.valueOf(configuration.get(LogConfigManager.LOG_ADDITIV).toString());
} else {
// If an appender is explicitly defined then set additive to false
// to be compatible with earlier Sling Logging behavior
additiv = false;
}
// verify categories
if (categories == null) {
throw new ConfigurationException(LogConfigManager.LOG_LOGGERS, "Missing categories in configuration "
+ pid);
}
// verify no other configuration has any of the categories
for (final String cat : categories) {
final LogConfig cfg = configByCategory.get(cat);
if (cfg != null && !pid.equals(cfg.getConfigPid())) {
throw new ConfigurationException(LogConfigManager.LOG_LOGGERS, "Category " + cat
+ " already defined by configuration " + cfg.getConfigPid());
}
}
// verify log level
if (level == null) {
throw new ConfigurationException(LogConfigManager.LOG_LEVEL, "Value required");
}
final Level logLevel;
final boolean resetToDefault;
if (LOG_LEVEL_RESET_TO_DEFAULT.equalsIgnoreCase(level)){
resetToDefault = true;
logLevel = null;
} else {
logLevel = Level.toLevel(level);
resetToDefault = false;
}
// verify pattern
if (pattern == null || pattern.length() == 0) {
pattern = LogConfigManager.LOG_PATTERN_DEFAULT;
}
//Map empty fileName to console logger
//null fileName is for scenario where intention is just to change the log level
if (fileName != null && fileName.trim().length() == 0) {
fileName = LogWriter.FILE_NAME_CONSOLE;
}
// FileName being just null means that we want to change the
// LogLevel
if (fileName != null && !isConsole(fileName)) {
fileName = getAbsoluteFilePath(fileName);
}
// create or modify existing configuration object
final LogConfig newConfig = new LogConfig(this, pattern, categories, logLevel, fileName, additiv,
pid, loggerContext, resetToDefault);
if (isPackagingDataEnabled()) {
newConfig.setPostProcessor(new OSGiAwareExceptionHandling(logbackManager.getPackageInfoCollector()));
}
LogConfig oldConfig = configByPid.get(pid);
if (oldConfig != null) {
configByCategory.keySet().removeAll(oldConfig.getCategories());
}
// relink categories
for (String cat : categories) {
configByCategory.put(cat, newConfig);
}
configByPid.put(pid, newConfig);
} else {
// configuration deleted if null
// remove configuration from pid list
LogConfig config = configByPid.remove(pid);
if (config != null) {
// remove all configured categories
configByCategory.keySet().removeAll(config.getCategories());
}
}
if (performRefresh) {
logbackManager.configChanged();
}
}
public void checkForNewConfigsWhileStarting(LoggerContext context){
Set<String> configPids = (Set<String>) context.getObject(CONFIG_PID_SET);
if (configPids == null) {
contextUtil.addWarn("Did not find any configPid set");
return;
}
if (!configPids.equals(configByPid.keySet())) {
contextUtil.addInfo("Config change detected post start. Scheduling config reload");
logbackManager.configChanged();
} else {
contextUtil.addInfo("Configured the Logback with " + configPids.size() + " configs");
}
}
public boolean isPackagingDataEnabled() {
return packagingDataEnabled;
}
public int getMaxCallerDataDepth() {
return maxCallerDataDepth;
}
public int getMaxOldFileCount() {
return maxOldFileCount;
}
/**
* Maximum number of lines from a log files to be included in txt mode dump
*/
public int getNumOfLines() {
return numOfLines;
}
// ---------- ManagedService interface -------------------------------------
private Dictionary<String, String> getBundleConfiguration(BundleContext bundleContext) {
Dictionary<String, String> config = new Hashtable<String, String>();
final String[] props = {
LOG_LEVEL, LOG_FILE, LOG_FILE_NUMBER, LOG_FILE_SIZE, LOG_PATTERN, LOGBACK_FILE,
LOG_PACKAGING_DATA
};
for (String prop : props) {
String value = bundleContext.getProperty(prop);
if (value != null) {
config.put(prop, value);
}
}
// ensure sensible default values for required configuration field(s)
if (config.get(LOG_LEVEL) == null) {
config.put(LOG_LEVEL, LOG_LEVEL_DEFAULT);
}
return config;
}
private void processGlobalConfig(Dictionary<String, String> configuration) {
String fileName = configuration.get(LOGBACK_FILE);
if (fileName != null && !fileName.isEmpty()) {
File file = new File(getAbsoluteFilePath(fileName));
final String path = file.getAbsolutePath();
if (!file.exists()) {
log.warn("Logback configuration file [{}] does not exist.", path);
} if (!file.isFile()) {
log.warn("Logback configuration file [{}] is not a file.", path);
}else if (!file.canRead()) {
log.warn("Logback configuration [{}]file cannot be read", path);
} else {
synchronized (configLock) {
logbackConfigFile = file;
}
}
}
//Process packaging data
Object packagingData = configuration.get(LOG_PACKAGING_DATA);
if (packagingData != null) {
packagingDataEnabled = Boolean.valueOf(packagingData.toString());
} else {
//Defaults to false i.e. disabled in OSGi env
packagingDataEnabled = false;
}
maxCallerDataDepth = Util.toInteger(configuration.get(LOG_MAX_CLALLER_DEPTH),
ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH);
maxOldFileCount = Util.toInteger(configuration.get(PRINTER_MAX_INCLUDED_FILES),
PRINTER_MAX_INCLUDED_FILES_DEFAULT);
numOfLines = Util.toInteger(configuration.get(PRINTER_NUM_OF_LINES),
PRINTER_NUM_OF_LINES_DEFAULT);
}
// ---------- Internal helpers ---------------------------------------------
private LogWriter createImplicitWriter(String logWriterName) {
LogWriter defaultWriter = getDefaultWriter();
if (defaultWriter == null) {
throw new IllegalStateException("Default logger configuration must have been configured by now");
}
return new LogWriter(getAppnderName(logWriterName),logWriterName, defaultWriter.getLogNumber(), defaultWriter.getLogRotation());
}
public LogWriter getDefaultWriter() {
return writerByPid.get(LogConfigManager.PID);
}
private LogConfig getDefaultConfig() {
return configByPid.get(LogConfigManager.PID);
}
private Layout<ILoggingEvent> getDefaultLayout() {
return getDefaultConfig().createLayout();
}
/**
* Returns the <code>logFileName</code> argument converted into an absolute
* path name. If <code>logFileName</code> is already absolute it is returned
* unmodified. Otherwise it is made absolute by resolving it relative to the
* root directory set on this instance.
* method.
*
* @throws NullPointerException if <code>logFileName</code> is
* <code>null</code>.
*/
private String getAbsoluteFilePath(String logFileName) {
// ensure proper separator in the path (esp. for systems, which do
// not use "slash" as a separator, e.g Windows)
logFileName = logFileName.replace('/', File.separatorChar);
// create a file instance and check whether this is absolute. If not
// create a new absolute file instance with the root dir and get
// the absolute path name from that
File logFile = new File(logFileName);
if (!logFile.isAbsolute()) {
logFile = new File(rootDir, logFileName);
logFileName = logFile.getAbsolutePath();
}
// return the correct log file name
return logFileName;
}
private String getAppnderName(String filePathAbsolute) {
String rootDirPath = rootDir.getAbsolutePath();
if(filePathAbsolute.startsWith(rootDirPath)){
//Make the fileName relative to the absolute dir
//Normalize the name to use '/'
return filePathAbsolute.substring(rootDirPath.length()).replace('\\','/');
}
return filePathAbsolute;
}
/**
* Decomposes the <code>loggers</code> configuration object into a set of
* logger names. The <code>loggers</code> object may be a single string, an
* array of strings or a collection of strings. Each string may in turn be a
* comma-separated list of strings. Each entry makes up an entry in the
* resulting set.
*
* @param loggers The configuration object to be decomposed. If this is
* <code>null</code>, <code>null</code> is returned immediately
* @return The set of logger names provided by the <code>loggers</code>
* object or <code>null</code> if the <code>loggers</code> object
* itself is <code>null</code>.
*/
private static Set<String> toCategoryList(Object loggers) {
// quick exit if there is no configuration
if (loggers == null) {
return null;
}
// prepare set of names (used already in case loggers == ROOT)
Set<String> loggerNames = new HashSet<String>();
// in case of the special setting ROOT, return a set of just the
// root logger name (SLING-529)
if (loggers == Logger.ROOT_LOGGER_NAME) {
loggerNames.add(Logger.ROOT_LOGGER_NAME);
return loggerNames;
}
List<String> loggerNamesList = Util.toList(loggers);
loggerNames.addAll(loggerNamesList);
// return those names
return loggerNames;
}
private static boolean isConsole(String logFileName) {
return LogWriter.FILE_NAME_CONSOLE.equals(logFileName);
}
}