Coverage Report

Created: 2025-08-29 06:29

/src/logging-log4cxx/src/main/cpp/propertyconfigurator.cpp
Line
Count
Source (jump to first uncovered line)
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/logstring.h>
19
#include <log4cxx/propertyconfigurator.h>
20
#include <log4cxx/helpers/properties.h>
21
#include <log4cxx/helpers/loglog.h>
22
#include <log4cxx/helpers/exception.h>
23
#include <log4cxx/logmanager.h>
24
#include <log4cxx/helpers/optionconverter.h>
25
#include <log4cxx/level.h>
26
#if LOG4CXX_ABI_VERSION <= 15
27
#include <log4cxx/defaultloggerfactory.h>
28
#else
29
#include <log4cxx/spi/loggerfactory.h>
30
#endif
31
#include <log4cxx/helpers/stringhelper.h>
32
#include <log4cxx/layout.h>
33
#include <log4cxx/config/propertysetter.h>
34
#include <log4cxx/helpers/stringtokenizer.h>
35
#include <log4cxx/helpers/transcoder.h>
36
#include <log4cxx/helpers/fileinputstream.h>
37
#include <log4cxx/helpers/loader.h>
38
#include <log4cxx/helpers/threadutility.h>
39
#include <log4cxx/helpers/singletonholder.h>
40
#include <log4cxx/rolling/rollingfileappender.h>
41
42
#define LOG4CXX 1
43
#include <log4cxx/helpers/aprinitializer.h>
44
45
using namespace LOG4CXX_NS;
46
using namespace LOG4CXX_NS::spi;
47
using namespace LOG4CXX_NS::helpers;
48
using namespace LOG4CXX_NS::config;
49
using namespace LOG4CXX_NS::rolling;
50
51
#include <log4cxx/helpers/filewatchdog.h>
52
namespace LOG4CXX_NS
53
{
54
class PropertyWatchdog  : public FileWatchdog
55
{
56
  public:
57
0
    PropertyWatchdog(const File& filename) : FileWatchdog(filename)
58
0
    {
59
0
    }
60
61
    /**
62
    Call PropertyConfigurator#doConfigure(const String& configFileName,
63
    const spi::LoggerRepositoryPtr& hierarchy) with the
64
    <code>filename</code> to reconfigure log4cxx.
65
    */
66
    void doOnChange()
67
0
    {
68
0
      PropertyConfigurator().doConfigure(file(),
69
0
        LogManager::getLoggerRepository());
70
0
    }
71
72
    static void startWatching(const File& filename, long delay)
73
0
    {
74
0
      using WatchdogHolder = SingletonHolder<PropertyWatchdog>;
75
0
      auto pHolder = APRInitializer::getOrAddUnique<WatchdogHolder>
76
0
        ( [&filename]() -> ObjectPtr
77
0
          { return std::make_shared<WatchdogHolder>(filename); }
78
0
        );
79
0
      auto& pdog = pHolder->value();
80
0
      pdog.setFile(filename);
81
0
      pdog.setDelay(0 < delay ? delay : FileWatchdog::DEFAULT_DELAY);
82
0
      pdog.start();
83
0
    }
84
};
85
}
86
87
IMPLEMENT_LOG4CXX_OBJECT(PropertyConfigurator)
88
89
PropertyConfigurator::PropertyConfigurator()
90
0
  : registry(new std::map<LogString, AppenderPtr>())
91
#if LOG4CXX_ABI_VERSION <= 15
92
0
  , loggerFactory(new DefaultLoggerFactory())
93
#else
94
  , loggerFactory(new LoggerFactory())
95
#endif
96
0
{
97
0
}
Unexecuted instantiation: log4cxx::PropertyConfigurator::PropertyConfigurator()
Unexecuted instantiation: log4cxx::PropertyConfigurator::PropertyConfigurator()
98
99
PropertyConfigurator::~PropertyConfigurator()
100
0
{
101
0
  delete registry;
102
0
}
103
104
spi::ConfigurationStatus PropertyConfigurator::doConfigure
105
  ( const File&                     configFileName
106
#if LOG4CXX_ABI_VERSION <= 15
107
  , spi::LoggerRepositoryPtr        repository
108
#else
109
  , const spi::LoggerRepositoryPtr& repository
110
#endif
111
  )
112
0
{
113
0
  auto hierarchy = repository ? repository : LogManager::getLoggerRepository();
114
0
  hierarchy->setConfigured(true);
115
116
0
  Properties props = Configurator::properties();
117
118
0
  try
119
0
  {
120
0
    InputStreamPtr inputStream = InputStreamPtr( new FileInputStream(configFileName) );
121
0
    props.load(inputStream);
122
0
  }
123
0
  catch (const IOException& ex)
124
0
  {
125
0
    LOG4CXX_DECODE_CHAR(lsMsg, ex.what());
126
0
    LogLog::error(((LogString) LOG4CXX_STR("Could not read configuration file ["))
127
0
      + configFileName.getPath() + LOG4CXX_STR("]: ") + lsMsg);
128
0
    return spi::ConfigurationStatus::NotConfigured;
129
0
  }
130
131
0
  try
132
0
  {
133
0
    if (LogLog::isDebugEnabled())
134
0
    {
135
0
      LogString debugMsg = LOG4CXX_STR("Loading configuration file [")
136
0
          + configFileName.getPath() + LOG4CXX_STR("]");
137
0
      LogLog::debug(debugMsg);
138
0
    }
139
0
    return doConfigure(props, hierarchy);
140
0
  }
141
0
  catch (const std::exception& ex)
142
0
  {
143
0
    LogLog::error(((LogString) LOG4CXX_STR("Could not parse configuration file ["))
144
0
      + configFileName.getPath() + LOG4CXX_STR("]: "), ex);
145
0
  }
146
147
0
  return spi::ConfigurationStatus::NotConfigured;
148
0
}
149
150
spi::ConfigurationStatus PropertyConfigurator::configure(const File& configFilename)
151
0
{
152
0
  return PropertyConfigurator().doConfigure(configFilename, LogManager::getLoggerRepository());
153
0
}
154
155
spi::ConfigurationStatus PropertyConfigurator::configure(helpers::Properties& properties)
156
0
{
157
0
  return PropertyConfigurator().doConfigure(properties, LogManager::getLoggerRepository());
158
0
}
159
160
#if LOG4CXX_ABI_VERSION <= 15
161
spi::ConfigurationStatus PropertyConfigurator::configureAndWatch(const File& configFilename)
162
0
{
163
0
  return configureAndWatch(configFilename, FileWatchdog::DEFAULT_DELAY);
164
0
}
165
#endif
166
167
spi::ConfigurationStatus PropertyConfigurator::configureAndWatch(
168
  const File& configFilename, long delay)
169
0
{
170
0
  spi::ConfigurationStatus stat = PropertyConfigurator().doConfigure(configFilename, LogManager::getLoggerRepository());
171
0
  PropertyWatchdog::startWatching(configFilename, delay);
172
0
  return stat;
173
0
}
174
175
spi::ConfigurationStatus PropertyConfigurator::doConfigure(helpers::Properties& properties,
176
  spi::LoggerRepositoryPtr hierarchy)
177
0
{
178
0
  hierarchy->setConfigured(true);
179
180
0
  LogString value(properties.getProperty(LOG4CXX_STR("log4j.debug")));
181
182
0
  if (!value.empty())
183
0
  {
184
0
    LogLog::setInternalDebugging(OptionConverter::toBoolean(value, true));
185
0
  }
186
187
0
  LogString thresholdStr =
188
0
    OptionConverter::findAndSubst(LOG4CXX_STR("log4j.threshold"), properties);
189
190
0
  if (!thresholdStr.empty())
191
0
  {
192
0
    hierarchy->setThreshold(OptionConverter::toLevel(thresholdStr, Level::getAll()));
193
0
    if (LogLog::isDebugEnabled())
194
0
    {
195
0
      LogLog::debug(LOG4CXX_STR("Repository threshold =[")
196
0
        + hierarchy->getThreshold()->toString()
197
0
        + LOG4CXX_STR("]"));
198
0
    }
199
0
  }
200
201
0
  LogString threadConfigurationValue(properties.getProperty(LOG4CXX_STR("log4j.threadConfiguration")));
202
203
0
  if ( threadConfigurationValue == LOG4CXX_STR("NoConfiguration") )
204
0
  {
205
0
    helpers::ThreadUtility::configure( ThreadConfigurationType::NoConfiguration );
206
0
  }
207
0
  else if ( threadConfigurationValue == LOG4CXX_STR("BlockSignalsOnly") )
208
0
  {
209
0
    helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsOnly );
210
0
  }
211
0
  else if ( threadConfigurationValue == LOG4CXX_STR("NameThreadOnly") )
212
0
  {
213
0
    helpers::ThreadUtility::configure( ThreadConfigurationType::NameThreadOnly );
214
0
  }
215
0
  else if ( threadConfigurationValue == LOG4CXX_STR("BlockSignalsAndNameThread") )
216
0
  {
217
0
    helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsAndNameThread );
218
0
  }
219
220
0
  configureRootLogger(properties, hierarchy);
221
0
  configureLoggerFactory(properties);
222
0
  parseCatsAndRenderers(properties, hierarchy);
223
224
0
  LogLog::debug(LOG4CXX_STR("Finished configuring."));
225
226
  // We don't want to hold references to appenders preventing their
227
  // destruction.
228
0
  registry->clear();
229
230
0
  return spi::ConfigurationStatus::Configured;
231
0
}
232
233
void PropertyConfigurator::configureLoggerFactory(helpers::Properties& props)
234
0
{
235
0
  LogString factoryClassName =
236
0
    OptionConverter::findAndSubst(LOG4CXX_STR("log4j.loggerFactory"), props);
237
238
0
  if (!factoryClassName.empty())
239
0
  {
240
0
    auto instance = OptionConverter::instantiateByClassName
241
0
      ( StringHelper::trim(factoryClassName)
242
0
      , LoggerFactory::getStaticClass()
243
0
#if LOG4CXX_ABI_VERSION <= 15
244
0
      , std::make_shared<DefaultLoggerFactory>()
245
#else
246
      , std::make_shared<LoggerFactory>()
247
#endif
248
0
      );
249
250
0
    loggerFactory = LOG4CXX_NS::cast<LoggerFactory>( instance );
251
0
    Pool p;
252
0
    PropertySetter::setProperties(loggerFactory, props, LOG4CXX_STR("log4j.factory."), p);
253
0
  }
254
0
}
255
256
void PropertyConfigurator::configureRootLogger(helpers::Properties& props,
257
  spi::LoggerRepositoryPtr& hierarchy)
258
0
{
259
0
  LogString effectivePrefix(LOG4CXX_STR("log4j.rootLogger"));
260
0
  LogString value = OptionConverter::findAndSubst(effectivePrefix, props);
261
262
0
  if (value.empty())
263
0
  {
264
0
    effectivePrefix = LOG4CXX_STR("log4j.rootCategory");
265
0
    value = OptionConverter::findAndSubst(effectivePrefix, props);
266
0
  }
267
268
0
  if (value.empty())
269
0
  {
270
0
    LogLog::debug(LOG4CXX_STR("Neither 'log4j.rootLogger' or 'log4j.rootCategory' found. Is this OK?"));
271
0
  }
272
0
  else
273
0
  {
274
0
    LoggerPtr root = hierarchy->getRootLogger();
275
0
    parseLogger(props, root, effectivePrefix, LOG4CXX_STR("root"), value, true);
276
0
  }
277
0
}
278
279
void PropertyConfigurator::parseCatsAndRenderers(helpers::Properties& props,
280
  spi::LoggerRepositoryPtr& hierarchy)
281
0
{
282
0
  for (auto key : props.propertyNames())
283
0
  {
284
0
    auto categoryFound = (0 == key.find(LOG4CXX_STR("log4j.category.")));
285
0
    if (categoryFound || 0 == key.find(LOG4CXX_STR("log4j.logger.")))
286
0
    {
287
0
      auto prefixLength =
288
0
        ( categoryFound
289
0
        ? LogString(LOG4CXX_STR("log4j.category."))
290
0
        : LogString(LOG4CXX_STR("log4j.logger."))
291
0
        ).length();
292
0
      auto loggerName = key.substr(prefixLength);
293
0
      auto value = OptionConverter::findAndSubst(key, props);
294
0
      auto logger = hierarchy->getLogger(loggerName, loggerFactory);
295
0
      auto additivity = parseAdditivityForLogger(props, logger, loggerName);
296
0
      parseLogger(props, logger, key, loggerName, value, additivity);
297
298
0
    }
299
0
  }
300
0
}
301
302
bool PropertyConfigurator::parseAdditivityForLogger(helpers::Properties& props,
303
  LoggerPtr& cat, const LogString& loggerName)
304
0
{
305
0
  LogString value(OptionConverter::findAndSubst(LOG4CXX_STR("log4j.additivity.") + loggerName, props));
306
  // touch additivity only if necessary
307
0
  if (!value.empty())
308
0
  {
309
0
    bool additivity = OptionConverter::toBoolean(value, true);
310
0
    if (LogLog::isDebugEnabled())
311
0
    {
312
0
      LogLog::debug(LOG4CXX_STR("Setting [") + loggerName + LOG4CXX_STR("] additivity to [")
313
0
        + (additivity ? LogString(LOG4CXX_STR("true")) : LogString(LOG4CXX_STR("false")) + LOG4CXX_STR("]")));
314
0
    }
315
316
0
    return additivity;
317
0
  }
318
319
0
  return true;
320
0
}
321
322
/**
323
        This method must work for the root logger as well.
324
*/
325
void PropertyConfigurator::parseLogger(
326
  helpers::Properties& props, LoggerPtr& logger, const LogString& /* optionKey */,
327
  const LogString& loggerName, const LogString& value, bool additivity)
328
0
{
329
0
  if (LogLog::isDebugEnabled())
330
0
  {
331
0
    LogLog::debug(((LogString) LOG4CXX_STR("Parsing for ["))
332
0
      + loggerName
333
0
      + LOG4CXX_STR("] with value=[")
334
0
      + value + LOG4CXX_STR("]"));
335
0
  }
336
337
  // We must skip over ',' but not white space
338
0
  StringTokenizer st(value, LOG4CXX_STR(","));
339
340
  // If value is not in the form ", appender.." or "", then we should set
341
  // the level of the logger.
342
0
  if (!(value.find(LOG4CXX_STR(",")) == 0 || value.empty()))
343
0
  {
344
    // just to be on the safe side...
345
0
    if (!st.hasMoreTokens())
346
0
    {
347
0
      return;
348
0
    }
349
350
0
    LogString levelStr = st.nextToken();
351
352
    // If the level value is inherited, set logger level value to
353
    // null. We also check that the user has not specified inherited for the
354
    // root logger.
355
0
    if (StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("INHERITED"), LOG4CXX_STR("inherited"))
356
0
      || StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("NULL"), LOG4CXX_STR("null")))
357
0
    {
358
0
      if (loggerName == LOG4CXX_STR("root"))
359
0
      {
360
0
        LogLog::warn(LOG4CXX_STR("Root level cannot be ") + levelStr + LOG4CXX_STR(". Ignoring directive."));
361
0
      }
362
0
      else
363
0
      {
364
0
        logger->setLevel(0);
365
0
      }
366
0
    }
367
0
    else
368
0
    {
369
0
      logger->setLevel(OptionConverter::toLevel(levelStr, Level::getDebug()));
370
0
    }
371
0
    if (LogLog::isDebugEnabled())
372
0
    {
373
0
      LogLog::debug(loggerName + LOG4CXX_STR(" level set to ") +
374
0
        logger->getEffectiveLevel()->toString());
375
0
    }
376
377
0
  }
378
379
0
  AppenderPtr appender;
380
0
  LogString appenderName;
381
0
  std::vector<AppenderPtr> newappenders;
382
383
0
  while (st.hasMoreTokens())
384
0
  {
385
0
    appenderName = StringHelper::trim(st.nextToken());
386
387
0
    if (appenderName.empty() || appenderName == LOG4CXX_STR(","))
388
0
    {
389
0
      continue;
390
0
    }
391
392
0
    if (LogLog::isDebugEnabled())
393
0
    {
394
0
      LogLog::debug(LOG4CXX_STR("Parsing ") + Appender::getStaticClass().getName()
395
0
        + LOG4CXX_STR(" named [") + appenderName + LOG4CXX_STR("]"));
396
0
    }
397
0
    appender = parseAppender(props, appenderName);
398
399
0
    if (appender != 0)
400
0
    {
401
0
      newappenders.push_back(appender);
402
0
    }
403
0
  }
404
405
0
  logger->reconfigure( newappenders, additivity );
406
0
}
407
408
AppenderPtr PropertyConfigurator::parseAppender(
409
  helpers::Properties& props, const LogString& appenderName)
410
0
{
411
0
  AppenderPtr appender = registryGet(appenderName);
412
413
0
  if (appender != 0)
414
0
  {
415
0
    if (LogLog::isDebugEnabled())
416
0
    {
417
0
      LogLog::debug((LogString) LOG4CXX_STR("Appender [")
418
0
        + appenderName + LOG4CXX_STR("] was already parsed."));
419
0
    }
420
421
0
    return appender;
422
0
  }
423
424
  // Appender was not previously initialized.
425
0
  LogString prefix = LOG4CXX_STR("log4j.appender.") + appenderName;
426
0
  LogString layoutPrefix = prefix + LOG4CXX_STR(".layout");
427
428
0
  std::shared_ptr<Object> obj =
429
0
    OptionConverter::instantiateByKey(
430
0
      props, prefix, Appender::getStaticClass(), 0);
431
0
  appender = LOG4CXX_NS::cast<Appender>( obj );
432
433
  // Map obsolete DailyRollingFileAppender property configuration
434
0
  if (!appender &&
435
0
    StringHelper::endsWith(OptionConverter::findAndSubst(prefix, props), LOG4CXX_STR("DailyRollingFileAppender")))
436
0
  {
437
0
    appender = std::make_shared<RollingFileAppender>();
438
0
    auto datePattern = OptionConverter::findAndSubst(prefix + LOG4CXX_STR(".datePattern"), props);
439
0
    if (!datePattern.empty())
440
0
      props.put(prefix + LOG4CXX_STR(".fileDatePattern"), datePattern);
441
0
  }
442
443
0
  if (!appender)
444
0
  {
445
0
    LogLog::error((LogString) LOG4CXX_STR("Could not instantiate ") + Appender::getStaticClass().getName()
446
0
      + LOG4CXX_STR(" named [") + appenderName + LOG4CXX_STR("]"));
447
0
    return 0;
448
0
  }
449
450
0
  appender->setName(appenderName);
451
452
0
  if (appender->instanceof(OptionHandler::getStaticClass()))
453
0
  {
454
0
    Pool p;
455
456
0
    if (appender->requiresLayout())
457
0
    {
458
0
      LayoutPtr layout;
459
0
      std::shared_ptr<Object> obj =
460
0
        OptionConverter::instantiateByKey(
461
0
          props, layoutPrefix, Layout::getStaticClass(), 0);
462
0
      layout = LOG4CXX_NS::cast<Layout>( obj );
463
464
0
      if (layout != 0)
465
0
      {
466
0
        appender->setLayout(layout);
467
0
        if (LogLog::isDebugEnabled())
468
0
        {
469
0
          LogLog::debug((LogString) LOG4CXX_STR("Parsing ") + Layout::getStaticClass().getName()
470
0
            + LOG4CXX_STR(" options for [") + appenderName + LOG4CXX_STR("]"));
471
0
        }
472
473
0
        PropertySetter::setProperties(layout, props, layoutPrefix + LOG4CXX_STR("."), p);
474
0
        if (LogLog::isDebugEnabled())
475
0
        {
476
0
          LogLog::debug((LogString) LOG4CXX_STR("End of parsing for [")
477
0
            + appenderName +  LOG4CXX_STR("]"));
478
0
        }
479
0
      }
480
0
    }
481
482
0
    RollingFileAppenderPtr rolling = LOG4CXX_NS::cast<rolling::RollingFileAppender>(appender);
483
0
    if (rolling)
484
0
    {
485
0
      LogString rollingPolicyKey = prefix + LOG4CXX_STR(".rollingPolicy");
486
0
      if (!OptionConverter::findAndSubst(rollingPolicyKey, props).empty())
487
0
      {
488
0
        RollingPolicyPtr rollingPolicy;
489
0
        std::shared_ptr<Object> rolling_obj =
490
0
          OptionConverter::instantiateByKey(
491
0
            props, rollingPolicyKey, RollingPolicy::getStaticClass(), 0);
492
0
        rollingPolicy = LOG4CXX_NS::cast<RollingPolicy>( rolling_obj );
493
0
        if(rollingPolicy)
494
0
        {
495
0
          rolling->setRollingPolicy(rollingPolicy);
496
497
0
          if (LogLog::isDebugEnabled())
498
0
          {
499
0
            LogLog::debug((LogString) LOG4CXX_STR("Parsing ") + RollingPolicy::getStaticClass().getName()
500
0
              + LOG4CXX_STR(" options for [") + appenderName + LOG4CXX_STR("]"));
501
0
          }
502
0
          PropertySetter::setProperties(rollingPolicy, props, rollingPolicyKey + LOG4CXX_STR("."), p);
503
0
        }
504
0
      }
505
506
0
      LogString triggeringPolicyKey = prefix + LOG4CXX_STR(".triggeringPolicy");
507
0
      if (!OptionConverter::findAndSubst(triggeringPolicyKey, props).empty())
508
0
      {
509
0
        TriggeringPolicyPtr triggeringPolicy;
510
0
        std::shared_ptr<Object> triggering_obj =
511
0
          OptionConverter::instantiateByKey(
512
0
            props, triggeringPolicyKey, TriggeringPolicy::getStaticClass(), 0);
513
0
        triggeringPolicy = LOG4CXX_NS::cast<TriggeringPolicy>( triggering_obj );
514
0
        if(triggeringPolicy)
515
0
        {
516
0
          rolling->setTriggeringPolicy(triggeringPolicy);
517
518
0
          if (LogLog::isDebugEnabled())
519
0
          {
520
0
            LogLog::debug((LogString) LOG4CXX_STR("Parsing ") + TriggeringPolicy::getStaticClass().getName()
521
0
              + LOG4CXX_STR(" options for [") + appenderName + LOG4CXX_STR("]"));
522
0
          }
523
0
          PropertySetter::setProperties(triggeringPolicy, props, triggeringPolicyKey + LOG4CXX_STR("."), p);
524
0
        }
525
0
      }
526
0
    }
527
528
0
    PropertySetter::setProperties(appender, props, prefix + LOG4CXX_STR("."), p);
529
0
    if (LogLog::isDebugEnabled())
530
0
    {
531
0
      LogLog::debug((LogString) LOG4CXX_STR("Parsed [")
532
0
        + appenderName + LOG4CXX_STR("] options."));
533
0
    }
534
0
  }
535
536
0
  registryPut(appender);
537
538
0
  return appender;
539
0
}
540
541
void PropertyConfigurator::registryPut(const AppenderPtr& appender)
542
0
{
543
0
  (*registry)[appender->getName()] = appender;
544
0
}
545
546
AppenderPtr PropertyConfigurator::registryGet(const LogString& name)
547
0
{
548
0
  return (*registry)[name];
549
0
}