Coverage Report

Created: 2026-05-30 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/asyncappender.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/asyncappender.h>
19
20
#include <log4cxx/helpers/loglog.h>
21
#include <log4cxx/spi/loggingevent.h>
22
#include <log4cxx/helpers/stringhelper.h>
23
#include <log4cxx/helpers/optionconverter.h>
24
#include <log4cxx/helpers/threadutility.h>
25
#include <log4cxx/private/appenderskeleton_priv.h>
26
#include <thread>
27
#include <atomic>
28
#include <condition_variable>
29
30
#if LOG4CXX_EVENTS_AT_EXIT
31
#include <log4cxx/private/atexitregistry.h>
32
#endif
33
34
using namespace LOG4CXX_NS;
35
using namespace LOG4CXX_NS::helpers;
36
using namespace LOG4CXX_NS::spi;
37
38
#if 15 < LOG4CXX_ABI_VERSION
39
namespace
40
{
41
#endif
42
43
/**
44
 * The default buffer size is set to 128 events.
45
*/
46
enum { DEFAULT_BUFFER_SIZE = 128 };
47
48
class DiscardSummary
49
{
50
  private:
51
    /**
52
     * First event of the highest severity.
53
    */
54
    LoggingEventPtr maxEvent;
55
56
    /**
57
    * Total count of messages discarded.
58
    */
59
    int count;
60
61
    /**
62
    * Why created
63
    */
64
    LogString reason;
65
66
  public:
67
    /**
68
     * Create new instance.
69
     *
70
     * @param event must not be null.
71
    */
72
    DiscardSummary(const LoggingEventPtr& event, const LogString& reason);
73
74
    /** Move values from \c src into a new instance.
75
    */
76
    DiscardSummary(DiscardSummary&& src);
77
#if 15 < LOG4CXX_ABI_VERSION
78
    /** Copy constructor.  */
79
    DiscardSummary(const DiscardSummary&) = delete;
80
    /** Assignment operator. */
81
    DiscardSummary& operator=(const DiscardSummary&) = delete;
82
#else
83
    /**
84
     * Create new instance.
85
     *
86
     * @param event event, may not be null.
87
    */
88
    DiscardSummary(const LoggingEventPtr& event);
89
    /** Copy constructor.  */
90
    DiscardSummary(const DiscardSummary& src);
91
    /** Assignment operator. */
92
    DiscardSummary& operator=(const DiscardSummary& src);
93
#endif
94
95
    /**
96
     * Add discarded event to summary.
97
     *
98
     * @param event event, may not be null.
99
    */
100
    void add(const LoggingEventPtr& event);
101
102
    /**
103
     * Create an event with a discard count and the message from \c maxEvent.
104
     *
105
     * @return the new event.
106
     */
107
    LoggingEventPtr createEvent();
108
109
#if LOG4CXX_ABI_VERSION <= 15
110
    LoggingEventPtr createEvent(Pool&);
111
    static
112
    ::LOG4CXX_NS::spi::LoggingEventPtr createEvent(::LOG4CXX_NS::helpers::Pool& p,
113
      size_t discardedCount);
114
#endif
115
116
    /**
117
    * The number of messages discarded.
118
    */
119
0
    int getCount() const { return count; }
120
};
121
122
typedef std::map<LogString, DiscardSummary> DiscardMap;
123
124
#if 15 < LOG4CXX_ABI_VERSION
125
}
126
#endif
127
128
#ifdef __cpp_lib_hardware_interference_size
129
  using std::hardware_constructive_interference_size;
130
  using std::hardware_destructive_interference_size;
131
#else
132
  // 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...
133
  constexpr std::size_t hardware_constructive_interference_size = 64;
134
  constexpr std::size_t hardware_destructive_interference_size = 64;
135
#endif
136
137
struct AsyncAppender::AsyncAppenderPriv : public AppenderSkeleton::AppenderSkeletonPrivate
138
{
139
  using BaseType = AppenderSkeleton::AppenderSkeletonPrivate;
140
  AsyncAppenderPriv()
141
0
    : AppenderSkeletonPrivate()
142
0
    , buffer(DEFAULT_BUFFER_SIZE)
143
0
    , bufferSize(DEFAULT_BUFFER_SIZE)
144
0
    , dispatcher()
145
0
    , locationInfo(false)
146
0
    , blocking(true)
147
#if LOG4CXX_EVENTS_AT_EXIT
148
    , atExitRegistryRaii([this]{if (setClosed()) stopDispatcher();})
149
#endif
150
0
    , eventCount(0)
151
0
    , dispatchedCount(0)
152
0
    , commitCount(0)
153
0
    { }
154
155
  ~AsyncAppenderPriv()
156
0
  {
157
0
    if (setClosed())
158
0
      close();
159
0
  }
160
161
  /**
162
   * Event buffer.
163
  */
164
  struct EventData
165
  {
166
    LoggingEventPtr event;
167
    size_t pendingCount;
168
  };
169
  std::vector<EventData> buffer;
170
171
  /**
172
   *  Mutex used to guard access to buffer and discardMap.
173
   */
174
  std::mutex bufferMutex;
175
176
  std::condition_variable bufferNotFull;
177
  std::condition_variable bufferNotEmpty;
178
179
  /**
180
    * Map of DiscardSummary objects keyed by logger name.
181
  */
182
  DiscardMap discardMap;
183
184
  /**
185
   * The maximum number of undispatched events.
186
  */
187
  int bufferSize;
188
189
  /**
190
   * Nested appenders.
191
  */
192
  helpers::AppenderAttachableImpl appenders;
193
194
  /**
195
   *  Dispatcher.
196
   */
197
  std::thread dispatcher;
198
199
  /**
200
   * Used to determine when to restart dispatch thread.
201
  */
202
  bool dispatcherActive{ false };
203
204
  /**
205
   * Used to determine whether to restart dispatch thread.
206
  */
207
  int dispatcherStartCount{ 0 };
208
209
  /**
210
   *  The function the dispatcher executes.
211
   */
212
  void dispatch(const LogString& appenderName);
213
214
  /**
215
   * Start dispatcher if not already running.
216
   */
217
  void checkDispatcher(const LogString& appenderName)
218
0
  {
219
    // Restart dispatcher if it has stopped by an exception in an attached appender.
220
0
    if (!this->dispatcherActive && this->dispatcher.joinable())
221
0
      this->dispatcher.join();
222
223
0
    if (!this->dispatcher.joinable() && this->dispatcherStartCount <= 1)
224
0
    {
225
0
      std::lock_guard<std::recursive_mutex> lock(this->mutex);
226
0
      if (!this->dispatcher.joinable())
227
0
      {
228
0
        ++this->dispatcherStartCount;
229
0
        this->dispatcherActive = true;
230
0
        this->dispatcher = ThreadUtility::instance()->createThread
231
0
          ( LOG4CXX_STR("AsyncAppender")
232
0
          , &AsyncAppender::AsyncAppenderPriv::dispatch
233
0
          , this
234
0
          , appenderName
235
0
          );
236
0
      }
237
0
    }
238
0
  }
239
240
  void stopDispatcher()
241
0
  {
242
0
    bufferNotEmpty.notify_all();
243
0
    bufferNotFull.notify_all();
244
245
0
    if (dispatcher.joinable())
246
0
    {
247
0
      dispatcher.join();
248
0
    }
249
0
  }
250
251
  void close();
252
253
  /**
254
   * Should location info be included in dispatched messages.
255
  */
256
  bool locationInfo;
257
258
  /**
259
   * Does appender block when buffer is full.
260
  */
261
  bool blocking;
262
263
#if LOG4CXX_EVENTS_AT_EXIT
264
  helpers::AtExitRegistry::Raii atExitRegistryRaii;
265
#endif
266
267
  /**
268
   * Used to calculate the buffer position at which to store the next event.
269
  */
270
  alignas(hardware_constructive_interference_size) std::atomic<size_t> eventCount;
271
272
  /**
273
   * Used to calculate the buffer position from which to extract the next event.
274
  */
275
  alignas(hardware_constructive_interference_size) std::atomic<size_t> dispatchedCount;
276
277
  /**
278
   * Used to communicate to the dispatch thread when an event is committed in buffer.
279
  */
280
  alignas(hardware_constructive_interference_size) std::atomic<size_t> commitCount;
281
282
  bool isClosed()
283
0
  {
284
0
    std::lock_guard<std::mutex> lock(this->bufferMutex);
285
0
    return this->closed;
286
0
  }
287
288
  bool setClosed()
289
0
  {
290
0
    std::lock_guard<std::mutex> lock(this->bufferMutex);
291
0
    return BaseType::setClosed();
292
0
  }
293
294
  /**
295
   * Used to ensure the dispatch thread does not wait when a logging thread is waiting.
296
  */
297
  alignas(hardware_constructive_interference_size) int blockedCount{0};
298
};
299
300
301
IMPLEMENT_LOG4CXX_OBJECT(AsyncAppender)
302
303
0
#define priv static_cast<AsyncAppenderPriv*>(m_priv.get())
304
305
AsyncAppender::AsyncAppender()
306
0
  : AppenderSkeleton(std::make_unique<AsyncAppenderPriv>())
307
0
{
308
0
}
Unexecuted instantiation: log4cxx::AsyncAppender::AsyncAppender()
Unexecuted instantiation: log4cxx::AsyncAppender::AsyncAppender()
309
310
AsyncAppender::~AsyncAppender()
311
0
{
312
0
}
313
314
void AsyncAppender::addAppender(const AppenderPtr newAppender)
315
0
{
316
0
  priv->appenders.addAppender(newAppender);
317
0
}
318
319
320
void AsyncAppender::setOption(const LogString& option,
321
  const LogString& value)
322
0
{
323
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
324
0
  {
325
0
    setLocationInfo(OptionConverter::toBoolean(value, false));
326
0
  }
327
328
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
329
0
  {
330
0
    setBufferSize(OptionConverter::toInt(value, DEFAULT_BUFFER_SIZE));
331
0
  }
332
333
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BLOCKING"), LOG4CXX_STR("blocking")))
334
0
  {
335
0
    setBlocking(OptionConverter::toBoolean(value, true));
336
0
  }
337
0
  else
338
0
  {
339
0
    AppenderSkeleton::setOption(option, value);
340
0
  }
341
0
}
342
343
344
void AsyncAppender::doAppend( LOG4CXX_APPEND_FORMAL_PARAMETERS )
345
0
{
346
0
  doAppendImpl( LOG4CXX_APPEND_PARAMETERS );
347
0
}
348
349
void AsyncAppender::append( LOG4CXX_APPEND_FORMAL_PARAMETERS )
350
0
{
351
0
  if (priv->bufferSize <= 0)
352
0
  {
353
0
    priv->appenders.appendLoopOnAppenders(event);
354
0
    return;
355
0
  }
356
357
  // Get a copy of this thread's diagnostic context
358
0
  event->LoadDC();
359
360
0
  priv->checkDispatcher(getName());
361
362
0
  if (priv->dispatcher.get_id() == std::this_thread::get_id()) // From an appender attached to this?
363
0
  {
364
0
    std::unique_lock<std::mutex> lock(priv->bufferMutex);
365
0
    auto loggerName = event->getLoggerName();
366
0
    auto iter = priv->discardMap.find(loggerName);
367
0
    if (priv->discardMap.end() == iter)
368
0
      priv->discardMap.emplace(loggerName, DiscardSummary{ event, LOG4CXX_STR("from an attached appender") });
369
0
    else
370
0
      iter->second.add(event);
371
0
  }
372
0
  else while (true)
373
0
  {
374
0
    auto pendingCount = priv->eventCount - priv->dispatchedCount;
375
0
    if (0 <= pendingCount && pendingCount < priv->bufferSize)
376
0
    {
377
      // Claim a slot in the ring buffer
378
0
      auto oldEventCount = priv->eventCount++;
379
0
      auto index = oldEventCount % priv->buffer.size();
380
      // Wait for a free slot
381
0
      while (priv->bufferSize <= oldEventCount - priv->dispatchedCount)
382
0
        std::this_thread::yield(); // Allow the dispatch thread to free a slot
383
      // Write to the ring buffer
384
0
      priv->buffer[index] = AsyncAppenderPriv::EventData{event, pendingCount};
385
      // Notify the dispatch thread that an event has been added
386
0
      auto failureCount = 0;
387
0
      auto savedEventCount = oldEventCount;
388
0
      while (!priv->commitCount.compare_exchange_weak(oldEventCount, oldEventCount + 1, std::memory_order_release))
389
0
      {
390
0
        oldEventCount = savedEventCount;
391
0
        if (2 < ++failureCount) // Did the scheduler suspend a thread between claiming a slot and advancing commitCount?
392
0
          std::this_thread::yield(); // Wait a bit
393
0
      }
394
0
      priv->bufferNotEmpty.notify_all();
395
0
      break;
396
0
    }
397
    //
398
    //   Following code is only reachable if buffer is full or eventCount has overflowed
399
    //
400
0
    std::unique_lock<std::mutex> lock(priv->bufferMutex);
401
0
    priv->bufferNotEmpty.notify_all();
402
    //
403
    //   if blocking and thread is not already interrupted
404
    //      and not the dispatcher then
405
    //      wait for a buffer notification
406
0
    bool discard = true;
407
408
0
    if (priv->blocking
409
0
      && !priv->closed)
410
0
    {
411
0
      ++priv->blockedCount;
412
0
      priv->bufferNotFull.wait(lock, [this]()
413
0
      {
414
0
        priv->checkDispatcher(getName());
415
0
        return priv->eventCount - priv->dispatchedCount < priv->bufferSize;
416
0
      });
417
0
      --priv->blockedCount;
418
0
      discard = false;
419
0
    }
420
421
    //
422
    //   if blocking is false or thread has been interrupted
423
    //   add event to discard map.
424
    //
425
0
    if (discard)
426
0
    {
427
0
      LogString loggerName = event->getLoggerName();
428
0
      DiscardMap::iterator iter = priv->discardMap.find(loggerName);
429
430
0
      if (iter == priv->discardMap.end())
431
0
      {
432
0
        priv->discardMap.emplace(loggerName, DiscardSummary{ event, LOG4CXX_STR("due to a full event buffer") });
433
0
      }
434
0
      else
435
0
      {
436
0
        iter->second.add(event);
437
0
      }
438
439
0
      break;
440
0
    }
441
0
  }
442
0
}
443
444
void AsyncAppender::close()
445
0
{
446
0
  if (priv->setClosed())
447
0
    priv->close();
448
0
}
449
450
void AsyncAppender::AsyncAppenderPriv::close()
451
0
{
452
0
  this->stopDispatcher();
453
0
  for (auto item : this->appenders.getAllAppenders())
454
0
  {
455
0
    item->close();
456
0
  }
457
0
}
458
459
AppenderList AsyncAppender::getAllAppenders() const
460
0
{
461
0
  return priv->appenders.getAllAppenders();
462
0
}
463
464
AppenderPtr AsyncAppender::getAppender(const LogString& n) const
465
0
{
466
0
  return priv->appenders.getAppender(n);
467
0
}
468
469
bool AsyncAppender::isAttached(const AppenderPtr appender) const
470
0
{
471
0
  return priv->appenders.isAttached(appender);
472
0
}
473
474
bool AsyncAppender::requiresLayout() const
475
0
{
476
0
  return false;
477
0
}
478
479
void AsyncAppender::removeAllAppenders()
480
0
{
481
0
  priv->appenders.removeAllAppenders();
482
0
}
483
484
void AsyncAppender::removeAppender(const AppenderPtr appender)
485
0
{
486
0
  priv->appenders.removeAppender(appender);
487
0
}
488
489
void AsyncAppender::removeAppender(const LogString& n)
490
0
{
491
0
  priv->appenders.removeAppender(n);
492
0
}
493
494
bool AsyncAppender::replaceAppender(const AppenderPtr& oldAppender, const AppenderPtr& newAppender)
495
0
{
496
0
  return priv->appenders.replaceAppender(oldAppender, newAppender);
497
0
}
498
499
void AsyncAppender::replaceAppenders( const AppenderList& newList)
500
0
{
501
0
  priv->appenders.replaceAppenders(newList);
502
0
}
503
504
bool AsyncAppender::getLocationInfo() const
505
0
{
506
0
  return priv->locationInfo;
507
0
}
508
509
void AsyncAppender::setLocationInfo(bool flag)
510
0
{
511
0
  priv->locationInfo = flag;
512
0
}
513
514
515
void AsyncAppender::setBufferSize(int size)
516
0
{
517
0
  if (size < 0)
518
0
  {
519
0
    throw IllegalArgumentException(LOG4CXX_STR("size argument must be non-negative"));
520
0
  }
521
522
0
  std::lock_guard<std::mutex> lock(priv->bufferMutex);
523
0
  if (priv->dispatcher.joinable())
524
0
  {
525
0
    throw RuntimeException(LOG4CXX_STR("AsyncAppender buffer size cannot be changed now"));
526
0
  }
527
0
  priv->bufferSize = (size < 1) ? 1 : size;
528
0
  priv->buffer.resize(priv->bufferSize);
529
0
  priv->bufferNotFull.notify_all();
530
0
}
531
532
int AsyncAppender::getBufferSize() const
533
0
{
534
0
  std::lock_guard<std::mutex> lock(priv->bufferMutex);
535
0
  return priv->bufferSize;
536
0
}
537
538
void AsyncAppender::setBlocking(bool value)
539
0
{
540
0
  std::lock_guard<std::mutex> lock(priv->bufferMutex);
541
0
  priv->blocking = value;
542
0
  priv->bufferNotFull.notify_all();
543
0
}
544
545
bool AsyncAppender::getBlocking() const
546
0
{
547
0
  std::lock_guard<std::mutex> lock(priv->bufferMutex);
548
0
  return priv->blocking;
549
0
}
550
551
DiscardSummary::DiscardSummary(const LoggingEventPtr& event, const LogString& reasonArg)
552
0
  : maxEvent(event)
553
0
  , count(1)
554
0
  , reason(reasonArg)
555
0
{
556
0
}
557
558
DiscardSummary::DiscardSummary(DiscardSummary&& other)
559
0
  : maxEvent(std::move(other.maxEvent))
560
0
  , count(other.count)
561
0
  , reason(std::move(other.reason))
562
0
{
563
0
}
564
565
#if LOG4CXX_ABI_VERSION <= 15
566
DiscardSummary::DiscardSummary(const LoggingEventPtr& event) :
567
0
  maxEvent(event), count(1)
568
0
{
569
0
}
570
571
DiscardSummary::DiscardSummary(const DiscardSummary& src) :
572
0
  maxEvent(src.maxEvent), count(src.count)
573
0
{
574
0
}
575
576
DiscardSummary& DiscardSummary::operator=(const DiscardSummary& src)
577
0
{
578
0
  maxEvent = src.maxEvent;
579
0
  count = src.count;
580
0
  return *this;
581
0
}
582
#endif
583
584
void DiscardSummary::add(const LoggingEventPtr& event)
585
0
{
586
0
  if (this->maxEvent->getLevel()->toInt() < event->getLevel()->toInt())
587
0
    this->maxEvent = event;
588
0
  ++this->count;
589
0
}
590
591
LoggingEventPtr DiscardSummary::createEvent()
592
0
{
593
0
  LogString msg(LOG4CXX_STR("Discarded "));
594
0
  StringHelper::toString(this->count, msg);
595
0
  msg.append(LOG4CXX_STR(" messages ") + this->reason + LOG4CXX_STR(" including: "));
596
0
  msg.append(this->maxEvent->getRenderedMessage());
597
0
  return std::make_shared<LoggingEvent>
598
0
    ( this->maxEvent->getLoggerName()
599
0
    , this->maxEvent->getLevel()
600
0
    , msg
601
0
    , LocationInfo::getLocationUnavailable()
602
0
    );
603
0
}
604
#if LOG4CXX_ABI_VERSION <= 15
605
LoggingEventPtr DiscardSummary::createEvent(Pool&)
606
0
{ return createEvent(); }
607
608
::LOG4CXX_NS::spi::LoggingEventPtr
609
DiscardSummary::createEvent(::LOG4CXX_NS::helpers::Pool& p,
610
  size_t discardedCount)
611
0
{
612
0
  LogString msg(LOG4CXX_STR("Discarded "));
613
0
  StringHelper::toString(discardedCount, msg);
614
0
  msg.append(LOG4CXX_STR(" messages due to a full event buffer"));
615
616
0
  return std::make_shared<LoggingEvent>(
617
0
        LOG4CXX_STR(""),
618
0
        LOG4CXX_NS::Level::getError(),
619
0
        msg,
620
0
        LocationInfo::getLocationUnavailable() );
621
0
}
622
#endif
623
624
void AsyncAppender::AsyncAppenderPriv::dispatch(const LogString& appenderName)
625
0
{
626
0
  size_t discardCount = 0;
627
0
  size_t iterationCount = 0;
628
0
  size_t waitCount = 0;
629
0
  size_t producerBlockedCount = 0;
630
0
  int failureCount = 0;
631
0
  std::vector<size_t> pendingCountHistogram(this->bufferSize, 0);
632
0
  bool isActive = true;
633
634
0
  while (isActive)
635
0
  {
636
0
    LoggingEventList events;
637
0
    events.reserve(this->bufferSize);
638
0
    for (int count = 0; count < 2 && this->dispatchedCount == this->commitCount; ++count)
639
0
      std::this_thread::yield(); // Wait a bit
640
0
    if (this->dispatchedCount == this->commitCount)
641
0
    {
642
0
      ++waitCount;
643
0
      std::unique_lock<std::mutex> lock(this->bufferMutex);
644
0
      this->bufferNotEmpty.wait(lock, [this]() -> bool
645
0
        { return 0 < this->blockedCount || this->dispatchedCount != this->commitCount || this->closed; }
646
0
      );
647
0
    }
648
0
    isActive = !this->isClosed();
649
650
0
    while (events.size() < this->bufferSize && this->dispatchedCount != this->commitCount)
651
0
    {
652
0
      auto index = this->dispatchedCount % this->buffer.size();
653
0
      const auto& data = this->buffer[index];
654
0
      events.push_back(data.event);
655
0
      if (data.pendingCount < pendingCountHistogram.size())
656
0
        ++pendingCountHistogram[data.pendingCount];
657
0
      ++this->dispatchedCount;
658
0
    }
659
0
    this->bufferNotFull.notify_all();
660
0
    {
661
0
      std::lock_guard<std::mutex> lock(this->bufferMutex);
662
0
      producerBlockedCount += this->blockedCount;
663
0
      for (auto& discardItem : this->discardMap)
664
0
      {
665
0
        events.push_back(discardItem.second.createEvent());
666
0
        discardCount += discardItem.second.getCount();
667
0
      }
668
0
      this->discardMap.clear();
669
0
    }
670
671
0
    for (auto item : events)
672
0
    {
673
0
      try
674
0
      {
675
0
        this->appenders.appendLoopOnAppenders(item);
676
0
      }
677
0
      catch (std::exception& ex)
678
0
      {
679
0
        if (1 < ++failureCount)
680
0
          break;
681
0
        if (!this->isClosed())
682
0
          this->errorHandler->error(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("] AsyncAppender"), ex, spi::ErrorCode::WRITE_FAILURE, item);
683
0
      }
684
0
      catch (...)
685
0
      {
686
0
        if (1 < ++failureCount)
687
0
          break;
688
0
        if (!this->isClosed())
689
0
          this->errorHandler->error(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("] AsyncAppender unknown exception thrown"));
690
0
      }
691
0
    }
692
0
    ++iterationCount;
693
0
    if (1 < failureCount)
694
0
      break;
695
0
  }
696
0
  if (LogLog::isDebugEnabled())
697
0
  {
698
0
    LogString msg(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("] AsyncAppender"));
699
#ifdef _DEBUG
700
    msg += LOG4CXX_STR(" iterationCount ");
701
    StringHelper::toString(iterationCount, msg);
702
    msg += LOG4CXX_STR(" waitCount ");
703
    StringHelper::toString(waitCount, msg);
704
    msg += LOG4CXX_STR(" producerBlockedCount ");
705
    StringHelper::toString(producerBlockedCount, msg);
706
    msg += LOG4CXX_STR(" commitCount ");
707
    StringHelper::toString(this->commitCount, msg);
708
#endif
709
0
    msg += LOG4CXX_STR(" dispatchedCount ");
710
0
    StringHelper::toString(this->dispatchedCount, msg);
711
0
    msg += LOG4CXX_STR(" discardCount ");
712
0
    StringHelper::toString(discardCount, msg);
713
0
    msg += LOG4CXX_STR(" pendingCountHistogram");
714
0
    for (auto item : pendingCountHistogram)
715
0
    {
716
0
      msg += logchar(' ');
717
0
      StringHelper::toString(item, msg);
718
0
    }
719
0
    LogLog::debug(msg);
720
0
  }
721
0
  this->dispatcherActive = false;
722
0
  if (0 < this->blockedCount) // Restart this dispatcher?
723
0
    this->bufferNotFull.notify_all();
724
0
}