| /* | |
| * 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 <log4cxx/rolling/rollingfileappender.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/rolling/rolloverdescription.h> | |
| #include <log4cxx/helpers/fileoutputstream.h> | |
| #include <log4cxx/helpers/bytebuffer.h> | |
| #include <log4cxx/helpers/optionconverter.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/rolling/fixedwindowrollingpolicy.h> | |
| #include <log4cxx/rolling/timebasedrollingpolicy.h> | |
| #include <log4cxx/rolling/sizebasedtriggeringpolicy.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <log4cxx/private/rollingfileappender_priv.h> | |
| #include <mutex> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::rolling; | |
| using namespace LOG4CXX_NS::helpers; | |
| using namespace LOG4CXX_NS::spi; | |
| #define _priv static_cast<RollingFileAppenderPriv*>(m_priv.get()) | |
| IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppender) | |
| /** | |
| * Construct a new instance. | |
| */ | |
| RollingFileAppender::RollingFileAppender() | |
| : FileAppender(std::make_unique<RollingFileAppenderPriv>()) | |
| { | |
| } | |
| RollingFileAppender::RollingFileAppender( std::unique_ptr<RollingFileAppenderPriv> priv ) | |
| : FileAppender(std::move(priv)) | |
| { | |
| } | |
| void RollingFileAppender::setOption(const LogString& option, const LogString& value) | |
| { | |
| if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MAXFILESIZE"), LOG4CXX_STR("maxfilesize")) | |
| || StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MAXIMUMFILESIZE"), LOG4CXX_STR("maximumfilesize"))) | |
| { | |
| setMaxFileSize(value); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MAXBACKUPINDEX"), LOG4CXX_STR("maxbackupindex")) | |
| || StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MAXIMUMBACKUPINDEX"), LOG4CXX_STR("maximumbackupindex"))) | |
| { | |
| setMaxBackupIndex(StringHelper::toInt(value)); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("FILEDATEPATTERN"), LOG4CXX_STR("filedatepattern"))) | |
| { | |
| setDatePattern(value); | |
| } | |
| else | |
| { | |
| FileAppender::setOption(option, value); | |
| } | |
| } | |
| int RollingFileAppender::getMaxBackupIndex() const | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| int result = 1; | |
| if (auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(_priv->rollingPolicy)) | |
| result = fwrp->getMaxIndex(); | |
| return result; | |
| } | |
| void RollingFileAppender::setMaxBackupIndex(int maxBackups) | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(_priv->rollingPolicy); | |
| if (!fwrp) | |
| { | |
| fwrp = std::make_shared<FixedWindowRollingPolicy>(); | |
| fwrp->setFileNamePattern(getFile() + LOG4CXX_STR(".%i")); | |
| _priv->rollingPolicy = fwrp; | |
| } | |
| fwrp->setMaxIndex(maxBackups); | |
| } | |
| size_t RollingFileAppender::getMaximumFileSize() const | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| size_t result = 10 * 1024 * 1024; | |
| if (auto sbtp = LOG4CXX_NS::cast<SizeBasedTriggeringPolicy>(_priv->triggeringPolicy)) | |
| result = sbtp->getMaxFileSize(); | |
| return result; | |
| } | |
| void RollingFileAppender::setMaximumFileSize(size_t maxFileSize) | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| auto sbtp = LOG4CXX_NS::cast<SizeBasedTriggeringPolicy>(_priv->triggeringPolicy); | |
| if (!sbtp) | |
| { | |
| sbtp = std::make_shared<SizeBasedTriggeringPolicy>(); | |
| _priv->triggeringPolicy = sbtp; | |
| } | |
| sbtp->setMaxFileSize(maxFileSize); | |
| } | |
| void RollingFileAppender::setMaxFileSize(const LogString& value) | |
| { | |
| setMaximumFileSize(OptionConverter::toFileSize(value, long(getMaximumFileSize() + 1))); | |
| } | |
| LogString RollingFileAppender::makeFileNamePattern(const LogString& datePattern) | |
| { | |
| LogString result(getFile()); | |
| bool inLiteral = false; | |
| bool inPattern = false; | |
| for (size_t i = 0; i < datePattern.length(); i++) | |
| { | |
| if (datePattern[i] == 0x27 /* '\'' */) | |
| { | |
| inLiteral = !inLiteral; | |
| if (inLiteral && inPattern) | |
| { | |
| result.append(1, (logchar) 0x7D /* '}' */); | |
| inPattern = false; | |
| } | |
| } | |
| else | |
| { | |
| if (!inLiteral && !inPattern) | |
| { | |
| const logchar dbrace[] = { 0x25, 0x64, 0x7B, 0 }; // "%d{" | |
| result.append(dbrace); | |
| inPattern = true; | |
| } | |
| result.append(1, datePattern[i]); | |
| } | |
| } | |
| if (inPattern) | |
| { | |
| result.append(1, (logchar) 0x7D /* '}' */); | |
| } | |
| return result; | |
| } | |
| void RollingFileAppender::setDatePattern(const LogString& newPattern) | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| auto tbrp = LOG4CXX_NS::cast<TimeBasedRollingPolicy>(_priv->rollingPolicy); | |
| if (!tbrp) | |
| { | |
| tbrp = std::make_shared<TimeBasedRollingPolicy>(); | |
| _priv->rollingPolicy = tbrp; | |
| } | |
| tbrp->setFileNamePattern(makeFileNamePattern(newPattern)); | |
| } | |
| /** | |
| * Prepare instance of use. | |
| */ | |
| void RollingFileAppender::activateOptions( ) | |
| { | |
| if (_priv->activateOptions()) | |
| { | |
| FileAppender::activateOptionsInternal(); | |
| } | |
| } | |
| bool RollingFileAppender::RollingFileAppenderPriv::activateOptions() | |
| { | |
| bool result = false; | |
| if (!this->rollingPolicy) | |
| { | |
| LogLog::warn(LOG4CXX_STR("No rolling policy configured for the appender named [") | |
| + this->name + LOG4CXX_STR("].")); | |
| auto fwrp = std::make_shared<FixedWindowRollingPolicy>(); | |
| fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i")); | |
| this->rollingPolicy = fwrp; | |
| } | |
| else if (auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(this->rollingPolicy)) | |
| { | |
| // Was fwrp created to store the maximum index before the file name was set? | |
| if (fwrp->getFileNamePattern() == LOG4CXX_STR(".%i")) | |
| fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i")); | |
| } | |
| // | |
| // if no explicit triggering policy and rolling policy is both. | |
| // | |
| if (!this->triggeringPolicy) | |
| { | |
| TriggeringPolicyPtr trig = LOG4CXX_NS::cast<TriggeringPolicy>(this->rollingPolicy); | |
| if (trig != NULL) | |
| { | |
| this->triggeringPolicy = trig; | |
| } | |
| } | |
| if (!this->triggeringPolicy) | |
| { | |
| LogLog::warn(LOG4CXX_STR("No triggering policy configured for the appender named [") | |
| + this->name + LOG4CXX_STR("].")); | |
| this->triggeringPolicy = std::make_shared<SizeBasedTriggeringPolicy>(); | |
| } | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(this->mutex); | |
| this->triggeringPolicy->activateOptions(); | |
| this->rollingPolicy->activateOptions(); | |
| try | |
| { | |
| RolloverDescriptionPtr rollover1 = | |
| this->rollingPolicy->initialize(this->fileName, this->fileAppend); | |
| if (rollover1 != NULL) | |
| { | |
| ActionPtr syncAction(rollover1->getSynchronous()); | |
| if (syncAction != NULL) | |
| { | |
| syncAction->execute(); | |
| } | |
| this->fileName = rollover1->getActiveFileName(); | |
| this->fileAppend = rollover1->getAppend(); | |
| // | |
| // async action not yet implemented | |
| // | |
| ActionPtr asyncAction(rollover1->getAsynchronous()); | |
| if (asyncAction != NULL) | |
| { | |
| asyncAction->execute(); | |
| } | |
| } | |
| File activeFile; | |
| activeFile.setPath(this->fileName); | |
| if (this->fileAppend) | |
| { | |
| this->fileLength = activeFile.length(); | |
| } | |
| else | |
| { | |
| this->fileLength = 0; | |
| } | |
| result = true; | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogLog::warn(LOG4CXX_STR("Exception activating RollingFileAppender ") + this->fileName, ex); | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| Implements the usual roll over behaviour. | |
| <p>If <code>MaxBackupIndex</code> is positive, then files | |
| {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>} | |
| are renamed to {<code>File.2</code>, ..., | |
| <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is | |
| renamed <code>File.1</code> and closed. A new <code>File</code> is | |
| created to receive further log output. | |
| <p>If <code>MaxBackupIndex</code> is equal to zero, then the | |
| <code>File</code> is truncated with no backup files created. | |
| * @return true if rollover performed. | |
| */ | |
| bool RollingFileAppender::rollover() | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| return rolloverInternal(); | |
| } | |
| bool RollingFileAppender::rolloverInternal() | |
| { | |
| // | |
| // can't roll without a policy | |
| // | |
| if (_priv->rollingPolicy != NULL) | |
| { | |
| { | |
| try | |
| { | |
| RolloverDescriptionPtr rollover1(_priv->rollingPolicy->rollover(this->getFile(), this->getAppend())); | |
| if (rollover1 != NULL) | |
| { | |
| if (rollover1->getActiveFileName() == getFile()) | |
| { | |
| _priv->close(); | |
| bool success = true; | |
| if (rollover1->getSynchronous() != NULL) | |
| { | |
| success = false; | |
| try | |
| { | |
| success = rollover1->getSynchronous()->execute(); | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogString msg(LOG4CXX_STR("Rollover of [")); | |
| msg.append(getFile()); | |
| msg.append(LOG4CXX_STR("] failed")); | |
| _priv->errorHandler->error(msg, ex, 0); | |
| } | |
| } | |
| bool appendToExisting = true; | |
| if (success) | |
| { | |
| appendToExisting = rollover1->getAppend(); | |
| if (appendToExisting) | |
| { | |
| _priv->fileLength = File().setPath(rollover1->getActiveFileName()).length(); | |
| } | |
| else | |
| { | |
| _priv->fileLength = 0; | |
| } | |
| ActionPtr asyncAction(rollover1->getAsynchronous()); | |
| if (asyncAction != NULL) | |
| { | |
| try | |
| { | |
| asyncAction->execute(); | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogString msg(LOG4CXX_STR("Rollover of [")); | |
| msg.append(getFile()); | |
| msg.append(LOG4CXX_STR("] failed")); | |
| _priv->errorHandler->error(msg, ex, 0); | |
| } | |
| } | |
| } | |
| setFileInternal(rollover1->getActiveFileName(), appendToExisting, _priv->bufferedIO, _priv->bufferSize); | |
| } | |
| else | |
| { | |
| _priv->close(); | |
| setFileInternal(rollover1->getActiveFileName()); | |
| // Call activateOptions to create any intermediate directories(if required) | |
| FileAppender::activateOptionsInternal(); | |
| OutputStreamPtr os = std::make_shared<FileOutputStream> | |
| ( rollover1->getActiveFileName() | |
| , rollover1->getAppend() | |
| ); | |
| _priv->setWriter(createWriter(os)); | |
| bool success = true; | |
| if (rollover1->getSynchronous() != NULL) | |
| { | |
| success = false; | |
| try | |
| { | |
| success = rollover1->getSynchronous()->execute(); | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogString msg(LOG4CXX_STR("Rollover of [")); | |
| msg.append(getFile()); | |
| msg.append(LOG4CXX_STR("] failed")); | |
| _priv->errorHandler->error(msg, ex, 0); | |
| } | |
| } | |
| if (success) | |
| { | |
| if (rollover1->getAppend()) | |
| { | |
| _priv->fileLength = File().setPath(rollover1->getActiveFileName()).length(); | |
| } | |
| else | |
| { | |
| _priv->fileLength = 0; | |
| } | |
| ActionPtr asyncAction(rollover1->getAsynchronous()); | |
| if (asyncAction != NULL) | |
| { | |
| asyncAction->execute(); | |
| } | |
| } | |
| _priv->writeHeader(); | |
| } | |
| return true; | |
| } | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogString msg(LOG4CXX_STR("Rollover of [")); | |
| msg.append(getFile()); | |
| msg.append(LOG4CXX_STR("] failed")); | |
| _priv->errorHandler->error(msg, ex, 0); | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| void RollingFileAppender::subAppend( const spi::LoggingEventPtr& event ) | |
| { | |
| // The rollover check must precede actual writing. This is the | |
| // only correct behavior for time driven triggers. | |
| if ( | |
| _priv->triggeringPolicy->isTriggeringEvent( | |
| this, event, getFile(), getFileLength())) | |
| { | |
| // | |
| // wrap rollover request in try block since | |
| // rollover may fail in case read access to directory | |
| // is not provided. However appender should still be in good | |
| // condition and the append should still happen. | |
| try | |
| { | |
| _priv->_event = event; | |
| rolloverInternal(); | |
| } | |
| catch (std::exception& ex) | |
| { | |
| LogString msg(LOG4CXX_STR("Rollover of [")); | |
| msg.append(getFile()); | |
| msg.append(LOG4CXX_STR("] failed")); | |
| _priv->errorHandler->error(msg, ex, 0); | |
| } | |
| } | |
| FileAppender::subAppend( event ); | |
| } | |
| /** | |
| * TThe policy that implements the scheme for rolling over a log file. | |
| */ | |
| RollingPolicyPtr RollingFileAppender::getRollingPolicy() const | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| return _priv->rollingPolicy; | |
| } | |
| /** | |
| * The policy that determine when to trigger a log file rollover. | |
| */ | |
| TriggeringPolicyPtr RollingFileAppender::getTriggeringPolicy() const | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| return _priv->triggeringPolicy; | |
| } | |
| /** | |
| * Set the scheme for rolling over log files. | |
| */ | |
| void RollingFileAppender::setRollingPolicy(const RollingPolicyPtr& policy) | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| _priv->rollingPolicy = policy; | |
| } | |
| /** | |
| * Set policy that determine when to trigger a log file rollover. | |
| */ | |
| void RollingFileAppender::setTriggeringPolicy(const TriggeringPolicyPtr& policy) | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(_priv->mutex); | |
| _priv->triggeringPolicy = policy; | |
| } | |
| /** | |
| * Close appender. Waits for any asynchronous file compression actions to be completed. | |
| */ | |
| void RollingFileAppender::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. | |
| */ | |
| RollingFileAppender* rfa; | |
| public: | |
| /** | |
| * Constructor. | |
| * @param os output stream to wrap. | |
| * @param rfa rolling file appender to inform. | |
| */ | |
| CountingOutputStream | |
| ( const OutputStreamPtr& os1 | |
| , RollingFileAppender* rfa1 | |
| ) | |
| : os(os1) | |
| , rfa(rfa1) | |
| { | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| void close( ) override | |
| { | |
| os->close(); | |
| rfa = 0; | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| void flush( ) override | |
| { | |
| os->flush(); | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| void write( ByteBuffer& buf ) override | |
| { | |
| os->write(buf); | |
| if (rfa != 0) | |
| { | |
| rfa->incrementFileLength(buf.limit()); | |
| } | |
| } | |
| }; | |
| } | |
| } | |
| /** | |
| Returns an OutputStreamWriter when passed an OutputStream. The | |
| encoding used will depend on the value of the | |
| <code>encoding</code> property. If the encoding value is | |
| specified incorrectly the writer will be opened using the default | |
| system encoding (an error message will be printed to the loglog. | |
| @param os output stream, may not be null. | |
| @return new writer. | |
| */ | |
| WriterPtr RollingFileAppender::createWriter(const OutputStreamPtr& os) | |
| { | |
| OutputStreamPtr cos = std::make_shared<CountingOutputStream>(os, this); | |
| return FileAppender::createWriter(cos); | |
| } | |
| /** | |
| * Get byte length of current active log file. | |
| * @return byte length of current active log file. | |
| */ | |
| size_t RollingFileAppender::getFileLength() const | |
| { | |
| return _priv->fileLength; | |
| } | |
| /** | |
| * Increments estimated byte length of current active log file. | |
| * @param increment additional bytes written to log file. | |
| */ | |
| void RollingFileAppender::incrementFileLength(size_t increment) | |
| { | |
| _priv->fileLength += increment; | |
| } |