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/fileappender.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/logstring.h>
19
#include <log4cxx/fileappender.h>
20
#include <log4cxx/helpers/stringhelper.h>
21
#include <log4cxx/helpers/loglog.h>
22
#include <log4cxx/helpers/optionconverter.h>
23
#include <log4cxx/helpers/pool.h>
24
#include <log4cxx/helpers/fileoutputstream.h>
25
#include <log4cxx/helpers/outputstreamwriter.h>
26
#include <log4cxx/helpers/bufferedwriter.h>
27
#include <log4cxx/helpers/bytebuffer.h>
28
#include "log4cxx/helpers/threadutility.h"
29
#include <log4cxx/private/writerappender_priv.h>
30
#include <log4cxx/private/fileappender_priv.h>
31
#include <limits>
32
#include <mutex>
33
34
using namespace LOG4CXX_NS;
35
using namespace LOG4CXX_NS::helpers;
36
using namespace LOG4CXX_NS::spi;
37
38
IMPLEMENT_LOG4CXX_OBJECT(FileAppender)
39
40
0
#define _priv static_cast<FileAppenderPriv*>(m_priv.get())
41
42
FileAppender::FileAppender() :
43
0
  WriterAppender (std::make_unique<FileAppenderPriv>())
44
0
{
45
0
}
Unexecuted instantiation: log4cxx::FileAppender::FileAppender()
Unexecuted instantiation: log4cxx::FileAppender::FileAppender()
46
47
FileAppender::FileAppender
48
  ( const LayoutPtr& layout1
49
  , const LogString& fileName1
50
  , bool append1
51
  , bool bufferedIO1
52
  , int bufferSize1
53
  )
54
0
  : WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1, append1, bufferedIO1, bufferSize1))
55
0
{
56
0
  activateOptionsInternal();
57
0
}
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, int)
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, int)
58
59
FileAppender::FileAppender
60
  ( const LayoutPtr& layout1
61
  , const LogString& fileName1
62
  , bool append1
63
  )
64
0
  : WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1, append1, false))
65
0
{
66
0
  activateOptionsInternal();
67
0
}
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
68
69
FileAppender::FileAppender(const LayoutPtr& layout1, const LogString& fileName1)
70
0
  : WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1))
71
0
{
72
0
  activateOptionsInternal();
73
0
}
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
74
75
FileAppender::FileAppender(std::unique_ptr<FileAppenderPriv> priv)
76
0
  : WriterAppender (std::move(priv))
77
0
{
78
0
}
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::unique_ptr<log4cxx::FileAppender::FileAppenderPriv, std::__1::default_delete<log4cxx::FileAppender::FileAppenderPriv> >)
Unexecuted instantiation: log4cxx::FileAppender::FileAppender(std::__1::unique_ptr<log4cxx::FileAppender::FileAppenderPriv, std::__1::default_delete<log4cxx::FileAppender::FileAppenderPriv> >)
79
80
FileAppender::~FileAppender()
81
0
{
82
0
  if (auto p = _priv->taskManager.lock())
83
0
    p->value().removePeriodicTask(getName());
84
0
}
85
86
void FileAppender::setAppend(bool fileAppend1)
87
0
{
88
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
89
0
  _priv->fileAppend = fileAppend1;
90
0
}
91
92
void FileAppender::setFile(const LogString& file)
93
0
{
94
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
95
0
  setFileInternal(file);
96
0
}
97
98
void FileAppender::setFileInternal(const LogString& file)
99
0
{
100
0
  _priv->fileName = file;
101
0
}
102
103
void FileAppender::setBufferedIO(bool bufferedIO1)
104
0
{
105
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
106
0
  _priv->bufferedIO = bufferedIO1;
107
108
0
  if (bufferedIO1)
109
0
  {
110
0
    setImmediateFlush(false);
111
0
  }
112
0
}
113
114
void FileAppender::setOption(const LogString& option,
115
  const LogString& value)
116
0
{
117
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FILE"), LOG4CXX_STR("file"))
118
0
    || StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FILENAME"), LOG4CXX_STR("filename")))
119
0
  {
120
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
121
0
    _priv->fileName = stripDuplicateBackslashes(value);
122
0
  }
123
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("APPEND"), LOG4CXX_STR("append")))
124
0
  {
125
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
126
0
    _priv->fileAppend = OptionConverter::toBoolean(value, true);
127
0
  }
128
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFEREDIO"), LOG4CXX_STR("bufferedio")))
129
0
  {
130
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
131
0
    _priv->bufferedIO = OptionConverter::toBoolean(value, false);
132
0
  }
133
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("IMMEDIATEFLUSH"), LOG4CXX_STR("immediateflush")))
134
0
  {
135
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
136
0
    _priv->bufferedIO = !OptionConverter::toBoolean(value, false);
137
0
  }
138
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
139
0
  {
140
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
141
0
    long parsed = OptionConverter::toFileSize(value, 8 * 1024);
142
0
    if (parsed < 0 || parsed > static_cast<long>((std::numeric_limits<int>::max)()))
143
0
    {
144
0
      LogLog::warn(LOG4CXX_STR("FileAppender BufferSize is out of range. Using the default value."));
145
0
      parsed = 8 * 1024;
146
0
    }
147
0
    _priv->bufferSize = static_cast<int>(parsed);
148
0
  }
149
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFEREDSECONDS"), LOG4CXX_STR("bufferedseconds")))
150
0
  {
151
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
152
0
    _priv->bufferedSeconds = OptionConverter::toInt(value, 0);
153
0
  }
154
0
  else
155
0
  {
156
0
    WriterAppender::setOption(option, value);
157
0
  }
158
0
}
159
160
void FileAppender::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
161
0
{
162
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
163
0
  activateOptionsInternal();
164
0
}
165
166
void FileAppender::activateOptionsInternal()
167
0
{
168
0
  int errors = 0;
169
170
0
  if (!_priv->fileName.empty())
171
0
  {
172
0
    try
173
0
    {
174
0
      setFileInternal(_priv->fileName, _priv->fileAppend, _priv->bufferedIO, _priv->bufferSize);
175
0
    }
176
0
    catch (IOException& e)
177
0
    {
178
0
      errors++;
179
0
      LogString msg(LOG4CXX_STR("setFile("));
180
0
      msg.append(_priv->fileName);
181
0
      msg.append(1, (logchar) 0x2C /* ',' */);
182
0
      StringHelper::toString(_priv->fileAppend, msg);
183
0
      msg.append(LOG4CXX_STR(") call failed."));
184
0
      _priv->errorHandler->error(msg, e, ErrorCode::FILE_OPEN_FAILURE);
185
0
    }
186
0
  }
187
0
  else
188
0
  {
189
0
    errors++;
190
0
    LogLog::error(LogString(LOG4CXX_STR("File option not set for appender ["))
191
0
      +  _priv->name + LOG4CXX_STR("]."));
192
0
    LogLog::warn(LOG4CXX_STR("Are you using FileAppender instead of ConsoleAppender?"));
193
0
  }
194
195
0
  if (errors == 0)
196
0
  {
197
0
    _priv->activateOptions();
198
0
    if (auto p = _priv->taskManager.lock())
199
0
      p->value().removePeriodicTask(getName());
200
201
0
    if (!_priv->bufferedIO)
202
0
      ;
203
0
    else if (0 < _priv->bufferedSeconds)
204
0
    {
205
0
      auto taskManager = ThreadUtility::instancePtr();
206
0
      taskManager->value().addPeriodicTask(getName()
207
0
        , std::bind(&WriterAppenderPriv::flush, _priv)
208
0
        , std::chrono::seconds(_priv->bufferedSeconds)
209
0
        );
210
0
      _priv->taskManager = taskManager;
211
0
    }
212
0
  }
213
0
}
214
215
216
/**
217
 * Replaces double backslashes (except the leading doubles of UNC's)
218
 * with single backslashes for compatibility with existing path
219
 * specifications that were working around use of
220
 * OptionConverter::convertSpecialChars in XML configuration files.
221
 *
222
 * @param src source string
223
 * @return modified string
224
 *
225
 *
226
 */
227
LogString FileAppender::stripDuplicateBackslashes(const LogString& src)
228
0
{
229
0
  logchar backslash = 0x5C; // '\\'
230
0
  LogString::size_type i = src.find_last_of(backslash);
231
232
0
  if (i != LogString::npos)
233
0
  {
234
0
    LogString tmp(src);
235
236
0
    for (;
237
0
      i != LogString::npos && i > 0;
238
0
      i = tmp.find_last_of(backslash, i - 1))
239
0
    {
240
      //
241
      //   if the preceding character is a slash then
242
      //      remove the preceding character
243
      //      and continue processing
244
0
      if (tmp[i - 1] == backslash)
245
0
      {
246
0
        tmp.erase(i, 1);
247
0
        i--;
248
249
0
        if (i == 0)
250
0
        {
251
0
          break;
252
0
        }
253
0
      }
254
0
      else
255
0
      {
256
        //
257
        //  if there an odd number of slashes
258
        //     the string wasn't trying to work around
259
        //     OptionConverter::convertSpecialChars
260
0
        return src;
261
0
      }
262
0
    }
263
264
0
    return tmp;
265
0
  }
266
267
0
  return src;
268
0
}
269
270
/**
271
  <p>Sets and <i>opens</i> the file where the log output will
272
  go. The specified file must be writable.
273
274
  <p>If there was already an opened file, then the previous file
275
  is closed first.
276
277
  <p><b>Do not use this method directly. To configure a FileAppender
278
  or one of its subclasses, set its properties one by one and then
279
  call activateOptions.</b>
280
281
  @param filename The path to the log file.
282
  @param append   If true will append to fileName. Otherwise will
283
      truncate fileName.
284
  @param bufferedIO
285
  @param bufferSize
286
287
  @throws IOException
288
289
 */
290
void FileAppender::setFileInternal(
291
  const LogString& filename,
292
  bool append1,
293
  bool bufferedIO1,
294
  size_t bufferSize1)
295
0
{
296
  // It does not make sense to have immediate flush and bufferedIO.
297
0
  if (bufferedIO1)
298
0
  {
299
0
    setImmediateFlush(false);
300
0
  }
301
302
0
  _priv->close();
303
304
0
  bool writeBOM = false;
305
306
0
  if (StringHelper::equalsIgnoreCase(getEncoding(),
307
0
      LOG4CXX_STR("utf-16"), LOG4CXX_STR("UTF-16")))
308
0
  {
309
    //
310
    //    don't want to write a byte order mark if the file exists
311
    //
312
0
    if (append1)
313
0
    {
314
0
      File outFile;
315
0
      outFile.setPath(filename);
316
0
      writeBOM = !outFile.exists();
317
0
    }
318
0
    else
319
0
    {
320
0
      writeBOM = true;
321
0
    }
322
0
  }
323
324
0
  OutputStreamPtr outStream;
325
326
0
  try
327
0
  {
328
0
    outStream = std::make_shared<FileOutputStream>(filename, append1);
329
0
  }
330
0
  catch (IOException&)
331
0
  {
332
0
    LogString parentName = File().setPath(filename).getParent();
333
334
0
    if (!parentName.empty())
335
0
    {
336
0
      File parentDir;
337
0
      parentDir.setPath(parentName);
338
339
0
      if (!parentDir.exists() && parentDir.mkdirs())
340
0
      {
341
0
        outStream = std::make_shared<FileOutputStream>(filename, append1);
342
0
      }
343
0
      else
344
0
      {
345
0
        throw;
346
0
      }
347
0
    }
348
0
    else
349
0
    {
350
0
      throw;
351
0
    }
352
0
  }
353
354
355
  //
356
  //   if a new file and UTF-16, then write a BOM
357
  //
358
0
  if (writeBOM)
359
0
  {
360
0
    Pool p;
361
0
    char bom[] = { (char) 0xFE, (char) 0xFF };
362
0
    ByteBuffer buf(bom, 2);
363
0
    outStream->write(buf);
364
0
  }
365
366
0
  WriterPtr newWriter(createWriter(outStream));
367
368
0
  if (bufferedIO1)
369
0
  {
370
0
    newWriter = std::make_shared<BufferedWriter>(newWriter, bufferSize1);
371
0
  }
372
373
0
  _priv->setWriter(newWriter);
374
375
0
  _priv->fileAppend = append1;
376
0
  _priv->bufferedIO = bufferedIO1;
377
0
  _priv->fileName = filename;
378
0
  const size_t intMax = static_cast<size_t>((std::numeric_limits<int>::max)());
379
0
  _priv->bufferSize = static_cast<int>(bufferSize1 > intMax ? intMax : bufferSize1);
380
0
  _priv->writeHeader();
381
382
0
}
383
384
LogString FileAppender::getFile() const
385
0
{
386
0
  return _priv->fileName;
387
0
}
388
389
bool FileAppender::getBufferedIO() const
390
0
{
391
0
  return _priv->bufferedIO;
392
0
}
393
394
int FileAppender::getBufferSize() const
395
0
{
396
0
  return _priv->bufferSize;
397
0
}
398
399
int FileAppender::getBufferedSeconds() const
400
0
{
401
0
  return _priv->bufferedSeconds;
402
0
}
403
404
void FileAppender::setBufferSize(int newValue)
405
0
{
406
0
  if (newValue < 0)
407
0
  {
408
0
    LogLog::warn(LOG4CXX_STR("FileAppender BufferSize must be non-negative. Using zero."));
409
0
    newValue = 0;
410
0
  }
411
0
  _priv->bufferSize = newValue;
412
0
}
413
414
void FileAppender::setBufferedSeconds(int newValue)
415
0
{
416
0
  _priv->bufferedSeconds = newValue;
417
0
}
418
419
bool FileAppender::getAppend() const
420
0
{
421
0
  return _priv->fileAppend;
422
0
}
423
424
#if LOG4CXX_ABI_VERSION <= 15
425
void FileAppender::activateOptionsInternal(helpers::Pool& )
426
0
{ activateOptionsInternal(); }
427
void FileAppender::setFileInternal(const LogString& file, bool append,
428
  bool bufferedIO, size_t bufferSize,
429
  helpers::Pool&)
430
0
{ setFileInternal(file, append, bufferedIO, bufferSize); }
431
#endif