Coverage Report

Created: 2025-10-10 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/hierarchy.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/hierarchy.h>
19
#if LOG4CXX_ABI_VERSION <= 15
20
#include <log4cxx/defaultloggerfactory.h>
21
#endif
22
#include <log4cxx/helpers/loglog.h>
23
#include <log4cxx/appender.h>
24
#include <log4cxx/helpers/stringhelper.h>
25
#include <log4cxx/spi/rootlogger.h>
26
#include <algorithm>
27
#include <map>
28
#include <mutex>
29
#include <vector>
30
31
32
using namespace LOG4CXX_NS;
33
using namespace LOG4CXX_NS::spi;
34
using namespace LOG4CXX_NS::helpers;
35
36
37
typedef std::map<LogString, LoggerPtr> LoggerMap;
38
typedef std::map<LogString, ProvisionNode> ProvisionNodeMap;
39
40
struct Hierarchy::HierarchyPrivate
41
{
42
  HierarchyPrivate()
43
1
    : configured(false)
44
1
    , emittedNoAppenderWarning(false)
45
1
    , emittedNoResourceBundleWarning(false)
46
1
    , thresholdInt(Level::ALL_INT)
47
1
  {
48
1
  }
49
50
  helpers::Pool pool;
51
  mutable std::recursive_mutex mutex;
52
  mutable std::recursive_mutex configuredMutex;
53
  bool configured;
54
  bool emittedNoAppenderWarning;
55
  bool emittedNoResourceBundleWarning;
56
  int thresholdInt;
57
58
  spi::HierarchyEventListenerList listeners;
59
  LoggerPtr root;
60
  LevelPtr threshold;
61
  LoggerMap loggers;
62
  ProvisionNodeMap provisionNodes;
63
64
  std::vector<AppenderPtr> allAppenders;
65
66
  mutable std::mutex listenerMutex;
67
68
  const char* alreadyTriedMethod{ NULL };
69
};
70
71
IMPLEMENT_LOG4CXX_OBJECT(Hierarchy)
72
73
Hierarchy::Hierarchy() :
74
1
  m_priv(std::make_unique<HierarchyPrivate>())
75
1
{
76
1
}
Unexecuted instantiation: log4cxx::Hierarchy::Hierarchy()
log4cxx::Hierarchy::Hierarchy()
Line
Count
Source
74
1
  m_priv(std::make_unique<HierarchyPrivate>())
75
1
{
76
1
}
77
78
Hierarchy::~Hierarchy()
79
1
{
80
1
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
81
1
  for (auto& item : m_priv->loggers)
82
0
  {
83
0
    if (auto& pLogger = item.second)
84
0
    {
85
0
      pLogger->removeHierarchy();
86
0
      pLogger->removeAllAppenders();
87
0
    }
88
0
  }
89
1
  if (m_priv->root)
90
0
  {
91
0
    m_priv->root->removeHierarchy();
92
0
    m_priv->root->removeAllAppenders();
93
0
  }
94
1
}
95
96
void Hierarchy::addHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener)
97
0
{
98
0
  std::lock_guard<std::mutex> lock(m_priv->listenerMutex);
99
100
0
  if (std::find(m_priv->listeners.begin(), m_priv->listeners.end(), listener) != m_priv->listeners.end())
101
0
  {
102
0
    LogLog::warn(LOG4CXX_STR("Ignoring attempt to add an existent listener."));
103
0
  }
104
0
  else
105
0
  {
106
0
    m_priv->listeners.push_back(listener);
107
0
  }
108
0
}
109
110
void Hierarchy::removeHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener)
111
0
{
112
0
  std::lock_guard<std::mutex> lock(m_priv->listenerMutex);
113
114
0
    auto found = std::find(m_priv->listeners.begin(), m_priv->listeners.end(), listener);
115
0
    if(found != m_priv->listeners.end()){
116
0
        m_priv->listeners.erase(found);
117
0
    }
118
0
}
119
120
void Hierarchy::clear()
121
0
{
122
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
123
0
  m_priv->loggers.clear();
124
0
}
125
126
void Hierarchy::emitNoAppenderWarning(const Logger* logger)
127
0
{
128
0
  bool emitWarning = false;
129
0
  {
130
0
    std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
131
0
    emitWarning = !m_priv->emittedNoAppenderWarning;
132
0
    m_priv->emittedNoAppenderWarning = true;
133
0
  }
134
135
  // No appender in hierarchy, warn user only once.
136
0
  if (emitWarning)
137
0
  {
138
0
    LogLog::warn(((LogString) LOG4CXX_STR("No appender could be found for logger ("))
139
0
      + logger->getName() + LOG4CXX_STR(")."));
140
0
    LogLog::warn(LOG4CXX_STR("Please initialize the log4cxx system properly."));
141
0
  }
142
0
}
143
144
145
LoggerPtr Hierarchy::exists(const LogString& name)
146
0
{
147
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
148
149
0
  LoggerPtr logger;
150
0
  LoggerMap::iterator it = m_priv->loggers.find(name);
151
152
0
  if (it != m_priv->loggers.end())
153
0
  {
154
0
    logger = it->second;
155
0
  }
156
157
158
0
  return logger;
159
0
}
160
161
void Hierarchy::setThreshold(const LevelPtr& l)
162
249
{
163
249
  if (l != 0)
164
249
  {
165
249
    std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
166
249
    setThresholdInternal(l);
167
249
  }
168
249
}
169
170
void Hierarchy::setThreshold(const LogString& levelStr)
171
0
{
172
0
  LevelPtr l(Level::toLevelLS(levelStr, 0));
173
174
0
  if (l != 0)
175
0
  {
176
0
    setThreshold(l);
177
0
  }
178
0
  else
179
0
  {
180
0
    LogLog::warn(((LogString) LOG4CXX_STR("No level could be found named \""))
181
0
      + levelStr + LOG4CXX_STR("\"."));
182
0
  }
183
0
}
184
185
void Hierarchy::setThresholdInternal(const LevelPtr& l)
186
249
{
187
249
  m_priv->thresholdInt = l->toInt();
188
249
  m_priv->threshold = l;
189
190
249
  if (m_priv->thresholdInt != Level::ALL_INT)
191
4
  {
192
4
    m_priv->configured = true;
193
4
  }
194
249
}
195
196
void Hierarchy::fireAddAppenderEvent(const Logger* logger, const Appender* appender)
197
0
{
198
0
  setConfigured(true);
199
0
  HierarchyEventListenerList clonedList;
200
0
  {
201
0
    std::lock_guard<std::mutex> lock(m_priv->listenerMutex);
202
0
    clonedList = m_priv->listeners;
203
0
  }
204
205
0
  for (auto& item : clonedList)
206
0
    item->addAppenderEvent(logger, appender);
207
0
}
208
209
void Hierarchy::fireRemoveAppenderEvent(const Logger* logger, const Appender* appender)
210
211
0
{
212
0
  HierarchyEventListenerList clonedList;
213
0
  {
214
0
    std::lock_guard<std::mutex> lock(m_priv->listenerMutex);
215
0
    clonedList = m_priv->listeners;
216
0
  }
217
0
  for (auto& item : clonedList)
218
0
    item->removeAppenderEvent(logger, appender);
219
0
}
220
221
LevelPtr Hierarchy::getThreshold() const
222
248
{
223
248
  return m_priv->threshold ? m_priv->threshold : Level::getAll();
224
248
}
225
226
LoggerPtr Hierarchy::getLogger(const LogString& name)
227
0
{
228
0
#if LOG4CXX_ABI_VERSION <= 15
229
0
  static WideLife<spi::LoggerFactoryPtr> defaultFactory = std::make_shared<DefaultLoggerFactory>();
230
#else
231
  static WideLife<spi::LoggerFactoryPtr> defaultFactory = std::make_shared<LoggerFactory>();
232
#endif
233
0
  return getLogger(name, defaultFactory);
234
0
}
235
236
LoggerPtr Hierarchy::getLogger(const LogString& name,
237
  const spi::LoggerFactoryPtr& factory)
238
0
{
239
0
  auto root = getRootLogger();
240
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
241
242
0
  LoggerMap::iterator it = m_priv->loggers.find(name);
243
0
  LoggerPtr result;
244
245
0
  if (it != m_priv->loggers.end())
246
0
  {
247
0
    result = it->second;
248
0
  }
249
0
  if (!result && factory)
250
0
  {
251
0
#if LOG4CXX_ABI_VERSION <= 15
252
0
    LoggerPtr logger(factory->makeNewLoggerInstance(m_priv->pool, name));
253
#else
254
    LoggerPtr logger(factory->makeNewLoggerInstance(name));
255
#endif
256
0
    logger->setHierarchy(this);
257
0
    m_priv->loggers.insert(LoggerMap::value_type(name, logger));
258
259
0
    ProvisionNodeMap::iterator it2 = m_priv->provisionNodes.find(name);
260
261
0
    if (it2 != m_priv->provisionNodes.end())
262
0
    {
263
0
      updateChildren(it2->second, logger);
264
0
      m_priv->provisionNodes.erase(it2);
265
0
    }
266
267
0
    updateParents(logger, root);
268
0
    result = logger;
269
0
  }
270
0
  return result;
271
272
0
}
273
274
LoggerList Hierarchy::getCurrentLoggers() const
275
0
{
276
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
277
278
0
  LoggerList v;
279
0
  for (auto& item : m_priv->loggers)
280
0
  {
281
0
    if (auto pLogger = item.second)
282
0
      v.push_back(pLogger);
283
0
  }
284
0
  return v;
285
0
}
286
287
LoggerPtr Hierarchy::getRootLogger() const
288
0
{
289
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
290
0
  if (!m_priv->root)
291
0
  {
292
0
    m_priv->root = std::make_shared<RootLogger>(Level::getDebug());
293
0
    m_priv->root->setHierarchy(const_cast<Hierarchy*>(this));
294
0
  }
295
296
0
  return m_priv->root;
297
0
}
298
299
bool Hierarchy::isDisabled(int level) const
300
0
{
301
0
  return m_priv->thresholdInt > level;
302
0
}
303
304
void Hierarchy::ensureIsConfigured(std::function<void()> configurator)
305
0
{
306
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->configuredMutex);
307
0
  if (!m_priv->configured && m_priv->alreadyTriedMethod != configurator.target_type().name())
308
0
  {
309
0
    configurator();
310
0
    m_priv->alreadyTriedMethod = configurator.target_type().name();
311
0
  }
312
0
}
313
314
void Hierarchy::resetConfiguration()
315
0
{
316
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
317
318
0
  if (m_priv->root)
319
0
  {
320
0
    m_priv->root->setLevel(Level::getDebug());
321
0
    m_priv->root->setResourceBundle(0);
322
0
  }
323
0
  setThresholdInternal(Level::getAll());
324
325
0
  shutdownInternal();
326
327
0
  for (auto& item : m_priv->loggers)
328
0
  {
329
0
    if (auto pLogger = item.second)
330
0
    {
331
0
      pLogger->setLevel(0);
332
0
      pLogger->setAdditivity(true);
333
0
      pLogger->setResourceBundle(0);
334
0
    }
335
0
  }
336
0
}
337
338
void Hierarchy::shutdown()
339
0
{
340
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
341
342
0
  shutdownInternal();
343
0
}
344
345
void Hierarchy::shutdownInternal()
346
0
{
347
0
  m_priv->configured = false;
348
0
  m_priv->alreadyTriedMethod = NULL;
349
350
  // begin by closing nested appenders
351
0
  if (m_priv->root)
352
0
    m_priv->root->closeNestedAppenders();
353
354
0
  for (auto& item : m_priv->loggers)
355
0
  {
356
0
    if (auto pLogger = item.second)
357
0
      pLogger->closeNestedAppenders();
358
0
  }
359
360
  // then, remove all appenders
361
0
  if (m_priv->root)
362
0
    m_priv->root->removeAllAppenders();
363
364
0
  for (auto& item : m_priv->loggers)
365
0
  {
366
0
    if (auto pLogger = item.second)
367
0
      pLogger->removeAllAppenders();
368
0
  }
369
0
}
370
371
void Hierarchy::updateParents(const LoggerPtr& logger, const LoggerPtr& root)
372
0
{
373
0
  const LogString name(logger->getName());
374
0
  size_t length = name.size();
375
0
  bool parentFound = false;
376
377
378
  // if name = "w.x.y.z", loop through "w.x.y", "w.x" and "w", but not "w.x.y.z"
379
0
  for (size_t i = name.find_last_of(0x2E /* '.' */, length - 1);
380
0
    (i != LogString::npos) && (i != 0);
381
0
    i = name.find_last_of(0x2E /* '.' */, i - 1))
382
0
  {
383
0
    LogString substr = name.substr(0, i);
384
385
0
    LoggerMap::iterator it = m_priv->loggers.find(substr);
386
387
0
    if (it != m_priv->loggers.end())
388
0
    {
389
0
      parentFound = true;
390
0
      logger->setParent( it->second );
391
0
      break; // no need to update the ancestors of the closest ancestor
392
0
    }
393
0
    else
394
0
    {
395
0
      ProvisionNodeMap::iterator it2 = m_priv->provisionNodes.find(substr);
396
397
0
      if (it2 != m_priv->provisionNodes.end())
398
0
      {
399
0
        it2->second.push_back(logger);
400
0
      }
401
0
      else
402
0
      {
403
0
        ProvisionNode node(1, logger);
404
0
        m_priv->provisionNodes.insert(
405
0
          ProvisionNodeMap::value_type(substr, node));
406
0
      }
407
0
    }
408
0
  }
409
410
  // If we could not find any existing parents, then link with root.
411
0
  if (!parentFound)
412
0
  {
413
0
    logger->setParent( root );
414
0
  }
415
0
}
416
417
void Hierarchy::updateChildren(ProvisionNode& pn, const LoggerPtr& logger)
418
0
{
419
0
  for (auto& l : pn)
420
0
  {
421
    // Unless this child already points to a correct (lower) parent,
422
    // make logger.parent point to l.parent and l.parent to logger.
423
0
    if (!StringHelper::startsWith(l->getParent()->getName(), logger->getName()))
424
0
    {
425
0
      logger->setParent( l->getParent() );
426
0
      l->setParent( logger );
427
0
    }
428
0
  }
429
    
430
0
}
431
432
void Hierarchy::updateChildren(const Logger* parent)
433
0
{
434
0
  for (auto& item : m_priv->loggers)
435
0
  {
436
0
    for (auto l = item.second; l; l = l->getParent())
437
0
    {
438
0
      if (l->getParent().get() == parent)
439
0
      {
440
0
        item.second->updateThreshold();
441
0
        break;
442
0
      }
443
0
    }
444
0
  }
445
0
}
446
447
void Hierarchy::setConfigured(bool newValue)
448
0
{
449
0
  std::unique_lock<std::recursive_mutex> lock(m_priv->configuredMutex, std::try_to_lock);
450
0
  if (lock.owns_lock()) // Not being auto-configured?
451
0
    m_priv->configured = newValue;
452
0
}
453
454
bool Hierarchy::isConfigured()
455
0
{
456
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->configuredMutex); // Blocks while auto-configuration is active
457
0
  return m_priv->configured;
458
0
}
459
460
HierarchyPtr Hierarchy::create()
461
1
{
462
1
  HierarchyPtr ret(new Hierarchy);
463
1
  return ret;
464
1
}
465
466
void Hierarchy::clearAppenders()
467
0
{
468
0
  m_priv->allAppenders.clear();
469
0
}
470
471
void Hierarchy::addAppender(AppenderPtr appender)
472
0
{
473
0
  m_priv->allAppenders.push_back(appender);
474
0
}
475
476
bool Hierarchy::removeLogger(const LogString& name, bool ifNotUsed)
477
0
{
478
0
  auto parentRefCount = [this](const LoggerPtr& child) -> int
479
0
  {
480
0
    int result = 0;
481
0
    for (auto& node : m_priv->provisionNodes)
482
0
    {
483
0
      if (node.second.end() != std::find(node.second.begin(), node.second.end(), child))
484
0
        ++result;
485
0
    }
486
0
    return result;
487
0
  };
488
0
  bool result = false;
489
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
490
0
  auto it = m_priv->loggers.find(name);
491
0
  if (it == m_priv->loggers.end())
492
0
    ;
493
0
  else if (ifNotUsed && 1 + parentRefCount(it->second) < it->second.use_count())
494
0
    ;
495
0
  else
496
0
  {
497
0
    for (auto& node : m_priv->provisionNodes)
498
0
    {
499
0
      for (size_t i = node.second.size(); 0 < i; )
500
0
      {
501
0
        if (node.second[--i] == it->second)
502
0
          node.second.erase(node.second.begin() + i);
503
0
      }
504
0
    }
505
0
    m_priv->loggers.erase(it);
506
0
    result = true;
507
0
  }
508
0
  return result;
509
0
}