Coverage Report

Created: 2025-07-11 07:00

/src/logging-log4cxx/src/main/cpp/domconfigurator.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
#include <log4cxx/private/string_c11.h>
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/xml/domconfigurator.h>
20
#include <log4cxx/appender.h>
21
#include <log4cxx/layout.h>
22
#include <log4cxx/logger.h>
23
#include <log4cxx/logmanager.h>
24
#include <log4cxx/level.h>
25
#include <log4cxx/spi/filter.h>
26
#include <log4cxx/helpers/loglog.h>
27
#include <log4cxx/helpers/stringhelper.h>
28
#include <log4cxx/helpers/loader.h>
29
#include <log4cxx/helpers/optionconverter.h>
30
#include <log4cxx/config/propertysetter.h>
31
#include <log4cxx/spi/errorhandler.h>
32
#include <log4cxx/spi/loggerfactory.h>
33
#if LOG4CXX_ABI_VERSION <= 15
34
#include <log4cxx/defaultloggerfactory.h>
35
#else
36
#include <log4cxx/spi/loggerfactory.h>
37
#endif
38
#include <log4cxx/helpers/filewatchdog.h>
39
#include <log4cxx/spi/loggerrepository.h>
40
#include <log4cxx/spi/loggingevent.h>
41
#include <log4cxx/helpers/pool.h>
42
#include <sstream>
43
#include <log4cxx/helpers/transcoder.h>
44
#include <log4cxx/rolling/rollingfileappender.h>
45
#include <log4cxx/rolling/filterbasedtriggeringpolicy.h>
46
#include <apr_xml.h>
47
#include <log4cxx/helpers/bytebuffer.h>
48
#include <log4cxx/helpers/charsetdecoder.h>
49
#include <log4cxx/net/smtpappender.h>
50
#include <log4cxx/helpers/messagebuffer.h>
51
#include <log4cxx/helpers/threadutility.h>
52
#include <log4cxx/helpers/singletonholder.h>
53
54
#define LOG4CXX 1
55
#include <log4cxx/helpers/aprinitializer.h>
56
57
using namespace LOG4CXX_NS;
58
using namespace LOG4CXX_NS::xml;
59
using namespace LOG4CXX_NS::helpers;
60
using namespace LOG4CXX_NS::spi;
61
using namespace LOG4CXX_NS::config;
62
using namespace LOG4CXX_NS::rolling;
63
64
0
#define MAX_ATTRIBUTE_NAME_LEN 2000
65
66
struct DOMConfigurator::DOMConfiguratorPrivate
67
{
68
  helpers::Properties props;
69
  spi::LoggerRepositoryPtr repository;
70
  spi::LoggerFactoryPtr loggerFactory;
71
};
72
73
namespace LOG4CXX_NS
74
{
75
namespace xml
76
{
77
class XMLWatchdog  : public FileWatchdog
78
{
79
  public:
80
0
    XMLWatchdog(const File& filename) : FileWatchdog(filename)
81
0
    {
82
0
    }
83
84
    /**
85
    Call DOMConfigurator#doConfigure with the
86
    <code>filename</code> to reconfigure log4cxx.
87
    */
88
    void doOnChange()
89
0
    {
90
0
      DOMConfigurator().doConfigure(file(),
91
0
        LogManager::getLoggerRepository());
92
0
    }
93
94
    static void startWatching(const File& filename, long delay)
95
0
    {
96
0
      using WatchdogHolder = SingletonHolder<XMLWatchdog>;
97
0
      auto pHolder = APRInitializer::getOrAddUnique<WatchdogHolder>
98
0
        ( [&filename]() -> ObjectPtr
99
0
          { return std::make_shared<WatchdogHolder>(filename); }
100
0
        );
101
0
      auto& xdog = pHolder->value();
102
0
      xdog.setFile(filename);
103
0
      xdog.setDelay(0 < delay ? delay : FileWatchdog::DEFAULT_DELAY);
104
0
      xdog.start();
105
0
    }
106
};
107
}
108
}
109
110
IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
111
112
0
#define CONFIGURATION_TAG "log4j:configuration"
113
0
#define OLD_CONFIGURATION_TAG "configuration"
114
0
#define APPENDER_TAG "appender"
115
0
#define APPENDER_REF_TAG "appender-ref"
116
0
#define PARAM_TAG "param"
117
0
#define LAYOUT_TAG "layout"
118
0
#define ROLLING_POLICY_TAG "rollingPolicy"
119
0
#define TRIGGERING_POLICY_TAG "triggeringPolicy"
120
0
#define CATEGORY "category"
121
0
#define LOGGER "logger"
122
0
#define LOGGER_REF "logger-ref"
123
0
#define CATEGORY_FACTORY_TAG "categoryFactory"
124
0
#define NAME_ATTR "name"
125
0
#define CLASS_ATTR "class"
126
0
#define VALUE_ATTR "value"
127
0
#define ROOT_TAG "root"
128
0
#define ROOT_REF "root-ref"
129
0
#define LEVEL_TAG "level"
130
0
#define PRIORITY_TAG "priority"
131
0
#define FILTER_TAG "filter"
132
0
#define ERROR_HANDLER_TAG "errorHandler"
133
0
#define REF_ATTR "ref"
134
0
#define ADDITIVITY_ATTR "additivity"
135
0
#define THRESHOLD_ATTR "threshold"
136
#define STRINGSTREAM_ATTR "stringstream"
137
0
#define CONFIG_DEBUG_ATTR "configDebug"
138
0
#define INTERNAL_DEBUG_ATTR "debug"
139
0
#define THREAD_CONFIG_ATTR "threadConfiguration"
140
141
DOMConfigurator::DOMConfigurator()
142
0
  : m_priv(std::make_unique<DOMConfiguratorPrivate>())
143
0
{
144
0
}
Unexecuted instantiation: log4cxx::xml::DOMConfigurator::DOMConfigurator()
Unexecuted instantiation: log4cxx::xml::DOMConfigurator::DOMConfigurator()
145
146
0
DOMConfigurator::~DOMConfigurator() {}
147
148
/**
149
Used internally to parse appenders by IDREF name.
150
*/
151
AppenderPtr DOMConfigurator::findAppenderByName(LOG4CXX_NS::helpers::Pool& p,
152
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
153
  apr_xml_elem* element,
154
  apr_xml_doc* doc,
155
  const LogString& appenderName,
156
  AppenderMap& appenders)
157
0
{
158
0
  AppenderPtr appender;
159
0
  std::string tagName(element->name);
160
161
0
  if (tagName == APPENDER_TAG)
162
0
  {
163
0
    if (appenderName == getAttribute(utf8Decoder, element, NAME_ATTR))
164
0
    {
165
0
      appender = parseAppender(p, utf8Decoder, element, doc, appenders);
166
0
    }
167
0
  }
168
169
0
  if (element->first_child && !appender)
170
0
  {
171
0
    appender = findAppenderByName(p, utf8Decoder, element->first_child, doc, appenderName, appenders);
172
0
  }
173
174
0
  if (element->next && !appender)
175
0
  {
176
0
    appender = findAppenderByName(p, utf8Decoder, element->next, doc, appenderName, appenders);
177
0
  }
178
179
0
  return appender;
180
0
}
181
182
/**
183
 Used internally to parse appenders by IDREF element.
184
*/
185
AppenderPtr DOMConfigurator::findAppenderByReference(
186
  LOG4CXX_NS::helpers::Pool& p,
187
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
188
  apr_xml_elem* appenderRef,
189
  apr_xml_doc* doc,
190
  AppenderMap& appenders)
191
0
{
192
0
  LogString appenderName(subst(getAttribute(utf8Decoder, appenderRef, REF_ATTR)));
193
0
  AppenderMap::const_iterator match = appenders.find(appenderName);
194
0
  AppenderPtr appender;
195
196
0
  if (match != appenders.end())
197
0
  {
198
0
    appender = match->second;
199
0
  }
200
0
  else if (doc)
201
0
  {
202
0
    appender = findAppenderByName(p, utf8Decoder, doc->root, doc, appenderName, appenders);
203
204
0
    if (appender)
205
0
    {
206
0
      appenders.insert(AppenderMap::value_type(appenderName, appender));
207
0
    }
208
0
  }
209
210
0
  if (!appender)
211
0
  {
212
0
    LogLog::error(LOG4CXX_STR("No appender named [") +
213
0
      appenderName + LOG4CXX_STR("] could be found."));
214
0
  }
215
216
0
  return appender;
217
0
}
218
219
/**
220
Used internally to parse an appender element.
221
*/
222
AppenderPtr DOMConfigurator::parseAppender(Pool& p,
223
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
224
  apr_xml_elem* appenderElement,
225
  apr_xml_doc* doc,
226
  AppenderMap& appenders)
227
0
{
228
229
0
  LogString className(subst(getAttribute(utf8Decoder, appenderElement, CLASS_ATTR)));
230
0
  if (LogLog::isDebugEnabled())
231
0
  {
232
0
    LogLog::debug(LOG4CXX_STR("Class name: [") + className + LOG4CXX_STR("]"));
233
0
  }
234
235
0
  try
236
0
  {
237
0
    ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
238
0
    AppenderPtr appender = LOG4CXX_NS::cast<Appender>(instance);
239
0
    if(!appender){
240
0
      LogLog::error(LOG4CXX_STR("Could not cast class of type [") + className + LOG4CXX_STR("] to appender"));
241
0
      return AppenderPtr();
242
0
    }
243
0
    PropertySetter propSetter(appender);
244
245
0
    appender->setName(subst(getAttribute(utf8Decoder, appenderElement, NAME_ATTR)));
246
247
0
    for (apr_xml_elem* currentElement = appenderElement->first_child;
248
0
      currentElement;
249
0
      currentElement = currentElement->next)
250
0
    {
251
252
0
      std::string tagName(currentElement->name);
253
254
      // Parse appender parameters
255
0
      if (tagName == PARAM_TAG)
256
0
      {
257
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
258
0
      }
259
      // Set appender layout
260
0
      else if (tagName == LAYOUT_TAG)
261
0
      {
262
0
        appender->setLayout(parseLayout(p, utf8Decoder, currentElement));
263
0
      }
264
      // Add filters
265
0
      else if (tagName == FILTER_TAG)
266
0
      {
267
0
        std::vector<LOG4CXX_NS::spi::FilterPtr> filters;
268
0
        parseFilters(p, utf8Decoder, currentElement, filters);
269
270
0
        for (auto& item : filters)
271
0
        {
272
0
          appender->addFilter(item);
273
0
        }
274
0
      }
275
0
      else if (tagName == ERROR_HANDLER_TAG)
276
0
      {
277
0
        parseErrorHandler(p, utf8Decoder, currentElement, appender, doc, appenders);
278
0
      }
279
0
      else if (tagName == ROLLING_POLICY_TAG)
280
0
      {
281
0
        RollingPolicyPtr rollPolicy(parseRollingPolicy(p, utf8Decoder, currentElement));
282
0
        RollingFileAppenderPtr rfa = LOG4CXX_NS::cast<RollingFileAppender>(appender);
283
284
0
        if (rfa != NULL)
285
0
        {
286
0
          rfa->setRollingPolicy(rollPolicy);
287
0
        }
288
0
      }
289
0
      else if (tagName == TRIGGERING_POLICY_TAG)
290
0
      {
291
0
        ObjectPtr policy(parseTriggeringPolicy(p, utf8Decoder, currentElement));
292
0
        RollingFileAppenderPtr rfa = LOG4CXX_NS::cast<RollingFileAppender>(appender);
293
0
        TriggeringPolicyPtr policyPtr = LOG4CXX_NS::cast<TriggeringPolicy>(policy);
294
295
0
        if (rfa != NULL)
296
0
        {
297
0
          rfa->setTriggeringPolicy(policyPtr);
298
0
        }
299
0
        else
300
0
        {
301
0
          auto smtpa = LOG4CXX_NS::cast<LOG4CXX_NS::net::SMTPAppender>(appender);
302
303
0
          if (smtpa != NULL)
304
0
          {
305
0
            auto evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(policy);
306
0
            smtpa->setEvaluator(evaluator);
307
0
          }
308
0
        }
309
0
      }
310
0
      else if (tagName == APPENDER_REF_TAG)
311
0
      {
312
0
        LogString refName = subst(getAttribute(utf8Decoder, currentElement, REF_ATTR));
313
314
0
        if (!refName.empty() && appender->instanceof(AppenderAttachable::getStaticClass()))
315
0
        {
316
0
          AppenderAttachablePtr aa = LOG4CXX_NS::cast<AppenderAttachable>(appender);
317
0
          if (LogLog::isDebugEnabled())
318
0
          {
319
0
            LogLog::debug(LOG4CXX_STR("Attaching appender named [") +
320
0
              refName + LOG4CXX_STR("] to appender named [") +
321
0
              appender->getName() + LOG4CXX_STR("]."));
322
0
          }
323
0
          aa->addAppender(findAppenderByReference(p, utf8Decoder, currentElement, doc, appenders));
324
0
        }
325
0
        else if (refName.empty())
326
0
        {
327
0
          LogLog::error(LOG4CXX_STR("Can't add appender with empty ref attribute"));
328
0
        }
329
0
        else
330
0
        {
331
0
          LogLog::error(LOG4CXX_STR("Requesting attachment of appender named [") +
332
0
            refName + LOG4CXX_STR("] to appender named [") + appender->getName() +
333
0
            LOG4CXX_STR("] which does not implement AppenderAttachable."));
334
0
        }
335
0
      }
336
0
    }
337
338
0
    propSetter.activate(p);
339
0
    return appender;
340
0
  }
341
  /* Yes, it's ugly.  But all of these exceptions point to the same
342
      problem: we can't create an Appender */
343
0
  catch (Exception& oops)
344
0
  {
345
0
    LogLog::error(LOG4CXX_STR("Could not create an Appender. Reported error follows."),
346
0
      oops);
347
0
    return 0;
348
0
  }
349
0
}
350
351
/**
352
Used internally to parse an {@link ErrorHandler} element.
353
*/
354
void DOMConfigurator::parseErrorHandler(Pool& p,
355
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
356
  apr_xml_elem* element,
357
  AppenderPtr& appender,
358
  apr_xml_doc* doc,
359
  AppenderMap& appenders)
360
0
{
361
362
0
  ErrorHandlerPtr eh;
363
0
  std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(
364
0
      subst(getAttribute(utf8Decoder, element, CLASS_ATTR)),
365
0
      ErrorHandler::getStaticClass(),
366
0
      0);
367
0
  eh = LOG4CXX_NS::cast<ErrorHandler>(obj);
368
369
0
  if (eh != 0)
370
0
  {
371
0
    eh->setAppender(appender);
372
373
0
    PropertySetter propSetter(eh);
374
375
0
    for (apr_xml_elem* currentElement = element->first_child;
376
0
      currentElement;
377
0
      currentElement = currentElement->next)
378
0
    {
379
0
      std::string tagName(currentElement->name);
380
381
0
      if (tagName == PARAM_TAG)
382
0
      {
383
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
384
0
      }
385
0
      else if (tagName == APPENDER_REF_TAG)
386
0
      {
387
0
        eh->setBackupAppender(findAppenderByReference(p, utf8Decoder, currentElement, doc, appenders));
388
0
      }
389
0
      else if (tagName == LOGGER_REF)
390
0
      {
391
0
        LogString loggerName(getAttribute(utf8Decoder, currentElement, REF_ATTR));
392
0
        LoggerPtr logger = m_priv->repository->getLogger(loggerName, m_priv->loggerFactory);
393
0
        eh->setLogger(logger);
394
0
      }
395
0
      else if (tagName == ROOT_REF)
396
0
      {
397
0
        LoggerPtr root = m_priv->repository->getRootLogger();
398
0
        eh->setLogger(root);
399
0
      }
400
0
    }
401
402
0
    propSetter.activate(p);
403
0
    std::shared_ptr<AppenderSkeleton> appSkeleton = LOG4CXX_NS::cast<AppenderSkeleton>(appender);
404
405
0
    if (appSkeleton != 0)
406
0
    {
407
0
      appSkeleton->setErrorHandler(eh);
408
0
    }
409
0
  }
410
0
}
411
412
/**
413
 Used internally to parse a filter element.
414
*/
415
void DOMConfigurator::parseFilters(Pool& p,
416
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
417
  apr_xml_elem* element,
418
  std::vector<LOG4CXX_NS::spi::FilterPtr>& filters)
419
0
{
420
0
  LogString clazz = subst(getAttribute(utf8Decoder, element, CLASS_ATTR));
421
0
  FilterPtr filter;
422
0
  std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(clazz,
423
0
      Filter::getStaticClass(), 0);
424
0
  filter = LOG4CXX_NS::cast<Filter>(obj);
425
426
0
  if (filter != 0)
427
0
  {
428
0
    PropertySetter propSetter(filter);
429
430
0
    for (apr_xml_elem* currentElement = element->first_child;
431
0
      currentElement;
432
0
      currentElement = currentElement->next)
433
0
    {
434
0
      std::string tagName(currentElement->name);
435
436
0
      if (tagName == PARAM_TAG)
437
0
      {
438
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
439
0
      }
440
0
    }
441
442
0
    propSetter.activate(p);
443
0
    filters.push_back(filter);
444
0
  }
445
0
}
446
447
/**
448
Used internally to parse an category or logger element.
449
*/
450
void DOMConfigurator::parseLogger(
451
  LOG4CXX_NS::helpers::Pool& p,
452
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
453
  apr_xml_elem* loggerElement,
454
  apr_xml_doc* doc,
455
  AppenderMap& appenders)
456
0
{
457
  // Create a new Logger object from the <category> element.
458
0
  LogString loggerName = subst(getAttribute(utf8Decoder, loggerElement, NAME_ATTR));
459
460
0
  if (LogLog::isDebugEnabled())
461
0
  {
462
0
    LogLog::debug(LOG4CXX_STR("Retreiving an instance of ") + loggerName);
463
0
  }
464
0
  LoggerPtr logger = m_priv->repository->getLogger(loggerName, m_priv->loggerFactory);
465
466
  // Setting up a logger needs to be an atomic operation, in order
467
  // to protect potential log operations while logger
468
  // configuration is in progress.
469
0
  bool additivity = OptionConverter::toBoolean(
470
0
      subst(getAttribute(utf8Decoder, loggerElement, ADDITIVITY_ATTR)),
471
0
      true);
472
473
0
  if (LogLog::isDebugEnabled())
474
0
  {
475
0
    LogLog::debug(LOG4CXX_STR("Setting [") + logger->getName() + LOG4CXX_STR("] additivity to [") +
476
0
      (additivity ? LogString(LOG4CXX_STR("true")) : LogString(LOG4CXX_STR("false"))) + LOG4CXX_STR("]."));
477
0
  }
478
0
  logger->setAdditivity(additivity);
479
0
  parseChildrenOfLoggerElement(p, utf8Decoder, loggerElement, logger, false, doc, appenders);
480
0
}
481
482
/**
483
 Used internally to parse the logger factory element.
484
*/
485
void DOMConfigurator::parseLoggerFactory(
486
  LOG4CXX_NS::helpers::Pool& p,
487
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
488
  apr_xml_elem* factoryElement)
489
0
{
490
0
  LogString className(subst(getAttribute(utf8Decoder, factoryElement, CLASS_ATTR)));
491
492
0
  if (className.empty())
493
0
  {
494
0
    LogLog::error(LOG4CXX_STR("Logger Factory tag class attribute not found."));
495
0
  }
496
0
  else
497
0
  {
498
0
    if (LogLog::isDebugEnabled())
499
0
    {
500
0
      LogLog::debug(LOG4CXX_STR("Desired logger factory: [") + className + LOG4CXX_STR("]"));
501
0
    }
502
0
    std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(
503
0
        className,
504
0
        LoggerFactory::getStaticClass(),
505
0
        0);
506
0
    m_priv->loggerFactory = LOG4CXX_NS::cast<LoggerFactory>(obj);
507
0
    PropertySetter propSetter(m_priv->loggerFactory);
508
509
0
    for (apr_xml_elem* currentElement = factoryElement->first_child;
510
0
      currentElement;
511
0
      currentElement = currentElement->next)
512
0
    {
513
0
      std::string tagName(currentElement->name);
514
515
0
      if (tagName == PARAM_TAG)
516
0
      {
517
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
518
0
      }
519
0
    }
520
0
  }
521
0
}
522
523
/**
524
 Used internally to parse the root logger element.
525
*/
526
void DOMConfigurator::parseRoot(
527
  LOG4CXX_NS::helpers::Pool& p,
528
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
529
  apr_xml_elem* rootElement,
530
  apr_xml_doc* doc,
531
  AppenderMap& appenders)
532
0
{
533
0
  LoggerPtr root = m_priv->repository->getRootLogger();
534
0
  parseChildrenOfLoggerElement(p, utf8Decoder, rootElement, root, true, doc, appenders);
535
0
}
536
537
/**
538
 Used internally to parse the children of a logger element.
539
*/
540
void DOMConfigurator::parseChildrenOfLoggerElement(
541
  LOG4CXX_NS::helpers::Pool& p,
542
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
543
  apr_xml_elem* loggerElement, LoggerPtr logger, bool isRoot,
544
  apr_xml_doc* doc,
545
  AppenderMap& appenders)
546
0
{
547
0
  PropertySetter propSetter(logger);
548
0
  std::vector<AppenderPtr> newappenders;
549
550
0
  for (apr_xml_elem* currentElement = loggerElement->first_child;
551
0
    currentElement;
552
0
    currentElement = currentElement->next)
553
0
  {
554
0
    std::string tagName(currentElement->name);
555
556
0
    if (tagName == APPENDER_REF_TAG)
557
0
    {
558
0
      AppenderPtr appender = findAppenderByReference(p, utf8Decoder, currentElement, doc, appenders);
559
0
      LogString refName =  subst(getAttribute(utf8Decoder, currentElement, REF_ATTR));
560
561
0
      if (appender)
562
0
      {
563
0
        if (LogLog::isDebugEnabled())
564
0
          LogLog::debug(LOG4CXX_STR("Adding appender named [") + refName +
565
0
            LOG4CXX_STR("] to logger [") + logger->getName() + LOG4CXX_STR("]."));
566
0
        newappenders.push_back(appender);
567
0
      }
568
0
      else
569
0
      {
570
0
        LogLog::debug(LOG4CXX_STR("Appender named [") + refName +
571
0
          LOG4CXX_STR("] not found."));
572
0
      }
573
574
0
    }
575
0
    else if (tagName == LEVEL_TAG)
576
0
    {
577
0
      parseLevel(p, utf8Decoder, currentElement, logger, isRoot);
578
0
    }
579
0
    else if (tagName == PRIORITY_TAG)
580
0
    {
581
0
      parseLevel(p, utf8Decoder, currentElement, logger, isRoot);
582
0
    }
583
0
    else if (tagName == PARAM_TAG)
584
0
    {
585
0
      setParameter(p, utf8Decoder, currentElement, propSetter);
586
0
    }
587
0
  }
588
0
  if (newappenders.empty())
589
0
    logger->removeAllAppenders();
590
0
  else
591
0
    logger->replaceAppenders(newappenders);
592
593
0
  propSetter.activate(p);
594
0
}
595
596
/**
597
 Used internally to parse a layout element.
598
*/
599
LayoutPtr DOMConfigurator::parseLayout (
600
  LOG4CXX_NS::helpers::Pool& p,
601
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
602
  apr_xml_elem* layout_element)
603
0
{
604
0
  LogString className(subst(getAttribute(utf8Decoder, layout_element, CLASS_ATTR)));
605
0
  if (LogLog::isDebugEnabled())
606
0
  {
607
0
    LogLog::debug(LOG4CXX_STR("Parsing layout of class: \"") + className + LOG4CXX_STR("\""));
608
0
  }
609
610
0
  try
611
0
  {
612
0
    ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
613
0
    LayoutPtr layout = LOG4CXX_NS::cast<Layout>(instance);
614
0
    PropertySetter propSetter(layout);
615
616
0
    for (apr_xml_elem* currentElement = layout_element->first_child;
617
0
      currentElement;
618
0
      currentElement = currentElement->next)
619
0
    {
620
0
      std::string tagName(currentElement->name);
621
622
0
      if (tagName == PARAM_TAG)
623
0
      {
624
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
625
0
      }
626
0
    }
627
628
0
    propSetter.activate(p);
629
0
    return layout;
630
0
  }
631
0
  catch (Exception& oops)
632
0
  {
633
0
    LogLog::error(LOG4CXX_STR("Could not create the Layout. Reported error follows."),
634
0
      oops);
635
0
    return 0;
636
0
  }
637
0
}
638
639
/**
640
 Used internally to parse a triggering policy
641
*/
642
ObjectPtr DOMConfigurator::parseTriggeringPolicy (
643
  LOG4CXX_NS::helpers::Pool& p,
644
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
645
  apr_xml_elem* layout_element)
646
0
{
647
0
  LogString className = subst(getAttribute(utf8Decoder, layout_element, CLASS_ATTR));
648
0
  if (LogLog::isDebugEnabled())
649
0
  {
650
0
    LogLog::debug(LOG4CXX_STR("Parsing triggering policy of class: \"") + className + LOG4CXX_STR("\""));
651
0
  }
652
653
0
  try
654
0
  {
655
0
    ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
656
0
    PropertySetter propSetter(instance);
657
658
0
    for (apr_xml_elem* currentElement = layout_element->first_child;
659
0
      currentElement;
660
0
      currentElement = currentElement->next)
661
0
    {
662
0
      std::string tagName(currentElement->name);
663
664
0
      if (tagName == PARAM_TAG)
665
0
      {
666
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
667
0
      }
668
0
      else if (tagName == FILTER_TAG)
669
0
      {
670
0
        std::vector<LOG4CXX_NS::spi::FilterPtr> filters;
671
0
        parseFilters(p, utf8Decoder, currentElement, filters);
672
0
        FilterBasedTriggeringPolicyPtr fbtp = LOG4CXX_NS::cast<FilterBasedTriggeringPolicy>(instance);
673
674
0
        if (fbtp != NULL)
675
0
        {
676
0
          for (auto& item : filters)
677
0
          {
678
0
            fbtp->addFilter(item);
679
0
          }
680
0
        }
681
0
      }
682
0
    }
683
684
0
    propSetter.activate(p);
685
0
    return instance;
686
0
  }
687
0
  catch (Exception& oops)
688
0
  {
689
0
    LogLog::error(LOG4CXX_STR("Could not create the TriggeringPolicy. Reported error follows."),
690
0
      oops);
691
0
    return 0;
692
0
  }
693
0
}
694
695
/**
696
 Used internally to parse a triggering policy
697
*/
698
RollingPolicyPtr DOMConfigurator::parseRollingPolicy (
699
  LOG4CXX_NS::helpers::Pool& p,
700
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
701
  apr_xml_elem* layout_element)
702
0
{
703
0
  LogString className = subst(getAttribute(utf8Decoder, layout_element, CLASS_ATTR));
704
0
  if (LogLog::isDebugEnabled())
705
0
  {
706
0
    LogLog::debug(LOG4CXX_STR("Parsing rolling policy of class: \"") + className + LOG4CXX_STR("\""));
707
0
  }
708
709
0
  try
710
0
  {
711
0
    ObjectPtr instance = ObjectPtr(Loader::loadClass(className).newInstance());
712
0
    RollingPolicyPtr layout = LOG4CXX_NS::cast<RollingPolicy>(instance);
713
0
    PropertySetter propSetter(layout);
714
715
0
    for (apr_xml_elem* currentElement = layout_element->first_child;
716
0
      currentElement;
717
0
      currentElement = currentElement->next)
718
0
    {
719
0
      std::string tagName(currentElement->name);
720
721
0
      if (tagName == PARAM_TAG)
722
0
      {
723
0
        setParameter(p, utf8Decoder, currentElement, propSetter);
724
0
      }
725
0
    }
726
727
0
    propSetter.activate(p);
728
0
    return layout;
729
0
  }
730
0
  catch (Exception& oops)
731
0
  {
732
0
    LogLog::error(LOG4CXX_STR("Could not create the RollingPolicy. Reported error follows."),
733
0
      oops);
734
0
    return 0;
735
0
  }
736
0
}
737
738
739
740
/**
741
 Used internally to parse a level  element.
742
*/
743
void DOMConfigurator::parseLevel(
744
  LOG4CXX_NS::helpers::Pool& p,
745
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
746
  apr_xml_elem* element, LoggerPtr logger, bool isRoot)
747
0
{
748
0
  LogString loggerName = logger->getName();
749
750
0
  if (isRoot)
751
0
  {
752
0
    loggerName = LOG4CXX_STR("root");
753
0
  }
754
755
0
  LogString levelStr(subst(getAttribute(utf8Decoder, element, VALUE_ATTR)));
756
0
  if (LogLog::isDebugEnabled())
757
0
  {
758
0
    LogLog::debug(LOG4CXX_STR("Level value for ") + loggerName + LOG4CXX_STR(" is [") + levelStr + LOG4CXX_STR("]."));
759
0
  }
760
761
0
  if (StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("INHERITED"), LOG4CXX_STR("inherited"))
762
0
    || StringHelper::equalsIgnoreCase(levelStr, LOG4CXX_STR("NULL"), LOG4CXX_STR("null")))
763
0
  {
764
0
    if (isRoot)
765
0
    {
766
0
      LogLog::error(LOG4CXX_STR("Root level cannot be inherited. Ignoring directive."));
767
0
    }
768
0
    else
769
0
    {
770
0
      logger->setLevel(0);
771
0
    }
772
0
  }
773
0
  else
774
0
  {
775
0
    LogString className(subst(getAttribute(utf8Decoder, element, CLASS_ATTR)));
776
777
0
    if (className.empty())
778
0
    {
779
0
      logger->setLevel(OptionConverter::toLevel(levelStr, Level::getDebug()));
780
0
    }
781
0
    else
782
0
    {
783
0
      if (LogLog::isDebugEnabled())
784
0
      {
785
0
        LogLog::debug(LOG4CXX_STR("Desired Level sub-class: [") + className + LOG4CXX_STR("]"));
786
0
      }
787
788
0
      try
789
0
      {
790
0
        Level::LevelClass& levelClass =
791
0
          (Level::LevelClass&)Loader::loadClass(className);
792
0
        LevelPtr level = levelClass.toLevel(levelStr);
793
0
        logger->setLevel(level);
794
0
      }
795
0
      catch (Exception& oops)
796
0
      {
797
0
        LogLog::error(
798
0
          LOG4CXX_STR("Could not create level [") + levelStr +
799
0
          LOG4CXX_STR("]. Reported error follows."),
800
0
          oops);
801
802
0
        return;
803
0
      }
804
0
      catch (...)
805
0
      {
806
0
        LogLog::error(
807
0
          LOG4CXX_STR("Could not create level [") + levelStr);
808
809
0
        return;
810
0
      }
811
0
    }
812
0
  }
813
814
0
  if (LogLog::isDebugEnabled())
815
0
  {
816
0
    LogLog::debug(loggerName + LOG4CXX_STR(" level set to ") +
817
0
      logger->getEffectiveLevel()->toString());
818
0
  }
819
0
}
820
821
void DOMConfigurator::setParameter(LOG4CXX_NS::helpers::Pool& p,
822
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
823
  apr_xml_elem* elem,
824
  PropertySetter& propSetter)
825
0
{
826
0
  LogString name(subst(getAttribute(utf8Decoder, elem, NAME_ATTR)));
827
0
  LogString value(subst(getAttribute(utf8Decoder, elem, VALUE_ATTR)));
828
0
  value = subst(value);
829
0
  propSetter.setProperty(name, value, p);
830
0
}
831
832
spi::ConfigurationStatus DOMConfigurator::doConfigure
833
  ( const File&                     filename
834
#if LOG4CXX_ABI_VERSION <= 15
835
  , spi::LoggerRepositoryPtr        repository
836
#else
837
  , const spi::LoggerRepositoryPtr& repository
838
#endif
839
  )
840
0
{
841
0
  m_priv->repository = repository ? repository : LogManager::getLoggerRepository();
842
0
  m_priv->repository->setConfigured(true);
843
0
  if (LogLog::isDebugEnabled())
844
0
  {
845
0
    LogString msg(LOG4CXX_STR("DOMConfigurator configuring file "));
846
0
    msg.append(filename.getPath());
847
0
    msg.append(LOG4CXX_STR("..."));
848
0
    LogLog::debug(msg);
849
0
  }
850
851
0
#if LOG4CXX_ABI_VERSION <= 15
852
0
  m_priv->loggerFactory = std::make_shared<DefaultLoggerFactory>();
853
#else
854
  m_priv->loggerFactory = std::make_shared<LoggerFactory>();
855
#endif
856
857
0
  Pool p;
858
0
  apr_file_t* fd;
859
860
0
  log4cxx_status_t rv = filename.open(&fd, APR_READ, APR_OS_DEFAULT, p);
861
862
0
  if (rv != APR_SUCCESS)
863
0
  {
864
    // There is not technically an exception thrown here, but this behavior matches
865
    // what the PropertyConfigurator does
866
0
    IOException io(rv);
867
0
    LogString msg2(LOG4CXX_STR("Could not read configuration file ["));
868
0
    msg2.append(filename.getPath());
869
0
    msg2.append(LOG4CXX_STR("]. "));
870
0
    LOG4CXX_DECODE_CHAR(msg, io.what());
871
0
    msg2.append(msg);
872
0
    LogLog::error(msg2);
873
0
    return spi::ConfigurationStatus::NotConfigured;
874
0
  }
875
0
  else
876
0
  {
877
0
    apr_xml_parser* parser = NULL;
878
0
    apr_xml_doc* doc = NULL;
879
880
0
    if (LogLog::isDebugEnabled())
881
0
    {
882
0
      LogString debugMsg = LOG4CXX_STR("Loading configuration file [")
883
0
          + filename.getPath() + LOG4CXX_STR("].");
884
0
      LogLog::debug(debugMsg);
885
0
    }
886
887
0
    rv = apr_xml_parse_file(p.getAPRPool(), &parser, &doc, fd, 2000);
888
889
0
    if (rv != APR_SUCCESS)
890
0
    {
891
0
      char errbuf[2000];
892
0
      char errbufXML[2000];
893
0
      LogString msg2(LOG4CXX_STR("Error parsing file ["));
894
0
      msg2.append(filename.getPath());
895
0
      msg2.append(LOG4CXX_STR("], "));
896
0
      apr_strerror(rv, errbuf, sizeof(errbuf));
897
0
      LOG4CXX_DECODE_CHAR(lerrbuf, std::string(errbuf));
898
0
      msg2.append(lerrbuf);
899
900
0
      if (parser)
901
0
      {
902
0
        apr_xml_parser_geterror(parser, errbufXML, sizeof(errbufXML));
903
0
        LOG4CXX_DECODE_CHAR(lerrbufXML, std::string(errbufXML));
904
0
        msg2.append(lerrbufXML);
905
0
      }
906
907
0
      LogLog::error(msg2);
908
0
      return spi::ConfigurationStatus::NotConfigured;
909
0
    }
910
0
    else
911
0
    {
912
0
      AppenderMap appenders;
913
0
      CharsetDecoderPtr utf8Decoder(CharsetDecoder::getUTF8Decoder());
914
0
      parse(p, utf8Decoder, doc->root, doc, appenders);
915
0
    }
916
0
  }
917
918
0
  return spi::ConfigurationStatus::Configured;
919
0
}
920
921
// Read configuration options from \c filename.
922
spi::ConfigurationStatus DOMConfigurator::configure(const File& filename)
923
0
{
924
0
  return DOMConfigurator().doConfigure(filename, LogManager::getLoggerRepository());
925
0
}
926
927
#if LOG4CXX_ABI_VERSION <= 15
928
spi::ConfigurationStatus DOMConfigurator::configure(const std::string& filename)
929
0
{
930
0
  File file(filename);
931
0
  return DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
932
0
}
933
934
#if LOG4CXX_WCHAR_T_API
935
spi::ConfigurationStatus DOMConfigurator::configure(const std::wstring& filename)
936
0
{
937
0
  File file(filename);
938
0
  return DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
939
0
}
940
#endif
941
942
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
943
spi::ConfigurationStatus DOMConfigurator::configure(const std::basic_string<UniChar>& filename)
944
{
945
  File file(filename);
946
  return DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
947
}
948
#endif
949
950
#if LOG4CXX_CFSTRING_API
951
spi::ConfigurationStatus DOMConfigurator::configure(const CFStringRef& filename)
952
{
953
  File file(filename);
954
  return DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
955
}
956
#endif
957
958
959
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::string& filename)
960
0
{
961
0
  return configureAndWatch(filename, FileWatchdog::DEFAULT_DELAY);
962
0
}
963
964
#if LOG4CXX_WCHAR_T_API
965
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::wstring& filename)
966
0
{
967
0
  return configureAndWatch(filename, FileWatchdog::DEFAULT_DELAY);
968
0
}
969
#endif
970
971
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
972
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::basic_string<UniChar>& filename)
973
{
974
  return configureAndWatch(filename, FileWatchdog::DEFAULT_DELAY);
975
}
976
#endif
977
978
#if LOG4CXX_CFSTRING_API
979
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const CFStringRef& filename)
980
{
981
  return configureAndWatch(filename, FileWatchdog::DEFAULT_DELAY);
982
}
983
#endif
984
985
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::string& filename, long delay)
986
0
{
987
0
  return configureAndWatch(File(filename), delay);
988
0
}
989
#endif // LOG4CXX_ABI_VERSION <= 15
990
991
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const File& file, long delay)
992
0
{
993
0
  spi::ConfigurationStatus status = DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
994
0
  XMLWatchdog::startWatching(file, delay);
995
0
  return status;
996
0
}
997
998
#if LOG4CXX_ABI_VERSION <= 15
999
#if LOG4CXX_WCHAR_T_API
1000
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::wstring& filename, long delay)
1001
0
{
1002
0
  File file(filename);
1003
0
  spi::ConfigurationStatus status = DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
1004
0
  XMLWatchdog::startWatching(file, delay);
1005
0
  return status;
1006
0
}
1007
#endif
1008
1009
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
1010
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::basic_string<UniChar>& filename, long delay)
1011
{
1012
  File file(filename);
1013
  spi::ConfigurationStatus status = DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
1014
  XMLWatchdog::startWatching(file, delay);
1015
  return status;
1016
}
1017
#endif
1018
1019
#if LOG4CXX_CFSTRING_API
1020
spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const CFStringRef& filename, long delay)
1021
{
1022
  File file(filename);
1023
  spi::ConfigurationStatus status = DOMConfigurator().doConfigure(file, LogManager::getLoggerRepository());
1024
  XMLWatchdog::startWatching(file, delay);
1025
  return status;
1026
}
1027
#endif
1028
#endif // LOG4CXX_ABI_VERSION <= 15
1029
1030
void DOMConfigurator::parse(
1031
  Pool& p,
1032
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
1033
  apr_xml_elem* element,
1034
  apr_xml_doc* doc,
1035
  AppenderMap& appenders)
1036
0
{
1037
0
  std::string rootElementName(element->name);
1038
1039
0
  if (rootElementName != CONFIGURATION_TAG)
1040
0
  {
1041
0
    if (rootElementName == OLD_CONFIGURATION_TAG)
1042
0
    {
1043
      //LogLog::warn(LOG4CXX_STR("The <")+String(OLD_CONFIGURATION_TAG)+
1044
      // LOG4CXX_STR("> element has been deprecated."));
1045
      //LogLog::warn(LOG4CXX_STR("Use the <")+String(CONFIGURATION_TAG)+
1046
      // LOG4CXX_STR("> element instead."));
1047
0
    }
1048
0
    else
1049
0
    {
1050
0
      LogLog::error(LOG4CXX_STR("DOM element is - not a <configuration> element."));
1051
0
      return;
1052
0
    }
1053
0
  }
1054
1055
0
  LogString debugAttrib = subst(getAttribute(utf8Decoder, element, INTERNAL_DEBUG_ATTR));
1056
1057
0
  static const WideLife<LogString> NULL_STRING(LOG4CXX_STR("NULL"));
1058
0
  if (LogLog::isDebugEnabled())
1059
0
  {
1060
0
    LogLog::debug(LOG4CXX_STR("debug attribute= \"") + debugAttrib + LOG4CXX_STR("\"."));
1061
0
  }
1062
1063
  // if the log4j.dtd is not specified in the XML file, then the
1064
  // "debug" attribute is returned as the empty string.
1065
0
  if (!debugAttrib.empty() && debugAttrib != NULL_STRING.value())
1066
0
  {
1067
0
    LogLog::setInternalDebugging(OptionConverter::toBoolean(debugAttrib, true));
1068
0
  }
1069
0
  else if (LogLog::isDebugEnabled())
1070
0
  {
1071
0
    LogLog::debug(LOG4CXX_STR("Ignoring internalDebug attribute."));
1072
0
  }
1073
1074
1075
0
  LogString confDebug = subst(getAttribute(utf8Decoder, element, CONFIG_DEBUG_ATTR));
1076
1077
0
  if (!confDebug.empty() && confDebug != NULL_STRING.value())
1078
0
  {
1079
0
    LogLog::warn(LOG4CXX_STR("The \"configDebug\" attribute is deprecated."));
1080
0
    LogLog::warn(LOG4CXX_STR("Use the \"internalDebug\" attribute instead."));
1081
0
    LogLog::setInternalDebugging(OptionConverter::toBoolean(confDebug, true));
1082
0
  }
1083
1084
0
  LogString thresholdStr = subst(getAttribute(utf8Decoder, element, THRESHOLD_ATTR));
1085
0
  if (LogLog::isDebugEnabled())
1086
0
  {
1087
0
    LogLog::debug(LOG4CXX_STR("Threshold =\"") + thresholdStr + LOG4CXX_STR("\"."));
1088
0
  }
1089
1090
0
  if (!thresholdStr.empty() && thresholdStr != NULL_STRING.value())
1091
0
  {
1092
0
    m_priv->repository->setThreshold(thresholdStr);
1093
0
  }
1094
1095
0
  LogString threadSignalValue = subst(getAttribute(utf8Decoder, element, THREAD_CONFIG_ATTR));
1096
1097
0
  if ( !threadSignalValue.empty() && threadSignalValue != NULL_STRING.value() )
1098
0
  {
1099
0
    if ( threadSignalValue == LOG4CXX_STR("NoConfiguration") )
1100
0
    {
1101
0
      helpers::ThreadUtility::configure( ThreadConfigurationType::NoConfiguration );
1102
0
    }
1103
0
    else if ( threadSignalValue == LOG4CXX_STR("BlockSignalsOnly") )
1104
0
    {
1105
0
      helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsOnly );
1106
0
    }
1107
0
    else if ( threadSignalValue == LOG4CXX_STR("NameThreadOnly") )
1108
0
    {
1109
0
      helpers::ThreadUtility::configure( ThreadConfigurationType::NameThreadOnly );
1110
0
    }
1111
0
    else if ( threadSignalValue == LOG4CXX_STR("BlockSignalsAndNameThread") )
1112
0
    {
1113
0
      helpers::ThreadUtility::configure( ThreadConfigurationType::BlockSignalsAndNameThread );
1114
0
    }
1115
0
  }
1116
1117
0
  apr_xml_elem* currentElement;
1118
1119
0
  for (currentElement = element->first_child;
1120
0
    currentElement;
1121
0
    currentElement = currentElement->next)
1122
0
  {
1123
0
    std::string tagName(currentElement->name);
1124
1125
0
    if (tagName == CATEGORY_FACTORY_TAG)
1126
0
    {
1127
0
      parseLoggerFactory(p, utf8Decoder, currentElement);
1128
0
    }
1129
0
  }
1130
1131
0
  for (currentElement = element->first_child;
1132
0
    currentElement;
1133
0
    currentElement = currentElement->next)
1134
0
  {
1135
0
    std::string tagName(currentElement->name);
1136
1137
0
    if (tagName == CATEGORY || tagName == LOGGER)
1138
0
    {
1139
0
      parseLogger(p, utf8Decoder, currentElement, doc, appenders);
1140
0
    }
1141
0
    else if (tagName == ROOT_TAG)
1142
0
    {
1143
0
      parseRoot(p, utf8Decoder, currentElement, doc, appenders);
1144
0
    }
1145
0
  }
1146
0
}
1147
1148
LogString DOMConfigurator::subst(const LogString& value)
1149
0
{
1150
0
  try
1151
0
  {
1152
0
    return OptionConverter::substVars(value, m_priv->props);
1153
0
  }
1154
0
  catch (IllegalArgumentException& e)
1155
0
  {
1156
0
    LogLog::warn(LOG4CXX_STR("Could not perform variable substitution."), e);
1157
0
    return value;
1158
0
  }
1159
0
}
1160
1161
1162
LogString DOMConfigurator::getAttribute(
1163
  LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
1164
  apr_xml_elem* element,
1165
  const std::string& attrName)
1166
0
{
1167
0
  LogString attrValue;
1168
1169
0
  for (apr_xml_attr* attr = element->attr;
1170
0
    attr;
1171
0
    attr = attr->next)
1172
0
  {
1173
0
    if (attrName == attr->name)
1174
0
    {
1175
0
      ByteBuffer buf((char*) attr->value, strnlen_s(attr->value, MAX_ATTRIBUTE_NAME_LEN));
1176
0
      utf8Decoder->decode(buf, attrValue);
1177
0
    }
1178
0
  }
1179
1180
0
  return attrValue;
1181
0
}