diff --git a/.github/workflows/log4cxx-ubuntu.yml b/.github/workflows/log4cxx-ubuntu.yml index 3ab8720d9..d38dd3ef6 100644 --- a/.github/workflows/log4cxx-ubuntu.yml +++ b/.github/workflows/log4cxx-ubuntu.yml @@ -32,6 +32,7 @@ jobs: fmt: OFF qt: ON odbc: OFF + multiprocess: ON multithread: OFF exitevents: OFF fuzzers: OFF @@ -41,6 +42,7 @@ jobs: fmt: ON qt: OFF odbc: ON + multiprocess: OFF multithread: OFF exitevents: OFF fuzzers: ON @@ -50,6 +52,7 @@ jobs: fmt: OFF qt: OFF odbc: OFF + multiprocess: OFF multithread: ON exitevents: ON fuzzers: OFF @@ -59,6 +62,7 @@ jobs: fmt: ON qt: OFF odbc: OFF + multiprocess: OFF multithread: ON exitevents: OFF fuzzers: ON @@ -96,6 +100,7 @@ jobs: -DLOG4CXX_ENABLE_ODBC=${{ matrix.odbc }} \ -DLOG4CXX_QT_SUPPORT=${{ matrix.qt }} \ -DENABLE_MULTITHREAD_TEST=${{ matrix.multithread }} \ + -DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=${{ matrix.multiprocess }} \ -DLOG4CXX_EVENTS_AT_EXIT=${{ matrix.exitevents }} \ -DBUILD_FUZZERS=${{ matrix.fuzzers }} \ .. diff --git a/.github/workflows/log4cxx-windows-static.yml b/.github/workflows/log4cxx-windows-static.yml index 38a09ec54..4712728c6 100644 --- a/.github/workflows/log4cxx-windows-static.yml +++ b/.github/workflows/log4cxx-windows-static.yml @@ -75,7 +75,7 @@ jobs: cd main mkdir build cd build - cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBUILD_SHARED_LIBS=off -DLOG4CXX_TEST_PROGRAM_PATH=C:\msys64\usr\bin "-DCMAKE_TOOLCHAIN_FILE=$THISDIR/vcpkg/scripts/buildsystems/vcpkg.cmake" .. + cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBUILD_SHARED_LIBS=off -DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=on -DLOG4CXX_TEST_PROGRAM_PATH=C:\msys64\usr\bin "-DCMAKE_TOOLCHAIN_FILE=$THISDIR/vcpkg/scripts/buildsystems/vcpkg.cmake" .. cmake --build . - name: run unit tests diff --git a/src/main/cpp/class.cpp b/src/main/cpp/class.cpp index cbfda736f..7718349e2 100644 --- a/src/main/cpp/class.cpp +++ b/src/main/cpp/class.cpp @@ -39,6 +39,9 @@ #include #endif #include +#if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER +#include +#endif #include #include #include @@ -199,6 +202,9 @@ void Class::registerClasses() StringMatchFilter::registerClass(); LocationInfoFilter::registerClass(); LOG4CXX_NS::rolling::RollingFileAppender::registerClass(); +#if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER + LOG4CXX_NS::rolling::MultiprocessRollingFileAppender::registerClass(); +#endif LOG4CXX_NS::rolling::SizeBasedTriggeringPolicy::registerClass(); LOG4CXX_NS::rolling::TimeBasedRollingPolicy::registerClass(); LOG4CXX_NS::rolling::ManualTriggeringPolicy::registerClass(); diff --git a/src/main/cpp/multiprocessrollingfileappender.cpp b/src/main/cpp/multiprocessrollingfileappender.cpp index 9614bb2be..fc60b24e1 100644 --- a/src/main/cpp/multiprocessrollingfileappender.cpp +++ b/src/main/cpp/multiprocessrollingfileappender.cpp @@ -32,140 +32,112 @@ #include #include #include -#include +#include #include -#include #include -using namespace LOG4CXX_NS; -using namespace LOG4CXX_NS::rolling; -using namespace LOG4CXX_NS::helpers; -using namespace LOG4CXX_NS::spi; - -struct MultiprocessRollingFileAppender::MultiprocessRollingFileAppenderPriv : public FileAppenderPriv +namespace LOG4CXX_NS { - MultiprocessRollingFileAppenderPriv() : - FileAppenderPriv(), - fileLength(0) {} +using namespace helpers; + +namespace rolling +{ +/** + * Wrapper for OutputStream that will report all log file + * size changes back to the appender for file length calculations. + */ +class MultiprocessOutputStream : public OutputStream +{ /** - * Triggering policy. + * Wrapped output stream. */ - TriggeringPolicyPtr triggeringPolicy; +private: + OutputStreamPtr os; /** - * Rolling policy. + * Rolling file appender to inform of stream writes. */ - RollingPolicyPtr rollingPolicy; + MultiprocessRollingFileAppender* rfa; +public: /** - * Length of current active log file. + * Constructor. + * @param os output stream to wrap. + * @param rfa rolling file appender to inform. */ - size_t fileLength; + MultiprocessOutputStream(const OutputStreamPtr& os1, MultiprocessRollingFileAppender* rfa1) + : os(os1), rfa(rfa1) + { + } /** - * save the loggingevent + * {@inheritDoc} */ - spi::LoggingEventPtr _event; -}; - -#define _priv static_cast(m_priv.get()) - -IMPLEMENT_LOG4CXX_OBJECT(MultiprocessRollingFileAppender) - - -/** - * Construct a new instance. - */ -MultiprocessRollingFileAppender::MultiprocessRollingFileAppender() : - FileAppender (std::make_unique()) -{ -} + void close(Pool& p) override + { + os->close(p); + rfa = 0; + } -/** - * Prepare instance of use. - */ -void MultiprocessRollingFileAppender::activateOptions(Pool& p) -{ - if (_priv->rollingPolicy == NULL) + /** + * {@inheritDoc} + */ + void flush(Pool& p) override { - auto fwrp = std::make_shared(); - fwrp->setFileNamePattern(getFile() + LOG4CXX_STR(".%i")); - _priv->rollingPolicy = fwrp; + os->flush(p); } - // - // if no explicit triggering policy and rolling policy is both. - // - if (_priv->triggeringPolicy == NULL) + /** + * {@inheritDoc} + */ + void write(ByteBuffer& buf, Pool& p) override { - TriggeringPolicyPtr trig = LOG4CXX_NS::cast(_priv->rollingPolicy); + os->write(buf, p); - if (trig != NULL) + if (rfa != 0) { - _priv->triggeringPolicy = trig; + rfa->setFileLength(File().setPath(rfa->getFile()).length(p)); } } - if (_priv->triggeringPolicy == NULL) + static FileOutputStreamPtr getFileOutputStream(const WriterPtr& writer) { - _priv->triggeringPolicy = std::make_shared(); + FileOutputStreamPtr result; + auto osw = LOG4CXX_NS::cast(writer); + if( !osw ){ + LogLog::error( LOG4CXX_STR("Can't cast writer to OutputStreamWriter") ); + return result; + } + auto cos = LOG4CXX_NS::cast(osw->getOutputStreamPtr()); + if( !cos ){ + LogLog::error( LOG4CXX_STR("Can't cast stream to MultiprocessOutputStream") ); + return result; + } + result = LOG4CXX_NS::cast(cos->os); + if( !result ){ + LogLog::error( LOG4CXX_STR("Can't cast stream to FileOutputStream") ); + } + return result; } +}; +} // namespace rolling +} // namespace LOG4CXX_NS - { - std::lock_guard lock(_priv->mutex); - _priv->triggeringPolicy->activateOptions(p); - _priv->rollingPolicy->activateOptions(p); - - try - { - RolloverDescriptionPtr rollover1 = - _priv->rollingPolicy->initialize(getFile(), getAppend(), p); - - if (rollover1 != NULL) - { - ActionPtr syncAction(rollover1->getSynchronous()); - - if (syncAction != NULL) - { - syncAction->execute(p); - } - - _priv->fileName = rollover1->getActiveFileName(); - _priv->fileAppend = rollover1->getAppend(); - - // - // async action not yet implemented - // - ActionPtr asyncAction(rollover1->getAsynchronous()); - - if (asyncAction != NULL) - { - asyncAction->execute(p); - } - } +using namespace LOG4CXX_NS; +using namespace LOG4CXX_NS::rolling; +using namespace LOG4CXX_NS::helpers; +using namespace LOG4CXX_NS::spi; - File activeFile; - activeFile.setPath(getFile()); +#define _priv static_cast(m_priv.get()) - if (getAppend()) - { - _priv->fileLength = activeFile.length(p); - } - else - { - _priv->fileLength = 0; - } +IMPLEMENT_LOG4CXX_OBJECT(MultiprocessRollingFileAppender) - FileAppender::activateOptionsInternal(p); - } - catch (std::exception&) - { - LogLog::warn( - LogString(LOG4CXX_STR("Exception will initializing RollingFileAppender named ")) - + getName()); - } - } +/** + * Construct a new instance. + */ +MultiprocessRollingFileAppender::MultiprocessRollingFileAppender() +{ } void MultiprocessRollingFileAppender::releaseFileLock(apr_file_t* lock_file) @@ -213,6 +185,9 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) if (_priv->rollingPolicy != NULL) { + if (auto pTimeBased = LOG4CXX_NS::cast(_priv->rollingPolicy)) + pTimeBased->setMultiprocess(true); + { LogString fileName(getFile()); RollingPolicyBasePtr basePolicy = LOG4CXX_NS::cast(_priv->rollingPolicy); @@ -230,32 +205,14 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) } bool bAlreadyRolled = true; - char szDirName[MAX_FILE_LEN] = {'\0'}; - char szBaseName[MAX_FILE_LEN] = {'\0'}; - char szUid[MAX_FILE_LEN] = {'\0'}; - memcpy(szDirName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size()); - memcpy(szBaseName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size()); - apr_uid_t uid; - apr_gid_t groupid; - apr_status_t stat = apr_uid_current(&uid, &groupid, p.getAPRPool()); - - if (stat == APR_SUCCESS) - { -#ifdef _WIN32 - snprintf(szUid, MAX_FILE_LEN, "%p", uid); -#else - snprintf(szUid, MAX_FILE_LEN, "%u", (unsigned int)uid); -#endif - } - LOG4CXX_NS::filesystem::path path = szDirName; - const auto lockname = path.parent_path() / (path.filename().string() + szUid + ".lock"); + LogString lockname = fileName + ".lock"; apr_file_t* lock_file; - stat = apr_file_open(&lock_file, lockname.string().c_str(), APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, p.getAPRPool()); + auto stat = apr_file_open(&lock_file, lockname.c_str(), APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, p.getAPRPool()); if (stat != APR_SUCCESS) { - LogString err = LOG4CXX_STR("lockfile return error: open lockfile failed. "); + LogString err = lockname + LOG4CXX_STR(": apr_file_open error: "); err += (strerror(errno)); LogLog::warn(err); bAlreadyRolled = false; @@ -267,7 +224,7 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) if (stat != APR_SUCCESS) { - LogString err = LOG4CXX_STR("apr_file_lock: lock failed. "); + LogString err = lockname + LOG4CXX_STR(": apr_file_lock error: "); err += (strerror(errno)); LogLog::warn(err); bAlreadyRolled = false; @@ -283,16 +240,11 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) if (bAlreadyRolled) { - apr_finfo_t finfo1, finfo2; - apr_status_t st1, st2; - const WriterPtr writer = getWriter(); - const FileOutputStreamPtr fos = LOG4CXX_NS::cast( writer ); - if( !fos ){ - LogLog::error( LOG4CXX_STR("Can't cast writer to FileOutputStream") ); + auto fos = MultiprocessOutputStream::getFileOutputStream(getWriter()); + if( !fos ) return false; - } - apr_file_t* _fd = fos->getFilePtr(); - st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, _fd); + apr_finfo_t finfo1; + apr_status_t st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, fos->getFilePtr()); if (st1 != APR_SUCCESS) { @@ -300,11 +252,12 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) } LogString fname = getFile(); - st2 = apr_stat(&finfo2, fname.c_str(), APR_FINFO_IDENT, p.getAPRPool()); + apr_finfo_t finfo2; + apr_status_t st2 = apr_stat(&finfo2, fname.c_str(), APR_FINFO_IDENT, p.getAPRPool()); if (st2 != APR_SUCCESS) { - LogLog::warn(LOG4CXX_STR("apr_stat failed.")); + LogLog::warn(fname + LOG4CXX_STR(": apr_stat failed.")); } bAlreadyRolled = ((st1 == APR_SUCCESS) && (st2 == APR_SUCCESS) @@ -316,9 +269,7 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) try { - RolloverDescriptionPtr rollover1(_priv->rollingPolicy->rollover(this->getFile(), this->getAppend(), p)); - - if (rollover1 != NULL) + if (auto rollover1 = _priv->rollingPolicy->rollover(getFile(), getAppend(), p)) { if (rollover1->getActiveFileName() == getFile()) { @@ -326,21 +277,9 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) bool success = true; - if (rollover1->getSynchronous() != NULL) + if (auto pAction = rollover1->getSynchronous()) { - success = false; - - try - { - success = rollover1->getSynchronous()->execute(p); - } - catch (std::exception& ex) - { - LogString msg(LOG4CXX_STR("Rollover of [")); - msg.append(getFile()); - msg.append(LOG4CXX_STR("] failed")); - _priv->errorHandler->error(msg, ex, 0); - } + success = pAction->execute(p); } bool appendToExisting = true; @@ -356,9 +295,7 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) _priv->fileLength = 0; } - ActionPtr asyncAction(rollover1->getAsynchronous()); - - if (asyncAction != NULL) + if (auto asyncAction = rollover1->getAsynchronous()) { try { @@ -366,13 +303,20 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) } catch (std::exception& ex) { - LogString msg(LOG4CXX_STR("Rollover of [")); + LogString msg(LOG4CXX_STR("Async action in rollover [")); msg.append(getFile()); msg.append(LOG4CXX_STR("] failed")); _priv->errorHandler->error(msg, ex, 0); } } } + else + { + LogString msg(LOG4CXX_STR("Rollover of [")); + msg.append(getFile()); + msg.append(LOG4CXX_STR("] failed")); + _priv->errorHandler->error(msg); + } setFileInternal(rollover1->getActiveFileName(), appendToExisting, _priv->bufferedIO, _priv->bufferSize, p); } else @@ -381,20 +325,21 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) setFileInternal(rollover1->getActiveFileName()); // Call activateOptions to create any intermediate directories(if required) FileAppender::activateOptionsInternal(p); - OutputStreamPtr os(new FileOutputStream( - rollover1->getActiveFileName(), rollover1->getAppend())); - WriterPtr newWriter(createWriter(os)); - setWriterInternal(newWriter); + OutputStreamPtr os = std::make_shared + ( rollover1->getActiveFileName() + , rollover1->getAppend() + ); + setWriterInternal(createWriter(os)); bool success = true; - if (rollover1->getSynchronous() != NULL) + if (auto pAction = rollover1->getSynchronous()) { success = false; try { - success = rollover1->getSynchronous()->execute(p); + success = pAction->execute(p); } catch (std::exception& ex) { @@ -419,9 +364,7 @@ bool MultiprocessRollingFileAppender::rolloverInternal(Pool& p) // // async action not yet implemented // - ActionPtr asyncAction(rollover1->getAsynchronous()); - - if (asyncAction != NULL) + if (auto asyncAction = rollover1->getAsynchronous()) { asyncAction->execute(p); } @@ -500,30 +443,27 @@ void MultiprocessRollingFileAppender::subAppend(const LoggingEventPtr& event, Po } } - //do re-check before every write - // - apr_finfo_t finfo1, finfo2; - apr_status_t st1, st2; - const WriterPtr writer = getWriter(); - const FileOutputStreamPtr fos = LOG4CXX_NS::cast( writer ); - if( !fos ){ - LogLog::error( LOG4CXX_STR("Can't cast writer to FileOutputStream") ); + auto fos = MultiprocessOutputStream::getFileOutputStream(getWriter()); + if( !fos ) return; - } - apr_file_t* _fd = fos->getFilePtr(); - st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, _fd); + + // check for a file rolloover before every write + // + apr_finfo_t finfo1; + apr_status_t st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, fos->getFilePtr()); if (st1 != APR_SUCCESS) { LogLog::warn(LOG4CXX_STR("apr_file_info_get failed")); } - st2 = apr_stat(&finfo2, std::string(getFile()).c_str(), APR_FINFO_IDENT, p.getAPRPool()); + LogString fname = getFile(); + apr_finfo_t finfo2; + apr_status_t st2 = apr_stat(&finfo2, fname.c_str(), APR_FINFO_IDENT, p.getAPRPool()); if (st2 != APR_SUCCESS) { - LogString err = "apr_stat failed. file:" + getFile(); - LogLog::warn(err); + LogLog::warn(fname + LOG4CXX_STR(": apr_stat failed.")); } bool bAlreadyRolled = ((st1 == APR_SUCCESS) && (st2 == APR_SUCCESS) @@ -537,126 +477,6 @@ void MultiprocessRollingFileAppender::subAppend(const LoggingEventPtr& event, Po FileAppender::subAppend(event, p); } -/** - * Get rolling policy. - * @return rolling policy. - */ -RollingPolicyPtr MultiprocessRollingFileAppender::getRollingPolicy() const -{ - return _priv->rollingPolicy; -} - -/** - * Get triggering policy. - * @return triggering policy. - */ -TriggeringPolicyPtr MultiprocessRollingFileAppender::getTriggeringPolicy() const -{ - return _priv->triggeringPolicy; -} - -/** - * Sets the rolling policy. - * @param policy rolling policy. - */ -void MultiprocessRollingFileAppender::setRollingPolicy(const RollingPolicyPtr& policy) -{ - _priv->rollingPolicy = policy; - - TimeBasedRollingPolicyPtr timeBased = LOG4CXX_NS::cast(policy); - if( timeBased ){ - timeBased->setMultiprocess(true); - } -} - -/** - * Set triggering policy. - * @param policy triggering policy. - */ -void MultiprocessRollingFileAppender::setTriggeringPolicy(const TriggeringPolicyPtr& policy) -{ - _priv->triggeringPolicy = policy; -} - -/** - * Close appender. Waits for any asynchronous file compression actions to be completed. - */ -void MultiprocessRollingFileAppender::close() -{ - FileAppender::close(); -} - -namespace LOG4CXX_NS -{ -namespace rolling -{ -/** - * Wrapper for OutputStream that will report all write - * operations back to this class for file length calculations. - */ -class CountingOutputStream : public OutputStream -{ - /** - * Wrapped output stream. - */ - private: - OutputStreamPtr os; - - /** - * Rolling file appender to inform of stream writes. - */ - MultiprocessRollingFileAppender* rfa; - - public: - /** - * Constructor. - * @param os output stream to wrap. - * @param rfa rolling file appender to inform. - */ - CountingOutputStream( - OutputStreamPtr& os1, MultiprocessRollingFileAppender* rfa1) : - os(os1), rfa(rfa1) - { - } - - /** - * {@inheritDoc} - */ - void close(Pool& p) - { - os->close(p); - rfa = 0; - } - - /** - * {@inheritDoc} - */ - void flush(Pool& p) - { - os->flush(p); - } - - /** - * {@inheritDoc} - */ - void write(ByteBuffer& buf, Pool& p) - { - os->write(buf, p); - - if (rfa != 0) - { - rfa->setFileLength(File().setPath(rfa->getFile()).length(p)); - } - } - - OutputStream& getFileOutPutStreamPtr() - { - return *os; - } -}; -} -} - /** Returns an OutputStreamWriter when passed an OutputStream. The encoding used will depend on the value of the @@ -668,29 +488,12 @@ class CountingOutputStream : public OutputStream */ WriterPtr MultiprocessRollingFileAppender::createWriter(OutputStreamPtr& os) { - OutputStreamPtr cos = std::make_shared(os, this); + OutputStreamPtr cos = std::make_shared(os, this); return FileAppender::createWriter(cos); } -/** - * Get byte length of current active log file. - * @return byte length of current active log file. - */ -size_t MultiprocessRollingFileAppender::getFileLength() const -{ - return _priv->fileLength; -} void MultiprocessRollingFileAppender::setFileLength(size_t length) { _priv->fileLength = length; } - -/** - * Increments estimated byte length of current active log file. - * @param increment additional bytes written to log file. - */ -void MultiprocessRollingFileAppender::incrementFileLength(size_t increment) -{ - _priv->fileLength += increment; -} diff --git a/src/main/cpp/rollingfileappender.cpp b/src/main/cpp/rollingfileappender.cpp index f96fbcbac..ae96921c3 100644 --- a/src/main/cpp/rollingfileappender.cpp +++ b/src/main/cpp/rollingfileappender.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include using namespace LOG4CXX_NS; @@ -34,33 +34,6 @@ using namespace LOG4CXX_NS::rolling; using namespace LOG4CXX_NS::helpers; using namespace LOG4CXX_NS::spi; -struct RollingFileAppender::RollingFileAppenderPriv : public FileAppenderPriv -{ - RollingFileAppenderPriv() : - FileAppenderPriv(), - fileLength(0) {} - - /** - * Triggering policy. - */ - TriggeringPolicyPtr triggeringPolicy; - - /** - * Rolling policy. - */ - RollingPolicyPtr rollingPolicy; - - /** - * Length of current active log file. - */ - size_t fileLength; - - /** - * save the loggingevent - */ - spi::LoggingEventPtr _event; -}; - #define _priv static_cast(m_priv.get()) IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppender) diff --git a/src/main/cpp/timebasedrollingpolicy.cpp b/src/main/cpp/timebasedrollingpolicy.cpp index 745cbbada..711684dd1 100644 --- a/src/main/cpp/timebasedrollingpolicy.cpp +++ b/src/main/cpp/timebasedrollingpolicy.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -131,7 +130,8 @@ bool TimeBasedRollingPolicy::isMapFileEmpty(LOG4CXX_NS::helpers::Pool& pool) LogLog::warn(LOG4CXX_STR("apr_stat failed.")); } - if (st == APR_SUCCESS && !finfo.size) + if (st == APR_SUCCESS && (0 == finfo.size || + (m_priv->_mmap && 0 == *static_cast(m_priv->_mmap->mm)))) { return true; } @@ -159,29 +159,14 @@ void TimeBasedRollingPolicy::initMMapFile(const LogString& lastFileName, LOG4CXX const std::string TimeBasedRollingPolicy::createFile(const std::string& fileName, const std::string& suffix, LOG4CXX_NS::helpers::Pool& pool) { - char szUid[MAX_FILE_LEN] = {'\0'}; - char szBaseName[MAX_FILE_LEN] = {'\0'}; - char szDirName[MAX_FILE_LEN] = {'\0'}; - memcpy(szDirName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size()); - memcpy(szBaseName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size()); - + char szUid[MAX_FILE_LEN] = "0000"; +#ifndef _WIN32 // The uid provided by the Windows version of apr_uid_current is not a constant value apr_uid_t uid; apr_gid_t groupid; - apr_status_t stat = apr_uid_current(&uid, &groupid, pool.getAPRPool()); - - if (stat == APR_SUCCESS) - { -#ifdef _WIN32 - snprintf(szUid, MAX_FILE_LEN, "%p", uid); -#else + if (APR_SUCCESS == apr_uid_current(&uid, &groupid, pool.getAPRPool())) snprintf(szUid, MAX_FILE_LEN, "%u", uid); #endif - } - - LOG4CXX_NS::filesystem::path path(fileName); - std::string newFilename = path.filename().string() + szUid + suffix; - LOG4CXX_NS::filesystem::path retval = path.parent_path() / newFilename; - return retval.string(); + return fileName + szUid + suffix; } int TimeBasedRollingPolicy::createMMapFile(const std::string& fileName, LOG4CXX_NS::helpers::Pool& pool) @@ -301,32 +286,6 @@ void TimeBasedRollingPolicy::activateOptions(LOG4CXX_NS::helpers::Pool& pool) formatFileName(obj, buf, pool); m_priv->lastFileName = buf; - if( m_priv->multiprocess ){ -#if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER - if (getPatternConverterList().size()) - { - (*(getPatternConverterList().begin()))->format(obj, m_priv->_fileNamePattern, pool); - } - else - { - m_priv->_fileNamePattern = m_priv->lastFileName; - } - - if (!m_priv->_lock_file) - { - const std::string lockname = createFile(std::string(m_priv->_fileNamePattern), LOCK_FILE_SUFFIX, m_priv->_mmapPool); - apr_status_t stat = apr_file_open(&m_priv->_lock_file, lockname.c_str(), APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, m_priv->_mmapPool.getAPRPool()); - - if (stat != APR_SUCCESS) - { - LogLog::warn(LOG4CXX_STR("open lock file failed.")); - } - } - - initMMapFile(m_priv->lastFileName, m_priv->_mmapPool); -#endif - } - m_priv->suffixLength = 0; if (m_priv->lastFileName.length() >= 3) @@ -406,6 +365,31 @@ RolloverDescriptionPtr TimeBasedRollingPolicy::rollover( if( m_priv->multiprocess ){ #if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER + + if (!m_priv->bAlreadyInitialized) + { + if (getPatternConverterList().size()) + { + (*(getPatternConverterList().begin()))->format(obj, m_priv->_fileNamePattern, pool); + } + else + { + m_priv->_fileNamePattern = m_priv->lastFileName; + } + + if (!m_priv->_lock_file) + { + const std::string lockname = createFile(std::string(m_priv->_fileNamePattern), LOCK_FILE_SUFFIX, m_priv->_mmapPool); + apr_status_t stat = apr_file_open(&m_priv->_lock_file, lockname.c_str(), APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, m_priv->_mmapPool.getAPRPool()); + + if (stat != APR_SUCCESS) + { + LogLog::warn(lockname + LOG4CXX_STR(": apr_file_open failed.")); + } + } + + initMMapFile(m_priv->lastFileName, m_priv->_mmapPool); + } m_priv->bAlreadyInitialized = true; if (m_priv->_mmap && !isMapFileEmpty(m_priv->_mmapPool)) diff --git a/src/main/include/log4cxx/private/rollingfileappender_priv.h b/src/main/include/log4cxx/private/rollingfileappender_priv.h new file mode 100644 index 000000000..c857594a4 --- /dev/null +++ b/src/main/include/log4cxx/private/rollingfileappender_priv.h @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOG4CXX_ROLLING_FILEAPPENDER_PRIV_H +#define _LOG4CXX_ROLLING_FILEAPPENDER_PRIV_H + +#include + +namespace LOG4CXX_NS +{ + +namespace rolling +{ + +struct RollingFileAppender::RollingFileAppenderPriv : public FileAppenderPriv +{ + RollingFileAppenderPriv() : + FileAppenderPriv(), + fileLength(0) {} + + /** + * Triggering policy. + */ + TriggeringPolicyPtr triggeringPolicy; + + /** + * Rolling policy. + */ + RollingPolicyPtr rollingPolicy; + + /** + * Length of current active log file. + */ + size_t fileLength; + + /** + * save the loggingevent + */ + spi::LoggingEventPtr _event; +}; + +} // namespace rolling +} // namespace LOG4CXX_NS + +#endif // _LOG4CXX_ROLLING_FILEAPPENDER_PRIV_H diff --git a/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h b/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h index 54b3f329f..ea0737d1c 100644 --- a/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h +++ b/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include @@ -34,12 +34,12 @@ namespace rolling /** * A special version of the RollingFileAppender that acts properly with multiple processes */ -class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public FileAppender +class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public RollingFileAppender { DECLARE_LOG4CXX_OBJECT(MultiprocessRollingFileAppender) BEGIN_LOG4CXX_CAST_MAP() LOG4CXX_CAST_ENTRY(MultiprocessRollingFileAppender) - LOG4CXX_CAST_ENTRY_CHAIN(FileAppender) + LOG4CXX_CAST_ENTRY_CHAIN(RollingFileAppender) END_LOG4CXX_CAST_MAP() protected: struct MultiprocessRollingFileAppenderPriv; @@ -47,16 +47,6 @@ class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public FileAppender public: MultiprocessRollingFileAppender(); - /** - \copybrief FileAppender::activateOptions() - - Activate the attached TriggeringPolicy and RollingPolicy. - - \sa FileAppender::activateOptions() - */ - void activateOptions(helpers::Pool&) override; - - /** Implements the usual roll over behaviour. @@ -82,28 +72,6 @@ class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public FileAppender bool rolloverInternal(LOG4CXX_NS::helpers::Pool& p); - public: - - RollingPolicyPtr getRollingPolicy() const; - - TriggeringPolicyPtr getTriggeringPolicy() const; - - /** - * Sets the rolling policy. In case the 'policy' argument also implements - * {@link TriggeringPolicy}, then the triggering policy for this appender - * is automatically set to be the policy argument. - * @param policy - */ - void setRollingPolicy(const RollingPolicyPtr& policy); - - void setTriggeringPolicy(const TriggeringPolicyPtr& policy); - - public: - /** - * Close appender. Waits for any asynchronous file compression actions to be completed. - */ - void close() override; - protected: /** Returns an OutputStreamWriter when passed an OutputStream. The @@ -116,19 +84,6 @@ class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public FileAppender */ helpers::WriterPtr createWriter(helpers::OutputStreamPtr& os) override; - public: - /** - * Get byte length of current active log file. - * @return byte length of current active log file. - */ - size_t getFileLength() const; - - /** - * Increments estimated byte length of current active log file. - * @param increment additional bytes written to log file. - */ - void incrementFileLength(size_t increment); - private: /** * Set byte length of current active log file. @@ -147,7 +102,7 @@ class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public FileAppender */ void reopenLatestFile(LOG4CXX_NS::helpers::Pool& p); - friend class CountingOutputStream; + friend class MultiprocessOutputStream; }; diff --git a/src/main/include/log4cxx/rolling/timebasedrollingpolicy.h b/src/main/include/log4cxx/rolling/timebasedrollingpolicy.h index 77a493335..f191e569d 100755 --- a/src/main/include/log4cxx/rolling/timebasedrollingpolicy.h +++ b/src/main/include/log4cxx/rolling/timebasedrollingpolicy.h @@ -40,8 +40,8 @@ namespace rolling *

In order to use TimeBasedRollingPolicy, the * FileNamePattern option must be set. It basically specifies the name of the * rolled log files. The value FileNamePattern should consist of - * the name of the file, plus a suitably placed %d conversion - * specifier. The %d conversion specifier may contain a date and + * the name of the file, plus a suitably placed \%d conversion + * specifier. The \%d conversion specifier may contain a date and * time pattern as specified by the {@link log4cxx::helpers::SimpleDateFormat} class. If * the date and time pattern is ommitted, then the default pattern of * "yyyy-MM-dd" is assumed. The following examples should clarify the point. @@ -54,9 +54,9 @@ namespace rolling * Example * * - * /wombat/folder/foo.%d + * /wombat/folder/foo.\%d * Daily rollover (at midnight). Due to the omission of the optional - * time and date pattern for the %d token specifier, the default pattern + * time and date pattern for the \%d token specifier, the default pattern * of "yyyy-MM-dd" is assumed, which corresponds to daily rollover. * * During November 23rd, 2004, logging output will go to @@ -66,7 +66,7 @@ namespace rolling * * * - * /wombat/foo.%d{yyyy-MM}.log + * /wombat/foo.\%d{yyyy-MM}.log * Rollover at the beginning of each month. * During the month of October 2004, logging output will go to * /wombat/foo.2004-10.log. After midnight of October 31st @@ -87,7 +87,7 @@ namespace rolling * Example * * - * /wombat/foo.%d.gz + * /wombat/foo.\%d.gz * Daily rollover (at midnight) with automatic GZIP compression of the * arcived files. * During November 23rd, 2004, logging output will go to @@ -116,7 +116,7 @@ namespace rolling * Example * * - * /wombat/foo.log.%d + * /wombat/foo.log.\%d * /wombat/foo.log * Daily rollover. * diff --git a/src/test/cpp/rolling/CMakeLists.txt b/src/test/cpp/rolling/CMakeLists.txt index f892b8b44..dc2c8a288 100644 --- a/src/test/cpp/rolling/CMakeLists.txt +++ b/src/test/cpp/rolling/CMakeLists.txt @@ -24,6 +24,10 @@ set(ROLLING_TESTS timebasedrollingtest rollingfileappenderpropertiestest ) +if(LOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER) + list(APPEND ROLLING_TESTS multiprocessrollingtest) +endif() + foreach(fileName IN LISTS ROLLING_TESTS) add_executable(${fileName} "${fileName}.cpp") endforeach() diff --git a/src/test/cpp/rolling/multiprocessrollingtest.cpp b/src/test/cpp/rolling/multiprocessrollingtest.cpp new file mode 100644 index 000000000..0b5f0b267 --- /dev/null +++ b/src/test/cpp/rolling/multiprocessrollingtest.cpp @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../util/compare.h" +#include "../logunit.h" +#include "../insertwide.h" +#include +#include +#include +#include +#include + +using namespace log4cxx; + +auto getLogger(const std::string& name) -> LoggerPtr { + static struct log4cxx_initializer { + log4cxx_initializer() { + xml::DOMConfigurator::configure("input/rolling/multiprocess.xml"); + } + ~log4cxx_initializer() { + LogManager::shutdown(); + } + } initAndShutdown; + return name.empty() + ? LogManager::getRootLogger() + : LogManager::getLogger(name); +} + +LOGUNIT_CLASS(MultiprocessRollingTest) +{ + LOGUNIT_TEST_SUITE(MultiprocessRollingTest); + LOGUNIT_TEST(test1); + LOGUNIT_TEST(test2); + LOGUNIT_TEST(test3); + LOGUNIT_TEST_SUITE_END(); + +public: + /** + * Test a numeric rolling policy with a log level based trigger. + */ + void test1() + { + auto logger = getLogger("Test1"); + for (int i = 0; i < 25; i++) + { + char msg[10]; +#if defined(__STDC_LIB_EXT1__) || defined(__STDC_SECURE_LIB__) + strcpy_s(msg, sizeof msg, "Hello---?"); +#else + strcpy(msg, "Hello---?"); +#endif + + if (i < 10) + { + msg[8] = (char) ('0' + i); + LOG4CXX_DEBUG(logger, msg); + } + else if (i < 100) + { + msg[7] = (char) ('0' + (i / 10)); + msg[8] = (char) ('0' + (i % 10)); + + if ((i % 10) == 0) + { + LOG4CXX_WARN(logger, msg); + } + else + { + LOG4CXX_DEBUG(logger, msg); + } + } + } + + LogString baseName = LOG4CXX_STR("output/rolling/multiprocess-test"); + LOGUNIT_ASSERT_EQUAL(true, + Compare::compare(baseName + LOG4CXX_STR(".log"), LogString(LOG4CXX_STR("witness/rolling/multiprocess-test.log")))); + LOGUNIT_ASSERT_EQUAL(true, + Compare::compare(baseName + LOG4CXX_STR(".0"), LogString(LOG4CXX_STR("witness/rolling/multiprocess-test.0")))); + LOGUNIT_ASSERT_EQUAL(true, + Compare::compare(baseName + LOG4CXX_STR(".1"), LogString(LOG4CXX_STR("witness/rolling/multiprocess-test.1")))); + } + + /** + * Test a time based rolling policy with a sized based trigger. + */ + void test2() + { + LogString expectedPrefix = LOG4CXX_STR("multiprocess-dated-"); + // remove any previously generated files + for (auto const& dir_entry : std::filesystem::directory_iterator{"output/rolling"}) + { + LogString filename(dir_entry.path().filename().string()); + if (expectedPrefix.size() < filename.size() && + filename.substr(0, expectedPrefix.size()) == expectedPrefix) + std::filesystem::remove(dir_entry); + } + auto logger = getLogger("Test2"); + auto approxBytesPerLogEvent = 40 + 23; + auto requiredLogFileCount = 3; + auto approxBytesPerLogFile = 1000; + auto requiredLogEventCount = (approxBytesPerLogFile * requiredLogFileCount + approxBytesPerLogEvent - 1) / approxBytesPerLogEvent; + for ( int x = 0; x < requiredLogEventCount; x++ ) + { + LOG4CXX_INFO( logger, "This is test message " << x ); + } + + // Count rolled files + helpers::Pool p; + helpers::StrftimeDateFormat("%Y-%m-%d").format(expectedPrefix, helpers::Date::currentTime(), p); + int fileCount = 0; + for (auto const& dir_entry : std::filesystem::directory_iterator{"output/rolling"}) + { + LogString filename(dir_entry.path().filename().string()); + if (expectedPrefix.size() < filename.size() && + filename.substr(0, expectedPrefix.size()) == expectedPrefix) + ++fileCount; + } + LOGUNIT_ASSERT(1 < fileCount); + } + + /** + * Generate about 30 rollovers per process using a time based rolling policy with a sized based trigger. + */ + void test3() + { + auto logger = getLogger("Test2"); + auto approxBytesPerLogEvent = 40 + 23; + auto requiredLogFileCount = 30; + auto approxBytesPerLogFile = 1000; + auto requiredLogEventCount = (approxBytesPerLogFile * requiredLogFileCount + approxBytesPerLogEvent - 1) / approxBytesPerLogEvent; + for ( int x = 0; x < requiredLogEventCount; x++ ) + { + LOG4CXX_INFO( logger, "This is test message " << x ); + } + } + +private: + /** + * Common aspects of test1 and test2 + */ + void common(const LogString & baseName) + { + + } +}; + +LOGUNIT_TEST_SUITE_REGISTRATION(MultiprocessRollingTest); + diff --git a/src/test/resources/input/rolling/multiprocess.xml b/src/test/resources/input/rolling/multiprocess.xml new file mode 100644 index 000000000..b64a0a016 --- /dev/null +++ b/src/test/resources/input/rolling/multiprocess.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/witness/rolling/multiprocess-test.0 b/src/test/resources/witness/rolling/multiprocess-test.0 new file mode 100644 index 000000000..ddeb00f7e --- /dev/null +++ b/src/test/resources/witness/rolling/multiprocess-test.0 @@ -0,0 +1,10 @@ +Hello--10 +Hello--11 +Hello--12 +Hello--13 +Hello--14 +Hello--15 +Hello--16 +Hello--17 +Hello--18 +Hello--19 diff --git a/src/test/resources/witness/rolling/multiprocess-test.1 b/src/test/resources/witness/rolling/multiprocess-test.1 new file mode 100644 index 000000000..e7850d14f --- /dev/null +++ b/src/test/resources/witness/rolling/multiprocess-test.1 @@ -0,0 +1,10 @@ +Hello---0 +Hello---1 +Hello---2 +Hello---3 +Hello---4 +Hello---5 +Hello---6 +Hello---7 +Hello---8 +Hello---9 diff --git a/src/test/resources/witness/rolling/multiprocess-test.log b/src/test/resources/witness/rolling/multiprocess-test.log new file mode 100644 index 000000000..724332882 --- /dev/null +++ b/src/test/resources/witness/rolling/multiprocess-test.log @@ -0,0 +1,5 @@ +Hello--20 +Hello--21 +Hello--22 +Hello--23 +Hello--24