Coverage Report

Created: 2025-07-11 07:00

/src/logging-log4cxx/src/main/cpp/optionconverter.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/spi/loggerfactory.h>
20
#include <log4cxx/spi/loggerrepository.h>
21
#include <log4cxx/appenderskeleton.h>
22
#include <log4cxx/helpers/optionconverter.h>
23
#include <algorithm>
24
#include <ctype.h>
25
#include <log4cxx/helpers/stringhelper.h>
26
#include <log4cxx/helpers/exception.h>
27
#include <stdlib.h>
28
#include <log4cxx/helpers/properties.h>
29
#include <log4cxx/helpers/loglog.h>
30
#include <log4cxx/level.h>
31
#include <log4cxx/helpers/object.h>
32
#include <log4cxx/helpers/class.h>
33
#include <log4cxx/helpers/loader.h>
34
#include <log4cxx/helpers/system.h>
35
#include <log4cxx/propertyconfigurator.h>
36
#include <log4cxx/helpers/transcoder.h>
37
#include <log4cxx/file.h>
38
#include <log4cxx/xml/domconfigurator.h>
39
#include <log4cxx/logmanager.h>
40
#if !defined(LOG4CXX)
41
  #define LOG4CXX 1
42
#endif
43
#include <log4cxx/helpers/aprinitializer.h>
44
#include <log4cxx/helpers/filewatchdog.h>
45
#include <log4cxx/helpers/singletonholder.h>
46
47
namespace LOG4CXX_NS
48
{
49
50
class ConfiguratorWatchdog  : public helpers::FileWatchdog
51
{
52
  spi::ConfiguratorPtr m_config;
53
  public:
54
    ConfiguratorWatchdog(const spi::ConfiguratorPtr& config, const File& filename)
55
0
        : helpers::FileWatchdog(filename)
56
0
        , m_config(config)
57
0
    {
58
0
    }
59
60
    /**
61
    Call PropertyConfigurator#doConfigure(const String& configFileName,
62
    const spi::LoggerRepositoryPtr& hierarchy) with the
63
    <code>filename</code> to reconfigure log4cxx.
64
    */
65
    void doOnChange() override
66
0
    {
67
0
        m_config->doConfigure(file(), LogManager::getLoggerRepository());
68
0
    }
69
70
  static void startWatching(const spi::ConfiguratorPtr& config, const File& filename, long delay)
71
0
  {
72
0
    using WatchdogHolder = helpers::SingletonHolder<ConfiguratorWatchdog>;
73
0
    auto pHolder = helpers::APRInitializer::getOrAddUnique<WatchdogHolder>
74
0
      ( [&config, &filename]() -> helpers::ObjectPtr
75
0
        { return std::make_shared<WatchdogHolder>(config, filename); }
76
0
      );
77
0
    auto& dog = pHolder->value();
78
0
    dog.m_config = config;
79
0
    dog.setFile(filename);
80
0
    dog.setDelay(delay);
81
0
    dog.start();
82
0
  }
83
};
84
85
}
86
87
using namespace LOG4CXX_NS;
88
using namespace LOG4CXX_NS::helpers;
89
using namespace LOG4CXX_NS::spi;
90
91
92
LogString OptionConverter::convertSpecialChars(const LogString& s)
93
0
{
94
0
  logchar c;
95
0
  LogString sbuf;
96
97
0
  LogString::const_iterator i = s.begin();
98
99
0
  while (i != s.end())
100
0
  {
101
0
    c = *i++;
102
103
0
    if (i != s.end() && c == 0x5C /* '\\' */)
104
0
    {
105
0
      c =  *i++;
106
107
0
      switch (c)
108
0
      {
109
0
        case 0x6E: //'n'
110
0
          c = 0x0A;
111
0
          break;
112
113
0
        case 0x72: //'r'
114
0
          c = 0x0D;
115
0
          break;
116
117
0
        case 0x74: //'t'
118
0
          c = 0x09;
119
0
          break;
120
121
0
        case 0x66: //'f'
122
0
          c = 0x0C;
123
0
          break;
124
125
0
        default:
126
0
          break;
127
0
      }
128
0
    }
129
130
0
    sbuf.append(1, c);
131
0
  }
132
133
0
  return sbuf;
134
0
}
135
136
137
bool OptionConverter::toBoolean(const LogString& value, bool dEfault)
138
1
{
139
1
  if (value.length() >= 4)
140
1
  {
141
1
    if (StringHelper::equalsIgnoreCase(value.substr(0, 4),
142
1
        LOG4CXX_STR("TRUE"), LOG4CXX_STR("true")))
143
0
    {
144
0
      return true;
145
0
    }
146
1
  }
147
148
1
  if (dEfault && value.length() >= 5)
149
0
  {
150
0
    if (StringHelper::equalsIgnoreCase(value.substr(0, 5),
151
0
        LOG4CXX_STR("FALSE"), LOG4CXX_STR("false")))
152
0
    {
153
0
      return false;
154
0
    }
155
0
  }
156
157
1
  return dEfault;
158
1
}
159
160
int OptionConverter::toInt(const LogString& value, int dEfault)
161
0
{
162
0
  LogString trimmed(StringHelper::trim(value));
163
164
0
  if (trimmed.empty())
165
0
  {
166
0
    return dEfault;
167
0
  }
168
169
0
  LOG4CXX_ENCODE_CHAR(cvalue, trimmed);
170
171
0
  return (int) atol(cvalue.c_str());
172
0
}
173
174
long OptionConverter::toFileSize(const LogString& s, long dEfault)
175
0
{
176
0
  if (s.empty())
177
0
  {
178
0
    return dEfault;
179
0
  }
180
181
0
  size_t index = s.find_first_of(LOG4CXX_STR("bB"));
182
183
0
  if (index != LogString::npos && index > 0)
184
0
  {
185
0
    long multiplier = 1;
186
0
    index--;
187
188
0
    if (s[index] == 0x6B /* 'k' */ || s[index] == 0x4B /* 'K' */)
189
0
    {
190
0
      multiplier = 1024;
191
0
    }
192
0
    else if (s[index] == 0x6D /* 'm' */ || s[index] == 0x4D /* 'M' */)
193
0
    {
194
0
      multiplier = 1024 * 1024;
195
0
    }
196
0
    else if (s[index] == 0x67 /* 'g'*/ || s[index] == 0x47 /* 'G' */)
197
0
    {
198
0
      multiplier = 1024 * 1024 * 1024;
199
0
    }
200
201
0
    return toInt(s.substr(0, index), 1) * multiplier;
202
0
  }
203
204
0
  return toInt(s, 1);
205
0
}
206
207
LogString OptionConverter::findAndSubst(const LogString& key, Properties& props)
208
0
{
209
0
  LogString value(props.getProperty(key));
210
211
0
  if (value.empty())
212
0
  {
213
0
    return value;
214
0
  }
215
216
0
  try
217
0
  {
218
0
    return substVars(value, props);
219
0
  }
220
0
  catch (IllegalArgumentException& e)
221
0
  {
222
0
    LogLog::error(((LogString) LOG4CXX_STR("Bad option value ["))
223
0
      + value + LOG4CXX_STR("]."), e);
224
0
    return value;
225
0
  }
226
0
}
227
228
LogString OptionConverter::substVars(const LogString& val, Properties& props)
229
0
{
230
0
  LogString sbuf;
231
0
  const logchar delimStartArray[] = { 0x24, 0x7B, 0 };
232
0
  const LogString delimStart(delimStartArray);
233
0
  const logchar delimStop = 0x7D; // '}';
234
0
  const size_t DELIM_START_LEN = 2;
235
0
  const size_t DELIM_STOP_LEN = 1;
236
237
0
  size_t i = 0;
238
239
0
  while (true)
240
0
  {
241
0
    size_t j = val.find(delimStart, i);
242
243
0
    if (j == val.npos)
244
0
    {
245
      // no more variables
246
0
      if (i == 0)
247
0
      {
248
        // this is a simple string
249
0
        return val;
250
0
      }
251
0
      else
252
0
      {
253
        // add the tail string which contails no variables and return the result.
254
0
        sbuf.append(val.substr(i, val.length() - i));
255
0
        return sbuf;
256
0
      }
257
0
    }
258
0
    else
259
0
    {
260
0
      sbuf.append(val.substr(i, j - i));
261
0
      size_t k = val.find(delimStop, j);
262
263
0
      if (k == val.npos)
264
0
      {
265
0
        LogString msg(1, (logchar) 0x22 /* '\"' */);
266
0
        msg.append(val);
267
0
        msg.append(LOG4CXX_STR("\" has no closing brace. Opening brace at position "));
268
0
        Pool p;
269
0
        StringHelper::toString(j, p, msg);
270
0
        msg.append(1, (logchar) 0x2E /* '.' */);
271
0
        throw IllegalArgumentException(msg);
272
0
      }
273
0
      else
274
0
      {
275
0
        j += DELIM_START_LEN;
276
0
        LogString key = val.substr(j, k - j);
277
        // first try in System properties
278
0
        LogString replacement(getSystemProperty(key, LogString()));
279
280
        // then try props parameter
281
0
        if (replacement.empty())
282
0
        {
283
0
          replacement = props.getProperty(key);
284
0
        }
285
286
0
        if (!replacement.empty())
287
0
        {
288
          // Do variable substitution on the replacement string
289
          // such that we can solve "Hello ${x2}" as "Hello p1"
290
          // the where the properties are
291
          // x1=p1
292
          // x2=${x1}
293
0
          LogString recursiveReplacement = substVars(replacement, props);
294
0
          sbuf.append(recursiveReplacement);
295
0
        }
296
297
0
        i = k + DELIM_STOP_LEN;
298
0
      }
299
0
    }
300
0
  }
301
0
}
302
303
LogString OptionConverter::getSystemProperty(const LogString& key, const LogString& def)
304
1
{
305
1
  if (!key.empty())
306
1
  {
307
1
    LogString value(System::getProperty(key));
308
309
1
    if (!value.empty())
310
0
    {
311
0
      return value;
312
0
    }
313
1
  }
314
315
1
  return def;
316
1
}
317
318
LevelPtr OptionConverter::toLevel(const LogString& value,
319
  const LevelPtr& defaultValue)
320
0
{
321
0
  size_t hashIndex = value.find(LOG4CXX_STR("#"));
322
323
0
  if (hashIndex == LogString::npos)
324
0
  {
325
0
    if (value.empty())
326
0
    {
327
0
      return defaultValue;
328
0
    }
329
0
    else
330
0
    {
331
0
      if (LogLog::isDebugEnabled())
332
0
      {
333
0
        LogLog::debug(
334
0
          ((LogString) LOG4CXX_STR("OptionConverter::toLevel: no class name specified, level=["))
335
0
          + value
336
0
          + LOG4CXX_STR("]"));
337
0
      }
338
      // no class name specified : use standard Level class
339
0
      return Level::toLevelLS(value, defaultValue);
340
0
    }
341
0
  }
342
343
0
  LogString clazz = value.substr(hashIndex + 1);
344
0
  LogString levelName = value.substr(0, hashIndex);
345
0
  if (LogLog::isDebugEnabled())
346
0
  {
347
0
    LogLog::debug(LOG4CXX_STR("OptionConverter::toLevel: class=[")
348
0
    + clazz + LOG4CXX_STR("], level=[")
349
0
    + levelName + LOG4CXX_STR("]")
350
0
    );
351
0
  }
352
353
  // This is degenerate case but you never know.
354
0
  if (levelName.empty())
355
0
  {
356
0
    return Level::toLevelLS(value, defaultValue);
357
0
  }
358
359
0
  try
360
0
  {
361
    // Note: the dynamic_cast could fail across DLL boundaries.
362
    // However, without the dynamic_cast a poorly formed XML file
363
    // could attempt to load an invalid class as a filter, causing
364
    // a crash.  If it can't be converted, a std::bad_cast should be
365
    // thrown(and caught by the exception handler below)
366
0
    const Level::LevelClass& levelClass =
367
0
      dynamic_cast<const Level::LevelClass&>(Loader::loadClass(clazz));
368
0
    return levelClass.toLevel(levelName);
369
0
  }
370
0
  catch (ClassNotFoundException&)
371
0
  {
372
0
    LogLog::warn(((LogString) LOG4CXX_STR("custom level class ["))
373
0
      + clazz + LOG4CXX_STR("] not found."));
374
0
  }
375
0
  catch (Exception& oops)
376
0
  {
377
0
    LogLog::warn(
378
0
      LOG4CXX_STR("class [") + clazz + LOG4CXX_STR("], level [") + levelName +
379
0
      LOG4CXX_STR("] conversion) failed."), oops);
380
0
  }
381
0
  catch(const std::bad_cast&)
382
0
  {
383
0
    LogLog::warn(
384
0
      LOG4CXX_STR("class [") + clazz + LOG4CXX_STR("] unable to be converted to "
385
0
      "Level::LevelClass"));
386
0
  }
387
0
  catch (...)
388
0
  {
389
0
    LogLog::warn(
390
0
      LOG4CXX_STR("class [") + clazz + LOG4CXX_STR("], level [") + levelName +
391
0
      LOG4CXX_STR("] conversion) failed."));
392
0
  }
393
394
0
  return defaultValue;
395
0
}
396
397
398
ObjectPtr OptionConverter::instantiateByKey(Properties& props, const LogString& key,
399
  const Class& superClass, const ObjectPtr& defaultValue)
400
0
{
401
  // Get the value of the property in string form
402
0
  LogString className(findAndSubst(key, props));
403
404
0
  if (className.empty())
405
0
  {
406
0
    LogLog::error(
407
0
      ((LogString) LOG4CXX_STR("Could not find value for key ")) + key);
408
0
    return defaultValue;
409
0
  }
410
411
  // Trim className to avoid trailing spaces that cause problems.
412
0
  return OptionConverter::instantiateByClassName(
413
0
      StringHelper::trim(className), superClass, defaultValue);
414
0
}
415
416
ObjectPtr OptionConverter::instantiateByClassName(const LogString& className,
417
  const Class& superClass, const ObjectPtr& defaultValue)
418
0
{
419
0
  if (!className.empty())
420
0
  {
421
0
    try
422
0
    {
423
0
      const Class& classObj = Loader::loadClass(className);
424
0
      ObjectPtr newObject =  ObjectPtr(classObj.newInstance());
425
426
0
      if (!newObject->instanceof(superClass))
427
0
      {
428
0
        return defaultValue;
429
0
      }
430
431
0
      return newObject;
432
0
    }
433
0
    catch (Exception& e)
434
0
    {
435
0
      LogLog::error(LOG4CXX_STR("Could not instantiate class [") +
436
0
        className + LOG4CXX_STR("]."), e);
437
0
    }
438
0
  }
439
440
0
  return defaultValue;
441
0
}
442
443
void OptionConverter::selectAndConfigure(const File& configFileName,
444
  const LogString& _clazz, spi::LoggerRepositoryPtr hierarchy, int delay)
445
0
{
446
0
  ConfiguratorPtr configurator;
447
0
  LogString clazz = _clazz;
448
449
0
  LogString filename(configFileName.getPath());
450
451
0
#if LOG4CXX_HAS_DOMCONFIGURATOR
452
0
  if (clazz.empty()
453
0
    && filename.length() > 4
454
0
    && StringHelper::equalsIgnoreCase(
455
0
      filename.substr(filename.length() - 4),
456
0
      LOG4CXX_STR(".XML"), LOG4CXX_STR(".xml")))
457
0
  {
458
0
    clazz = LOG4CXX_NS::xml::DOMConfigurator::getStaticClass().getName();
459
0
  }
460
0
#endif
461
462
0
  if (!clazz.empty())
463
0
  {
464
0
    if (LogLog::isDebugEnabled())
465
0
      LogLog::debug(LOG4CXX_STR("Preferred configurator class: ") + clazz);
466
0
    const Class& clazzObj = Loader::loadClass(clazz);
467
0
    ObjectPtr obj = ObjectPtr(clazzObj.newInstance());
468
0
    configurator = LOG4CXX_NS::cast<Configurator>(obj);
469
470
0
    if (configurator == 0)
471
0
    {
472
0
      LogLog::error(LOG4CXX_STR("Could not instantiate configurator [")
473
0
        + clazz + LOG4CXX_STR("]."));
474
0
      return;
475
0
    }
476
0
  }
477
0
  else
478
0
  {
479
0
    configurator = std::make_shared<PropertyConfigurator>();
480
0
  }
481
482
0
  if (0 < delay)
483
0
    ConfiguratorWatchdog::startWatching(configurator, configFileName, delay);
484
0
  else
485
0
    configurator->doConfigure(configFileName, hierarchy);
486
0
}