Coverage Report

Created: 2026-06-15 06:22

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