Coverage Report

Created: 2026-03-12 07:00

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