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