| /* | |
| * 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 <vector> | |
| #include <log4cxx/rolling/rollingfileappender.h> | |
| #include <log4cxx/basicconfigurator.h> | |
| #include <log4cxx/logger.h> | |
| #include <log4cxx/consoleappender.h> | |
| #include <log4cxx/logmanager.h> | |
| #include <log4cxx/patternlayout.h> | |
| #include <log4cxx/rolling/timebasedrollingpolicy.h> | |
| #include <log4cxx/helpers/simpledateformat.h> | |
| #include <log4cxx/helpers/date.h> | |
| #include <iostream> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include "../util/compare.h" | |
| #include "../logunit.h" | |
| #include <fstream> | |
| #include <apr_strings.h> | |
| #include <apr_time.h> | |
| #if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER | |
| #include <apr_user.h> | |
| #endif | |
| #include <random> | |
| #ifndef INT64_C | |
| #define INT64_C(x) x ## LL | |
| #endif | |
| // We often need one and the same date pattern, but in different contexts, to either easily embed it | |
| // into other string literals or as an object. While macros are hard to debug, embedding into string | |
| // literals is easier this way, because the compiler can automatically collaps them, and if we have | |
| // one macro already, a second for a similar purpose shouldn't hurt as well. | |
| #define DATE_PATTERN "yyyy-MM-dd_HH_mm_ss" | |
| #define DATE_PATTERN_STR LogString(LOG4CXX_STR("yyyy-MM-dd_HH_mm_ss")) | |
| #define PATTERN_LAYOUT LOG4CXX_STR("%c{1} - %m%n") | |
| #define DIR_PRE_OUTPUT "output/rolling/tbr-" | |
| #define DIR_PRE_WITNESS "witness/rolling/tbr-" | |
| using namespace log4cxx; | |
| using namespace log4cxx::helpers; | |
| using namespace log4cxx::rolling; | |
| /** | |
| * A rather exhaustive set of tests. Tests include leaving the ActiveFileName | |
| * argument blank, or setting it, with and without compression, and tests | |
| * with or without stopping/restarting the RollingFileAppender. | |
| * | |
| * The regression tests log a few times using a RollingFileAppender. Then, | |
| * they predict the names of the files which sould be generated and compare | |
| * them with witness files. | |
| * | |
| * <pre> | |
| Compression ActiveFileName Stop/Restart | |
| Test1 NO BLANK NO | |
| Test2 NO BLANK YES | |
| Test3 YES BLANK NO | |
| Test4 NO SET YES | |
| Test5 NO SET NO | |
| Test6 YES SET NO | |
| * </pre> | |
| */ | |
| LOGUNIT_CLASS(TimeBasedRollingTest) | |
| { | |
| LOGUNIT_TEST_SUITE(TimeBasedRollingTest); | |
| LOGUNIT_TEST(test1); | |
| LOGUNIT_TEST(test2); | |
| LOGUNIT_TEST(test3); | |
| LOGUNIT_TEST(test4); | |
| LOGUNIT_TEST(test5); | |
| LOGUNIT_TEST(test6); | |
| LOGUNIT_TEST(test7); | |
| LOGUNIT_TEST(rollIntoDir); | |
| #if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER | |
| LOGUNIT_TEST(unterminatedMultiprocessMapFile); | |
| #endif | |
| LOGUNIT_TEST_SUITE_END(); | |
| private: | |
| LoggerPtr logger; | |
| log4cxx_time_t current_time; | |
| /** | |
| * Build file names with timestamps. | |
| * <p> | |
| * This method builds some file names based on a hard coded date pattern, while the important | |
| * thing is that the used time is "now" for the first and one additional second into the future | |
| * for each subsequent file name. The method is used to build names which are expected to get | |
| * created by loggers afterwards, so the existence of those files can be checked easily. | |
| * </p> | |
| * <p> | |
| * The given {@code prefix} is for the file name only, we hard code "output" as directory. | |
| * </p> | |
| * <p> | |
| * Using {@code startInFuture} the caller can specify if the first created file name uses "now" | |
| * or already starts one second in the future. This depends on what the caller wants to check: | |
| * If it is the existence of specially named files and the caller uses some generic log file to | |
| * start with, it's most likely that the first file name to checks needs to be in the future, | |
| * because new files are only created with subsequent log statements. The first goes to the | |
| * generically named file, afterwards the test sleeps a bit and the next statement is creating | |
| * the first file to check for. During the time between those two calls the current second and | |
| * therefore "now" could have easily past and the first file name will never be found, because | |
| * the rolling appender creates it with a newer "now", at least one second in the future. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] prefix | |
| * @param[in] fnames | |
| * @param[in,opt] compressed | |
| * @param[in,opt] startInFuture | |
| */ | |
| template<size_t N> | |
| void buildTsFnames( | |
| Pool& pool, | |
| const LogString& prefix, | |
| LogString (&fnames)[N], | |
| bool compressed = false, | |
| bool startInFuture = false) | |
| { | |
| SimpleDateFormat sdf(DATE_PATTERN_STR); | |
| log4cxx_time_t now(current_time); | |
| LogString ext(compressed ? LOG4CXX_STR(".gz") : LOG4CXX_STR("")); | |
| now += startInFuture ? APR_USEC_PER_SEC : 0; | |
| for (size_t i = 0; i < N; ++i) | |
| { | |
| fnames[i].assign(LogString(LOG4CXX_STR("" DIR_PRE_OUTPUT)) + prefix); | |
| sdf.format(fnames[i], now); | |
| fnames[i].append(ext); | |
| now += APR_USEC_PER_SEC; | |
| } | |
| } | |
| /** | |
| * Log some msg and sleep. | |
| * <p> | |
| * Most tests need to log some message to some files and sleep afterwards, to spread the msgs | |
| * over time and timestamp based named files. This method handles this for all tests to not need | |
| * to replicate the same code AND deals with the fact that the logigng statements should contain | |
| * the original src function and line. For that to work each caller needs to provide us the | |
| * additional information and we redefine LOG4CXX_LOCATION to use that provided data instead of | |
| * that from the compiler when a log statement is issued. While this is a bit ugly, because we | |
| * need to duplicate the definition of LOG4CXX_LOCATION, it is easier than to duplicate the code | |
| * for logging and sleeping per test in this file. | |
| * </p> | |
| * <p> | |
| * The given wait factor is always multiplied with 1 second and the result is waited for, so a | |
| * caller needs to e.g. provide {@code 0.5} if it needs log statements to distribute over given | |
| * files, resulting in two statements per file. If the caller needs indivdual file to check for | |
| * their existence, {@code 1} can be provided, which ensures that we wait for at least the next | |
| * second. | |
| * </p> | |
| * <p> | |
| * It is important the the caller provides aus with {@code __LOG4CXX_FUNC__} instead of only | |
| * {@code __FUNC__} or such, because the latter is compiler specific and log4cxx already deals | |
| * with such things. | |
| * </p> | |
| * <p> | |
| * We had macro wrappers around this method dealing with such things in the past, but as args | |
| * where added, those become more and more difficult to maintain properly and therefore removed. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] howOften | |
| * @param[in] srcFunc | |
| * @param[in] srcLine | |
| * @param[in,opt] startWith | |
| * @param[in] waitFactor | |
| */ | |
| void logMsgAndSleep | |
| ( Pool& pool | |
| , int howOften | |
| , const std::string& srcFunc | |
| , int srcLine | |
| , int startWith = 0 | |
| , float waitFactor = 0.5 | |
| ) | |
| { | |
| #undef LOG4CXX_LOCATION | |
| #define LOG4CXX_LOCATION ::log4cxx::spi::LocationInfo( \ | |
| __FILE__, \ | |
| __FILE__, \ | |
| srcFunc.c_str(), \ | |
| srcLine) | |
| for (int i = startWith; i < startWith + howOften; ++i) | |
| { | |
| std::string message("Hello---"); | |
| message.append(pool.itoa(i)); | |
| LOG4CXX_DEBUG(logger, message); | |
| current_time += int(APR_USEC_PER_SEC * waitFactor); | |
| } | |
| #undef LOG4CXX_LOCATION | |
| #define LOG4CXX_LOCATION ::log4cxx::spi::LocationInfo( \ | |
| __FILE__, \ | |
| __FILE__, \ | |
| __LOG4CXX_FUNC__, \ | |
| __LINE__) | |
| } | |
| /** | |
| * Check witness by comparing file contents. | |
| * <p> | |
| * This method checks the witness for some test by comparing the contents of the given file used | |
| * by that test. To find the corresponding witness, the prefix of the test needs to be provided, | |
| * which is some unique part of the file name to use, while we hard code the parent dir and | |
| * other non changing parts of the name. | |
| * </p> | |
| * <p> | |
| * We don't use a wrapper macro this time because the src line should have the same name in all | |
| * compilers and is easily to add for the caller. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] prefix | |
| * @param[in] fname | |
| * @param[in] witnessIdx | |
| * @param[in] srcLine | |
| */ | |
| void compareWitness | |
| ( Pool& pool | |
| , const LogString& prefix | |
| , const LogString& fname | |
| , int witnessIdx | |
| , int srcLine | |
| ) | |
| { | |
| LogString witness(LOG4CXX_STR("" DIR_PRE_WITNESS)); | |
| witness.append(prefix); | |
| StringHelper::toString(witnessIdx, witness); | |
| //std::wcerr << L"Comparing file: " << fname << L"\n"; | |
| //std::wcerr << L"Comparing witness: " << witness << L"\n"; | |
| LOGUNIT_ASSERT_SRCL(Compare::compare(fname, File(witness)), srcLine); | |
| } | |
| /** | |
| * Check witnesses by comparing file contents. | |
| * <p> | |
| * This method is a wrapper around {@link compareWitness}, used to iterate over all files from a | |
| * given test. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] prefix | |
| * @param[in] fnames | |
| * @param[in] srcLine | |
| */ | |
| template<size_t N> | |
| void compareWitnesses | |
| ( Pool& pool | |
| , const LogString& prefix | |
| , const LogString (&fnames)[N] | |
| , int srcLine | |
| ) | |
| { | |
| for (size_t i = 0; i < N; ++i) | |
| { | |
| this->compareWitness(pool, prefix, fnames[i], int(i), srcLine); | |
| } | |
| } | |
| /** | |
| * Check existing files. | |
| * <p> | |
| * This method checks that the first N - 1 files of the given array actually exist and compares | |
| * the last one by content to some witness. | |
| * </p> | |
| * <p> | |
| * We don't use a wrapper macro this time because the src line should have the same name in all | |
| * compilers and is easily to add for the caller. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] prefix | |
| * @param[in] fnames | |
| * @param[in] witnessIdx | |
| * @param[in] srcLine | |
| */ | |
| template<size_t N> | |
| void checkFilesExist | |
| ( Pool& pool | |
| , const LogString& prefix | |
| , const LogString (&fnames)[N] | |
| , int witnessIdx | |
| , int srcLine | |
| ) | |
| { | |
| for (size_t i = 0; i < 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); | |
| } | |
| /** | |
| * Let the current second pass. | |
| * <p> | |
| * This method assures that the current second and some additional time gets passed before | |
| * returning. | |
| * </p> | |
| * @param[in,opt] millis | |
| */ | |
| void delayUntilNextSecond() | |
| { | |
| current_time += APR_USEC_PER_SEC; | |
| } | |
| /** | |
| * Let the current second pass with some msg. | |
| * <p> | |
| * This method works exactly like {@link delayUntilNextSecond(size_t)}, but additionally prints | |
| * a message before and after the wait on STDOUT for debugging purposes. | |
| * </p> | |
| * @param[in,opt] millis | |
| */ | |
| void delayUntilNextSecondWithMsg() | |
| { | |
| std::cout << "Advancing one second\n"; | |
| delayUntilNextSecond(); | |
| } | |
| /** | |
| * Delete generic log file. | |
| * <p> | |
| * Some tests use generic log file names which may already be available during subsequent calls | |
| * to the test and influence their behavior, e.g. because RollingFileAppender uses the last | |
| * modification time of already existing files to create its internal name. Such a date might be | |
| * from the arbitrary past, but most of the tests assume operations like rollovers within a few | |
| * seconds around "new". Those assumptions will fail for older existing files. This method can | |
| * be used to delete those files, while documenting the problem at the same time. | |
| * </p> | |
| * @param[in,out] pool | |
| * @param[in] path | |
| */ | |
| void delGenericLogFile( | |
| Pool& pool, | |
| LogString path) | |
| { | |
| File(path).deleteFile(); | |
| } | |
| /** | |
| * Set the current time for each individual test methhod run. | |
| * <p> | |
| * There's at least one test running multiple other tests WITHIN the same lifecycle of | |
| * {@code setUp} and {@code tearDown}, but each of the test methods need the same current time | |
| * to start with. So this method can be used to set that and is simply used by {@code setUp} | |
| * as well. | |
| * </p> | |
| */ | |
| void setUpCurrTime() | |
| { | |
| //current_time = log4cxx::helpers::Date::currentTime(); // Start at "about" now. | |
| //current_time -= (current_time % APR_USEC_PER_SEC); // Go to the top of the second | |
| //current_time++; // Need to not be at the top of a second for rollover logic to work correctly | |
| current_time = 1; // Start at about unix epoch | |
| } | |
| public: | |
| log4cxx_time_t currentTime() | |
| { | |
| return current_time; | |
| } | |
| void setUp() | |
| { | |
| BasicConfigurator::configure(std::make_shared<PatternLayout> ( | |
| LOG4CXX_STR("%d{ABSOLUTE} [%t] %level %c{2}#%M:%L - %m%n") | |
| )); | |
| this->logger = LogManager::getLogger("org.apache.log4j.TimeBasedRollingTest"); | |
| this->setUpCurrTime(); | |
| helpers::Date::setGetCurrentTimeFunction( std::bind( &TimeBasedRollingTest::currentTime, this ) ); | |
| } | |
| void tearDown() | |
| { | |
| helpers::Date::setGetCurrentTimeFunction(&Date::getCurrentTimeStd); | |
| LogManager::shutdown(); | |
| } | |
| /** | |
| * Test rolling without compression, activeFileName left blank, no stop/start. | |
| */ | |
| void test1() | |
| { | |
| const int nrOfFnames(4); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout( new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa( new RollingFileAppender()); | |
| rfa->setAppend(false); | |
| rfa->setLayout(layout); | |
| TimeBasedRollingPolicyPtr tbrp(new TimeBasedRollingPolicy()); | |
| tbrp->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test1-%d{" DATE_PATTERN "}")); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| Pool pool; | |
| 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__); | |
| } | |
| /** | |
| * No compression, with stop/restart, activeFileName left blank. | |
| */ | |
| void test2() | |
| { | |
| const int nrOfFnames(4); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout1(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa1( new RollingFileAppender()); | |
| rfa1->setAppend(false); | |
| rfa1->setLayout(layout1); | |
| TimeBasedRollingPolicyPtr tbrp1(new TimeBasedRollingPolicy()); | |
| tbrp1->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test2-%d{" DATE_PATTERN "}")); | |
| tbrp1->activateOptions(); | |
| rfa1->setRollingPolicy(tbrp1); | |
| rfa1->activateOptions(); | |
| logger->addAppender(rfa1); | |
| Pool pool; | |
| this->buildTsFnames<4>(pool, LOG4CXX_STR("test2-"), fnames); | |
| this->delayUntilNextSecondWithMsg(); | |
| this->logMsgAndSleep(pool, 3, __LOG4CXX_FUNC__, __LINE__); | |
| LOGUNIT_ASSERT(logger->removeAppender(rfa1)); | |
| rfa1->close(); | |
| PatternLayoutPtr layout2(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa2( new RollingFileAppender()); | |
| rfa2->setLayout(layout2); | |
| TimeBasedRollingPolicyPtr tbrp2 = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp2->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test2-%d{" DATE_PATTERN "}")); | |
| tbrp2->activateOptions(); | |
| rfa2->setRollingPolicy(tbrp2); | |
| rfa2->activateOptions(); | |
| rfa2->setAppend(false); | |
| logger->addAppender(rfa2); | |
| this->logMsgAndSleep( pool, 2, __LOG4CXX_FUNC__, __LINE__, 3); | |
| this->compareWitnesses<4>( pool, LOG4CXX_STR("test2."), fnames, __LINE__); | |
| } | |
| /** | |
| * With compression, activeFileName left blank, no stop/restart. | |
| */ | |
| void test3() | |
| { | |
| const int nrOfFnames(4); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout( new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa( new RollingFileAppender()); | |
| rfa->setAppend(false); | |
| rfa->setLayout(layout); | |
| TimeBasedRollingPolicyPtr tbrp = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp->setFileNamePattern(LogString(LOG4CXX_STR("" DIR_PRE_OUTPUT "test3-%d{" DATE_PATTERN "}.gz"))); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| Pool pool; | |
| this->buildTsFnames<4>(pool, LOG4CXX_STR("test3-"), fnames, true); | |
| 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__); | |
| } | |
| /** | |
| * Without compression, activeFileName set, with stop/restart | |
| * | |
| * Note: because this test stops and restarts, it is not possible to use the | |
| * date pattern in the filenames, as log4cxx will use the file's last modified | |
| * time when rolling over and determining the new filename. | |
| */ | |
| void test4() | |
| { | |
| const int nrOfFnames(4); | |
| const int nrOfLogMsgs(((nrOfFnames - 1) * 2) - 1); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout1(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa1( new RollingFileAppender()); | |
| rfa1->setLayout(layout1); | |
| TimeBasedRollingPolicyPtr tbrp1 = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp1->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test4-%d{" DATE_PATTERN "}")); | |
| tbrp1->activateOptions(); | |
| rfa1->setFile(LOG4CXX_STR("" DIR_PRE_OUTPUT "test4.log")); | |
| rfa1->setRollingPolicy(tbrp1); | |
| rfa1->activateOptions(); | |
| logger->addAppender(rfa1); | |
| Pool pool; | |
| this->delGenericLogFile(pool, rfa1->getFile()); | |
| this->buildTsFnames<4>(pool, LOG4CXX_STR("test4-"), fnames); | |
| fnames[0].assign(rfa1->getFile()); | |
| this->delayUntilNextSecondWithMsg(); | |
| this->logMsgAndSleep(pool, nrOfLogMsgs, __LOG4CXX_FUNC__, __LINE__); | |
| LOGUNIT_ASSERT(logger->removeAppender(rfa1)); | |
| rfa1->close(); | |
| PatternLayoutPtr layout2(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa2( new RollingFileAppender()); | |
| rfa2->setLayout(layout2); | |
| TimeBasedRollingPolicyPtr tbrp2 = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp2->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test4-%d{" DATE_PATTERN "}")); | |
| tbrp2->activateOptions(); | |
| rfa2->setFile(rfa1->getFile()); | |
| rfa2->setRollingPolicy(tbrp2); | |
| rfa2->activateOptions(); | |
| logger->addAppender(rfa2); | |
| // The one file not checked is the active file from the old appender reused by the new one | |
| // and rolled over afterwards. The problem is that because the reused file exists already, | |
| // it doesn't get an expected fname, but the last write time instead. Though, should be safe | |
| // enough to not check that special file. | |
| this->logMsgAndSleep(pool, 2, __LOG4CXX_FUNC__, __LINE__, nrOfLogMsgs); | |
| this->compareWitness(pool, LOG4CXX_STR("test4."), fnames[0], 0, __LINE__); | |
| this->compareWitness(pool, LOG4CXX_STR("test4."), fnames[1], 1, __LINE__); | |
| this->compareWitness(pool, LOG4CXX_STR("test4."), fnames[2], 2, __LINE__); | |
| } | |
| /** | |
| * No compression, activeFileName set, without stop/restart. | |
| */ | |
| void test5() | |
| { | |
| const int nrOfFnames(4); | |
| const int nrOfLogMsgs((nrOfFnames * 2) - 1); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa(new RollingFileAppender()); | |
| rfa->setLayout(layout); | |
| TimeBasedRollingPolicyPtr tbrp = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp->setFileNamePattern(LOG4CXX_STR("" DIR_PRE_OUTPUT "test5-%d{" DATE_PATTERN "}")); | |
| rfa->setFile(LOG4CXX_STR("" DIR_PRE_OUTPUT "test5.log")); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| Pool pool; | |
| this->delGenericLogFile(pool, rfa->getFile()); | |
| this->buildTsFnames<4>(pool, LOG4CXX_STR("test5-"), fnames); | |
| fnames[0].assign(rfa->getFile()); | |
| this->delayUntilNextSecondWithMsg(); | |
| this->logMsgAndSleep( pool, nrOfLogMsgs, __LOG4CXX_FUNC__, __LINE__); | |
| this->compareWitnesses<4>( pool, LOG4CXX_STR("test5."), fnames, __LINE__); | |
| } | |
| /** | |
| * With compression, activeFileName set, no stop/restart. | |
| */ | |
| void test6() | |
| { | |
| const int nrOfFnames(4); | |
| const int nrOfLogMsgs((nrOfFnames * 2) - 1); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout( new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa( new RollingFileAppender()); | |
| rfa->setAppend(false); | |
| rfa->setLayout(layout); | |
| TimeBasedRollingPolicyPtr tbrp = TimeBasedRollingPolicyPtr(new TimeBasedRollingPolicy()); | |
| tbrp->setFileNamePattern(LogString(LOG4CXX_STR("" DIR_PRE_OUTPUT "test6-%d{" DATE_PATTERN "}.gz"))); | |
| rfa->setFile(LOG4CXX_STR("" DIR_PRE_OUTPUT "test6.log")); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| Pool pool; | |
| this->delGenericLogFile(pool, rfa->getFile()); | |
| this->buildTsFnames<4>(pool, LOG4CXX_STR("test6-"), fnames, true); | |
| fnames[0].assign(rfa->getFile()); | |
| this->delayUntilNextSecondWithMsg(); | |
| this->logMsgAndSleep( pool, nrOfLogMsgs, __LOG4CXX_FUNC__, __LINE__); | |
| this->checkFilesExist( pool, LOG4CXX_STR("test6."), fnames, 0, __LINE__); | |
| } | |
| /** | |
| * Repeat some tests with generic file names. | |
| * <p> | |
| * This test calls some tests which use generic file names and will only work properly if those | |
| * got deleted before running the test during setup. | |
| * </p> | |
| */ | |
| void test7() | |
| { | |
| typedef void (TimeBasedRollingTest::*Test)(); | |
| typedef std::vector<Test> Tests; | |
| Tests tests(10); | |
| tests.at(4) = &TimeBasedRollingTest::test4; | |
| tests.at(5) = &TimeBasedRollingTest::test5; | |
| tests.at(6) = &TimeBasedRollingTest::test6; | |
| for (int numTest = 1; numTest < tests.size(); ++numTest) | |
| { | |
| Test test(tests.at(numTest)); | |
| if (!test) | |
| { | |
| continue; | |
| } | |
| this->setUpCurrTime(); | |
| (this->*test)(); | |
| } | |
| } | |
| void rollIntoDir() | |
| { | |
| const int nrOfFnames(4); | |
| const int nrOfLogMsgs((nrOfFnames * 2) - 1); | |
| LogString fnames[nrOfFnames]; | |
| PatternLayoutPtr layout( new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa( new RollingFileAppender()); | |
| rfa->setLayout(layout); | |
| rfa->setFile(LOG4CXX_STR("" DIR_PRE_OUTPUT "rollIntoDir.log")); | |
| std::random_device dev; | |
| std::mt19937 rng(dev()); | |
| std::uniform_int_distribution<std::mt19937::result_type> dist(1,100000); | |
| LogString filenamePattern = LOG4CXX_STR("" DIR_PRE_OUTPUT); | |
| LogString dirNumber; | |
| StringHelper::toString(static_cast<int>(dist(rng)), dirNumber); | |
| LogString directoryName = LOG4CXX_STR("tbr-rollIntoDir-"); | |
| directoryName.append( dirNumber ); | |
| filenamePattern.append( directoryName ); | |
| LogString filenamePatternPrefix = filenamePattern; | |
| filenamePattern.append( LOG4CXX_STR("/file-%d{" DATE_PATTERN "}") ); | |
| TimeBasedRollingPolicyPtr tbrp(new TimeBasedRollingPolicy()); | |
| tbrp->setFileNamePattern(filenamePattern); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| const logchar* prefix = directoryName.append(LOG4CXX_STR("/file-")).data(); | |
| Pool pool; | |
| this->delGenericLogFile(pool, rfa->getFile()); | |
| this->buildTsFnames<4>(pool, prefix, fnames); | |
| fnames[0].assign(rfa->getFile()); | |
| this->delayUntilNextSecondWithMsg(); | |
| this->logMsgAndSleep( pool, nrOfLogMsgs, __LOG4CXX_FUNC__, __LINE__); | |
| this->checkFilesExist( pool, LOG4CXX_STR("test6."), fnames, 0, __LINE__); | |
| } | |
| #if LOG4CXX_HAS_MULTIPROCESS_ROLLING_FILE_APPENDER | |
| void unterminatedMultiprocessMapFile() | |
| { | |
| Pool pool; | |
| char uidBuffer[64] = "0000"; | |
| #ifndef _WIN32 | |
| apr_uid_t uid; | |
| apr_gid_t groupid; | |
| if (APR_SUCCESS == apr_uid_current(&uid, &groupid, pool.getAPRPool())) | |
| apr_snprintf(uidBuffer, sizeof(uidBuffer), "%u", uid); | |
| #endif | |
| std::string mapFile("output/rolling/tbr-map-corrupt-"); | |
| mapFile += uidBuffer; | |
| mapFile += ".map"; | |
| std::string lockFile("output/rolling/tbr-map-corrupt-"); | |
| lockFile += uidBuffer; | |
| lockFile += ".maplck"; | |
| File("output/rolling").mkdirs(); | |
| File(mapFile).deleteFile(); | |
| File(lockFile).deleteFile(); | |
| std::ofstream corruptMap(mapFile, std::ios::binary | std::ios::trunc); | |
| LOGUNIT_ASSERT(corruptMap.is_open()); | |
| std::string unterminatedMap(4096, 'x'); | |
| corruptMap.write(unterminatedMap.data(), unterminatedMap.size()); | |
| corruptMap.close(); | |
| PatternLayoutPtr layout(new PatternLayout(PATTERN_LAYOUT)); | |
| RollingFileAppenderPtr rfa(new RollingFileAppender()); | |
| rfa->setAppend(false); | |
| rfa->setLayout(layout); | |
| rfa->setFile(LOG4CXX_STR("output/rolling/tbr-map-corrupt.log")); | |
| TimeBasedRollingPolicyPtr tbrp(new TimeBasedRollingPolicy()); | |
| tbrp->setMultiprocess(true); | |
| tbrp->setFileNamePattern(LOG4CXX_STR("output/rolling/tbr-map-corrupt-%d{" DATE_PATTERN "}")); | |
| tbrp->activateOptions(); | |
| rfa->setRollingPolicy(tbrp); | |
| rfa->activateOptions(); | |
| logger->addAppender(rfa); | |
| this->delayUntilNextSecond(); | |
| LOG4CXX_DEBUG(logger, "roll despite corrupt map"); | |
| LOGUNIT_ASSERT(File("output/rolling/tbr-map-corrupt.log").exists()); | |
| LOGUNIT_ASSERT(logger->removeAppender(rfa)); | |
| rfa->close(); | |
| File(mapFile).deleteFile(); | |
| File(lockFile).deleteFile(); | |
| } | |
| #endif | |
| }; | |
| LOGUNIT_TEST_SUITE_REGISTRATION(TimeBasedRollingTest); |