Coverage Report

Created: 2025-07-01 06:08

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