You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by sw...@apache.org on 2026/10/16 02:01:41 UTC
(logging-log4cxx) branch master updated: Lazily create logging files to avoid creating empty files (#775)
This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
The following commit(s) were added to refs/heads/master by this push:
new 36347c0a Lazily create logging files to avoid creating empty files (#775)
36347c0a is described below
commit 36347c0a48a059ac50f8dcde197fc14541a5d0be
Author: niqiangpro-cell <ni...@gmail.com>
AuthorDate: Fri Oct 16 04:01:35 2026 +0200
Lazily create logging files to avoid creating empty files (#775)
* Add FileAppender documentation and a change report entry
---
src/main/cpp/fileappender.cpp | 62 ++++++++++++++++++----
src/main/cpp/fileoutputstream.cpp | 50 +++++++++++++----
src/main/cpp/multiprocessrollingfileappender.cpp | 32 +++--------
src/main/include/log4cxx/fileappender.h | 30 +++++++++--
.../include/log4cxx/helpers/fileoutputstream.h | 12 ++++-
.../include/log4cxx/private/fileappender_priv.h | 4 ++
src/site/markdown/change-report-gh.md | 3 ++
src/test/cpp/fileappendertest.cpp | 26 +++++++++
src/test/cpp/hierarchythresholdtestcase.cpp | 27 +++++++---
src/test/cpp/rolling/timebasedrollingtest.cpp | 33 ++++++++----
10 files changed, 212 insertions(+), 67 deletions(-)
diff --git a/src/main/cpp/fileappender.cpp b/src/main/cpp/fileappender.cpp
index 9d5b3954..1ff956b3 100644
--- a/src/main/cpp/fileappender.cpp
+++ b/src/main/cpp/fileappender.cpp
@@ -143,6 +143,11 @@ void FileAppender::setOption(const LogString& option,
std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
_priv->bufferedSeconds = OptionConverter::toInt(value, 0);
}
+ else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LAZYFILECREATION"), LOG4CXX_STR("lazyfilecreation")))
+ {
+ std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
+ _priv->lazyFileCreation = OptionConverter::toBoolean(value, false);
+ }
else
{
WriterAppender::setOption(option, value);
@@ -296,11 +301,7 @@ void FileAppender::FileAppenderPriv::open()
OutputStreamPtr outStream;
- try
- {
- outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
- }
- catch (IOException&)
+ if (this->lazyFileCreation)
{
LogString parentName = this->targetFile.getParent();
@@ -308,19 +309,47 @@ void FileAppender::FileAppenderPriv::open()
{
File parentDir(parentName);
- if (!parentDir.exists() && parentDir.mkdirs())
+ if (!parentDir.exists())
+ {
+ LogLog::debug(LOG4CXX_STR("Creating dirs ") + parentDir.getPath());
+ // The file is not opened here, so a failure must be reported now.
+ if (!parentDir.mkdirs() && !parentDir.exists())
+ {
+ throw IOException(LOG4CXX_STR("Unable to create directory ") + parentDir.getPath());
+ }
+ }
+ }
+
+ outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend, true);
+ }
+ else
+ {
+ try
+ {
+ outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
+ }
+ catch (IOException&)
+ {
+ LogString parentName = this->targetFile.getParent();
+
+ if (!parentName.empty())
{
- outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
+ File parentDir(parentName);
+
+ if (!parentDir.exists() && parentDir.mkdirs())
+ {
+ outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
+ }
+ else
+ {
+ throw;
+ }
}
else
{
throw;
}
}
- else
- {
- throw;
- }
}
@@ -388,3 +417,14 @@ bool FileAppender::getAppend() const
return _priv->fileAppend;
}
+void FileAppender::setLazyFileCreation(bool newValue)
+{
+ std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
+ _priv->lazyFileCreation = newValue;
+}
+
+bool FileAppender::getLazyFileCreation() const
+{
+ return _priv->lazyFileCreation;
+}
+
diff --git a/src/main/cpp/fileoutputstream.cpp b/src/main/cpp/fileoutputstream.cpp
index 0264b61f..fae252f2 100644
--- a/src/main/cpp/fileoutputstream.cpp
+++ b/src/main/cpp/fileoutputstream.cpp
@@ -27,30 +27,45 @@ using namespace LOG4CXX_NS::helpers;
struct FileOutputStream::FileOutputStreamPrivate
{
- FileOutputStreamPrivate(const LogString& filename)
- : path(filename)
+ FileOutputStreamPrivate(const LogString& filename, bool append)
+ : path(filename), logAppend(append)
{}
File path;
Pool pool;
apr_file_t* fileptr{ nullptr };
- void open(bool append);
+ bool logAppend;
+ void open();
+ void openUnlessLazy(bool lazyOpen);
};
IMPLEMENT_LOG4CXX_OBJECT(FileOutputStream)
-FileOutputStream::FileOutputStream(const LogString& filename, bool append)
- : m_priv(std::make_unique<FileOutputStreamPrivate>(filename))
+FileOutputStream::FileOutputStream(const LogString& filename, bool append, bool lazyOpen)
+ : m_priv(std::make_unique<FileOutputStreamPrivate>(filename, append))
{
- m_priv->open(append);
+ m_priv->openUnlessLazy(lazyOpen);
}
-void FileOutputStream::FileOutputStreamPrivate::open(bool append)
+void FileOutputStream::FileOutputStreamPrivate::openUnlessLazy(bool lazyOpen)
+{
+ if (!lazyOpen)
+ {
+ this->open();
+ }
+ else if (!this->logAppend && this->path.exists())
+ {
+ // Truncate the file (i.e. remove its contents)
+ this->open();
+ }
+}
+
+void FileOutputStream::FileOutputStreamPrivate::open()
{
apr_fileperms_t perm = APR_OS_DEFAULT;
apr_int32_t flags = APR_WRITE | APR_CREATE;
- if (append)
+ if (this->logAppend)
{
flags |= APR_APPEND;
}
@@ -72,6 +87,7 @@ FileOutputStream::~FileOutputStream()
if (m_priv->fileptr)
{
apr_file_close(m_priv->fileptr);
+ m_priv->fileptr = nullptr;
}
}
@@ -98,7 +114,12 @@ void FileOutputStream::write( ByteBuffer& buf )
{
if (m_priv->fileptr == NULL)
{
- throw NullPointerException(LOG4CXX_STR("FileOutputStream"));
+ m_priv->open();
+
+ if (m_priv->fileptr == nullptr)
+ {
+ throw NullPointerException(LOG4CXX_STR("FileOutputStream"));
+ }
}
size_t nbytes = buf.remaining();
@@ -117,6 +138,17 @@ void FileOutputStream::write( ByteBuffer& buf )
}
apr_file_t* FileOutputStream::getFilePtr() const{
+ if (!m_priv->fileptr)
+ {
+ try
+ {
+ m_priv->open();
+ }
+ catch (IOException&)
+ {
+ // write() will open (and report any failure) again
+ }
+ }
return m_priv->fileptr;
}
diff --git a/src/main/cpp/multiprocessrollingfileappender.cpp b/src/main/cpp/multiprocessrollingfileappender.cpp
index 92add2f2..12a2a029 100644
--- a/src/main/cpp/multiprocessrollingfileappender.cpp
+++ b/src/main/cpp/multiprocessrollingfileappender.cpp
@@ -57,7 +57,7 @@ struct MultiprocessRollingFileAppender::MultiprocessRollingFileAppenderPriv
apr_file_close(lock_file);
}
- apr_file_t* log_file = NULL;
+ FileOutputStreamPtr fileOutputStream;
apr_file_t* lock_file = NULL;
public: // Support classes
@@ -159,12 +159,6 @@ void MultiprocessRollingFileAppender::activateOptions( )
{
if (_priv->activateOptions())
{
- // FileAppenderPriv::activate() closes any existing writer,
- // destroying the FileOutputStream (and the pool) that owns the cached
- // apr_file_t, before opening the new file (which may fail). Clear the
- // cache first so a failed open cannot leave it dangling;
- // createWriter() re-caches the handle on success.
- _priv->log_file = NULL;
_priv->activate();
}
@@ -190,13 +184,13 @@ bool MultiprocessRollingFileAppender::isRolloverCheckNeeded()
*/
bool MultiprocessRollingFileAppender::isAlreadyRolled(const LogString& fileName, size_t* pSize)
{
- if( !_priv->log_file )
+ if( !_priv->fileOutputStream || !_priv->fileOutputStream->getFilePtr() )
return false;
apr_int32_t wantedInfo = APR_FINFO_IDENT;
if (pSize)
wantedInfo |= APR_FINFO_SIZE;
apr_finfo_t finfo1;
- apr_status_t st1 = apr_file_info_get(&finfo1, wantedInfo, _priv->log_file);
+ apr_status_t st1 = apr_file_info_get(&finfo1, wantedInfo, _priv->fileOutputStream->getFilePtr());
if (st1 != APR_SUCCESS)
LogLog::warn(LOG4CXX_STR("apr_file_info_get failed"));
@@ -229,11 +223,11 @@ bool MultiprocessRollingFileAppender::isAlreadyRolled(const LogString& fileName,
*/
bool MultiprocessRollingFileAppender::getCurrentFileSize(size_t* pSize)
{
- if( !_priv->log_file )
+ if( !_priv->fileOutputStream || !_priv->fileOutputStream->getFilePtr() )
return false;
apr_int32_t wantedInfo = APR_FINFO_SIZE;
apr_finfo_t finfo;
- if (apr_file_info_get(&finfo, wantedInfo, _priv->log_file) != APR_SUCCESS)
+ if (apr_file_info_get(&finfo, wantedInfo, _priv->fileOutputStream->getFilePtr()) != APR_SUCCESS)
{
LogLog::warn(LOG4CXX_STR("apr_file_info_get failed"));
return false;
@@ -289,13 +283,6 @@ bool MultiprocessRollingFileAppender::synchronizedRollover(const TriggeringPolic
else if (auto rollover1 = _priv->rollingPolicy->rollover(fileName, getAppend()))
{
_priv->close();
- // The cached apr_file_t was allocated from the pool of the
- // FileOutputStream that close() just destroyed. Clear it now:
- // any failure (e.g. ENOSPC/EACCES opening the new active file
- // throws, and subAppend merely logs the exception) would leave a
- // dangling handle that isAlreadyRolled()/getCurrentFileSize()
- // pass to apr_file_info_get() on the next append.
- _priv->log_file = NULL;
if (rollover1->getActiveFileName() == fileName)
{
bool success = true; // A synchronous action is not required
@@ -391,16 +378,11 @@ bool MultiprocessRollingFileAppender::synchronizedRollover(const TriggeringPolic
void MultiprocessRollingFileAppender::reopenFile(const LogString& fileName)
{
_priv->close();
- // Clear the cached apr_file_t: it was allocated from the pool of the
- // FileOutputStream that close() just destroyed, and the FileOutputStream
- // constructor below throws on open failure (leaving this appender without
- // a writer until a later reopen succeeds).
- _priv->log_file = NULL;
#if USING_ROLLOVER_REQUIRED_CHECK_IS_FASTER
if (auto pTimeBased = LOG4CXX_NS::cast<TimeBasedRollingPolicy>(_priv->rollingPolicy))
pTimeBased->loadLastFileName();
#endif
- OutputStreamPtr os = std::make_shared<FileOutputStream>(fileName, true);
+ OutputStreamPtr os = std::make_shared<FileOutputStream>(fileName, true, _priv->lazyFileCreation);
WriterPtr newWriter(_priv->createWriter(os));
setFile(fileName);
_priv->setWriter(newWriter);
@@ -446,7 +428,7 @@ WriterPtr MultiprocessRollingFileAppender::MultiprocessRollingFileAppenderPriv::
{
auto fos = LOG4CXX_NS::cast<FileOutputStream>(os);
if( fos )
- this->log_file = fos->getFilePtr();
+ this->fileOutputStream = fos;
else
{
LogString msg(LOG4CXX_STR("Can't cast stream to FileOutputStream"));
diff --git a/src/main/include/log4cxx/fileappender.h b/src/main/include/log4cxx/fileappender.h
index 06e76b9b..5743b167 100644
--- a/src/main/include/log4cxx/fileappender.h
+++ b/src/main/include/log4cxx/fileappender.h
@@ -34,6 +34,10 @@ class Pool;
/**
* FileAppender appends log events to a file.
*
+* The file is opened/created immediately by #activateOptions()
+* unless the <code>LazyFileCreation</code> option is set <code>true</code>,
+* in which case the file is opened/created when the first spi::LoggingEvent is added.
+*
* Uses a background thread to periodically flush the output buffer
* when <code>BufferedIO</code> option is set <code>true</code>.
* Use the <code>BufferedSeconds</code> option to control the frequency,
@@ -118,11 +122,13 @@ class LOG4CXX_EXPORT FileAppender : public WriterAppender
/**
\copybrief WriterAppender::activateOptions()
- Sets and <i>opens</i> the file where the log output will
- go. The specified file must be writable.
+ When <code>LazyFileCreation</code> and <code>Append</code> are both set to <code>true</code>,
+ the file is opened/created when the first spi::LoggingEvent is added,
+ otherwise #activateOptions will open/create the file.
- If there was already an opened file, then the previous file
- is closed first.
+ When <code>BufferedIO</code> is <code>true</code> and
+ <code>BufferedSeconds</code> is greater than zero,
+ a background task is started that will periodically flush the buffer.
*/
void activateOptions( ) override;
@@ -135,6 +141,7 @@ class LOG4CXX_EXPORT FileAppender : public WriterAppender
Append | True,False | True |
BufferedIO | True,False | False |
BufferedSeconds | {any} | 5 |
+ LazyFileCreation | True,False | False |
ImmediateFlush | True,False | False |
BufferSize | (\ref fileSz1 "1") | 8 KB |
@@ -210,6 +217,21 @@ class LOG4CXX_EXPORT FileAppender : public WriterAppender
*/
void setBufferedSeconds(int newValue);
+ /**
+ Create the file when the first message is written (rather than in #activateOptions)
+ if \c newValue is true, so that no empty file is created when nothing is logged.
+ The default is false.
+
+ Note: Behavior change occurs when
+ #activateOptions is called, not when the options are set.
+ */
+ void setLazyFileCreation(bool newValue);
+
+ /**
+ Get the value of the <b>LazyFileCreation</b> option.
+ */
+ bool getLazyFileCreation() const;
+
/**
* Replaces double backslashes with single backslashes
* for compatibility with paths from earlier XML configurations files.
diff --git a/src/main/include/log4cxx/helpers/fileoutputstream.h b/src/main/include/log4cxx/helpers/fileoutputstream.h
index f9b70247..24430ec1 100644
--- a/src/main/include/log4cxx/helpers/fileoutputstream.h
+++ b/src/main/include/log4cxx/helpers/fileoutputstream.h
@@ -30,6 +30,10 @@ namespace helpers
/**
* OutputStream implemented on top of APR file IO.
+*
+* This class assumes a single active thread: the file is opened lazily
+* without synchronization. The guarantee is provided by the mutex
+* held in AppenderSkeleton::doAppend.
*/
class LOG4CXX_EXPORT FileOutputStream : public OutputStream
{
@@ -43,7 +47,13 @@ class LOG4CXX_EXPORT FileOutputStream : public OutputStream
LOG4CXX_CAST_ENTRY_CHAIN(OutputStream)
END_LOG4CXX_CAST_MAP()
- FileOutputStream(const LogString& filename, bool append = false);
+ /**
+ Open \c filename unless \c lazyOpen is true,
+ in which case the file is opened on first write
+ (or when #getFilePtr is called)
+ and an existing file is truncated immediately if \c append is false.
+ */
+ FileOutputStream(const LogString& filename, bool append = false, bool lazyOpen = false);
virtual ~FileOutputStream();
using OutputStream::close;
diff --git a/src/main/include/log4cxx/private/fileappender_priv.h b/src/main/include/log4cxx/private/fileappender_priv.h
index 986c5bbe..5100f494 100644
--- a/src/main/include/log4cxx/private/fileappender_priv.h
+++ b/src/main/include/log4cxx/private/fileappender_priv.h
@@ -70,6 +70,10 @@ struct FileAppender::FileAppenderPriv : public WriterAppender::WriterAppenderPri
*/
int bufferedSeconds{ 5 };
+ /**
+ Create the file when the first message is written? */
+ bool lazyFileCreation{ false };
+
/**
Control block shared with the periodic output buffer flush task.
The flush task only reaches this appender through the control block,
diff --git a/src/site/markdown/change-report-gh.md b/src/site/markdown/change-report-gh.md
index 9c07e863..815d99d7 100644
--- a/src/site/markdown/change-report-gh.md
+++ b/src/site/markdown/change-report-gh.md
@@ -70,6 +70,9 @@ Support remains for 'LOG4CXX_CHAR=wchar_t' for applications using std::wstring.
Release 2.0.0 includes the following new features:
+* A new log4cxx::FileAppender option to delay log file creation until an event is added.
+ \[[#775](https://github.com/apache/logging-log4cxx/pull/775)\]
+
* A new log4cxx::helpers::FileWatchdog::startWatching method to create a periodic task without a needing to sub-class.
\[[#769](https://github.com/apache/logging-log4cxx/pull/769)\]
diff --git a/src/test/cpp/fileappendertest.cpp b/src/test/cpp/fileappendertest.cpp
index d2a35bcb..790d0729 100644
--- a/src/test/cpp/fileappendertest.cpp
+++ b/src/test/cpp/fileappendertest.cpp
@@ -63,6 +63,7 @@ LOGUNIT_CLASS(FileAppenderTest)
{
LOGUNIT_TEST_SUITE(FileAppenderTest);
LOGUNIT_TEST(testDirectoryCreation);
+ LOGUNIT_TEST(testLazyFileCreation);
LOGUNIT_TEST(testgetSetThreshold);
LOGUNIT_TEST(testIsAsSevereAsThreshold);
LOGUNIT_TEST(testFlushTaskRemovedAfterRename);
@@ -102,6 +103,31 @@ public:
LOGUNIT_ASSERT(File(LOG4CXX_STR("output/newdir/temp.log")).exists());
}
+ /**
+ * Tests that the LazyFileCreation option defers file creation until the first write.
+ */
+ void testLazyFileCreation()
+ {
+ LogString fileName(LOG4CXX_STR("output/lazydir/lazy.log"));
+ File(fileName).deleteFile();
+ File(LOG4CXX_STR("output/lazydir")).deleteFile();
+
+ FileAppenderPtr wa(new FileAppender());
+ wa->setFile(fileName);
+ wa->setLazyFileCreation(true);
+ wa->setLayout(PatternLayoutPtr(new PatternLayout(LOG4CXX_STR("%m%n"))));
+ wa->activateOptions();
+ LOGUNIT_ASSERT(!File(fileName).exists());
+
+ auto logger = Logger::getLogger(LOG4CXX_STR("testLazyFileCreation"));
+ logger->addAppender(wa);
+ logger->setAdditivity(false);
+ LOG4CXX_INFO(logger, "trigger lazy file creation");
+ logger->removeAppender(wa);
+
+ LOGUNIT_ASSERT(File(fileName).exists());
+ }
+
/**
* Tests getThreshold and setThreshold.
*/
diff --git a/src/test/cpp/hierarchythresholdtestcase.cpp b/src/test/cpp/hierarchythresholdtestcase.cpp
index 65383cd8..1f261c7d 100644
--- a/src/test/cpp/hierarchythresholdtestcase.cpp
+++ b/src/test/cpp/hierarchythresholdtestcase.cpp
@@ -62,7 +62,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold1.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.1")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.1")));
}
void test2()
@@ -70,7 +70,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold2.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.2")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.2")));
}
void test3()
@@ -78,7 +78,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold3.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.3")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.3")));
}
void test4()
@@ -86,7 +86,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold4.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.4")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.4")));
}
void test5()
@@ -94,7 +94,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold5.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.5")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.5")));
}
void test6()
@@ -102,7 +102,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold6.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.6")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.6")));
}
void test7()
@@ -110,7 +110,7 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold7.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.7")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.7")));
}
void test8()
@@ -118,7 +118,18 @@ public:
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/hierarchyThreshold8.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
- LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.8")));
+ LOGUNIT_ASSERT(compareAllowingMissingEmpty(TEMP, LOG4CXX_FILE("witness/hierarchyThreshold.8")));
+ }
+
+ // File creation is lazy, so a threshold that filters out every message leaves no file at all.
+ static bool compareAllowingMissingEmpty(const File& actual, const File& witness)
+ {
+ if (!actual.exists())
+ {
+ return witness.length() == 0;
+ }
+
+ return Compare::compare(actual, witness);
}
static void common()
diff --git a/src/test/cpp/rolling/timebasedrollingtest.cpp b/src/test/cpp/rolling/timebasedrollingtest.cpp
index 6c80a0bd..eb5a6d8f 100644
--- a/src/test/cpp/rolling/timebasedrollingtest.cpp
+++ b/src/test/cpp/rolling/timebasedrollingtest.cpp
@@ -261,16 +261,18 @@ private:
* @param[in] prefix
* @param[in] fnames
* @param[in] srcLine
+ * @param[in] startingAt
*/
template<size_t N>
void compareWitnesses
- ( Pool& pool
+ ( Pool& pool
, const LogString& prefix
, const LogString (&fnames)[N]
, int srcLine
- )
+ , int startingAt = 0
+ )
{
- for (size_t i = 0; i < N; ++i)
+ for (int i = startingAt; i < int(N); ++i)
{
this->compareWitness(pool, prefix, fnames[i], int(i), srcLine);
}
@@ -291,6 +293,7 @@ private:
* @param[in] fnames
* @param[in] witnessIdx
* @param[in] srcLine
+ * @param[in] startingAt
*/
template<size_t N>
void checkFilesExist
@@ -299,15 +302,19 @@ private:
, const LogString (&fnames)[N]
, int witnessIdx
, int srcLine
+ , int startingAt = 0
)
{
- for (size_t i = 0; i < N - 1; ++i)
+ for (int i = startingAt; i < int(N) - 1; ++i)
{
//std::wcerr << L"Check: " << fnames[i] << L"\n";
LOGUNIT_ASSERT_EQUAL_SRCL(true, File(fnames[i]).exists(), srcLine);
}
- this->compareWitness(pool, prefix, fnames[witnessIdx], witnessIdx, srcLine);
+ if (witnessIdx >= startingAt)
+ {
+ this->compareWitness(pool, prefix, fnames[witnessIdx], witnessIdx, srcLine);
+ }
}
/**
@@ -420,7 +427,9 @@ public:
this->buildTsFnames<4>(pool, LOG4CXX_STR("test1-"), fnames);
this->delayUntilNextSecondWithMsg();
this->logMsgAndSleep( pool, nrOfFnames + 1, __LOG4CXX_FUNC__, __LINE__);
- this->compareWitnesses<4>( pool, LOG4CXX_STR("test1."), fnames, __LINE__);
+ // Nothing was logged to fnames[0] and since file creation
+ // is lazy the file doesn't exist.
+ this->compareWitnesses<4>( pool, LOG4CXX_STR("test1."), fnames, __LINE__, 1);
}
@@ -465,7 +474,9 @@ public:
logger->addAppender(rfa2);
this->logMsgAndSleep( pool, 2, __LOG4CXX_FUNC__, __LINE__, 3);
- this->compareWitnesses<4>( pool, LOG4CXX_STR("test2."), fnames, __LINE__);
+ // Nothing was logged to fnames[0] and since file creation
+ // is lazy the file doesn't exist.
+ this->compareWitnesses<4>( pool, LOG4CXX_STR("test2."), fnames, __LINE__, 1);
}
@@ -494,7 +505,9 @@ public:
fnames[nrOfFnames - 1].resize(fnames[nrOfFnames - 1].size() - 3);
this->delayUntilNextSecondWithMsg();
this->logMsgAndSleep( pool, nrOfFnames + 1, __LOG4CXX_FUNC__, __LINE__);
- this->checkFilesExist<4>( pool, LOG4CXX_STR("test3."), fnames, nrOfFnames - 1, __LINE__);
+ // Nothing was logged to fnames[0] and since file creation
+ // is lazy the file doesn't exist.
+ this->checkFilesExist<4>( pool, LOG4CXX_STR("test3."), fnames, nrOfFnames - 1, __LINE__, 1);
}
/**
@@ -583,7 +596,9 @@ public:
this->delayUntilNextSecondWithMsg();
this->logMsgAndSleep( pool, nrOfLogMsgs, __LOG4CXX_FUNC__, __LINE__);
- this->compareWitnesses<4>( pool, LOG4CXX_STR("test5."), fnames, __LINE__);
+ // Nothing was logged to fnames[0] and since file creation
+ // is lazy the file doesn't exist.
+ this->compareWitnesses<4>( pool, LOG4CXX_STR("test5."), fnames, __LINE__, 1);
}
/**