blob: 905f9680705ebdb143342a761085016d1ecacf86 [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.
*/
#include <log4cxx/logstring.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/spi/loggerfactory.h>
#include <log4cxx/helpers/properties.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/logmanager.h>
#include <log4cxx/helpers/optionconverter.h>
#include <log4cxx/level.h>
#include <log4cxx/defaultloggerfactory.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/appender.h>
#include <log4cxx/logger.h>
#include <log4cxx/layout.h>
#include <log4cxx/config/propertysetter.h>
#include <log4cxx/spi/loggerrepository.h>
#include <log4cxx/helpers/stringtokenizer.h>
#include <log4cxx/helpers/synchronized.h>
#include <apr_file_io.h>
#include <apr_file_info.h>
#include <apr_pools.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/fileinputstream.h>
#define LOG4CXX 1
#include <log4cxx/helpers/aprinitializer.h>
using namespace log4cxx;
using namespace log4cxx::spi;
using namespace log4cxx::helpers;
using namespace log4cxx::config;
#if APR_HAS_THREADS
#include <log4cxx/helpers/filewatchdog.h>
namespace log4cxx
{
class PropertyWatchdog : public FileWatchdog
{
public:
PropertyWatchdog(const File& filename) : FileWatchdog(filename)
{
}
/**
Call PropertyConfigurator#doConfigure(const String& configFileName,
const spi::LoggerRepositoryPtr& hierarchy) with the
<code>filename</code> to reconfigure log4cxx.
*/
void doOnChange()
{
PropertyConfigurator().doConfigure(file,
LogManager::getLoggerRepository());
}
};
}
PropertyWatchdog* PropertyConfigurator::pdog = NULL;
#endif
IMPLEMENT_LOG4CXX_OBJECT(PropertyConfigurator)
PropertyConfigurator::PropertyConfigurator()
: registry(new std::map<LogString, AppenderPtr>()), loggerFactory(new DefaultLoggerFactory())
{
}
PropertyConfigurator::~PropertyConfigurator()
{
delete registry;
}
void PropertyConfigurator::addRef() const
{
ObjectImpl::addRef();
}
void PropertyConfigurator::releaseRef() const
{
ObjectImpl::releaseRef();
}
void PropertyConfigurator::doConfigure(const File& configFileName,
spi::LoggerRepositoryPtr& hierarchy)
{
hierarchy->setConfigured(true);
Properties props;
try
{
InputStreamPtr inputStream = new FileInputStream(configFileName);
props.load(inputStream);
}
catch (const IOException&)
{
LogLog::error(((LogString) LOG4CXX_STR("Could not read configuration file ["))
+ configFileName.getPath() + LOG4CXX_STR("]."));
return;
}
try
{
doConfigure(props, hierarchy);
}
catch (const std::exception& ex)
{
LogLog::error(((LogString) LOG4CXX_STR("Could not parse configuration file ["))
+ configFileName.getPath() + LOG4CXX_STR("]."), ex);
}
}
void PropertyConfigurator::configure(const File& configFilename)
{
PropertyConfigurator().doConfigure(configFilename, LogManager::getLoggerRepository());
}
void PropertyConfigurator::configure(helpers::Properties& properties)
{
PropertyConfigurator().doConfigure(properties, LogManager::getLoggerRepository());
}
#if APR_HAS_THREADS
void PropertyConfigurator::configureAndWatch(const File& configFilename)
{
configureAndWatch(configFilename, FileWatchdog::DEFAULT_DELAY);
}
void PropertyConfigurator::configureAndWatch(
const File& configFilename, long delay)
{
if (pdog)
{
APRInitializer::unregisterCleanup(pdog);
delete pdog;
}
pdog = new PropertyWatchdog(configFilename);
APRInitializer::registerCleanup(pdog);
pdog->setDelay(delay);
pdog->start();
}
#endif
void PropertyConfigurator::doConfigure(helpers::Properties& properties,
spi::LoggerRepositoryPtr& hierarchy)
{
hierarchy->setConfigured(true);
static const LogString DEBUG_KEY(LOG4CXX_STR("log4j.debug"));
LogString value(properties.getProperty(DEBUG_KEY));
if (!value.empty())
{
LogLog::setInternalDebugging(OptionConverter::toBoolean(value, true));
}
static const LogString THRESHOLD_PREFIX(LOG4CXX_STR("log4j.threshold"));
LogString thresholdStr =
OptionConverter::findAndSubst(THRESHOLD_PREFIX, properties);
if (!thresholdStr.empty())
{
hierarchy->setThreshold(OptionConverter::toLevel(thresholdStr, Level::getAll()));
LogLog::debug(((LogString) LOG4CXX_STR("Hierarchy threshold set to ["))
+ hierarchy->getThreshold()->toString()
+ LOG4CXX_STR("]."));
}
static const LogString STRINGSTREAM_KEY(LOG4CXX_STR("log4j.stringstream"));
LogString strstrValue(properties.getProperty(STRINGSTREAM_KEY));
if (strstrValue == LOG4CXX_STR("static"))
{
MessageBufferUseStaticStream();
}
configureRootLogger(properties, hierarchy);
configureLoggerFactory(properties);
parseCatsAndRenderers(properties, hierarchy);
LogLog::debug(LOG4CXX_STR("Finished configuring."));
// We don't want to hold references to appenders preventing their
// destruction.
registry->clear();
}
void PropertyConfigurator::configureLoggerFactory(helpers::Properties& props)
{
static const LogString LOGGER_FACTORY_KEY(LOG4CXX_STR("log4j.loggerFactory"));
LogString factoryClassName =
OptionConverter::findAndSubst(LOGGER_FACTORY_KEY, props);
if (!factoryClassName.empty())
{
LogString msg(LOG4CXX_STR("Setting logger factory to ["));
msg += factoryClassName;
msg += LOG4CXX_STR("].");
LogLog::debug(msg);
loggerFactory =
OptionConverter::instantiateByClassName(
factoryClassName, LoggerFactory::getStaticClass(), loggerFactory);
static const LogString FACTORY_PREFIX(LOG4CXX_STR("log4j.factory."));
Pool p;
PropertySetter::setProperties(loggerFactory, props, FACTORY_PREFIX, p);
}
}
void PropertyConfigurator::configureRootLogger(helpers::Properties& props,
spi::LoggerRepositoryPtr& hierarchy)
{
static const LogString ROOT_CATEGORY_PREFIX(LOG4CXX_STR("log4j.rootCategory"));
static const LogString ROOT_LOGGER_PREFIX(LOG4CXX_STR("log4j.rootLogger"));
LogString effectiveFrefix(ROOT_LOGGER_PREFIX);
LogString value = OptionConverter::findAndSubst(ROOT_LOGGER_PREFIX, props);
if (value.empty())
{
value = OptionConverter::findAndSubst(ROOT_CATEGORY_PREFIX, props);
effectiveFrefix = ROOT_CATEGORY_PREFIX;
}
if (value.empty())
{
LogLog::debug(LOG4CXX_STR("Could not find root logger information. Is this OK?"));
}
else
{
LoggerPtr root = hierarchy->getRootLogger();
LOCK_W sync(root->getMutex());
static const LogString INTERNAL_ROOT_NAME(LOG4CXX_STR("root"));
parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
}
}
void PropertyConfigurator::parseCatsAndRenderers(helpers::Properties& props,
spi::LoggerRepositoryPtr& hierarchy)
{
static const LogString CATEGORY_PREFIX(LOG4CXX_STR("log4j.category."));
static const LogString LOGGER_PREFIX(LOG4CXX_STR("log4j.logger."));
std::vector<LogString> names = props.propertyNames();
std::vector<LogString>::iterator it = names.begin();
std::vector<LogString>::iterator itEnd = names.end();
while (it != itEnd)
{
LogString key = *it++;
if (key.find(CATEGORY_PREFIX) == 0 || key.find(LOGGER_PREFIX) == 0)
{
LogString loggerName;
if (key.find(CATEGORY_PREFIX) == 0)
{
loggerName = key.substr(CATEGORY_PREFIX.length());
}
else if (key.find(LOGGER_PREFIX) == 0)
{
loggerName = key.substr(LOGGER_PREFIX.length());
}
LogString value = OptionConverter::findAndSubst(key, props);
LoggerPtr logger = hierarchy->getLogger(loggerName, loggerFactory);
LOCK_W sync(logger->getMutex());
parseLogger(props, logger, key, loggerName, value);
parseAdditivityForLogger(props, logger, loggerName);
}
}
}
void PropertyConfigurator::parseAdditivityForLogger(helpers::Properties& props,
LoggerPtr& cat, const LogString& loggerName)
{
static const LogString ADDITIVITY_PREFIX(LOG4CXX_STR("log4j.additivity."));
LogString value(OptionConverter::findAndSubst(ADDITIVITY_PREFIX + loggerName, props));
LogLog::debug((LogString) LOG4CXX_STR("Handling ") + ADDITIVITY_PREFIX
+ loggerName + LOG4CXX_STR("=[") + value + LOG4CXX_STR("]"));
// touch additivity only if necessary
if (!value.empty())
{
bool additivity = OptionConverter::toBoolean(value, true);
LogLog::debug(((LogString) LOG4CXX_STR("Setting additivity for \""))
+ loggerName
+ ((additivity) ? LOG4CXX_STR("\" to true") :
LOG4CXX_STR("\" to false")));
cat->setAdditivity(additivity);
}
}
/**
This method must work for the root logger as well.
*/
void PropertyConfigurator::parseLogger(
helpers::Properties& props, LoggerPtr& logger, const LogString& /* optionKey */,
const LogString& loggerName, const LogString& value)
{
LogLog::debug(((LogString) LOG4CXX_STR("Parsing for ["))
+ loggerName
+ LOG4CXX_STR("] with value=[")
+ value + LOG4CXX_STR("]."));
// We must skip over ',' but not white space
StringTokenizer st(value, LOG4CXX_STR(","));
// If value is not in the form ", appender.." or "", then we should set
// the level of the logger.
if (!(value.find(LOG4CXX_STR(",")) == 0 || value.empty()))
{
// just to be on the safe side...
if (!st.hasMoreTokens())
{
return;
}
LogString levelStr = st.nextToken();
LogLog::debug((LogString) LOG4CXX_STR("Level token is [")
+ levelStr + LOG4CXX_STR("]."));
// If the level value is inherited, set logger level value to
// null. We also check that the user has not specified inherited for the
// root logger.
if (StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("INHERITED"), LOG4CXX_STR("inherited"))
|| StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("NULL"), LOG4CXX_STR("null")))
{
static const LogString INTERNAL_ROOT_NAME(LOG4CXX_STR("root"));
if (loggerName == INTERNAL_ROOT_NAME)
{
LogLog::warn(LOG4CXX_STR("The root logger cannot be set to null."));
}
else
{
logger->setLevel(0);
LogLog::debug((LogString) LOG4CXX_STR("Logger ")
+ loggerName + LOG4CXX_STR(" set to null"));
}
}
else
{
logger->setLevel(OptionConverter::toLevel(levelStr, Level::getDebug()));
LogLog::debug((LogString) LOG4CXX_STR("Logger ")
+ loggerName + LOG4CXX_STR(" set to ")
+ logger->getLevel()->toString());
}
}
// Begin by removing all existing appenders.
logger->removeAllAppenders();
AppenderPtr appender;
LogString appenderName;
while (st.hasMoreTokens())
{
appenderName = StringHelper::trim(st.nextToken());
if (appenderName.empty() || appenderName == LOG4CXX_STR(","))
{
continue;
}
LogLog::debug(LOG4CXX_STR("Parsing appender named ")
+ appenderName + LOG4CXX_STR("\"."));
appender = parseAppender(props, appenderName);
if (appender != 0)
{
logger->addAppender(appender);
}
}
}
AppenderPtr PropertyConfigurator::parseAppender(
helpers::Properties& props, const LogString& appenderName)
{
AppenderPtr appender = registryGet(appenderName);
if (appender != 0)
{
LogLog::debug((LogString) LOG4CXX_STR("Appender \"")
+ appenderName + LOG4CXX_STR("\" was already parsed."));
return appender;
}
static const LogString APPENDER_PREFIX(LOG4CXX_STR("log4j.appender."));
// Appender was not previously initialized.
LogString prefix = APPENDER_PREFIX + appenderName;
LogString layoutPrefix = prefix + LOG4CXX_STR(".layout");
appender =
OptionConverter::instantiateByKey(
props, prefix, Appender::getStaticClass(), 0);
if (appender == 0)
{
LogLog::error((LogString) LOG4CXX_STR("Could not instantiate appender named \"")
+ appenderName + LOG4CXX_STR("\"."));
return 0;
}
appender->setName(appenderName);
if (appender->instanceof(OptionHandler::getStaticClass()))
{
Pool p;
if (appender->requiresLayout())
{
LayoutPtr layout =
OptionConverter::instantiateByKey(
props, layoutPrefix, Layout::getStaticClass(), 0);
if (layout != 0)
{
appender->setLayout(layout);
LogLog::debug((LogString) LOG4CXX_STR("Parsing layout options for \"")
+ appenderName + LOG4CXX_STR("\"."));
//configureOptionHandler(layout, layoutPrefix + ".", props);
PropertySetter::setProperties(layout, props, layoutPrefix + LOG4CXX_STR("."), p);
LogLog::debug((LogString) LOG4CXX_STR("End of parsing for \"")
+ appenderName + LOG4CXX_STR("\"."));
}
}
//configureOptionHandler((OptionHandler) appender, prefix + _T("."), props);
PropertySetter::setProperties(appender, props, prefix + LOG4CXX_STR("."), p);
LogLog::debug((LogString) LOG4CXX_STR("Parsed \"")
+ appenderName + LOG4CXX_STR("\" options."));
}
registryPut(appender);
return appender;
}
void PropertyConfigurator::registryPut(const AppenderPtr& appender)
{
(*registry)[appender->getName()] = appender;
}
AppenderPtr PropertyConfigurator::registryGet(const LogString& name)
{
return (*registry)[name];
}