Coverage Report

Created: 2025-07-01 06:08

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