// Module: LOG4CPLUS
// File: configurator.cxx
// Created: 3/2003
// Author: Tad E. Smith
//
// Copyright (C) Tad E. Smith All rights reserved.
//
// This software is published under the terms of the Apache Software
// License version 1.1, a copy of which has been included with this
// distribution in the LICENSE.APL file.
//
// $Log: configurator.cxx,v $
// Revision 1.20 2004/01/29 02:49:29 tcsmith
// Fixed Bug #875724 - "ConfigureAndWatchThread and mounted file systems"
//
// Revision 1.19 2003/12/07 06:48:57 tcsmith
// Fixed bug #827804 - "UMR in ConfigurationWatchdogThread".
//
// Revision 1.18 2003/10/22 06:02:46 tcsmith
// Explicitly use the log4cplus::helpers::sleep() method.
//
// Revision 1.17 2003/08/05 15:57:18 tcsmith
// Fixed a UNICODE compilation error.
//
// Revision 1.16 2003/08/04 03:08:53 tcsmith
// Fixed the "no newline at the end of file" warning.
//
// Revision 1.15 2003/08/04 01:26:01 tcsmith
// Added the ConfigureAndWatchThread and ConfigurationWatchDogThread classes.
//
// Revision 1.14 2003/07/20 19:57:40 baldheadedguy
// Updated to include an istream constructor so that the calling program
// can open the configuration file and deal with the exceptions if the file
// is not found, etc.
//
// Revision 1.13 2003/06/29 16:48:24 tcsmith
// Modified to support that move of the getLogLog() method into the LogLog
// class.
//
// Revision 1.12 2003/06/12 23:50:22 tcsmith
// Modified to support the rename of the toupper and tolower methods.
//
// Revision 1.11 2003/06/03 20:25:57 tcsmith
// Modified configure() so that it calls initializeLog4cplus().
//
// Revision 1.10 2003/05/19 14:36:31 tcsmith
// Added doConfigure() static methods.
//
// Revision 1.9 2003/05/04 01:07:42 tcsmith
// Modified PropertyConfigurator so that it can be used to configure a
// Hierarchy other than the default one.
//
// Revision 1.8 2003/05/02 16:36:49 tcsmith
// Added "#include <log4cplus/spi/loggerimpl.h>" for VC++ .NET
//
// Revision 1.7 2003/05/01 19:43:33 tcsmith
// Fixed: "warning: comparison between signed and unsigned".
//
// Revision 1.6 2003/04/19 23:04:30 tcsmith
// Fixed UNICODE support.
//
// Revision 1.5 2003/04/19 21:35:14 tcsmith
// Replaced use of back_insert_iterator with string_append_iterator.
//
// Revision 1.4 2003/04/18 21:00:38 tcsmith
// Converted from std::string to log4cplus::tstring.
//
// Revision 1.3 2003/04/05 20:14:38 tcsmith
// Added replaceEnvironVariables() implementation.
//
// Revision 1.2 2003/04/04 00:31:14 tcsmith
// Changed to support the rename of propertyconfig.h to configurator.h
//
// Revision 1.1 2003/04/03 23:46:14 tcsmith
// Renamed from propertyconfig.cxx
//
// Revision 1.3 2003/04/03 01:21:36 tcsmith
// Changed to support the rename of Category to Logger and Priority to
// LogLevel.
//
#include <log4cplus/configurator.h>
#include <log4cplus/hierarchylocker.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/sleep.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/helpers/property.h>
#include <log4cplus/spi/factory.h>
#include <log4cplus/spi/loggerimpl.h>
#include <sys/stat.h>
#include <algorithm>
using namespace std;
using namespace log4cplus;
using namespace log4cplus::helpers;
using namespace log4cplus::spi;
//////////////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////////////
namespace log4cplus {
void initializeLog4cplus();
}
//////////////////////////////////////////////////////////////////////////////
// File LOCAL methods
//////////////////////////////////////////////////////////////////////////////
#define DELIM_START LOG4CPLUS_TEXT("${")
#define DELIM_STOP LOG4CPLUS_TEXT("}")
#define DELIM_START_LEN 2
#define DELIM_STOP_LEN 1
namespace {
/**
* Perform variable substitution in string <code>val</code> from
* environment variables.
*
* <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
*
* <p>For example, if the System properties contains "key=value", then
* the call
* <pre>
* string s = substEnvironVars("Value of key is ${key}.");
* </pre>
*
* will set the variable <code>s</code> to "Value of key is value.".
*
* <p>If no value could be found for the specified key, then
* substitution defaults to the empty string.
*
* <p>For example, if there is no environment variable "inexistentKey",
* then the call
*
* <pre>
* string s = substEnvironVars("Value of inexistentKey is [${inexistentKey}]");
* </pre>
* will set <code>s</code> to "Value of inexistentKey is []"
*
* @param val The string on which variable substitution is performed.
*/
log4cplus::tstring substEnvironVars(const log4cplus::tstring& val,
log4cplus::helpers::LogLog& loglog)
{
log4cplus::tstring sbuf;
tstring::size_type i = 0;
tstring::size_type j, k;
while(true) {
j=val.find(DELIM_START, i);
if(j == log4cplus::tstring::npos) {
if(i==0)
return val;
else {
sbuf += val.substr(i);
return sbuf;
}
}
else {
sbuf += val.substr(i, j - i);
k = val.find(DELIM_STOP, j);
if(k == log4cplus::tstring::npos) {
log4cplus::tostringstream buffer;
buffer << '"' << val
<< "\" has no closing brace. "
<< "Opening brace at position " << j << ".";
loglog.error(buffer.str());
return val;
}
else {
j += DELIM_START_LEN;
log4cplus::tstring key = val.substr(j, k - j);
char* replacement =
getenv(LOG4CPLUS_TSTRING_TO_STRING(key).c_str());
if(replacement != 0)
sbuf += LOG4CPLUS_STRING_TO_TSTRING(replacement);
i = k + DELIM_STOP_LEN;
}
}
} // end while loop
} // end substEnvironVars()
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::PropertyConfigurator ctor and dtor
//////////////////////////////////////////////////////////////////////////////
log4cplus::PropertyConfigurator::PropertyConfigurator(const log4cplus::tstring& propertyFile,
Hierarchy& h)
: h(h),
propertyFilename(propertyFile),
properties(propertyFile)
{
init();
}
log4cplus::PropertyConfigurator::PropertyConfigurator(const log4cplus::helpers::Properties& props,
Hierarchy& h)
: h(h),
propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") ),
properties( props )
{
init();
}
log4cplus::PropertyConfigurator::PropertyConfigurator(log4cplus::tistream& propertyStream,
Hierarchy& h)
: h(h),
propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") ),
properties(propertyStream)
{
init();
}
void
log4cplus::PropertyConfigurator::init()
{
replaceEnvironVariables();
properties = properties.getPropertySubset( LOG4CPLUS_TEXT("log4cplus.") );
}
log4cplus::PropertyConfigurator::~PropertyConfigurator()
{
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::PropertyConfigurator static methods
//////////////////////////////////////////////////////////////////////////////
void
log4cplus::PropertyConfigurator::doConfigure(const log4cplus::tstring& file,
Hierarchy& h)
{
PropertyConfigurator tmp(file, h);
tmp.configure();
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::PropertyConfigurator public methods
//////////////////////////////////////////////////////////////////////////////
void
log4cplus::PropertyConfigurator::configure()
{
initializeLog4cplus();
configureAppenders();
configureLoggers();
configureAdditivity();
// Erase the appenders to that we are not artificially keeping the "alive".
appenders.erase(appenders.begin(), appenders.end());
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::PropertyConfigurator protected methods
//////////////////////////////////////////////////////////////////////////////
void
log4cplus::PropertyConfigurator::reconfigure()
{
properties = Properties(propertyFilename);
init();
configure();
}
void
log4cplus::PropertyConfigurator::replaceEnvironVariables()
{
std::vector<log4cplus::tstring> keys = properties.propertyNames();
std::vector<log4cplus::tstring>::iterator it = keys.begin();
for(; it!=keys.end(); ++it) {
log4cplus::tstring key = *it;
log4cplus::tstring val = properties.getProperty(key);
log4cplus::tstring subKey = substEnvironVars(key, getLogLog());
if(subKey != key) {
properties.removeProperty(key);
properties.setProperty(subKey, val);
}
log4cplus::tstring subVal = substEnvironVars(val, getLogLog());
if(subVal != val) {
properties.setProperty(subKey, subVal);
}
}
}
void
log4cplus::PropertyConfigurator::configureLoggers()
{
if(properties.exists( LOG4CPLUS_TEXT("rootLogger") )) {
Logger root = h.getRoot();
configureLogger(root,
properties.getProperty(LOG4CPLUS_TEXT("rootLogger")));
}
Properties loggerProperties =
properties.getPropertySubset(LOG4CPLUS_TEXT("logger."));
vector<tstring> loggers = loggerProperties.propertyNames();
for(vector<tstring>::iterator it=loggers.begin(); it!=loggers.end(); ++it) {
Logger log = getLogger(*it);
configureLogger(log, loggerProperties.getProperty(*it));
}
}
void
log4cplus::PropertyConfigurator::configureLogger(log4cplus::Logger logger,
const log4cplus::tstring& config)
{
// Remove all spaces from config
tstring configString;
remove_copy_if(config.begin(), config.end(),
string_append_iterator<tstring>(configString),
bind1st(equal_to<tchar>(), ' '));
// "Tokenize" configString
vector<tstring> tokens;
tokenize(configString, ',',
back_insert_iterator<vector<tstring> >(tokens));
if(tokens.size() == 0) {
getLogLog().error( LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()- Invalid config string(Logger = ")
+ logger.getName()
+ LOG4CPLUS_TEXT("): \"")
+ config
+ LOG4CPLUS_TEXT("\""));
return;
}
// Set the loglevel
tstring loglevel = tokens[0];
if(loglevel != LOG4CPLUS_TEXT("INHERITED")) {
logger.setLogLevel( getLogLevelManager().fromString(loglevel) );
}
// Set the Appenders
for(vector<tstring>::size_type j=1; j<tokens.size(); ++j) {
AppenderMap::iterator appenderIt = appenders.find(tokens[j]);
if(appenderIt == appenders.end()) {
getLogLog().error(LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()- Invalid appender: ")
+ tokens[j]);
continue;
}
addAppender(logger, (*appenderIt).second);
}
}
void
log4cplus::PropertyConfigurator::configureAppenders()
{
Properties appenderProperties =
properties.getPropertySubset(LOG4CPLUS_TEXT("appender."));
vector<tstring> appendersProps = appenderProperties.propertyNames();
for(vector<tstring>::iterator it=appendersProps.begin();
it!=appendersProps.end();
++it)
{
if( (*it).find( LOG4CPLUS_TEXT('.') ) == tstring::npos ) {
tstring factoryName = appenderProperties.getProperty(*it);
AppenderFactory* factory = getAppenderFactoryRegistry().get(factoryName);
if(factory == 0) {
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::configureAppenders()- Cannot find AppenderFactory: ");
getLogLog().error(err + factoryName);
continue;
}
Properties properties =
appenderProperties.getPropertySubset((*it) + LOG4CPLUS_TEXT("."));
try {
SharedAppenderPtr appender = factory->createObject(properties);
if(appender.get() == 0) {
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::configureAppenders()- Failed to create appender: ");
getLogLog().error(err + *it);
}
else {
appender->setName(*it);
appenders[*it] = appender;
}
}
catch(std::exception& e) {
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::configureAppenders()- Error while creating Appender: ");
getLogLog().error(err + LOG4CPLUS_C_STR_TO_TSTRING(e.what()));
}
}
} // end for loop
}
void
log4cplus::PropertyConfigurator::configureAdditivity()
{
Properties additivityProperties =
properties.getPropertySubset(LOG4CPLUS_TEXT("additivity."));
vector<tstring> additivitysProps = additivityProperties.propertyNames();
for(vector<tstring>::iterator it=additivitysProps.begin();
it!=additivitysProps.end();
++it)
{
Logger logger = getLogger(*it);
tstring actualValue = additivityProperties.getProperty(*it);
tstring value = toLower(actualValue);
if(value == LOG4CPLUS_TEXT("true")) {
logger.setAdditivity(true);
}
else if(value == LOG4CPLUS_TEXT("false")) {
logger.setAdditivity(false);
}
else {
getLogLog().warn( LOG4CPLUS_TEXT("Invalid Additivity value: \"")
+ actualValue
+ LOG4CPLUS_TEXT("\""));
}
}
}
Logger
log4cplus::PropertyConfigurator::getLogger(const log4cplus::tstring& name)
{
return h.getInstance(name);
}
void
log4cplus::PropertyConfigurator::addAppender(Logger &logger,
log4cplus::SharedAppenderPtr& appender)
{
logger.addAppender(appender);
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::BasicConfigurator ctor and dtor
//////////////////////////////////////////////////////////////////////////////
log4cplus::BasicConfigurator::BasicConfigurator(Hierarchy& h)
: log4cplus::PropertyConfigurator( LOG4CPLUS_TEXT(""), h )
{
properties.setProperty(LOG4CPLUS_TEXT("rootLogger"),
LOG4CPLUS_TEXT("DEBUG, STDOUT"));
properties.setProperty(LOG4CPLUS_TEXT("appender.STDOUT"),
LOG4CPLUS_TEXT("log4cplus::ConsoleAppender"));
}
log4cplus::BasicConfigurator::~BasicConfigurator()
{
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::BasicConfigurator static methods
//////////////////////////////////////////////////////////////////////////////
void
log4cplus::BasicConfigurator::doConfigure(Hierarchy& h)
{
BasicConfigurator tmp(h);
tmp.configure();
}
#if !defined(LOG4CPLUS_SINGLE_THREADED)
//////////////////////////////////////////////////////////////////////////////
// log4cplus::ConfigurationWatchDogThread implementation
//////////////////////////////////////////////////////////////////////////////
namespace log4cplus {
class ConfigurationWatchDogThread : public thread::AbstractThread,
public PropertyConfigurator
{
public:
ConfigurationWatchDogThread(const tstring& file, unsigned int millis)
: PropertyConfigurator(file),
waitSecs(millis/1000),
shouldTerminate(false),
lastModTime(Time::gettimeofday()),
lock(NULL)
{
updateLastModTime();
if(waitSecs <= 0) {
waitSecs = 1;
}
}
void terminate() { shouldTerminate = true; }
protected:
virtual void run();
virtual Logger getLogger(const log4cplus::tstring& name);
virtual void addAppender(Logger &logger, log4cplus::SharedAppenderPtr& appender);
bool checkForFileModification();
void updateLastModTime();
virtual ~ConfigurationWatchDogThread(){}
private:
unsigned int waitSecs;
bool shouldTerminate;
Time lastModTime;
HierarchyLocker* lock;
};
}
void
ConfigurationWatchDogThread::run()
{
while(!shouldTerminate) {
log4cplus::helpers::sleep(waitSecs);
bool modified = checkForFileModification();
if(modified) {
// Lock the Hierarchy
HierarchyLocker theLock(h);
lock = &theLock;
// reconfigure the Hierarchy
theLock.resetConfiguration();
reconfigure();
updateLastModTime();
// release the lock
lock = NULL;
}
}
}
Logger
ConfigurationWatchDogThread::getLogger(const log4cplus::tstring& name)
{
if(lock) {
return lock->getInstance(name);
}
else {
return PropertyConfigurator::getLogger(name);
}
}
void
ConfigurationWatchDogThread::addAppender(Logger& logger,
log4cplus::SharedAppenderPtr& appender)
{
if(lock) {
lock->addAppender(logger, appender);
}
else {
PropertyConfigurator::addAppender(logger, appender);
}
}
bool
ConfigurationWatchDogThread::checkForFileModification()
{
struct stat fileStatus;
if(::stat(LOG4CPLUS_TSTRING_TO_STRING(propertyFilename).c_str(), &fileStatus) == -1) {
return false; // stat() returned error, so the file must not exist
}
Time modTime(fileStatus.st_mtime);
bool modified = (modTime > lastModTime);
#if defined(HAVE_LSTAT)
if(!modified && S_ISLNK(fileStatus.st_mode)) {
::lstat(LOG4CPLUS_TSTRING_TO_STRING(propertyFilename).c_str(), &fileStatus);
Time linkModTime(fileStatus.st_mtime);
modified = (linkModTime > lastModTime);
}
#endif
return modified;
}
void
ConfigurationWatchDogThread::updateLastModTime()
{
struct stat fileStatus;
if(::stat(LOG4CPLUS_TSTRING_TO_STRING(propertyFilename).c_str(), &fileStatus) == -1) {
return; // stat() returned error, so the file must not exist
}
lastModTime = Time(fileStatus.st_mtime);
}
//////////////////////////////////////////////////////////////////////////////
// log4cplus::PropertyConfiguratorWatchDog ctor and dtor
//////////////////////////////////////////////////////////////////////////////
ConfigureAndWatchThread::ConfigureAndWatchThread(const tstring& file,
unsigned int millis)
: watchDogThread(0)
{
watchDogThread = new ConfigurationWatchDogThread(file, millis);
watchDogThread->configure();
watchDogThread->start();
}
ConfigureAndWatchThread::~ConfigureAndWatchThread()
{
if(watchDogThread.get() != 0) {
watchDogThread->terminate();
}
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1