Coverage Report

Created: 2026-09-14 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/rollingfileappender.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/rolling/rollingfileappender.h>
19
#include <log4cxx/helpers/loglog.h>
20
#include <log4cxx/rolling/rolloverdescription.h>
21
#include <log4cxx/helpers/fileoutputstream.h>
22
#include <log4cxx/helpers/bytebuffer.h>
23
#include <log4cxx/helpers/optionconverter.h>
24
#include <log4cxx/helpers/stringhelper.h>
25
#include <log4cxx/rolling/fixedwindowrollingpolicy.h>
26
#include <log4cxx/rolling/timebasedrollingpolicy.h>
27
#include <log4cxx/rolling/sizebasedtriggeringpolicy.h>
28
#include <log4cxx/helpers/transcoder.h>
29
#include <log4cxx/private/rollingfileappender_priv.h>
30
#include <mutex>
31
32
using namespace LOG4CXX_NS;
33
using namespace LOG4CXX_NS::rolling;
34
using namespace LOG4CXX_NS::helpers;
35
using namespace LOG4CXX_NS::spi;
36
37
0
#define _priv static_cast<RollingFileAppenderPriv*>(m_priv.get())
38
39
IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppender)
40
41
42
/**
43
 * Construct a new instance.
44
 */
45
RollingFileAppender::RollingFileAppender()
46
0
  : FileAppender(std::make_unique<RollingFileAppenderPriv>())
47
0
{
48
0
}
Unexecuted instantiation: log4cxx::rolling::RollingFileAppender::RollingFileAppender()
Unexecuted instantiation: log4cxx::rolling::RollingFileAppender::RollingFileAppender()
49
50
RollingFileAppender::RollingFileAppender( std::unique_ptr<RollingFileAppenderPriv> priv )
51
0
  : FileAppender(std::move(priv))
52
0
{
53
0
}
Unexecuted instantiation: log4cxx::rolling::RollingFileAppender::RollingFileAppender(std::__1::unique_ptr<log4cxx::rolling::RollingFileAppender::RollingFileAppenderPriv, std::__1::default_delete<log4cxx::rolling::RollingFileAppender::RollingFileAppenderPriv> >)
Unexecuted instantiation: log4cxx::rolling::RollingFileAppender::RollingFileAppender(std::__1::unique_ptr<log4cxx::rolling::RollingFileAppender::RollingFileAppenderPriv, std::__1::default_delete<log4cxx::rolling::RollingFileAppender::RollingFileAppenderPriv> >)
54
55
void RollingFileAppender::setOption(const LogString& option, const LogString& value)
56
0
{
57
0
  if (StringHelper::equalsIgnoreCase(option,
58
0
      LOG4CXX_STR("MAXFILESIZE"), LOG4CXX_STR("maxfilesize"))
59
0
    || StringHelper::equalsIgnoreCase(option,
60
0
      LOG4CXX_STR("MAXIMUMFILESIZE"), LOG4CXX_STR("maximumfilesize")))
61
0
  {
62
0
    setMaxFileSize(value);
63
0
  }
64
0
  else if (StringHelper::equalsIgnoreCase(option,
65
0
      LOG4CXX_STR("MAXBACKUPINDEX"), LOG4CXX_STR("maxbackupindex"))
66
0
    || StringHelper::equalsIgnoreCase(option,
67
0
      LOG4CXX_STR("MAXIMUMBACKUPINDEX"), LOG4CXX_STR("maximumbackupindex")))
68
0
  {
69
0
    setMaxBackupIndex(StringHelper::toInt(value));
70
0
  }
71
0
  else if (StringHelper::equalsIgnoreCase(option,
72
0
      LOG4CXX_STR("FILEDATEPATTERN"), LOG4CXX_STR("filedatepattern")))
73
0
  {
74
0
    setDatePattern(value);
75
0
  }
76
0
  else
77
0
  {
78
0
    FileAppender::setOption(option, value);
79
0
  }
80
0
}
81
82
int RollingFileAppender::getMaxBackupIndex() const
83
0
{
84
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
85
0
  int result = 1;
86
0
  if (auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(_priv->rollingPolicy))
87
0
    result = fwrp->getMaxIndex();
88
0
  return result;
89
0
}
90
91
void RollingFileAppender::setMaxBackupIndex(int maxBackups)
92
0
{
93
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
94
0
  auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(_priv->rollingPolicy);
95
0
  if (!fwrp)
96
0
  {
97
0
    fwrp = std::make_shared<FixedWindowRollingPolicy>();
98
0
    fwrp->setFileNamePattern(getFile() + LOG4CXX_STR(".%i"));
99
0
    _priv->rollingPolicy = fwrp;
100
0
  }
101
0
  fwrp->setMaxIndex(maxBackups);
102
0
}
103
104
size_t RollingFileAppender::getMaximumFileSize() const
105
0
{
106
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
107
0
  size_t result = 10 * 1024 * 1024;
108
0
  if (auto sbtp = LOG4CXX_NS::cast<SizeBasedTriggeringPolicy>(_priv->triggeringPolicy))
109
0
    result = sbtp->getMaxFileSize();
110
0
  return result;
111
0
}
112
113
void RollingFileAppender::setMaximumFileSize(size_t maxFileSize)
114
0
{
115
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
116
0
  auto sbtp = LOG4CXX_NS::cast<SizeBasedTriggeringPolicy>(_priv->triggeringPolicy);
117
0
  if (!sbtp)
118
0
  {
119
0
    sbtp = std::make_shared<SizeBasedTriggeringPolicy>();
120
0
    _priv->triggeringPolicy = sbtp;
121
0
  }
122
0
  sbtp->setMaxFileSize(maxFileSize);
123
0
}
124
125
void RollingFileAppender::setMaxFileSize(const LogString& value)
126
0
{
127
0
  setMaximumFileSize(OptionConverter::toFileSize(value, long(getMaximumFileSize() + 1)));
128
0
}
129
130
LogString RollingFileAppender::makeFileNamePattern(const LogString& datePattern)
131
0
{
132
0
  LogString result(getFile());
133
0
  bool inLiteral = false;
134
0
  bool inPattern = false;
135
136
0
  for (size_t i = 0; i < datePattern.length(); i++)
137
0
  {
138
0
    if (datePattern[i] == 0x27 /* '\'' */)
139
0
    {
140
0
      inLiteral = !inLiteral;
141
142
0
      if (inLiteral && inPattern)
143
0
      {
144
0
        result.append(1, (logchar) 0x7D /* '}' */);
145
0
        inPattern = false;
146
0
      }
147
0
    }
148
0
    else
149
0
    {
150
0
      if (!inLiteral && !inPattern)
151
0
      {
152
0
        const logchar dbrace[] = { 0x25, 0x64, 0x7B, 0 }; // "%d{"
153
0
        result.append(dbrace);
154
0
        inPattern = true;
155
0
      }
156
157
0
      result.append(1, datePattern[i]);
158
0
    }
159
0
  }
160
161
0
  if (inPattern)
162
0
  {
163
0
    result.append(1, (logchar) 0x7D /* '}' */);
164
0
  }
165
0
  return result;
166
0
}
167
168
void RollingFileAppender::setDatePattern(const LogString& newPattern)
169
0
{
170
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
171
0
  auto tbrp = LOG4CXX_NS::cast<TimeBasedRollingPolicy>(_priv->rollingPolicy);
172
0
  if (!tbrp)
173
0
  {
174
0
    tbrp = std::make_shared<TimeBasedRollingPolicy>();
175
0
    _priv->rollingPolicy = tbrp;
176
0
  }
177
0
  tbrp->setFileNamePattern(makeFileNamePattern(newPattern));
178
0
}
179
180
/**
181
 * Prepare instance of use.
182
 */
183
void RollingFileAppender::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
184
0
{
185
0
  if (_priv->activateOptions())
186
0
  {
187
0
    FileAppender::activateOptionsInternal();
188
0
  }
189
0
}
190
191
bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
192
0
{
193
0
  bool result = false;
194
0
  if (!this->rollingPolicy)
195
0
  {
196
0
    LogLog::warn(LOG4CXX_STR("No rolling policy configured for the appender named [")
197
0
      + this->name + LOG4CXX_STR("]."));
198
0
    auto fwrp = std::make_shared<FixedWindowRollingPolicy>();
199
0
    fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i"));
200
0
    this->rollingPolicy = fwrp;
201
0
  }
202
0
  else if (auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(this->rollingPolicy))
203
0
  {
204
    // Was fwrp created to store the maximum index before the file name was set?
205
0
    if (fwrp->getFileNamePattern() == LOG4CXX_STR(".%i"))
206
0
      fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i"));
207
0
  }
208
209
  //
210
  //  if no explicit triggering policy and rolling policy is both.
211
  //
212
0
  if (!this->triggeringPolicy)
213
0
  {
214
0
    TriggeringPolicyPtr trig = LOG4CXX_NS::cast<TriggeringPolicy>(this->rollingPolicy);
215
216
0
    if (trig != NULL)
217
0
    {
218
0
      this->triggeringPolicy = trig;
219
0
    }
220
0
  }
221
222
0
  if (!this->triggeringPolicy)
223
0
  {
224
0
    LogLog::warn(LOG4CXX_STR("No triggering policy configured for the appender named [")
225
0
      + this->name + LOG4CXX_STR("]."));
226
0
    this->triggeringPolicy = std::make_shared<SizeBasedTriggeringPolicy>();
227
0
  }
228
229
0
  {
230
0
    std::lock_guard<std::recursive_mutex> lock(this->mutex);
231
0
    this->triggeringPolicy->activateOptions();
232
0
    this->rollingPolicy->activateOptions();
233
234
0
    try
235
0
    {
236
0
      RolloverDescriptionPtr rollover1 =
237
0
        this->rollingPolicy->initialize(this->fileName, this->fileAppend);
238
239
0
      if (rollover1 != NULL)
240
0
      {
241
0
        ActionPtr syncAction(rollover1->getSynchronous());
242
243
0
        if (syncAction != NULL)
244
0
        {
245
0
          syncAction->execute();
246
0
        }
247
248
0
        this->fileName = rollover1->getActiveFileName();
249
0
        this->fileAppend = rollover1->getAppend();
250
251
        //
252
        //  async action not yet implemented
253
        //
254
0
        ActionPtr asyncAction(rollover1->getAsynchronous());
255
256
0
        if (asyncAction != NULL)
257
0
        {
258
0
          asyncAction->execute();
259
0
        }
260
0
      }
261
262
0
      File activeFile;
263
0
      activeFile.setPath(this->fileName);
264
265
0
      if (this->fileAppend)
266
0
      {
267
0
        this->fileLength = activeFile.length();
268
0
      }
269
0
      else
270
0
      {
271
0
        this->fileLength = 0;
272
0
      }
273
274
0
      result = true;
275
0
    }
276
0
    catch (std::exception& ex)
277
0
    {
278
0
      LogLog::warn(LOG4CXX_STR("Exception activating RollingFileAppender ") + this->fileName, ex);
279
0
    }
280
0
  }
281
0
  return result;
282
0
}
283
284
/**
285
   Implements the usual roll over behaviour.
286
287
   <p>If <code>MaxBackupIndex</code> is positive, then files
288
   {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>}
289
   are renamed to {<code>File.2</code>, ...,
290
   <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is
291
   renamed <code>File.1</code> and closed. A new <code>File</code> is
292
   created to receive further log output.
293
294
   <p>If <code>MaxBackupIndex</code> is equal to zero, then the
295
   <code>File</code> is truncated with no backup files created.
296
297
 * @return true if rollover performed.
298
 */
299
bool RollingFileAppender::rollover()
300
0
{
301
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
302
0
  return rolloverInternal();
303
0
}
304
#if LOG4CXX_ABI_VERSION <= 15
305
bool RollingFileAppender::rollover(Pool& )
306
0
{
307
0
  return rollover();
308
0
}
309
#endif
310
311
bool RollingFileAppender::rolloverInternal()
312
0
{
313
  //
314
  //   can't roll without a policy
315
  //
316
0
  if (_priv->rollingPolicy != NULL)
317
0
  {
318
0
    {
319
0
        try
320
0
        {
321
0
          RolloverDescriptionPtr rollover1(_priv->rollingPolicy->rollover(this->getFile(), this->getAppend()));
322
323
0
          if (rollover1 != NULL)
324
0
          {
325
0
            if (rollover1->getActiveFileName() == getFile())
326
0
            {
327
0
              _priv->close();
328
329
0
              bool success = true;
330
331
0
              if (rollover1->getSynchronous() != NULL)
332
0
              {
333
0
                success = false;
334
335
0
                try
336
0
                {
337
0
                  success = rollover1->getSynchronous()->execute();
338
0
                }
339
0
                catch (std::exception& ex)
340
0
                {
341
0
                  LogString msg(LOG4CXX_STR("Rollover of ["));
342
0
                  msg.append(getFile());
343
0
                  msg.append(LOG4CXX_STR("] failed"));
344
0
                  _priv->errorHandler->error(msg, ex, 0);
345
0
                }
346
0
              }
347
348
0
              bool appendToExisting = true;
349
0
              if (success)
350
0
              {
351
0
                appendToExisting = rollover1->getAppend();
352
0
                if (appendToExisting)
353
0
                {
354
0
                  _priv->fileLength = File().setPath(rollover1->getActiveFileName()).length();
355
0
                }
356
0
                else
357
0
                {
358
0
                  _priv->fileLength = 0;
359
0
                }
360
361
0
                ActionPtr asyncAction(rollover1->getAsynchronous());
362
363
0
                if (asyncAction != NULL)
364
0
                {
365
0
                  try
366
0
                  {
367
0
                    asyncAction->execute();
368
0
                  }
369
0
                  catch (std::exception& ex)
370
0
                  {
371
0
                    LogString msg(LOG4CXX_STR("Rollover of ["));
372
0
                    msg.append(getFile());
373
0
                    msg.append(LOG4CXX_STR("] failed"));
374
0
                    _priv->errorHandler->error(msg, ex, 0);
375
0
                  }
376
0
                }
377
0
              }
378
0
              setFileInternal(rollover1->getActiveFileName(), appendToExisting, _priv->bufferedIO, _priv->bufferSize);
379
0
            }
380
0
            else
381
0
            {
382
0
              _priv->close();
383
0
              setFileInternal(rollover1->getActiveFileName());
384
              // Call activateOptions to create any intermediate directories(if required)
385
0
              FileAppender::activateOptionsInternal();
386
0
              OutputStreamPtr os = std::make_shared<FileOutputStream>
387
0
                  ( rollover1->getActiveFileName()
388
0
                  , rollover1->getAppend()
389
0
                  );
390
0
              _priv->setWriter(createWriter(os));
391
392
0
              bool success = true;
393
394
0
              if (rollover1->getSynchronous() != NULL)
395
0
              {
396
0
                success = false;
397
398
0
                try
399
0
                {
400
0
                  success = rollover1->getSynchronous()->execute();
401
0
                }
402
0
                catch (std::exception& ex)
403
0
                {
404
0
                  LogString msg(LOG4CXX_STR("Rollover of ["));
405
0
                  msg.append(getFile());
406
0
                  msg.append(LOG4CXX_STR("] failed"));
407
0
                  _priv->errorHandler->error(msg, ex, 0);
408
0
                }
409
0
              }
410
411
0
              if (success)
412
0
              {
413
0
                if (rollover1->getAppend())
414
0
                {
415
0
                  _priv->fileLength = File().setPath(rollover1->getActiveFileName()).length();
416
0
                }
417
0
                else
418
0
                {
419
0
                  _priv->fileLength = 0;
420
0
                }
421
422
0
                ActionPtr asyncAction(rollover1->getAsynchronous());
423
424
0
                if (asyncAction != NULL)
425
0
                {
426
0
                  asyncAction->execute();
427
0
                }
428
0
              }
429
430
0
              _priv->writeHeader();
431
0
            }
432
0
            return true;
433
0
          }
434
0
        }
435
0
        catch (std::exception& ex)
436
0
        {
437
0
          LogString msg(LOG4CXX_STR("Rollover of ["));
438
0
          msg.append(getFile());
439
0
          msg.append(LOG4CXX_STR("] failed"));
440
0
          _priv->errorHandler->error(msg, ex, 0);
441
0
        }
442
0
    }
443
0
  }
444
445
0
  return false;
446
0
}
447
#if LOG4CXX_ABI_VERSION <= 15
448
bool RollingFileAppender::rolloverInternal(Pool&)
449
0
{
450
0
  return rolloverInternal();
451
0
}
452
#endif
453
454
/**
455
 * {@inheritDoc}
456
*/
457
void RollingFileAppender::subAppend( LOG4CXX_APPEND_FORMAL_PARAMETERS )
458
0
{
459
  // The rollover check must precede actual writing. This is the
460
  // only correct behavior for time driven triggers.
461
0
  if (
462
0
    _priv->triggeringPolicy->isTriggeringEvent(
463
0
      this, event, getFile(), getFileLength()))
464
0
  {
465
    //
466
    //   wrap rollover request in try block since
467
    //    rollover may fail in case read access to directory
468
    //    is not provided.  However appender should still be in good
469
    //     condition and the append should still happen.
470
0
    try
471
0
    {
472
0
      _priv->_event = event;
473
0
      rolloverInternal();
474
0
    }
475
0
    catch (std::exception& ex)
476
0
    {
477
0
      LogString msg(LOG4CXX_STR("Rollover of ["));
478
0
      msg.append(getFile());
479
0
      msg.append(LOG4CXX_STR("] failed"));
480
0
      _priv->errorHandler->error(msg, ex, 0);
481
0
    }
482
0
  }
483
484
0
  FileAppender::subAppend( LOG4CXX_APPEND_PARAMETERS );
485
0
}
486
487
/**
488
 * TThe policy that implements the scheme for rolling over a log file.
489
 */
490
RollingPolicyPtr RollingFileAppender::getRollingPolicy() const
491
0
{
492
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
493
0
  return _priv->rollingPolicy;
494
0
}
495
496
/**
497
 * The policy that determine when to trigger a log file rollover.
498
 */
499
TriggeringPolicyPtr RollingFileAppender::getTriggeringPolicy() const
500
0
{
501
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
502
0
  return _priv->triggeringPolicy;
503
0
}
504
505
/**
506
 * Set the scheme for rolling over log files.
507
 */
508
void RollingFileAppender::setRollingPolicy(const RollingPolicyPtr& policy)
509
0
{
510
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
511
0
  _priv->rollingPolicy = policy;
512
0
}
513
514
/**
515
 * Set policy that determine when to trigger a log file rollover.
516
 */
517
void RollingFileAppender::setTriggeringPolicy(const TriggeringPolicyPtr& policy)
518
0
{
519
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
520
0
  _priv->triggeringPolicy = policy;
521
0
}
522
523
/**
524
 * Close appender.  Waits for any asynchronous file compression actions to be completed.
525
 */
526
void RollingFileAppender::close()
527
0
{
528
0
  FileAppender::close();
529
0
}
530
531
namespace LOG4CXX_NS
532
{
533
namespace rolling
534
{
535
/**
536
 * Wrapper for OutputStream that will report all write
537
 * operations back to this class for file length calculations.
538
 */
539
class CountingOutputStream : public OutputStream
540
{
541
    /**
542
     * Wrapped output stream.
543
     */
544
  private:
545
    OutputStreamPtr os;
546
547
    /**
548
     * Rolling file appender to inform of stream writes.
549
     */
550
    RollingFileAppender* rfa;
551
552
  public:
553
    /**
554
     * Constructor.
555
     * @param os output stream to wrap.
556
     * @param rfa rolling file appender to inform.
557
     */
558
    CountingOutputStream
559
      ( const OutputStreamPtr& os1
560
      , RollingFileAppender* rfa1
561
      )
562
0
      : os(os1)
563
0
      , rfa(rfa1)
564
0
    {
565
0
    }
566
567
    /**
568
     * {@inheritDoc}
569
     */
570
    void close( LOG4CXX_CLOSE_OUTPUT_STREAM_FORMAL_PARAMETERS ) override
571
0
    {
572
0
      os->close();
573
0
      rfa = 0;
574
0
    }
575
576
    /**
577
     * {@inheritDoc}
578
     */
579
    void flush( LOG4CXX_FLUSH_OUTPUT_STREAM_FORMAL_PARAMETERS ) override
580
0
    {
581
0
      os->flush();
582
0
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587
    void write( LOG4CXX_WRITE_OUTPUT_STREAM_FORMAL_PARAMETERS ) override
588
0
    {
589
0
      os->write(buf);
590
591
0
      if (rfa != 0)
592
0
      {
593
0
        rfa->incrementFileLength(buf.limit());
594
0
      }
595
0
    }
596
};
597
}
598
}
599
600
/**
601
   Returns an OutputStreamWriter when passed an OutputStream.  The
602
   encoding used will depend on the value of the
603
   <code>encoding</code> property.  If the encoding value is
604
   specified incorrectly the writer will be opened using the default
605
   system encoding (an error message will be printed to the loglog.
606
 @param os output stream, may not be null.
607
 @return new writer.
608
 */
609
WriterPtr RollingFileAppender::createWriter(LOG4CXX_16_CONST OutputStreamPtr& os)
610
0
{
611
0
  OutputStreamPtr cos = std::make_shared<CountingOutputStream>(os, this);
612
0
  return FileAppender::createWriter(cos);
613
0
}
614
615
/**
616
 * Get byte length of current active log file.
617
 * @return byte length of current active log file.
618
 */
619
size_t RollingFileAppender::getFileLength() const
620
0
{
621
0
  return _priv->fileLength;
622
0
}
623
624
/**
625
 * Increments estimated byte length of current active log file.
626
 * @param increment additional bytes written to log file.
627
 */
628
void RollingFileAppender::incrementFileLength(size_t increment)
629
0
{
630
0
  _priv->fileLength += increment;
631
0
}