Coverage Report

Created: 2025-07-11 07:00

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