| /* | |
| * 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/logger.h> | |
| #include <log4cxx/propertyconfigurator.h> | |
| #include <log4cxx/mdc.h> | |
| #include <log4cxx/patternlayout.h> | |
| #include <log4cxx/fileappender.h> | |
| #include "util/compare.h" | |
| #include "util/transformer.h" | |
| #include "util/absolutedateandtimefilter.h" | |
| #include "util/iso8601filter.h" | |
| #include "util/absolutetimefilter.h" | |
| #include "util/relativetimefilter.h" | |
| #include "util/controlfilter.h" | |
| #include "util/threadfilter.h" | |
| #include "util/linenumberfilter.h" | |
| #include "util/filenamefilter.h" | |
| #include <iostream> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <log4cxx/helpers/pool.h> | |
| #include <apr_strings.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include "testchar.h" | |
| #include "logunit.h" | |
| #include <log4cxx/spi/loggerrepository.h> | |
| #include <log4cxx/spi/loggingevent.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #define REGEX_STR(x) x | |
| #define PAT0 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* - Message [0-9]\\{1,2\\}") | |
| #define PAT1 ISO8601_PAT REGEX_STR(" ") PAT0 | |
| #define PAT2 ABSOLUTE_DATE_AND_TIME_PAT REGEX_STR(" ") PAT0 | |
| #define PAT3 ABSOLUTE_TIME_PAT REGEX_STR(" ") PAT0 | |
| #define PAT4 RELATIVE_TIME_PAT REGEX_STR(" ") PAT0 | |
| #define PAT5 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* : Message [0-9]\\{1,2\\}") | |
| #define PAT6 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) .*patternlayouttest.cpp\\([0-9]\\{1,4\\}\\): Message [0-9]\\{1,3\\}") | |
| #define PAT11a REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ log4j.PatternLayoutTest: Message [0-9]\\{1,2\\}") | |
| #define PAT11b REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ root: Message [0-9]\\{1,2\\}") | |
| #define PAT12 REGEX_STR("^\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) ")\ | |
| REGEX_STR(".*patternlayouttest.cpp([0-9]\\{1,4\\}): ")\ | |
| REGEX_STR("Message [0-9]\\{1,2\\}") | |
| #define PAT_MDC_1 REGEX_STR("") | |
| using namespace log4cxx; | |
| using namespace log4cxx::helpers; | |
| LOGUNIT_CLASS(PatternLayoutTest) | |
| { | |
| LOGUNIT_TEST_SUITE(PatternLayoutTest); | |
| #if ENABLE_2GB_STRING_TESTING | |
| LOGUNIT_TEST(test2GbMessageFormatting); | |
| #endif | |
| LOGUNIT_TEST(test1); | |
| LOGUNIT_TEST(test2); | |
| LOGUNIT_TEST(test3); | |
| LOGUNIT_TEST(test4); | |
| LOGUNIT_TEST(test5); | |
| LOGUNIT_TEST(test6); | |
| LOGUNIT_TEST(test7); | |
| LOGUNIT_TEST(test8); | |
| LOGUNIT_TEST(test9); | |
| LOGUNIT_TEST(test10); | |
| LOGUNIT_TEST(test11); | |
| LOGUNIT_TEST(test12); | |
| LOGUNIT_TEST(test13); | |
| LOGUNIT_TEST(test14); | |
| LOGUNIT_TEST(testMDC1); | |
| LOGUNIT_TEST(testMDC2); | |
| LOGUNIT_TEST(testAbbreviateNoDots); | |
| LOGUNIT_TEST(testAbbreviatePrecisionExceedsSegments); | |
| LOGUNIT_TEST(testAbbreviateKeepLastTwoSegments); | |
| LOGUNIT_TEST(testAbbreviateKeepLastSegmentOnly); | |
| LOGUNIT_TEST(testAbbreviatePrecisionZero); | |
| LOGUNIT_TEST(testAbbreviateEachSegmentToOneChar); | |
| LOGUNIT_TEST(testAbbreviateEachSegmentToTwoChars); | |
| LOGUNIT_TEST(testAbbreviateShortSegmentUnchanged); | |
| LOGUNIT_TEST(testAbbreviateConsecutiveDots); | |
| LOGUNIT_TEST(testAbbreviateLeadingDot); | |
| LOGUNIT_TEST(testAbbreviateTrailingDot); | |
| LOGUNIT_TEST(testAbbreviateEmptyName); | |
| LOGUNIT_TEST(testAbbreviateDegenerateName); | |
| LOGUNIT_TEST(testAbbreviateSegmentEqualToCharCountBoundary); | |
| LOGUNIT_TEST(testAbbreviateSegmentOneCharOverBoundary); | |
| LOGUNIT_TEST(testAbbreviateManySegmentsDeepHierarchy); | |
| LOGUNIT_TEST(testAbbreviateLongFinalSegmentPreserved); | |
| LOGUNIT_TEST(testAbbreviateIdempotentOnAlreadyAbbreviated); | |
| LOGUNIT_TEST_SUITE_END(); | |
| LoggerPtr root; | |
| LoggerPtr logger; | |
| public: | |
| void setUp() | |
| { | |
| root = Logger::getRootLogger(); | |
| MDC::clear(); | |
| logger = Logger::getLogger(LOG4CXX_TEST_STR("java.org.apache.log4j.PatternLayoutTest")); | |
| } | |
| void tearDown() | |
| { | |
| MDC::clear(); | |
| auto rep = root->getLoggerRepository(); | |
| if (rep) | |
| { | |
| rep->resetConfiguration(); | |
| } | |
| } | |
| void test2GbMessageFormatting() | |
| { | |
| LogString msg; | |
| try | |
| { | |
| msg.append(size_t(INT_MAX) + 1000, 'E'); | |
| } | |
| catch (const std::exception& ex) | |
| { | |
| LOGLOG_DEBUG(logger, ex.what() << " - skipping 2Gb message formatting test"); | |
| return; | |
| } | |
| msg[msg.length() - 1] = 'X'; | |
| LogString output1; | |
| PatternLayout l1{ LOG4CXX_STR("%-5p %c{2} - %.30m") }; | |
| l1.format(output1, std::make_shared<spi::LoggingEvent>(logger->getName(), Level::getInfo(), msg, spi::LocationInfo::getLocationUnavailable())); | |
| LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("INFO log4j.PatternLayoutTest - EEEEEEEEEEEEEEEEEEEEEEEEEEEEEX"), output1); | |
| PatternLayout l2{ LOG4CXX_STR("%p %.30m %p") }; | |
| LogString output2; | |
| l2.format(output2, std::make_shared<spi::LoggingEvent>(logger->getName(), Level::getDebug(), msg, spi::LocationInfo::getLocationUnavailable())); | |
| LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("DEBUG EEEEEEEEEEEEEEEEEEEEEEEEEEEEEX DEBUG"), output2); | |
| } | |
| void test1() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout1.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.1"))); | |
| } | |
| void test2() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout2.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT1; | |
| ISO8601Filter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.2"))); | |
| } | |
| void test3() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout3.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT1; | |
| ISO8601Filter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.3"))); | |
| } | |
| // Output format: | |
| // 06 avr. 2002 18:30:58,937 [12345] DEBUG atternLayoutTest - Message 0 | |
| void test4() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout4.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT2; | |
| AbsoluteDateAndTimeFilter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.4"))); | |
| } | |
| void test5() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout5.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT2; | |
| AbsoluteDateAndTimeFilter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.5"))); | |
| } | |
| void test6() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout6.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT3; | |
| AbsoluteTimeFilter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.6"))); | |
| } | |
| void test7() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout7.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT3; | |
| AbsoluteTimeFilter filter2; | |
| ThreadFilter filter3; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.7"))); | |
| } | |
| void test8() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout8.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT4; | |
| // | |
| // combo of relative time and thread identifier | |
| // (the \\\\1 preserve a leading space) | |
| Filter filter2(".*]", "[main]"); | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.8"))); | |
| } | |
| void test9() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout9.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT5; | |
| ThreadFilter filter2; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.9"))); | |
| } | |
| void test10() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout10.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT6; | |
| ThreadFilter filter2; | |
| LineNumberFilter filter3; | |
| FilenameFilter filenameFilter(__FILE__, "patternlayouttest.cpp"); | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filenameFilter); | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.10"))); | |
| } | |
| void test11() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout11.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT11a << PAT11b; | |
| ThreadFilter filter2; | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.11"))); | |
| } | |
| void test12() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout12.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| ControlFilter filter1; | |
| filter1 << PAT12; | |
| ThreadFilter filter2; | |
| LineNumberFilter filter3; | |
| FilenameFilter filenameFilter(__FILE__, "patternlayouttest.cpp"); | |
| std::vector<Filter*> filters; | |
| filters.push_back(&filenameFilter); | |
| filters.push_back(&filter1); | |
| filters.push_back(&filter2); | |
| filters.push_back(&filter3); | |
| try | |
| { | |
| Transformer::transform(TEMP, FILTERED, filters); | |
| } | |
| catch (UnexpectedFormatException& e) | |
| { | |
| std::cout << "UnexpectedFormatException :" << e.what() << std::endl; | |
| throw; | |
| } | |
| LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.12"))); | |
| } | |
| void test13() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout13.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.13"))); | |
| } | |
| void test14() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout14.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| common(); | |
| LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.14"))); | |
| } | |
| void testMDC1() | |
| { | |
| auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout.mdc.1.properties")); | |
| LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured); | |
| MDC::put(LOG4CXX_TEST_STR("key1"), LOG4CXX_TEST_STR("va11")); | |
| MDC::put(LOG4CXX_TEST_STR("key2"), LOG4CXX_TEST_STR("va12")); | |
| logger->debug(LOG4CXX_TEST_STR("Hello World")); | |
| MDC::clear(); | |
| LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.mdc.1"))); | |
| } | |
| void testMDC2() | |
| { | |
| LogString OUTPUT_FILE = LOG4CXX_STR("output/patternLayout.mdc.2"); | |
| File WITNESS_FILE = LOG4CXX_FILE("witness/patternLayout.mdc.2"); | |
| LogString mdcMsgPattern1 = LOG4CXX_STR("%m : %X%n"); | |
| LogString mdcMsgPattern2 = LOG4CXX_STR("%m : %X{key1}%n"); | |
| LogString mdcMsgPattern3 = LOG4CXX_STR("%m : %X{key2}%n"); | |
| LogString mdcMsgPattern4 = LOG4CXX_STR("%m : %X{key3}%n"); | |
| LogString mdcMsgPattern5 = LOG4CXX_STR("%m : %X{key1},%X{key2},%X{key3}%n"); | |
| // set up appender | |
| PatternLayoutPtr layout = PatternLayoutPtr(new PatternLayout(LOG4CXX_STR("%m%n"))); | |
| AppenderPtr appender = FileAppenderPtr(new FileAppender(layout, OUTPUT_FILE, false)); | |
| // set appender on root and set level to debug | |
| root->addAppender(appender); | |
| root->setLevel(Level::getDebug()); | |
| // output starting message | |
| root->debug(LOG4CXX_TEST_STR("starting mdc pattern test")); | |
| layout->setConversionPattern(mdcMsgPattern1); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("empty mdc, no key specified in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern2); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("empty mdc, key1 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern3); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("empty mdc, key2 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern4); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("empty mdc, key3 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern5); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("empty mdc, key1, key2, and key3 in pattern")); | |
| MDC::put(LOG4CXX_TEST_STR("key1"), LOG4CXX_TEST_STR("value1")); | |
| MDC::put(LOG4CXX_TEST_STR("key2"), LOG4CXX_TEST_STR("value2")); | |
| layout->setConversionPattern(mdcMsgPattern1); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("filled mdc, no key specified in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern2); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("filled mdc, key1 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern3); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("filled mdc, key2 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern4); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("filled mdc, key3 in pattern")); | |
| layout->setConversionPattern(mdcMsgPattern5); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("filled mdc, key1, key2, and key3 in pattern")); | |
| MDC::remove(LOG4CXX_TEST_STR("key1")); | |
| MDC::remove(LOG4CXX_TEST_STR("key2")); | |
| layout->setConversionPattern(LOG4CXX_STR("%m%n")); | |
| layout->activateOptions(); | |
| root->debug(LOG4CXX_TEST_STR("finished mdc pattern test")); | |
| LOGUNIT_ASSERT(Compare::compare(OUTPUT_FILE, WITNESS_FILE)); | |
| } | |
| /* | |
| * Tests for logger-name abbreviation, exercised via %c{precision} in PatternLayout. | |
| */ | |
| /** | |
| * No dots at all: name shorter than or equal to any precision should be | |
| * returned unchanged (nextDot == npos path, no truncation possible). | |
| */ | |
| void testAbbreviateNoDots() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{2}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("NoDotsHere") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("NoDotsHere")), result); | |
| } | |
| /** | |
| * Precision larger than number of segments: entire name is preserved, | |
| * exercising the "keep trailing/last element(s) whole" path. | |
| */ | |
| void testAbbreviatePrecisionExceedsSegments() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{5}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org.apache.log4cxx") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("org.apache.log4cxx")), result); | |
| } | |
| /** | |
| * Standard case: precision 2 keeps the last two dotted segments and | |
| * drops the leading ones entirely (no ellipsis involved at this level). | |
| */ | |
| void testAbbreviateKeepLastTwoSegments() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{2}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org.apache.log4cxx.PatternLayout") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("log4cxx.PatternLayout")), result); | |
| } | |
| /** | |
| * Precision 1 keeps only the final segment; every prior segment, | |
| * however long, must be dropped by abbreviate()'s erase/rewrite step. | |
| */ | |
| void testAbbreviateKeepLastSegmentOnly() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("com.example.verylongcompany.module.ClassName") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ClassName")), result); | |
| } | |
| /** | |
| * Precision 0: degenerate case, only the final element remains | |
| * (mirrors log4j semantics where 0 behaves like 1 for the last element). | |
| */ | |
| void testAbbreviatePrecisionZero() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{0}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("a.b.c.d.Ending") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Ending")), result); | |
| } | |
| /** | |
| * Fractional / dotted-count abbreviator ("%c{1.}" family): each retained | |
| * leading segment is itself truncated to 1 character plus a trailing dot, | |
| * directly exercising the charCount/erase/insert logic inside abbreviate(). | |
| */ | |
| void testAbbreviateEachSegmentToOneChar() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org.apache.log4cxx.PatternLayout") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.l.PatternLayout")), result); | |
| } | |
| /** | |
| * Segment truncated to 2 characters ("%c{2.}"): checks charCount > 1 | |
| * truncation path (nextDot - startPos > charCount) rather than the | |
| * charCount == 1 special case. | |
| */ | |
| void testAbbreviateEachSegmentToTwoChars() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{2.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org.apache.log4cxx.PatternLayout") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("or.ap.lo.PatternLayout")), result); | |
| } | |
| /** | |
| * Segment shorter than or equal to charCount is left untouched | |
| * (the "(nextDot - startPos) > charCount" guard should skip erase/insert). | |
| */ | |
| void testAbbreviateShortSegmentUnchanged() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{3.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("a.bb.ccc.LongFinalSegment") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| // "a" (1<=3), "bb" (2<=3), "ccc" (3<=3) all pass through unabbreviated | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("a.bb.ccc.LongFinalSegment")), result); | |
| } | |
| /** | |
| * Consecutive dots produce a zero-length segment; abbreviate() must | |
| * not throw or corrupt the buffer, and should leave the empty | |
| * segment as-is (nextDot - startPos == 0, not > charCount). | |
| */ | |
| void testAbbreviateConsecutiveDots() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org..apache.Logger") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o..a.Logger")), result); | |
| } | |
| /** | |
| * Leading dot creates an empty first segment; verifies startPos handling | |
| * at position 0 doesn't underflow or mis-index into the buffer. | |
| */ | |
| void testAbbreviateLeadingDot() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR(".apache.Logger") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".a.Logger")), result); | |
| } | |
| /** | |
| * Trailing dot with nothing after it: the final "segment" after the | |
| * last dot is empty, and nextDot == npos is never reached for that | |
| * position, so the loop must still terminate correctly. | |
| */ | |
| void testAbbreviateTrailingDot() | |
| { | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("org.apache.") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString maxElementCharCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.")), maxElementCharCountResult); | |
| LogString maxElementCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{2}")).format(maxElementCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("apache.")), maxElementCountResult); | |
| } | |
| /** | |
| * Empty logger name: startPos == buf.size() from the start, find() | |
| * immediately returns npos, function must be a no-op. | |
| */ | |
| void testAbbreviateEmptyName() | |
| { | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString maxElementCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{2}")).format(maxElementCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("")), maxElementCountResult); | |
| LogString maxElementCharCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("")), maxElementCharCountResult); | |
| } | |
| /** | |
| * Degenerate logger name. | |
| */ | |
| void testAbbreviateDegenerateName() | |
| { | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR(".") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString maxElementCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{1}")).format(maxElementCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".")), maxElementCountResult); | |
| LogString maxElementCharCountResult; | |
| PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".")), maxElementCharCountResult); | |
| } | |
| /** | |
| * Single-character segments equal to charCount exactly: boundary check | |
| * on "> charCount" vs ">= charCount" (should NOT truncate when equal, | |
| * since the guard is strictly "(nextDot - startPos) > charCount"). | |
| */ | |
| void testAbbreviateSegmentEqualToCharCountBoundary() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{2.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("ab.cd.RestOfName") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| // "ab" (len 2 == charCount 2) and "cd" (len 2 == charCount 2) both | |
| // pass through untouched; only the final segment is never abbreviated. | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ab.cd.RestOfName")), result); | |
| } | |
| /** | |
| * Segment exactly one character longer than charCount: smallest case | |
| * that must actually trigger the erase/insert (or rewrite) path. | |
| */ | |
| void testAbbreviateSegmentOneCharOverBoundary() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{2.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("abc.de.RestOfName") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| // "abc" (len 3 > 2) truncates to "ab"; "de" (len 2 == 2) stays whole. | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ab.de.RestOfName")), result); | |
| } | |
| /** | |
| * Many short segments (deep package hierarchy): the classic case that | |
| * exposes O(n*k) blowup in an erase/insert-per-segment implementation. | |
| */ | |
| void testAbbreviateManySegmentsDeepHierarchy() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.FinalClass") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL | |
| ( LogString(LOG4CXX_STR("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.FinalClass")) | |
| , result | |
| ); | |
| } | |
| /** | |
| * Very long single final segment combined with many short leading ones: | |
| * ensures the tail (unabbreviated, no trailing dot) is copied intact | |
| * even when it is far longer than charCount, since the last element is | |
| * never subject to truncation regardless of length. | |
| */ | |
| void testAbbreviateLongFinalSegmentPreserved() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("x.y.z.ThisIsAVeryLongFinalClassNameThatShouldNotBeTruncatedAtAll") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL | |
| ( LogString(LOG4CXX_STR("x.y.z.ThisIsAVeryLongFinalClassNameThatShouldNotBeTruncatedAtAll")) | |
| , result | |
| ); | |
| } | |
| /** | |
| * Idempotency / stability check: running the same abbreviation pattern | |
| * against an already-abbreviated name should be a no-op, since every | |
| * remaining segment is already <= charCount. | |
| */ | |
| void testAbbreviateIdempotentOnAlreadyAbbreviated() | |
| { | |
| PatternLayout layout(LOG4CXX_STR("%c{1.}")); | |
| auto event = std::make_shared<spi::LoggingEvent> | |
| ( LOG4CXX_STR("o.a.l.PatternLayout") | |
| , Level::getInfo() | |
| , LOG4CXX_STR("msg") | |
| , LOG4CXX_LOCATION | |
| ); | |
| LogString result; | |
| layout.format(result, event); | |
| LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.l.PatternLayout")), result); | |
| } | |
| std::string createMessage(Pool & pool, int i) | |
| { | |
| std::string msg("Message "); | |
| msg.append(pool.itoa(i)); | |
| return msg; | |
| } | |
| void common() | |
| { | |
| int i = -1; | |
| Pool pool; | |
| LOG4CXX_DEBUG(logger, createMessage(pool, ++i)); | |
| LOG4CXX_DEBUG(root, createMessage(pool, i)); | |
| LOG4CXX_INFO(logger, createMessage(pool, ++i)); | |
| LOG4CXX_INFO(root, createMessage(pool, i)); | |
| LOG4CXX_WARN(logger, createMessage(pool, ++i)); | |
| LOG4CXX_WARN(root, createMessage(pool, i)); | |
| LOG4CXX_ERROR(logger, createMessage(pool, ++i)); | |
| LOG4CXX_ERROR(root, createMessage(pool, i)); | |
| LOG4CXX_FATAL(logger, createMessage(pool, ++i)); | |
| LOG4CXX_FATAL(root, createMessage(pool, i)); | |
| } | |
| private: | |
| static const LogString FILTERED; | |
| static const LogString TEMP; | |
| }; | |
| const LogString PatternLayoutTest::TEMP(LOG4CXX_STR("output/patternlayout")); | |
| const LogString PatternLayoutTest::FILTERED(LOG4CXX_STR("output/patternlayoutfiltered")); | |
| LOGUNIT_TEST_SUITE_REGISTRATION(PatternLayoutTest); |