Coverage Report

Created: 2025-07-01 06:08

/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(new FileOutputStream(
361
0
                  rollover1->getActiveFileName(), rollover1->getAppend()));
362
0
              WriterPtr newWriter(createWriter(os));
363
0
              setWriterInternal(newWriter);
364
365
0
              bool success = true;
366
367
0
              if (rollover1->getSynchronous() != NULL)
368
0
              {
369
0
                success = false;
370
371
0
                try
372
0
                {
373
0
                  success = rollover1->getSynchronous()->execute(p);
374
0
                }
375
0
                catch (std::exception& ex)
376
0
                {
377
0
                  LogString msg(LOG4CXX_STR("Rollover of ["));
378
0
                  msg.append(getFile());
379
0
                  msg.append(LOG4CXX_STR("] failed"));
380
0
                  _priv->errorHandler->error(msg, ex, 0);
381
0
                }
382
0
              }
383
384
0
              if (success)
385
0
              {
386
0
                if (rollover1->getAppend())
387
0
                {
388
0
                  _priv->fileLength = File().setPath(rollover1->getActiveFileName()).length(p);
389
0
                }
390
0
                else
391
0
                {
392
0
                  _priv->fileLength = 0;
393
0
                }
394
395
0
                ActionPtr asyncAction(rollover1->getAsynchronous());
396
397
0
                if (asyncAction != NULL)
398
0
                {
399
0
                  asyncAction->execute(p);
400
0
                }
401
0
              }
402
403
0
              writeHeader(p);
404
0
            }
405
0
            return true;
406
0
          }
407
0
        }
408
0
        catch (std::exception& ex)
409
0
        {
410
0
          LogString msg(LOG4CXX_STR("Rollover of ["));
411
0
          msg.append(getFile());
412
0
          msg.append(LOG4CXX_STR("] failed"));
413
0
          _priv->errorHandler->error(msg, ex, 0);
414
0
        }
415
0
    }
416
0
  }
417
418
0
  return false;
419
0
}
420
421
/**
422
 * {@inheritDoc}
423
*/
424
void RollingFileAppender::subAppend(const LoggingEventPtr& event, Pool& p)
425
0
{
426
  // The rollover check must precede actual writing. This is the
427
  // only correct behavior for time driven triggers.
428
0
  if (
429
0
    _priv->triggeringPolicy->isTriggeringEvent(
430
0
      this, event, getFile(), getFileLength()))
431
0
  {
432
    //
433
    //   wrap rollover request in try block since
434
    //    rollover may fail in case read access to directory
435
    //    is not provided.  However appender should still be in good
436
    //     condition and the append should still happen.
437
0
    try
438
0
    {
439
0
      _priv->_event = event;
440
0
      rolloverInternal(p);
441
0
    }
442
0
    catch (std::exception& ex)
443
0
    {
444
0
      LogString msg(LOG4CXX_STR("Rollover of ["));
445
0
      msg.append(getFile());
446
0
      msg.append(LOG4CXX_STR("] failed"));
447
0
      _priv->errorHandler->error(msg, ex, 0);
448
0
    }
449
0
  }
450
451
0
  FileAppender::subAppend(event, p);
452
0
}
453
454
/**
455
 * TThe policy that implements the scheme for rolling over a log file.
456
 */
457
RollingPolicyPtr RollingFileAppender::getRollingPolicy() const
458
0
{
459
0
  return _priv->rollingPolicy;
460
0
}
461
462
/**
463
 * The policy that determine when to trigger a log file rollover.
464
 */
465
TriggeringPolicyPtr RollingFileAppender::getTriggeringPolicy() const
466
0
{
467
0
  return _priv->triggeringPolicy;
468
0
}
469
470
/**
471
 * Set the scheme for rolling over log files.
472
 */
473
void RollingFileAppender::setRollingPolicy(const RollingPolicyPtr& policy)
474
0
{
475
0
  _priv->rollingPolicy = policy;
476
0
}
477
478
/**
479
 * Set policy that determine when to trigger a log file rollover.
480
 */
481
void RollingFileAppender::setTriggeringPolicy(const TriggeringPolicyPtr& policy)
482
0
{
483
0
  _priv->triggeringPolicy = policy;
484
0
}
485
486
/**
487
 * Close appender.  Waits for any asynchronous file compression actions to be completed.
488
 */
489
void RollingFileAppender::close()
490
0
{
491
0
  FileAppender::close();
492
0
}
493
494
namespace LOG4CXX_NS
495
{
496
namespace rolling
497
{
498
/**
499
 * Wrapper for OutputStream that will report all write
500
 * operations back to this class for file length calculations.
501
 */
502
class CountingOutputStream : public OutputStream
503
{
504
    /**
505
     * Wrapped output stream.
506
     */
507
  private:
508
    OutputStreamPtr os;
509
510
    /**
511
     * Rolling file appender to inform of stream writes.
512
     */
513
    RollingFileAppender* rfa;
514
515
  public:
516
    /**
517
     * Constructor.
518
     * @param os output stream to wrap.
519
     * @param rfa rolling file appender to inform.
520
     */
521
    CountingOutputStream(
522
      OutputStreamPtr& os1, RollingFileAppender* rfa1) :
523
0
      os(os1), rfa(rfa1)
524
0
    {
525
0
    }
526
527
    /**
528
     * {@inheritDoc}
529
     */
530
    void close(Pool& p)
531
0
    {
532
0
      os->close(p);
533
0
      rfa = 0;
534
0
    }
535
536
    /**
537
     * {@inheritDoc}
538
     */
539
    void flush(Pool& p)
540
0
    {
541
0
      os->flush(p);
542
0
    }
543
544
    /**
545
     * {@inheritDoc}
546
     */
547
    void write(ByteBuffer& buf, Pool& p)
548
0
    {
549
0
      os->write(buf, p);
550
551
0
      if (rfa != 0)
552
0
      {
553
0
        rfa->incrementFileLength(buf.limit());
554
0
      }
555
0
    }
556
};
557
}
558
}
559
560
/**
561
   Returns an OutputStreamWriter when passed an OutputStream.  The
562
   encoding used will depend on the value of the
563
   <code>encoding</code> property.  If the encoding value is
564
   specified incorrectly the writer will be opened using the default
565
   system encoding (an error message will be printed to the loglog.
566
 @param os output stream, may not be null.
567
 @return new writer.
568
 */
569
WriterPtr RollingFileAppender::createWriter(OutputStreamPtr& os)
570
0
{
571
0
  OutputStreamPtr cos = std::make_shared<CountingOutputStream>(os, this);
572
0
  return FileAppender::createWriter(cos);
573
0
}
574
575
/**
576
 * Get byte length of current active log file.
577
 * @return byte length of current active log file.
578
 */
579
size_t RollingFileAppender::getFileLength() const
580
0
{
581
0
  return _priv->fileLength;
582
0
}
583
584
/**
585
 * Increments estimated byte length of current active log file.
586
 * @param increment additional bytes written to log file.
587
 */
588
void RollingFileAppender::incrementFileLength(size_t increment)
589
0
{
590
0
  _priv->fileLength += increment;
591
0
}