blob: 830a64875a260179a7032d69bbe84e3e57864d55 [file]
/*
* 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/xml/domconfigurator.h>
#include <log4cxx/appender.h>
#include <log4cxx/asyncappender.h>
#include <log4cxx/layout.h>
#include <log4cxx/logger.h>
#include <log4cxx/logmanager.h>
#include <log4cxx/level.h>
#include <log4cxx/spi/filter.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/loader.h>
#include <log4cxx/helpers/optionconverter.h>
#include <log4cxx/config/propertysetter.h>
#include <log4cxx/varia/fallbackerrorhandler.h>
#include <log4cxx/spi/loggerfactory.h>
#include <log4cxx/helpers/filewatchdog.h>
#include <log4cxx/spi/loggerrepository.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/pool.h>
#include <sstream>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/rolling/rollingfileappender.h>
#include <log4cxx/rolling/filterbasedtriggeringpolicy.h>
#include <apr_xml.h>
#include <log4cxx/helpers/bytebuffer.h>
#include <log4cxx/helpers/charsetdecoder.h>
#include <log4cxx/net/smtpappender.h>
#include <log4cxx/helpers/messagebuffer.h>
#include <log4cxx/helpers/threadutility.h>
#include <log4cxx/helpers/singletonholder.h>
#define LOG4CXX 1
#include <log4cxx/helpers/aprinitializer.h>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::xml;
using namespace LOG4CXX_NS::helpers;
using namespace LOG4CXX_NS::spi;
using namespace LOG4CXX_NS::config;
using namespace LOG4CXX_NS::rolling;
#define MAX_ATTRIBUTE_NAME_LEN 2000
using FilterStore = std::vector<FilterPtr>;
class DOMConfiguratorContext
{
public: // Types
struct AppenderStatus
{
AppenderPtr pAppender;
bool activated;
};
using AppenderMap = std::map<LogString, AppenderStatus>;
public: // Attributes
Properties props = Configurator::properties();
LoggerRepositoryPtr repository;
LoggerFactoryPtr loggerFactory{ std::make_shared<LoggerFactory>() };
bool appenderAdded{ false };
AppenderMap appenders;
Pool p;
CharsetDecoderPtr utf8Decoder{ CharsetDecoder::getDecoder(LOG4CXX_STR("UTF-8")) };
apr_xml_doc* doc{ nullptr };
public: // ...structor
DOMConfiguratorContext()
: repository(LogManager::getLoggerRepository())
{}
DOMConfiguratorContext(const LoggerRepositoryPtr& r)
: repository(r)
{}
public: // Methods
AppenderPtr findAppenderByName(apr_xml_elem* elem, const LogString& appenderName);
AppenderPtr findAppenderByReference(apr_xml_elem* appenderRef, const char* optionalAttributeName = nullptr);
AppenderPtr parseAppender(apr_xml_elem* appenderElement);
void parseFallbackAppender(apr_xml_elem* element, const LogString& holderName, const AppenderAttachablePtr& holder, const AppenderPtr& primary, const AppenderSkeletonPtr& aSkel);
void parseFallbackAppender(apr_xml_elem* element, const LoggerPtr& l, const AppenderSkeletonPtr& primary);
void parseErrorHandler(apr_xml_elem* element, const AppenderPtr& appender);
FilterStore parseFilters(apr_xml_elem* element);
void parseLogger(apr_xml_elem* loggerElement);
void parseLoggerFactory(apr_xml_elem* factoryElement);
ObjectPtr parseTriggeringPolicy(apr_xml_elem* factoryElement);
RollingPolicyPtr parseRollingPolicy(apr_xml_elem* factoryElement);
void parseRoot(apr_xml_elem* rootElement);
void parseChildrenOfLoggerElement(apr_xml_elem* catElement, const LoggerPtr& logger, bool isRoot);
LayoutPtr parseLayout(apr_xml_elem* layout_element);
void parseLevel(apr_xml_elem* element, const LoggerPtr& logger, bool isRoot);
void setParameter(apr_xml_elem* elem, config::PropertySetter& propSetter);
void parse(apr_xml_elem* element);
LogString getAttribute(apr_xml_elem*, const std::string& attrName);
LogString subst(const LogString& value);
};
// Provision for DOMConfigurator properties
struct DOMConfigurator::DOMConfiguratorPrivate
{
};
IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
#define CONFIGURATION_TAG "log4j:configuration"
#define OLD_CONFIGURATION_TAG "configuration"
#define APPENDER_TAG "appender"
#define APPENDER_REF_TAG "appender-ref"
#define PARAM_TAG "param"
#define LAYOUT_TAG "layout"
#define ROLLING_POLICY_TAG "rollingPolicy"
#define TRIGGERING_POLICY_TAG "triggeringPolicy"
#define CATEGORY "category"
#define LOGGER "logger"
#define LOGGER_REF "logger-ref"
#define CATEGORY_FACTORY_TAG "categoryFactory"
#define NAME_ATTR "name"
#define CLASS_ATTR "class"
#define VALUE_ATTR "value"
#define ROOT_TAG "root"
#define ROOT_REF "root-ref"
#define LEVEL_TAG "level"
#define PRIORITY_TAG "priority"
#define FILTER_TAG "filter"
#define ERROR_HANDLER_TAG "errorHandler"
#define REF_ATTR "ref"
#define FALLBACK_REF_ATTR "fallback-ref"
#define ADDITIVITY_ATTR "additivity"
#define ASYNCHRONOUS_ATTR "asynchronous"
#define THRESHOLD_ATTR "threshold"
#define STRINGSTREAM_ATTR "stringstream"
#define CONFIG_DEBUG_ATTR "configDebug"
#define INTERNAL_DEBUG_ATTR "debug"
#define INTERNAL_COLOR_ATTR "color"
#define THREAD_CONFIG_ATTR "threadConfiguration"
DOMConfigurator::DOMConfigurator() {}
DOMConfigurator::~DOMConfigurator() {}
/**
Used internally to parse appenders by IDREF name.
*/
AppenderPtr DOMConfiguratorContext::findAppenderByName(apr_xml_elem* element, const LogString& appenderName)
{
AppenderPtr appender;
while (element)
{
if (std::string(element->name) == APPENDER_TAG)
{
if (appenderName == getAttribute(element, NAME_ATTR))
{
appender = parseAppender(element);
if (appender)
break;
}
}
if (element->first_child)
{
appender = findAppenderByName(element->first_child, appenderName);
if (appender)
break;
}
element = element->next;
}
return appender;
}
/**
Used internally to parse appenders by IDREF element.
*/
AppenderPtr DOMConfiguratorContext::findAppenderByReference(apr_xml_elem* appenderRef, const char* optionalAttributeName)
{
AppenderPtr appender;
LogString appenderName = subst(getAttribute(appenderRef, optionalAttributeName ? optionalAttributeName : REF_ATTR));
if (optionalAttributeName && appenderName.empty())
return appender;
if (appenderName.empty())
{
LogString msg(LOG4CXX_STR("["));
utf8Decoder->decode(appenderRef->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] attribute [");
utf8Decoder->decode(REF_ATTR, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] not found");
LogLog::warn(msg);
return appender;
}
AppenderMap::const_iterator match = appenders.find(appenderName);
if (match != appenders.end())
{
if (!match->second.activated)
{
LogString msg(LOG4CXX_STR("Ignoring recursive reference to [") + appenderName + LOG4CXX_STR("]"));
LogLog::warn(msg);
}
else
appender = match->second.pAppender;
}
else if (doc)
{
appender = findAppenderByName(doc->root, appenderName);
}
if (!appender)
{
LogLog::error(LOG4CXX_STR("No ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appenderName + LOG4CXX_STR("] could be found."));
}
return appender;
}
/**
Used internally to parse an appender element.
*/
AppenderPtr DOMConfiguratorContext::parseAppender(apr_xml_elem* appenderElement)
{
LogString className(subst(getAttribute(appenderElement, CLASS_ATTR)));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Desired ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
}
try
{
ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
AppenderPtr appender = LOG4CXX_NS::cast<Appender>(instance);
if(!appender){
LogLog::error(LOG4CXX_STR("Could not cast [") + className + LOG4CXX_STR("] to ") + Appender::getStaticClass().getName());
return AppenderPtr();
}
PropertySetter propSetter(appender);
appender->setName(subst(getAttribute(appenderElement, NAME_ATTR)));
appenders.emplace(appender->getName(), AppenderStatus{appender, false});
for (apr_xml_elem* currentElement = appenderElement->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
// Parse appender parameters
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
// Set appender layout
else if (tagName == LAYOUT_TAG)
{
appender->setLayout(parseLayout(currentElement));
}
// Add filters
else if (tagName == FILTER_TAG)
{
auto filters = parseFilters(currentElement);
for (auto& item : filters)
{
appender->addFilter(item);
}
}
else if (tagName == ERROR_HANDLER_TAG)
{
parseErrorHandler(currentElement, appender);
}
else if (tagName == ROLLING_POLICY_TAG)
{
auto rollPolicy = parseRollingPolicy(currentElement);
RollingFileAppenderPtr rfa = LOG4CXX_NS::cast<RollingFileAppender>(appender);
if (rfa != NULL)
{
rfa->setRollingPolicy(rollPolicy);
}
}
else if (tagName == TRIGGERING_POLICY_TAG)
{
auto policy = parseTriggeringPolicy(currentElement);
RollingFileAppenderPtr rfa = LOG4CXX_NS::cast<RollingFileAppender>(appender);
TriggeringPolicyPtr policyPtr = LOG4CXX_NS::cast<TriggeringPolicy>(policy);
if (rfa != NULL)
{
rfa->setTriggeringPolicy(policyPtr);
}
else
{
auto smtpa = LOG4CXX_NS::cast<LOG4CXX_NS::net::SMTPAppender>(appender);
if (smtpa != NULL)
{
auto evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(policy);
smtpa->setEvaluator(evaluator);
}
}
}
else if (tagName == APPENDER_REF_TAG)
{
if (appender->instanceof(AppenderAttachable::getStaticClass()))
{
AppenderAttachablePtr aa = LOG4CXX_NS::cast<AppenderAttachable>(appender);
if (auto delegateAppender = findAppenderByReference(currentElement))
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Attaching ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + delegateAppender->getName() + LOG4CXX_STR("] to ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appender->getName() + LOG4CXX_STR("]"));
}
aa->addAppender(delegateAppender);
if (auto appSkeleton = LOG4CXX_NS::cast<AppenderSkeleton>(appender))
parseFallbackAppender(currentElement, appender->getName(), aa, delegateAppender, appSkeleton);
}
}
else
{
LogLog::error(LOG4CXX_STR("Cannot attach to ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appender->getName() + LOG4CXX_STR("]")
+ LOG4CXX_STR(" which does not implement ") + AppenderAttachable::getStaticClass().getName());
}
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(appenderElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
appenders[appender->getName()].activated = true;
return appender;
}
/* Yes, it's ugly. But all of these exceptions point to the same
problem: we can't create an Appender */
catch (Exception& oops)
{
LogLog::error(LOG4CXX_STR("Could not create ") + Appender::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
return 0;
}
}
void DOMConfiguratorContext::parseFallbackAppender(apr_xml_elem* element, const LogString& holderName, const AppenderAttachablePtr& holder, const AppenderPtr& primary, const AppenderSkeletonPtr& aSkel)
{
if (auto fallbackAppender = findAppenderByReference(element, FALLBACK_REF_ATTR))
{
auto fb = std::make_shared<varia::FallbackErrorHandler>();
fb->setAppender(primary);
fb->setBackupAppender(fallbackAppender);
fb->addAppenderHolder(holderName, holder);
aSkel->setErrorHandler(fb);
}
}
void DOMConfiguratorContext::parseFallbackAppender(apr_xml_elem* element, const LoggerPtr& l, const AppenderSkeletonPtr& primary)
{
if (auto fallbackAppender = findAppenderByReference(element, FALLBACK_REF_ATTR))
{
auto fb = std::make_shared<varia::FallbackErrorHandler>();
fb->setAppender(primary);
fb->setBackupAppender(fallbackAppender);
fb->setLogger(l);
primary->setErrorHandler(fb);
}
}
/**
Used internally to parse an {@link ErrorHandler} element.
*/
void DOMConfiguratorContext::parseErrorHandler(apr_xml_elem* element, const AppenderPtr& appender)
{
ErrorHandlerPtr eh;
std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(
subst(getAttribute(element, CLASS_ATTR)),
ErrorHandler::getStaticClass(),
0);
eh = LOG4CXX_NS::cast<ErrorHandler>(obj);
if (eh != 0)
{
eh->setAppender(appender);
PropertySetter propSetter(eh);
for (apr_xml_elem* currentElement = element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else if (tagName == APPENDER_REF_TAG)
{
if (auto appender = findAppenderByReference(currentElement))
eh->setBackupAppender(appender);
}
else if (tagName == LOGGER_REF)
{
LogString loggerName(getAttribute(currentElement, REF_ATTR));
LoggerPtr logger = this->repository->getLogger(loggerName, this->loggerFactory);
eh->setLogger(logger);
}
else if (tagName == ROOT_REF)
{
LoggerPtr root = this->repository->getRootLogger();
eh->setLogger(root);
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
if (auto appSkeleton = LOG4CXX_NS::cast<AppenderSkeleton>(appender))
appSkeleton->setErrorHandler(eh);
}
}
/**
Used internally to parse a filter element.
*/
FilterStore DOMConfiguratorContext::parseFilters(apr_xml_elem* element)
{
FilterStore result;
LogString clazz = subst(getAttribute(element, CLASS_ATTR));
FilterPtr filter;
std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(clazz,
Filter::getStaticClass(), 0);
filter = LOG4CXX_NS::cast<Filter>(obj);
if (filter != 0)
{
PropertySetter propSetter(filter);
for (apr_xml_elem* currentElement = element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
result.push_back(filter);
}
return result;
}
/**
Used internally to parse an category or logger element.
*/
void DOMConfiguratorContext::parseLogger(apr_xml_elem* loggerElement)
{
// Create a new Logger object from the <category> element.
LogString loggerName = subst(getAttribute(loggerElement, NAME_ATTR));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Getting [") + loggerName + LOG4CXX_STR("]"));
}
LoggerPtr logger = this->repository->getLogger(loggerName, this->loggerFactory);
// Setting up a logger needs to be an atomic operation, in order
// to protect potential log operations while logger
// configuration is in progress.
bool additivity = OptionConverter::toBoolean(
subst(getAttribute(loggerElement, ADDITIVITY_ATTR)),
true);
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Setting [") + logger->getName() + LOG4CXX_STR("] additivity to [") +
(additivity ? LogString(LOG4CXX_STR("true")) : LogString(LOG4CXX_STR("false"))) + LOG4CXX_STR("]"));
}
logger->setAdditivity(additivity);
parseChildrenOfLoggerElement(loggerElement, logger, false);
}
/**
Used internally to parse the logger factory element.
*/
void DOMConfiguratorContext::parseLoggerFactory(apr_xml_elem* factoryElement)
{
LogString className(subst(getAttribute(factoryElement, CLASS_ATTR)));
if (className.empty())
{
LogString msg(LOG4CXX_STR("["));
utf8Decoder->decode(factoryElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] attribute [");
utf8Decoder->decode(CLASS_ATTR, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] not found");
LogLog::warn(msg);
}
else
{
auto obj = OptionConverter::instantiateByClassName
( StringHelper::trim(className)
, LoggerFactory::getStaticClass()
, std::make_shared<LoggerFactory>()
);
this->loggerFactory = LOG4CXX_NS::cast<LoggerFactory>(obj);
PropertySetter propSetter(this->loggerFactory);
for (apr_xml_elem* currentElement = factoryElement->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
}
}
}
/**
Used internally to parse the root logger element.
*/
void DOMConfiguratorContext::parseRoot(apr_xml_elem* rootElement)
{
LoggerPtr root = this->repository->getRootLogger();
parseChildrenOfLoggerElement(rootElement, root, true);
}
/**
Used internally to parse the children of a logger element.
*/
void DOMConfiguratorContext::parseChildrenOfLoggerElement(apr_xml_elem* loggerElement, const LoggerPtr& logger, bool isRoot)
{
PropertySetter propSetter(logger);
auto loggerName = this->repository->getRootLogger() == logger
? LogString(LOG4CXX_STR("root"))
: logger->getName();
AsyncAppenderPtr async;
auto lsAsynchronous = subst(getAttribute(loggerElement, ASYNCHRONOUS_ATTR));
if (!lsAsynchronous.empty() && OptionConverter::toBoolean(lsAsynchronous, true))
{
async = std::make_shared<AsyncAppender>();
async->setName(loggerName);
}
std::vector<AppenderPtr> newappenders;
for (apr_xml_elem* currentElement = loggerElement->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == APPENDER_REF_TAG)
{
if (auto appender = findAppenderByReference(currentElement))
{
if (log4cxx::cast<AsyncAppender>(appender)) // An explicitly configured AsyncAppender?
async.reset(); // Not required
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Adding ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appender->getName() + LOG4CXX_STR("]")
+ LOG4CXX_STR(" to logger [") + logger->getName() + LOG4CXX_STR("]"));
}
newappenders.push_back(appender);
if (async)
{
async->addAppender(appender);
parseFallbackAppender(currentElement, async->getName(), async, appender, async);
}
else if (auto appSkeleton = LOG4CXX_NS::cast<AppenderSkeleton>(appender))
parseFallbackAppender(currentElement, logger, appSkeleton);
}
}
else if (tagName == LEVEL_TAG)
{
parseLevel(currentElement, logger, isRoot);
}
else if (tagName == PRIORITY_TAG)
{
parseLevel(currentElement, logger, isRoot);
}
else if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(loggerElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
if (async && !newappenders.empty())
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Asynchronous logging for [")
+ logger->getName() + LOG4CXX_STR("] is on"));
}
logger->replaceAppenders({async});
this->appenderAdded = true;
}
else if (newappenders.empty())
logger->removeAllAppenders();
else
{
logger->replaceAppenders(newappenders);
this->appenderAdded = true;
}
propSetter.activate();
}
/**
Used internally to parse a layout element.
*/
LayoutPtr DOMConfiguratorContext::parseLayout(apr_xml_elem* layout_element)
{
LogString className(subst(getAttribute(layout_element, CLASS_ATTR)));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Desired ") + Layout::getStaticClass().getName()
+ LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
}
try
{
ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
LayoutPtr layout = LOG4CXX_NS::cast<Layout>(instance);
PropertySetter propSetter(layout);
for (apr_xml_elem* currentElement = layout_element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(layout_element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
return layout;
}
catch (Exception& oops)
{
LogLog::error(LOG4CXX_STR("Could not create ") + Layout::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
return 0;
}
}
/**
Used internally to parse a triggering policy
*/
ObjectPtr DOMConfiguratorContext::parseTriggeringPolicy(apr_xml_elem* policy_element)
{
LogString className = subst(getAttribute(policy_element, CLASS_ATTR));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Desired ") + TriggeringPolicy::getStaticClass().getName()
+ LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
}
try
{
ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
PropertySetter propSetter(instance);
for (apr_xml_elem* currentElement = policy_element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else if (tagName == FILTER_TAG)
{
auto filters = parseFilters(currentElement);
if (auto fbtp = LOG4CXX_NS::cast<FilterBasedTriggeringPolicy>(instance))
{
for (auto& item : filters)
{
fbtp->addFilter(item);
}
}
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(policy_element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
return instance;
}
catch (Exception& oops)
{
LogLog::error(LOG4CXX_STR("Could not create ") + TriggeringPolicy::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
return 0;
}
}
/**
Used internally to parse a triggering policy
*/
RollingPolicyPtr DOMConfiguratorContext::parseRollingPolicy(apr_xml_elem* policy_element)
{
LogString className = subst(getAttribute(policy_element, CLASS_ATTR));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Desired ") + RollingPolicy::getStaticClass().getName()
+ LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
}
try
{
ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
PropertySetter propSetter(instance);
for (apr_xml_elem* currentElement = policy_element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == PARAM_TAG)
{
setParameter(currentElement, propSetter);
}
else
{
LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
utf8Decoder->decode(currentElement->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] ");
utf8Decoder->decode(policy_element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR(" element");
LogLog::warn(msg);
}
}
propSetter.activate();
return LOG4CXX_NS::cast<RollingPolicy>(instance);
}
catch (Exception& oops)
{
LogLog::error(LOG4CXX_STR("Could not create ") + RollingPolicy::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
return 0;
}
}
/**
Used internally to parse a level element.
*/
void DOMConfiguratorContext::parseLevel(apr_xml_elem* element, const LoggerPtr& logger, bool isRoot)
{
LogString loggerName = logger->getName();
if (isRoot)
{
loggerName = LOG4CXX_STR("root");
}
LogString levelStr(subst(getAttribute(element, VALUE_ATTR)));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Setting [") + loggerName + LOG4CXX_STR("] level to [") + levelStr + LOG4CXX_STR("]"));
}
if (StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("INHERITED"), LOG4CXX_STR("inherited"))
|| StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("NULL"), LOG4CXX_STR("null")))
{
if (isRoot)
{
LogLog::error(LOG4CXX_STR("Root level cannot be ") + levelStr + LOG4CXX_STR(". Ignoring directive."));
}
else
{
logger->setLevel(0);
}
}
else
{
LogString className(subst(getAttribute(element, CLASS_ATTR)));
if (className.empty())
{
logger->setLevel(OptionConverter::toLevel(levelStr, Level::getDebug()));
}
else
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Desired ") + Level::getStaticClass().getName()
+ LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
}
try
{
logger->setLevel(dynamic_cast<const Level::LevelClass&>(Loader::loadClass(className)).toLevel(levelStr));
}
catch (Exception& oops)
{
LogLog::error(LOG4CXX_STR("Could not create ") + Level::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
return;
}
catch (...)
{
LogLog::error(LOG4CXX_STR("Could not create ") + Level::getStaticClass().getName() + LOG4CXX_STR(" sub-class")
+ LOG4CXX_STR(" from [") + className
+ LOG4CXX_STR("]"));
return;
}
}
}
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("[") + loggerName + LOG4CXX_STR("] level is ") +
logger->getEffectiveLevel()->toString());
}
}
void DOMConfiguratorContext::setParameter(apr_xml_elem* elem, PropertySetter& propSetter)
{
LogString name(subst(getAttribute(elem, NAME_ATTR)));
LogString value(subst(getAttribute(elem, VALUE_ATTR)));
value = subst(value);
propSetter.setProperty(name, value);
}
spi::ConfigurationStatus DOMConfigurator::doConfigure
( const File& filename
, const spi::LoggerRepositoryPtr& repository
)
{
DOMConfiguratorContext ctx( repository ? repository : LogManager::getLoggerRepository() );
apr_file_t* fd;
log4cxx_status_t rv = apr_file_open(&fd, filename.getAPRPath(), APR_READ, APR_OS_DEFAULT, ctx.p.getAPRPool());
if (rv != APR_SUCCESS)
{
LogLog::error(LOG4CXX_STR("Could not open configuration file [")
+ filename.getPath() + LOG4CXX_STR("]")
, IOException(rv));
return spi::ConfigurationStatus::NotConfigured;
}
else
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Loading configuration file [")
+ filename.getPath() + LOG4CXX_STR("]"));
}
apr_xml_parser* parser = NULL;
rv = apr_xml_parse_file(ctx.p.getAPRPool(), &parser, &ctx.doc, fd, 2000);
if (rv != APR_SUCCESS)
{
LogString reason;
if (parser)
{
char errbuf[2000];
apr_xml_parser_geterror(parser, errbuf, sizeof(errbuf));
LOG4CXX_DECODE_CHAR(lsErrbuf, std::string(errbuf));
reason.append(lsErrbuf);
}
else
{
char errbuf[2000];
apr_strerror(rv, errbuf, sizeof(errbuf));
LOG4CXX_DECODE_CHAR(lsErrbuf, std::string(errbuf));
reason.append(lsErrbuf);
}
LogLog::error(LOG4CXX_STR("Error parsing file [")
+ filename.getPath() + LOG4CXX_STR("]")
, RuntimeException(reason));
return spi::ConfigurationStatus::NotConfigured;
}
else
{
ctx.parse(ctx.doc->root);
}
}
auto result = spi::ConfigurationStatus::NotConfigured;
if (ctx.appenderAdded)
{
result = spi::ConfigurationStatus::Configured;
ctx.repository->setConfigured(true);
}
else
{
LogLog::warn(LOG4CXX_STR("[") + filename.getPath()
+ LOG4CXX_STR("] did not add an ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" to a logger"));
}
return result;
}
// Read configuration options from \c filename.
spi::ConfigurationStatus DOMConfigurator::configure(const File& filename)
{
return DOMConfigurator().doConfigure(filename, LogManager::getLoggerRepository());
}
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const File& file, long delay)
{
return FileWatchdog::startWatching(file, std::make_shared<DOMConfigurator>(), LogManager::getLoggerRepository(), delay);
}
void DOMConfiguratorContext::parse(apr_xml_elem* element)
{
std::string rootElementName(element->name);
if (rootElementName != CONFIGURATION_TAG)
{
if (rootElementName == OLD_CONFIGURATION_TAG)
{
//LogLog::warn(LOG4CXX_STR("The <")+String(OLD_CONFIGURATION_TAG)+
// LOG4CXX_STR("> element has been deprecated."));
//LogLog::warn(LOG4CXX_STR("Use the <")+String(CONFIGURATION_TAG)+
// LOG4CXX_STR("> element instead."));
}
else
{
LogString msg(LOG4CXX_STR("Root element ["));
utf8Decoder->decode(element->name, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("] is not [");
utf8Decoder->decode(CONFIGURATION_TAG, MAX_ATTRIBUTE_NAME_LEN, msg);
msg += LOG4CXX_STR("]");
LogLog::error(msg);
return;
}
}
LogString debugAttrib = subst(getAttribute(element, INTERNAL_DEBUG_ATTR));
// if the log4j.dtd is not specified in the XML file, then the
// "debug" attribute is returned as the empty string.
if (!debugAttrib.empty() && debugAttrib != LOG4CXX_STR("NULL"))
{
LogLog::setInternalDebugging(OptionConverter::toBoolean(debugAttrib, true));
}
LogString colorAttrib = subst(getAttribute(element, INTERNAL_COLOR_ATTR));
if (!colorAttrib.empty())
{
LogLog::setColorEnabled(OptionConverter::toBoolean(colorAttrib, true));
}
LogString thresholdStr = subst(getAttribute(element, THRESHOLD_ATTR));
if (!thresholdStr.empty() && thresholdStr != LOG4CXX_STR("NULL"))
{
this->repository->setThreshold(OptionConverter::toLevel(thresholdStr, Level::getAll()));
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Repository threshold =[")
+ this->repository->getThreshold()->toString()
+ LOG4CXX_STR("]"));
}
}
LogString threadSignalValue = subst(getAttribute(element, THREAD_CONFIG_ATTR));
if ( !threadSignalValue.empty() && threadSignalValue != LOG4CXX_STR("NULL") )
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("ThreadUtility configuration =[") + threadSignalValue + LOG4CXX_STR("]"));
}
if ( threadSignalValue == LOG4CXX_STR("NoConfiguration") )
{
helpers::ThreadUtility::configure( ThreadConfigurationType::NoConfiguration );
}
else if ( threadSignalValue == LOG4CXX_STR("BlockSignalsOnly") )
{
helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsOnly );
}
else if ( threadSignalValue == LOG4CXX_STR("NameThreadOnly") )
{
helpers::ThreadUtility::configure( ThreadConfigurationType::NameThreadOnly );
}
else if ( threadSignalValue == LOG4CXX_STR("BlockSignalsAndNameThread") )
{
helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsAndNameThread );
}
else
{
LogLog::warn(LOG4CXX_STR("threadConfiguration value [") + threadSignalValue + LOG4CXX_STR("]") + LOG4CXX_STR(" is not valid"));
}
}
apr_xml_elem* currentElement;
for (currentElement = element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == CATEGORY_FACTORY_TAG)
{
parseLoggerFactory(currentElement);
}
}
for (currentElement = element->first_child;
currentElement;
currentElement = currentElement->next)
{
std::string tagName(currentElement->name);
if (tagName == CATEGORY || tagName == LOGGER)
{
parseLogger(currentElement);
}
else if (tagName == ROOT_TAG)
{
parseRoot(currentElement);
}
}
}
LogString DOMConfiguratorContext::subst(const LogString& value)
{
try
{
return OptionConverter::substVars(value, this->props);
}
catch (IllegalArgumentException& e)
{
LogLog::warn(LOG4CXX_STR("Could not substitute variables using [") + value + LOG4CXX_STR("]"), e);
return value;
}
}
LogString DOMConfiguratorContext::getAttribute(apr_xml_elem* element, const std::string& attrName)
{
LogString attrValue;
for (apr_xml_attr* attr = element->attr;
attr;
attr = attr->next)
{
if (attrName == attr->name)
{
utf8Decoder->decode(attr->value, MAX_ATTRIBUTE_NAME_LEN, attrValue);
}
}
return attrValue;
}