You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

749 lines
21 KiB

// Module: LOG4CPLUS
// File: configurator.cxx
// Created: 3/2003
// Author: Tad E. Smith
//
//
// Copyright 2003-2010 Tad E. Smith
//
// Licensed 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 <log4cplus/configurator.h>
#include <log4cplus/hierarchylocker.h>
#include <log4cplus/hierarchy.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/sleep.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/helpers/property.h>
#include <log4cplus/helpers/timehelper.h>
#include <log4cplus/helpers/fileinfo.h>
#include <log4cplus/thread/threads.h>
#include <log4cplus/thread/syncprims-pub-impl.h>
#include <log4cplus/spi/factory.h>
#include <log4cplus/spi/loggerimpl.h>
#include <log4cplus/internal/env.h>
#ifdef LOG4CPLUS_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef LOG4CPLUS_HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef LOG4CPLUS_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if defined (_WIN32)
#include <tchar.h>
#endif
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <sstream>
#include <functional>
namespace log4cplus
{
void initializeLog4cplus();
namespace
{
static tchar const DELIM_START[] = LOG4CPLUS_TEXT("${");
static tchar const DELIM_STOP[] = LOG4CPLUS_TEXT("}");
static std::size_t const DELIM_START_LEN = 2;
static std::size_t const DELIM_STOP_LEN = 1;
/**
* 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(s, "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(s, "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.
* @param dest The result.
*/
static
bool
substVars (tstring & dest, const tstring & val,
helpers::Properties const & props, helpers::LogLog& loglog,
unsigned flags)
{
tstring::size_type i = 0;
tstring::size_type var_start, var_end;
tstring pattern (val);
tstring key;
tstring replacement;
bool changed = false;
bool const empty_vars
= !! (flags & PropertyConfigurator::fAllowEmptyVars);
bool const shadow_env
= !! (flags & PropertyConfigurator::fShadowEnvironment);
bool const rec_exp
= !! (flags & PropertyConfigurator::fRecursiveExpansion);
while (true)
{
// Find opening paren of variable substitution.
var_start = pattern.find(DELIM_START, i);
if (var_start == tstring::npos)
{
dest = pattern;
return changed;
}
// Find closing paren of variable substitution.
var_end = pattern.find(DELIM_STOP, var_start);
if (var_end == tstring::npos)
{
tostringstream buffer;
buffer << '"' << pattern
<< "\" has no closing brace. "
<< "Opening brace at position " << var_start << ".";
loglog.error(buffer.str());
dest = val;
return false;
}
key.assign (pattern, var_start + DELIM_START_LEN,
var_end - (var_start + DELIM_START_LEN));
replacement.clear ();
if (shadow_env)
replacement = props.getProperty (key);
if (! shadow_env || (! empty_vars && replacement.empty ()))
internal::get_env_var (replacement, key);
if (empty_vars || ! replacement.empty ())
{
// Substitute the variable with its value in place.
pattern.replace (var_start, var_end - var_start + DELIM_STOP_LEN,
replacement);
changed = true;
if (rec_exp)
// Retry expansion on the same spot.
continue;
else
// Move beyond the just substituted part.
i = var_start + replacement.size ();
}
else
// Nothing has been subtituted, just move beyond the
// unexpanded variable.
i = var_end + DELIM_STOP_LEN;
} // end while loop
} // end substVars()
//! Translates encoding in ProtpertyConfigurator::PCFlags
//! to helpers::Properties::PFlags
static
unsigned
pcflag_to_pflags_encoding (unsigned pcflags)
{
switch (pcflags
& (PropertyConfigurator::fEncodingMask
<< PropertyConfigurator::fEncodingShift))
{
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) && defined (UNICODE)
case PropertyConfigurator::fUTF8:
return helpers::Properties::fUTF8;
#endif
#if (defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) || defined (WIN32)) \
&& defined (UNICODE)
case PropertyConfigurator::fUTF16:
return helpers::Properties::fUTF16;
#endif
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET) && defined (UNICODE)
case PropertyConfigurator::fUTF32:
return helpers::Properties::fUTF32;
#endif
case PropertyConfigurator::fUnspecEncoding:;
default:
return 0;
}
}
} // namespace
//////////////////////////////////////////////////////////////////////////////
// PropertyConfigurator ctor and dtor
//////////////////////////////////////////////////////////////////////////////
PropertyConfigurator::PropertyConfigurator(const tstring& propertyFile,
Hierarchy& hier, unsigned f)
: h(hier)
, propertyFilename(propertyFile)
, properties(propertyFile, pcflag_to_pflags_encoding (f))
, flags (f)
{
init();
}
PropertyConfigurator::PropertyConfigurator(const helpers::Properties& props,
Hierarchy& hier, unsigned f)
: h(hier)
, propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") )
, properties( props )
, flags (f)
{
init();
}
PropertyConfigurator::PropertyConfigurator(tistream& propertyStream,
Hierarchy& hier, unsigned f)
: h(hier)
, propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") )
, properties(propertyStream)
, flags (f)
{
init();
}
void
PropertyConfigurator::init()
{
replaceEnvironVariables();
properties = properties.getPropertySubset( LOG4CPLUS_TEXT("log4cplus.") );
}
PropertyConfigurator::~PropertyConfigurator()
{
}
//////////////////////////////////////////////////////////////////////////////
// PropertyConfigurator static methods
//////////////////////////////////////////////////////////////////////////////
void
PropertyConfigurator::doConfigure(const tstring& file, Hierarchy& h,
unsigned flags)
{
PropertyConfigurator tmp(file, h, flags);
tmp.configure();
}
//////////////////////////////////////////////////////////////////////////////
// PropertyConfigurator public methods
//////////////////////////////////////////////////////////////////////////////
void
PropertyConfigurator::configure()
{
// Configure log4cplus internals.
bool internal_debugging = false;
if (properties.getBool (internal_debugging, LOG4CPLUS_TEXT ("configDebug")))
helpers::getLogLog ().setInternalDebugging (internal_debugging);
bool quiet_mode = false;
if (properties.getBool (quiet_mode, LOG4CPLUS_TEXT ("quietMode")))
helpers::getLogLog ().setQuietMode (quiet_mode);
bool disable_override = false;
if (properties.getBool (disable_override,
LOG4CPLUS_TEXT ("disableOverride")))
initializeLog4cplus();
configureAppenders();
configureLoggers();
configureAdditivity();
if (disable_override)
h.disable (Hierarchy::DISABLE_OVERRIDE);
// Erase the appenders so that we are not artificially keeping them "alive".
appenders.clear ();
}
helpers::Properties const &
PropertyConfigurator::getProperties () const
{
return properties;
}
log4cplus::tstring const &
PropertyConfigurator::getPropertyFilename () const
{
return propertyFilename;
}
//////////////////////////////////////////////////////////////////////////////
// PropertyConfigurator protected methods
//////////////////////////////////////////////////////////////////////////////
void
PropertyConfigurator::reconfigure()
{
properties = helpers::Properties(propertyFilename);
init();
configure();
}
void
PropertyConfigurator::replaceEnvironVariables()
{
tstring val, subKey, subVal;
std::vector<tstring> keys;
bool const rec_exp
= !! (flags & PropertyConfigurator::fRecursiveExpansion);
bool changed;
do
{
changed = false;
properties.propertyNames().swap (keys);
for (std::vector<tstring>::const_iterator it = keys.begin();
it != keys.end(); ++it)
{
tstring const & key = *it;
val = properties.getProperty(key);
subKey.clear ();
if (substVars(subKey, key, properties, helpers::getLogLog(), flags))
{
properties.removeProperty(key);
properties.setProperty(subKey, val);
changed = true;
}
subVal.clear ();
if (substVars(subVal, val, properties, helpers::getLogLog(), flags))
{
properties.setProperty(subKey, subVal);
changed = true;
}
}
}
while (changed && rec_exp);
}
void
PropertyConfigurator::configureLoggers()
{
if(properties.exists( LOG4CPLUS_TEXT("rootLogger") ))
{
Logger root = h.getRoot();
configureLogger(root,
properties.getProperty(LOG4CPLUS_TEXT("rootLogger")));
}
helpers::Properties loggerProperties
= properties.getPropertySubset(LOG4CPLUS_TEXT("logger."));
std::vector<tstring> loggers = loggerProperties.propertyNames();
for(std::vector<tstring>::iterator it=loggers.begin(); it!=loggers.end();
++it)
{
Logger log = getLogger(*it);
configureLogger(log, loggerProperties.getProperty(*it));
}
}
void
PropertyConfigurator::configureLogger(Logger logger, const tstring& config)
{
// Remove all spaces from config
tstring configString;
std::remove_copy_if(config.begin(), config.end(),
std::back_inserter (configString),
std::bind1st(std::equal_to<tchar>(), LOG4CPLUS_TEXT(' ')));
// "Tokenize" configString
std::vector<tstring> tokens;
helpers::tokenize(configString, LOG4CPLUS_TEXT(','),
std::back_insert_iterator<std::vector<tstring> >(tokens));
if (tokens.empty ())
{
helpers::getLogLog().error(
LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()")
LOG4CPLUS_TEXT("- Invalid config string(Logger = ")
+ logger.getName()
+ LOG4CPLUS_TEXT("): \"")
+ config
+ LOG4CPLUS_TEXT("\""));
return;
}
// Set the loglevel
tstring const & loglevel = tokens[0];
if (loglevel != LOG4CPLUS_TEXT("INHERITED"))
logger.setLogLevel( getLogLevelManager().fromString(loglevel) );
else
logger.setLogLevel (NOT_SET_LOG_LEVEL);
// Remove all existing appenders first so that we do not duplicate output.
logger.removeAllAppenders ();
// Set the Appenders
for(std::vector<tstring>::size_type j=1; j<tokens.size(); ++j)
{
AppenderMap::iterator appenderIt = appenders.find(tokens[j]);
if (appenderIt == appenders.end())
{
helpers::getLogLog().error(
LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()")
LOG4CPLUS_TEXT("- Invalid appender: ")
+ tokens[j]);
continue;
}
addAppender(logger, appenderIt->second);
}
}
void
PropertyConfigurator::configureAppenders()
{
helpers::Properties appenderProperties =
properties.getPropertySubset(LOG4CPLUS_TEXT("appender."));
std::vector<tstring> appendersProps = appenderProperties.propertyNames();
tstring factoryName;
for(std::vector<tstring>::iterator it=appendersProps.begin();
it != appendersProps.end(); ++it)
{
if( it->find( LOG4CPLUS_TEXT('.') ) == tstring::npos )
{
factoryName = appenderProperties.getProperty(*it);
spi::AppenderFactory* factory
= spi::getAppenderFactoryRegistry().get(factoryName);
if (! factory)
{
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::configureAppenders()")
LOG4CPLUS_TEXT("- Cannot find AppenderFactory: ");
helpers::getLogLog().error(err + factoryName);
continue;
}
helpers::Properties props_subset
= appenderProperties.getPropertySubset((*it)
+ LOG4CPLUS_TEXT("."));
try
{
SharedAppenderPtr appender
= factory->createObject(props_subset);
if (! appender)
{
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::")
LOG4CPLUS_TEXT("configureAppenders()")
LOG4CPLUS_TEXT("- Failed to create appender: ");
helpers::getLogLog().error(err + *it);
}
else
{
appender->setName(*it);
appenders[*it] = appender;
}
}
catch(std::exception const & e)
{
tstring err =
LOG4CPLUS_TEXT("PropertyConfigurator::")
LOG4CPLUS_TEXT("configureAppenders()")
LOG4CPLUS_TEXT("- Error while creating Appender: ");
helpers::getLogLog().error(err + LOG4CPLUS_C_STR_TO_TSTRING(e.what()));
}
}
} // end for loop
}
void
PropertyConfigurator::configureAdditivity()
{
helpers::Properties additivityProperties =
properties.getPropertySubset(LOG4CPLUS_TEXT("additivity."));
std::vector<tstring> additivitysProps
= additivityProperties.propertyNames();
for(std::vector<tstring>::const_iterator it = additivitysProps.begin();
it != additivitysProps.end(); ++it)
{
Logger logger = getLogger(*it);
bool additivity;
if (additivityProperties.getBool (additivity, *it))
logger.setAdditivity (additivity);
}
}
Logger
PropertyConfigurator::getLogger(const tstring& name)
{
return h.getInstance(name);
}
void
PropertyConfigurator::addAppender(Logger &logger, SharedAppenderPtr& appender)
{
logger.addAppender(appender);
}
//////////////////////////////////////////////////////////////////////////////
// BasicConfigurator ctor and dtor
//////////////////////////////////////////////////////////////////////////////
log4cplus::tstring DISABLE_OVERRIDE_KEY (
LOG4CPLUS_TEXT ("log4cplus.disableOverride"));
BasicConfigurator::BasicConfigurator(Hierarchy& hier, bool logToStdErr)
: PropertyConfigurator( LOG4CPLUS_TEXT(""), hier )
{
properties.setProperty(LOG4CPLUS_TEXT("rootLogger"),
LOG4CPLUS_TEXT("DEBUG, STDOUT"));
properties.setProperty(LOG4CPLUS_TEXT("appender.STDOUT"),
LOG4CPLUS_TEXT("log4cplus::ConsoleAppender"));
properties.setProperty(LOG4CPLUS_TEXT("appender.STDOUT.logToStdErr"),
logToStdErr ? LOG4CPLUS_TEXT("1")
: LOG4CPLUS_TEXT("0"));
}
BasicConfigurator::~BasicConfigurator()
{
}
//////////////////////////////////////////////////////////////////////////////
// BasicConfigurator static methods
//////////////////////////////////////////////////////////////////////////////
void
BasicConfigurator::doConfigure(Hierarchy& h, bool logToStdErr)
{
BasicConfigurator tmp(h, logToStdErr);
tmp.configure();
}
#if !defined(LOG4CPLUS_SINGLE_THREADED)
//////////////////////////////////////////////////////////////////////////////
// ConfigurationWatchDogThread implementation
//////////////////////////////////////////////////////////////////////////////
class ConfigurationWatchDogThread
: public thread::AbstractThread,
public PropertyConfigurator
{
public:
ConfigurationWatchDogThread(const tstring& file, unsigned int millis)
: PropertyConfigurator(file)
, waitMillis(millis < 1000 ? 1000 : millis)
, shouldTerminate(false)
, lock(NULL)
{
lastFileInfo.mtime = helpers::Time::gettimeofday ();
lastFileInfo.size = 0;
lastFileInfo.is_link = false;
updateLastModInfo();
}
virtual ~ConfigurationWatchDogThread ()
{ }
void terminate ()
{
shouldTerminate.signal ();
join ();
}
protected:
virtual void run();
virtual Logger getLogger(const tstring& name);
virtual void addAppender(Logger &logger, SharedAppenderPtr& appender);
bool checkForFileModification();
void updateLastModInfo();
private:
ConfigurationWatchDogThread (ConfigurationWatchDogThread const &);
ConfigurationWatchDogThread & operator = (
ConfigurationWatchDogThread const &);
unsigned int const waitMillis;
thread::ManualResetEvent shouldTerminate;
helpers::FileInfo lastFileInfo;
HierarchyLocker* lock;
};
void
ConfigurationWatchDogThread::run()
{
while (! shouldTerminate.timed_wait (waitMillis))
{
bool modified = checkForFileModification();
if(modified) {
// Lock the Hierarchy
HierarchyLocker theLock(h);
lock = &theLock;
// reconfigure the Hierarchy
theLock.resetConfiguration();
reconfigure();
updateLastModInfo();
// release the lock
lock = NULL;
}
}
}
Logger
ConfigurationWatchDogThread::getLogger(const tstring& name)
{
if(lock)
return lock->getInstance(name);
else
return PropertyConfigurator::getLogger(name);
}
void
ConfigurationWatchDogThread::addAppender(Logger& logger,
SharedAppenderPtr& appender)
{
if(lock)
lock->addAppender(logger, appender);
else
PropertyConfigurator::addAppender(logger, appender);
}
bool
ConfigurationWatchDogThread::checkForFileModification()
{
helpers::FileInfo fi;
if (helpers::getFileInfo (&fi, propertyFilename) != 0)
return false;
bool modified = fi.mtime > lastFileInfo.mtime
|| fi.size != lastFileInfo.size;
#if defined(LOG4CPLUS_HAVE_LSTAT)
if (!modified && fi.is_link)
{
struct stat fileStatus;
if (lstat(LOG4CPLUS_TSTRING_TO_STRING(propertyFilename).c_str(),
&fileStatus) == -1)
return false;
helpers::Time linkModTime(fileStatus.st_mtime);
modified = (linkModTime > fi.mtime);
}
#endif
return modified;
}
void
ConfigurationWatchDogThread::updateLastModInfo()
{
helpers::FileInfo fi;
if (helpers::getFileInfo (&fi, propertyFilename) == 0)
lastFileInfo = fi;
}
//////////////////////////////////////////////////////////////////////////////
// PropertyConfiguratorWatchDog ctor and dtor
//////////////////////////////////////////////////////////////////////////////
ConfigureAndWatchThread::ConfigureAndWatchThread(const tstring& file,
unsigned int millis)
: watchDogThread(0)
{
watchDogThread = new ConfigurationWatchDogThread(file, millis);
watchDogThread->addReference ();
watchDogThread->configure();
watchDogThread->start();
}
ConfigureAndWatchThread::~ConfigureAndWatchThread()
{
if (watchDogThread)
{
watchDogThread->terminate();
watchDogThread->removeReference ();
}
}
#endif
} // namespace log4cplus