| /* | |
| * 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/logstring.h> | |
| #include <log4cxx/rolling/fixedwindowrollingpolicy.h> | |
| #include <log4cxx/helpers/pool.h> | |
| #include <log4cxx/helpers/integer.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/helpers/optionconverter.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/helpers/exception.h> | |
| #include <log4cxx/rolling/rolloverdescription.h> | |
| #include <log4cxx/rolling/filerenameaction.h> | |
| #include <log4cxx/rolling/gzcompressaction.h> | |
| #include <log4cxx/rolling/zipcompressaction.h> | |
| #include <log4cxx/pattern/integerpatternconverter.h> | |
| #include <log4cxx/private/rollingpolicybase_priv.h> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::rolling; | |
| using namespace LOG4CXX_NS::helpers; | |
| using namespace LOG4CXX_NS::pattern; | |
| #define priv static_cast<FixedWindowRollingPolicyPrivate*>(m_priv.get()) | |
| struct FixedWindowRollingPolicy::FixedWindowRollingPolicyPrivate : public RollingPolicyBasePrivate { | |
| FixedWindowRollingPolicyPrivate() : | |
| RollingPolicyBasePrivate(), | |
| minIndex(1), | |
| maxIndex(7), | |
| explicitActiveFile(false) | |
| {} | |
| int minIndex; | |
| int maxIndex; | |
| bool explicitActiveFile; | |
| bool throwIOExceptionOnForkFailure = true; | |
| }; | |
| IMPLEMENT_LOG4CXX_OBJECT(FixedWindowRollingPolicy) | |
| FixedWindowRollingPolicy::FixedWindowRollingPolicy() : | |
| RollingPolicyBase (std::make_unique<FixedWindowRollingPolicyPrivate>()) | |
| { | |
| } | |
| FixedWindowRollingPolicy::~FixedWindowRollingPolicy(){} | |
| void FixedWindowRollingPolicy::setMaxIndex(int maxIndex1) | |
| { | |
| priv->maxIndex = maxIndex1; | |
| } | |
| void FixedWindowRollingPolicy::setMinIndex(int minIndex1) | |
| { | |
| priv->minIndex = minIndex1; | |
| } | |
| void FixedWindowRollingPolicy::setOption(const LogString& option, | |
| const LogString& value) | |
| { | |
| if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MININDEX"), | |
| LOG4CXX_STR("minindex"))) | |
| { | |
| priv->minIndex = OptionConverter::toInt(value, 1); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("MAXINDEX"), | |
| LOG4CXX_STR("maxindex"))) | |
| { | |
| priv->maxIndex = OptionConverter::toInt(value, 7); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, | |
| LOG4CXX_STR("THROWIOEXCEPTIONONFORKFAILURE"), | |
| LOG4CXX_STR("throwioexceptiononforkfailure"))) | |
| { | |
| priv->throwIOExceptionOnForkFailure = OptionConverter::toBoolean(value, true); | |
| } | |
| else | |
| { | |
| RollingPolicyBase::setOption(option, value); | |
| } | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| void FixedWindowRollingPolicy::activateOptions( ) | |
| { | |
| priv->activateOptions(getFormatSpecifiers()); | |
| if (priv->maxIndex < priv->minIndex) | |
| { | |
| LogLog::warn( | |
| LOG4CXX_STR("MaxIndex cannot be smaller than MinIndex.")); | |
| priv->maxIndex = priv->minIndex; | |
| } | |
| if ((priv->maxIndex - priv->minIndex) > MAX_WINDOW_SIZE) | |
| { | |
| LogLog::warn(LOG4CXX_STR("Large window sizes are not allowed.")); | |
| priv->maxIndex = priv->minIndex + MAX_WINDOW_SIZE; | |
| } | |
| PatternConverterPtr itc = getIntegerPatternConverter(); | |
| if (itc == NULL) | |
| { | |
| throw IllegalStateException(); | |
| } | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| RolloverDescriptionPtr FixedWindowRollingPolicy::initialize( const LogString& currentActiveFile, bool append ) | |
| { | |
| LogString newActiveFile(currentActiveFile); | |
| priv->explicitActiveFile = false; | |
| if (currentActiveFile.length() > 0) | |
| { | |
| priv->explicitActiveFile = true; | |
| newActiveFile = currentActiveFile; | |
| } | |
| if (!priv->explicitActiveFile) | |
| { | |
| LogString buf; | |
| ObjectPtr obj = std::make_shared<Integer>(priv->minIndex); | |
| formatFileName(obj, buf); | |
| newActiveFile = buf; | |
| } | |
| ActionPtr noAction; | |
| return std::make_shared<RolloverDescription>(newActiveFile, append, noAction, noAction); | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| RolloverDescriptionPtr FixedWindowRollingPolicy::rollover( const LogString& currentActiveFile, bool append ) | |
| { | |
| RolloverDescriptionPtr desc; | |
| if (priv->maxIndex < 0) | |
| { | |
| return desc; | |
| } | |
| int purgeStart = priv->minIndex; | |
| if (!priv->explicitActiveFile) | |
| { | |
| purgeStart++; | |
| } | |
| if (!purge(purgeStart, priv->maxIndex)) | |
| { | |
| return desc; | |
| } | |
| LogString buf; | |
| ObjectPtr obj = std::make_shared<Integer>(purgeStart); | |
| formatFileName(obj, buf); | |
| LogString renameTo(buf); | |
| LogString compressedName(renameTo); | |
| ActionPtr compressAction ; | |
| if(getCreateIntermediateDirectories()){ | |
| File compressedFile(compressedName); | |
| File compressedParent (compressedFile.getParent()); | |
| compressedParent.mkdirs(); | |
| } | |
| if (StringHelper::endsWith(renameTo, LOG4CXX_STR(".gz"))) | |
| { | |
| renameTo.resize(renameTo.size() - 3); | |
| GZCompressActionPtr comp = std::make_shared<GZCompressAction>( | |
| File().setPath(renameTo), | |
| File().setPath(compressedName), | |
| true); | |
| comp->setThrowIOExceptionOnForkFailure(priv->throwIOExceptionOnForkFailure); | |
| compressAction = comp; | |
| } | |
| else if (StringHelper::endsWith(renameTo, LOG4CXX_STR(".zip"))) | |
| { | |
| renameTo.resize(renameTo.size() - 4); | |
| ZipCompressActionPtr comp = std::make_shared<ZipCompressAction>( | |
| File().setPath(renameTo), | |
| File().setPath(compressedName), | |
| true); | |
| comp->setThrowIOExceptionOnForkFailure(priv->throwIOExceptionOnForkFailure); | |
| compressAction = comp; | |
| } | |
| auto renameAction = std::make_shared<FileRenameAction>( | |
| File().setPath(currentActiveFile), | |
| File().setPath(renameTo), | |
| false); | |
| desc = std::make_shared<RolloverDescription>( | |
| currentActiveFile, append, | |
| renameAction, compressAction); | |
| return desc; | |
| } | |
| /** | |
| * Get index of oldest log file to be retained. | |
| * @return index of oldest log file. | |
| */ | |
| int FixedWindowRollingPolicy::getMaxIndex() const | |
| { | |
| return priv->maxIndex; | |
| } | |
| /** | |
| * Get index of most recent log file. | |
| * @return index of oldest log file. | |
| */ | |
| int FixedWindowRollingPolicy::getMinIndex() const | |
| { | |
| return priv->minIndex; | |
| } | |
| /** | |
| * Purge and rename old log files in preparation for rollover | |
| * @param lowIndex low index | |
| * @param highIndex high index. Log file associated with high | |
| * index will be deleted if needed. | |
| * @return true if purge was successful and rollover should be attempted. | |
| */ | |
| bool FixedWindowRollingPolicy::purge(int lowIndex, int highIndex) const | |
| { | |
| int suffixLength = 0; | |
| std::vector<FileRenameActionPtr> renames; | |
| LogString buf; | |
| ObjectPtr obj = std::make_shared<Integer>(lowIndex); | |
| formatFileName(obj, buf); | |
| LogString lowFilename(buf); | |
| if (StringHelper::endsWith(lowFilename, LOG4CXX_STR(".gz"))) | |
| { | |
| suffixLength = 3; | |
| } | |
| else if (StringHelper::endsWith(lowFilename, LOG4CXX_STR(".zip"))) | |
| { | |
| suffixLength = 4; | |
| } | |
| for (int i = lowIndex; i <= highIndex; i++) | |
| { | |
| File toRenameCompressed; | |
| toRenameCompressed.setPath(lowFilename); | |
| File toRenameBase; | |
| toRenameBase.setPath(lowFilename.substr(0, lowFilename.length() - suffixLength)); | |
| File* toRename = &toRenameCompressed; | |
| bool isBase = false; | |
| bool exists = toRenameCompressed.exists(); | |
| if (suffixLength > 0) | |
| { | |
| if (exists) | |
| { | |
| if (toRenameBase.exists()) | |
| { | |
| toRenameBase.deleteFile(); | |
| } | |
| } | |
| else | |
| { | |
| toRename = &toRenameBase; | |
| exists = toRenameBase.exists(); | |
| isBase = true; | |
| } | |
| } | |
| if (exists) | |
| { | |
| // | |
| // if at upper index then | |
| // attempt to delete last file | |
| // if that fails then abandon purge | |
| if (i == highIndex) | |
| { | |
| if (!toRename->deleteFile()) | |
| { | |
| return false; | |
| } | |
| break; | |
| } | |
| // | |
| // if intermediate index | |
| // add a rename action to the list | |
| buf.erase(buf.begin(), buf.end()); | |
| obj = std::make_shared<Integer>(i + 1); | |
| formatFileName(obj, buf); | |
| LogString highFilename(buf); | |
| LogString renameTo(highFilename); | |
| if (isBase) | |
| { | |
| renameTo = | |
| highFilename.substr(0, highFilename.length() - suffixLength); | |
| } | |
| renames.push_back(std::make_shared<FileRenameAction>(*toRename, File().setPath(renameTo), true)); | |
| lowFilename = highFilename; | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| // | |
| // work renames backwards | |
| // | |
| for (std::vector<FileRenameActionPtr>::reverse_iterator iter = renames.rbegin(); | |
| iter != renames.rend(); | |
| iter++) | |
| { | |
| auto& action = *iter; | |
| try | |
| { | |
| if (!action->execute()) | |
| { | |
| return false; | |
| } | |
| } | |
| catch (const std::exception& ex) | |
| { | |
| LogLog::warn(action->getName() + LOG4CXX_STR(" raised the following exception"), ex); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| #define RULES_PUT(spec, cls) \ | |
| specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR(spec)), (PatternConstructor) cls ::newInstance)) | |
| LOG4CXX_NS::pattern::PatternMap FixedWindowRollingPolicy::getFormatSpecifiers() const | |
| { | |
| PatternMap specs; | |
| RULES_PUT("i", IntegerPatternConverter); | |
| RULES_PUT("index", IntegerPatternConverter); | |
| return specs; | |
| } |