blob: 7f78036982582ac43477ddcadc04a94c429398e4 [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/spi/loggerfactory.h>
#include <log4cxx/hierarchy.h>
#include <log4cxx/defaultloggerfactory.h>
#include <log4cxx/logger.h>
#include <log4cxx/spi/hierarchyeventlistener.h>
#include <log4cxx/level.h>
#include <algorithm>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/appender.h>
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/stringhelper.h>
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include <log4cxx/spi/rootlogger.h>
#include <mutex>
#include "assert.h"
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::spi;
using namespace LOG4CXX_NS::helpers;
typedef std::map<LogString, LoggerPtr> LoggerMap;
typedef std::map<LogString, ProvisionNode> ProvisionNodeMap;
struct Hierarchy::HierarchyPrivate
{
HierarchyPrivate()
: configured(false)
, emittedNoAppenderWarning(false)
, emittedNoResourceBundleWarning(false)
, thresholdInt(Level::ALL_INT)
{
}
helpers::Pool pool;
mutable std::recursive_mutex mutex;
mutable std::mutex configuredMutex;
bool configured;
bool emittedNoAppenderWarning;
bool emittedNoResourceBundleWarning;
int thresholdInt;
spi::HierarchyEventListenerList listeners;
LoggerPtr root;
LevelPtr threshold;
LoggerMap loggers;
ProvisionNodeMap provisionNodes;
std::vector<AppenderPtr> allAppenders;
};
IMPLEMENT_LOG4CXX_OBJECT(Hierarchy)
Hierarchy::Hierarchy() :
m_priv(std::make_unique<HierarchyPrivate>())
{
}
Hierarchy::~Hierarchy()
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
for (auto& item : m_priv->loggers)
{
if (auto& pLogger = item.second)
{
pLogger->removeHierarchy();
pLogger->removeAllAppenders();
}
}
if (m_priv->root)
{
m_priv->root->removeHierarchy();
m_priv->root->removeAllAppenders();
}
}
void Hierarchy::addHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener)
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
if (std::find(m_priv->listeners.begin(), m_priv->listeners.end(), listener) != m_priv->listeners.end())
{
LogLog::warn(LOG4CXX_STR("Ignoring attempt to add an existent listener."));
}
else
{
m_priv->listeners.push_back(listener);
}
}
void Hierarchy::removeHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener)
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
auto found = std::find(m_priv->listeners.begin(), m_priv->listeners.end(), listener);
if(found != m_priv->listeners.end()){
m_priv->listeners.erase(found);
}
}
void Hierarchy::clear()
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
m_priv->loggers.clear();
}
void Hierarchy::emitNoAppenderWarning(const Logger* logger)
{
bool emitWarning = false;
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
emitWarning = !m_priv->emittedNoAppenderWarning;
m_priv->emittedNoAppenderWarning = true;
}
// No appender in hierarchy, warn user only once.
if (emitWarning)
{
LogLog::warn(((LogString) LOG4CXX_STR("No appender could be found for logger ("))
+ logger->getName() + LOG4CXX_STR(")."));
LogLog::warn(LOG4CXX_STR("Please initialize the log4cxx system properly."));
}
}
LoggerPtr Hierarchy::exists(const LogString& name)
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
LoggerPtr logger;
LoggerMap::iterator it = m_priv->loggers.find(name);
if (it != m_priv->loggers.end())
{
logger = it->second;
}
return logger;
}
void Hierarchy::setThreshold(const LevelPtr& l)
{
if (l != 0)
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
setThresholdInternal(l);
}
}
void Hierarchy::setThreshold(const LogString& levelStr)
{
LevelPtr l(Level::toLevelLS(levelStr, 0));
if (l != 0)
{
setThreshold(l);
}
else
{
LogLog::warn(((LogString) LOG4CXX_STR("No level could be found named \""))
+ levelStr + LOG4CXX_STR("\"."));
}
}
void Hierarchy::setThresholdInternal(const LevelPtr& l)
{
m_priv->thresholdInt = l->toInt();
m_priv->threshold = l;
if (m_priv->thresholdInt != Level::ALL_INT)
{
m_priv->configured = true;
}
}
void Hierarchy::fireAddAppenderEvent(const Logger* logger, const Appender* appender)
{
setConfigured(true);
HierarchyEventListenerList clonedList;
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
clonedList = m_priv->listeners;
}
for (auto& item : clonedList)
item->addAppenderEvent(logger, appender);
}
void Hierarchy::fireRemoveAppenderEvent(const Logger* logger, const Appender* appender)
{
HierarchyEventListenerList clonedList;
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
clonedList = m_priv->listeners;
}
for (auto& item : clonedList)
item->removeAppenderEvent(logger, appender);
}
LevelPtr Hierarchy::getThreshold() const
{
return m_priv->threshold ? m_priv->threshold : Level::getAll();
}
LoggerPtr Hierarchy::getLogger(const LogString& name)
{
static WideLife<spi::LoggerFactoryPtr> defaultFactory = std::make_shared<DefaultLoggerFactory>();
return getLogger(name, defaultFactory);
}
LoggerPtr Hierarchy::getLogger(const LogString& name,
const spi::LoggerFactoryPtr& factory)
{
auto root = getRootLogger();
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
LoggerMap::iterator it = m_priv->loggers.find(name);
LoggerPtr result;
if (it != m_priv->loggers.end())
{
result = it->second;
}
if (!result && factory)
{
LoggerPtr logger(factory->makeNewLoggerInstance(m_priv->pool, name));
logger->setHierarchy(this);
m_priv->loggers.insert(LoggerMap::value_type(name, logger));
ProvisionNodeMap::iterator it2 = m_priv->provisionNodes.find(name);
if (it2 != m_priv->provisionNodes.end())
{
updateChildren(it2->second, logger);
m_priv->provisionNodes.erase(it2);
}
updateParents(logger, root);
result = logger;
}
return result;
}
LoggerList Hierarchy::getCurrentLoggers() const
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
LoggerList v;
for (auto& item : m_priv->loggers)
{
if (auto pLogger = item.second)
v.push_back(pLogger);
}
return v;
}
LoggerPtr Hierarchy::getRootLogger() const
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
if (!m_priv->root)
{
m_priv->root = std::make_shared<RootLogger>(m_priv->pool, Level::getDebug());
m_priv->root->setHierarchy(const_cast<Hierarchy*>(this));
}
return m_priv->root;
}
bool Hierarchy::isDisabled(int level) const
{
return m_priv->thresholdInt > level;
}
void Hierarchy::ensureIsConfigured(std::function<void()> configurator)
{
std::lock_guard<std::mutex> lock(m_priv->configuredMutex);
if (!m_priv->configured)
{
configurator();
m_priv->configured = true;
}
}
void Hierarchy::resetConfiguration()
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
if (m_priv->root)
{
m_priv->root->setLevel(Level::getDebug());
m_priv->root->setResourceBundle(0);
}
setThresholdInternal(Level::getAll());
shutdownInternal();
for (auto& item : m_priv->loggers)
{
if (auto pLogger = item.second)
{
pLogger->setLevel(0);
pLogger->setAdditivity(true);
pLogger->setResourceBundle(0);
}
}
}
void Hierarchy::shutdown()
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
shutdownInternal();
}
void Hierarchy::shutdownInternal()
{
m_priv->configured = false;
// begin by closing nested appenders
if (m_priv->root)
m_priv->root->closeNestedAppenders();
for (auto& item : m_priv->loggers)
{
if (auto pLogger = item.second)
pLogger->closeNestedAppenders();
}
// then, remove all appenders
if (m_priv->root)
m_priv->root->removeAllAppenders();
for (auto& item : m_priv->loggers)
{
if (auto pLogger = item.second)
pLogger->removeAllAppenders();
}
}
void Hierarchy::updateParents(const LoggerPtr& logger, const LoggerPtr& root)
{
const LogString name(logger->getName());
size_t length = name.size();
bool parentFound = false;
// if name = "w.x.y.z", loop through "w.x.y", "w.x" and "w", but not "w.x.y.z"
for (size_t i = name.find_last_of(0x2E /* '.' */, length - 1);
(i != LogString::npos) && (i != 0);
i = name.find_last_of(0x2E /* '.' */, i - 1))
{
LogString substr = name.substr(0, i);
LoggerMap::iterator it = m_priv->loggers.find(substr);
if (it != m_priv->loggers.end())
{
parentFound = true;
logger->setParent( it->second );
break; // no need to update the ancestors of the closest ancestor
}
else
{
ProvisionNodeMap::iterator it2 = m_priv->provisionNodes.find(substr);
if (it2 != m_priv->provisionNodes.end())
{
it2->second.push_back(logger);
}
else
{
ProvisionNode node(1, logger);
m_priv->provisionNodes.insert(
ProvisionNodeMap::value_type(substr, node));
}
}
}
// If we could not find any existing parents, then link with root.
if (!parentFound)
{
logger->setParent( root );
}
}
void Hierarchy::updateChildren(ProvisionNode& pn, const LoggerPtr& logger)
{
for (auto& l : pn)
{
// Unless this child already points to a correct (lower) parent,
// make logger.parent point to l.parent and l.parent to logger.
if (!StringHelper::startsWith(l->getParent()->getName(), logger->getName()))
{
logger->setParent( l->getParent() );
l->setParent( logger );
}
}
}
void Hierarchy::updateChildren(const Logger* parent)
{
for (auto& item : m_priv->loggers)
{
for (auto l = item.second; l; l = l->getParent())
{
if (l->getParent().get() == parent)
{
item.second->updateThreshold();
break;
}
}
}
}
void Hierarchy::setConfigured(bool newValue)
{
std::unique_lock<std::mutex> lock(m_priv->configuredMutex, std::try_to_lock);
if (lock.owns_lock()) // Not being auto-configured?
m_priv->configured = newValue;
}
bool Hierarchy::isConfigured()
{
std::lock_guard<std::mutex> lock(m_priv->configuredMutex); // Blocks while auto-configuration is active
return m_priv->configured;
}
HierarchyPtr Hierarchy::create()
{
HierarchyPtr ret(new Hierarchy);
return ret;
}
void Hierarchy::clearAppenders()
{
m_priv->allAppenders.clear();
}
void Hierarchy::addAppender(AppenderPtr appender)
{
m_priv->allAppenders.push_back(appender);
}
bool Hierarchy::removeLogger(const LogString& name, bool ifNotUsed)
{
auto parentRefCount = [this](const LoggerPtr& child) -> int
{
int result = 0;
for (auto& node : m_priv->provisionNodes)
{
if (node.second.end() != std::find(node.second.begin(), node.second.end(), child))
++result;
}
return result;
};
bool result = false;
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
auto it = m_priv->loggers.find(name);
if (it == m_priv->loggers.end())
;
else if (ifNotUsed && 1 + parentRefCount(it->second) < it->second.use_count())
;
else
{
for (auto& node : m_priv->provisionNodes)
{
for (size_t i = node.second.size(); 0 < i; )
{
if (node.second[--i] == it->second)
node.second.erase(node.second.begin() + i);
}
}
m_priv->loggers.erase(it);
result = true;
}
return result;
}