// Module: Log4CPLUS // File: patternlayout.cxx // Created: 6/2001 // Author: Tad E. Smith // // // Copyright 2001-2017 Tad E. Smith // // Licensed 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 #include #include #include #include #include #include #include #include #include #include namespace { static log4cplus::tstring get_basename (const log4cplus::tstring& filename) { #if defined(_WIN32) log4cplus::tchar const dir_sep(LOG4CPLUS_TEXT('\\')); #else log4cplus::tchar const dir_sep(LOG4CPLUS_TEXT('/')); #endif log4cplus::tstring::size_type pos = filename.rfind(dir_sep); if (pos != log4cplus::tstring::npos) return filename.substr(pos+1); else return filename; } } // namespace namespace log4cplus { static tchar const ESCAPE_CHAR = LOG4CPLUS_TEXT('%'); extern void formatRelativeTimestamp (log4cplus::tostream & output, log4cplus::spi::InternalLoggingEvent const & event); namespace pattern { /** * This is used by PatternConverter class to inform them how to format * their output. */ struct FormattingInfo { int minLen; std::size_t maxLen; bool leftAlign : 1; bool trimStart : 1; FormattingInfo() { reset(); } void reset(); void dump(helpers::LogLog&); }; /** * This is the base class of all "Converter" classes that format a * field of InternalLoggingEvent objects. In fact, the PatternLayout * class simply uses an array of PatternConverter objects to format * and append a logging event. */ class PatternConverter { public: explicit PatternConverter(const FormattingInfo& info); virtual ~PatternConverter() {} void formatAndAppend(tostream& output, const spi::InternalLoggingEvent& event); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event) = 0; private: int minLen; std::size_t maxLen; bool leftAlign : 1; bool trimStart : 1; }; typedef std::vector > PatternConverterList; /** * This PatternConverter returns a constant string. */ class LiteralPatternConverter : public PatternConverter { public: LiteralPatternConverter(); explicit LiteralPatternConverter(const tstring& str); virtual void convert(tstring & result, const spi::InternalLoggingEvent&) { result = str; } private: tstring str; }; /** * This PatternConverter is used to format most of the "simple" fields * found in the InternalLoggingEvent object. */ class BasicPatternConverter : public PatternConverter { public: enum Type { THREAD_CONVERTER, THREAD2_CONVERTER, PROCESS_CONVERTER, LOGLEVEL_CONVERTER, NDC_CONVERTER, MESSAGE_CONVERTER, NEWLINE_CONVERTER, BASENAME_CONVERTER, FILE_CONVERTER, LINE_CONVERTER, FULL_LOCATION_CONVERTER, FUNCTION_CONVERTER }; BasicPatternConverter(const FormattingInfo& info, Type type); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: // Disable copy BasicPatternConverter(const BasicPatternConverter&); BasicPatternConverter& operator=(BasicPatternConverter&); LogLevelManager& llmCache; Type type; }; /** * This PatternConverter is used to format the Logger field found in * the InternalLoggingEvent object. */ class LoggerPatternConverter : public PatternConverter { public: LoggerPatternConverter(const FormattingInfo& info, int precision); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: int precision; }; /** * This PatternConverter is used to format the timestamp field found in * the InternalLoggingEvent object. It will be formatted according to * the specified "pattern". */ class DatePatternConverter : public PatternConverter { public: DatePatternConverter(const FormattingInfo& info, const tstring& pattern, bool use_gmtime); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: bool use_gmtime; tstring format; }; /** * This PatternConverter is used to format an environment variable */ class EnvPatternConverter : public PatternConverter { public: EnvPatternConverter(const FormattingInfo& info, const log4cplus::tstring& env); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: log4cplus::tstring envKey; }; //! This pattern is used to format miliseconds since process start. class RelativeTimestampConverter: public PatternConverter { public: RelativeTimestampConverter(const FormattingInfo& info); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); }; /** * This PatternConverter is used to format the hostname field. */ class HostnamePatternConverter : public PatternConverter { public: HostnamePatternConverter(const FormattingInfo& info, bool fqdn); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: tstring hostname_; }; /** * This PatternConverter is used to format the MDC field found in * the InternalLoggingEvent object, optionally limited to * \c k Mapped diagnostic context key. */ class MDCPatternConverter : public PatternConverter { public: MDCPatternConverter(const FormattingInfo& info, tstring const & k); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: tstring key; }; /** * This PatternConverter is used to format the NDC field found in * the InternalLoggingEvent object, optionally limited to * \c precision levels (using space to separate levels). */ class NDCPatternConverter : public PatternConverter { public: NDCPatternConverter(const FormattingInfo& info, int precision); virtual void convert(tstring & result, const spi::InternalLoggingEvent& event); private: int precision; }; /** * This class parses a "pattern" string into an array of * PatternConverter objects. *

* @see PatternLayout for the formatting of the "pattern" string. */ class PatternParser { public: PatternParser(const tstring& pattern, unsigned ndcMaxDepth); PatternConverterList parse(); private: // Types enum ParserState { LITERAL_STATE, CONVERTER_STATE, DOT_STATE, MIN_STATE, MAX_STATE }; // Methods tstring extractOption(); int extractPrecisionOption(); void finalizeConverter(tchar c); // Data tstring pattern; FormattingInfo formattingInfo; PatternConverterList list; ParserState state; tstring::size_type pos; tstring currentLiteral; unsigned ndcMaxDepth; }; //////////////////////////////////////////////// // FormattingInfo methods: //////////////////////////////////////////////// void FormattingInfo::reset() { minLen = -1; maxLen = std::numeric_limits::max (); leftAlign = false; trimStart = true; } void FormattingInfo::dump(helpers::LogLog& loglog) { tostringstream buf; buf << LOG4CPLUS_TEXT("min=") << minLen << LOG4CPLUS_TEXT(", max=") << maxLen << LOG4CPLUS_TEXT(", leftAlign=") << std::boolalpha << leftAlign << LOG4CPLUS_TEXT(", trimStart=") << std::boolalpha << trimStart; loglog.debug(buf.str()); } //////////////////////////////////////////////// // PatternConverter methods: //////////////////////////////////////////////// PatternConverter::PatternConverter(const FormattingInfo& i) { minLen = i.minLen; maxLen = i.maxLen; leftAlign = i.leftAlign; trimStart = i.trimStart; } void PatternConverter::formatAndAppend( tostream& output, const spi::InternalLoggingEvent& event) { tstring & s = internal::get_ptd ()->faa_str; convert (s, event); std::size_t len = s.length(); if (len > maxLen) { if (trimStart) output << s.substr(len - maxLen); else output << s.substr(0, maxLen); } else if (static_cast(len) < minLen) { std::ios_base::fmtflags const original_flags = output.flags (); tchar const fill = output.fill (LOG4CPLUS_TEXT(' ')); output.setf (leftAlign ? std::ios_base::left : std::ios_base::right, std::ios_base::adjustfield); output.width (minLen); output << s; output.fill (fill); output.flags (original_flags); } else output << s; } //////////////////////////////////////////////// // LiteralPatternConverter methods: //////////////////////////////////////////////// LiteralPatternConverter::LiteralPatternConverter() : PatternConverter(FormattingInfo()) , str() { } LiteralPatternConverter::LiteralPatternConverter( const tstring& str_) : PatternConverter(FormattingInfo()) , str(str_) { } //////////////////////////////////////////////// // BasicPatternConverter methods: //////////////////////////////////////////////// BasicPatternConverter::BasicPatternConverter( const FormattingInfo& info, Type type_) : PatternConverter(info) , llmCache(getLogLevelManager()) , type(type_) { } void BasicPatternConverter::convert(tstring & result, const spi::InternalLoggingEvent& event) { switch(type) { case LOGLEVEL_CONVERTER: result = llmCache.toString(event.getLogLevel()); return; case BASENAME_CONVERTER: result = get_basename(event.getFile()); return; case PROCESS_CONVERTER: helpers::convertIntegerToString(result, internal::get_process_id ()); return; case NDC_CONVERTER: result = event.getNDC(); return; case MESSAGE_CONVERTER: result = event.getMessage(); return; case NEWLINE_CONVERTER: result = LOG4CPLUS_TEXT("\n"); return; case FILE_CONVERTER: result = event.getFile(); return; case THREAD_CONVERTER: result = event.getThread(); return; case THREAD2_CONVERTER: result = event.getThread2(); return; case LINE_CONVERTER: { if(event.getLine() != -1) helpers::convertIntegerToString(result, event.getLine()); else result.clear (); return; } case FULL_LOCATION_CONVERTER: { tstring const & file = event.getFile(); if (! file.empty ()) { result = file; result += LOG4CPLUS_TEXT(":"); result += helpers::convertIntegerToString(event.getLine()); } else result = LOG4CPLUS_TEXT(":"); return; } case FUNCTION_CONVERTER: result = event.getFunction (); return; } result = LOG4CPLUS_TEXT("INTERNAL LOG4CPLUS ERROR"); } //////////////////////////////////////////////// // LoggerPatternConverter methods: //////////////////////////////////////////////// LoggerPatternConverter::LoggerPatternConverter( const FormattingInfo& info, int prec) : PatternConverter(info) , precision(prec) { } void LoggerPatternConverter::convert(tstring & result, const spi::InternalLoggingEvent& event) { const tstring& name = event.getLoggerName(); if (precision <= 0) { result = name; } else { auto len = name.length(); // We substract 1 from 'len' when assigning to 'end' to avoid out of // bounds exception in return r.substring(end+1, len). This can happen // if precision is 1 and the logger name ends with a dot. auto end = len - 1; for (int i = precision; i > 0; --i) { end = name.rfind(LOG4CPLUS_TEXT('.'), end - 1); if(end == tstring::npos) { result = name; return; } } result.assign (name, end + 1, tstring::npos); } } //////////////////////////////////////////////// // DatePatternConverter methods: //////////////////////////////////////////////// DatePatternConverter::DatePatternConverter( const FormattingInfo& info, const tstring& pattern, bool use_gmtime_) : PatternConverter(info) , use_gmtime(use_gmtime_) , format(pattern) { } void DatePatternConverter::convert(tstring & result, const spi::InternalLoggingEvent& event) { result = helpers::getFormattedTime(format, event.getTimestamp(), use_gmtime); } //////////////////////////////////////////////// // EnvPatternConverter methods: //////////////////////////////////////////////// EnvPatternConverter::EnvPatternConverter( const FormattingInfo& info, const tstring& env) : PatternConverter(info) , envKey(env) { } void EnvPatternConverter::convert(tstring & result, const spi::InternalLoggingEvent&) { if (! internal::get_env_var (result, envKey)) { // Variable doesn't exist, return empty string. result.clear (); } } // // // RelativeTimestampConverter::RelativeTimestampConverter (FormattingInfo const & info) : PatternConverter (info) { } void RelativeTimestampConverter::convert (tstring & result, spi::InternalLoggingEvent const & event) { tostringstream & oss = internal::get_ptd ()->layout_oss; detail::clear_tostringstream (oss); log4cplus::formatRelativeTimestamp (oss, event); result = oss.str (); } //////////////////////////////////////////////// // HostnamePatternConverter methods: //////////////////////////////////////////////// HostnamePatternConverter::HostnamePatternConverter ( const FormattingInfo& info, bool fqdn) : PatternConverter(info) , hostname_ (helpers::getHostname (fqdn)) { } void HostnamePatternConverter::convert ( tstring & result, const spi::InternalLoggingEvent&) { result = hostname_; } //////////////////////////////////////////////// // MDCPatternConverter methods: //////////////////////////////////////////////// log4cplus::pattern::MDCPatternConverter::MDCPatternConverter ( const FormattingInfo& info, tstring const & k) : PatternConverter(info) , key (k) { } void log4cplus::pattern::MDCPatternConverter::convert (tstring & result, const spi::InternalLoggingEvent& event) { if (!key.empty()) { result = event.getMDC (key); } else { result.clear (); MappedDiagnosticContextMap const & mdcMap = event.getMDCCopy(); for (auto const & kv : mdcMap) { tstring const & name = kv.first; tstring const & value = kv.second; result += LOG4CPLUS_TEXT("{"); result += name; result += LOG4CPLUS_TEXT(", "); result += value; result += LOG4CPLUS_TEXT("}"); } } } //////////////////////////////////////////////// // NDCPatternConverter methods: //////////////////////////////////////////////// log4cplus::pattern::NDCPatternConverter::NDCPatternConverter ( const FormattingInfo& info, int precision_) : PatternConverter(info) , precision(precision_) { } void log4cplus::pattern::NDCPatternConverter::convert (tstring & result, const spi::InternalLoggingEvent& event) { const log4cplus::tstring& text = event.getNDC(); if (precision <= 0) result = text; else { tstring::size_type p = text.find(LOG4CPLUS_TEXT(' ')); for (int i = 1; i < precision && p != tstring::npos; ++i) p = text.find(LOG4CPLUS_TEXT(' '), p + 1); result.assign (text, 0, p); } } //////////////////////////////////////////////// // PatternParser methods: //////////////////////////////////////////////// PatternParser::PatternParser( const tstring& pattern_, unsigned ndcMaxDepth_) : pattern(pattern_) , state(LITERAL_STATE) , pos(0) , ndcMaxDepth (ndcMaxDepth_) { } tstring PatternParser::extractOption() { tstring r; if ( (pos < pattern.length()) && (pattern[pos] == LOG4CPLUS_TEXT('{'))) { tstring::size_type end = pattern.find_first_of(LOG4CPLUS_TEXT('}'), pos); if (end != tstring::npos) { r.assign (pattern, pos + 1, end - pos - 1); pos = end + 1; return r; } else { log4cplus::tostringstream buf; buf << LOG4CPLUS_TEXT("No matching '}' found in conversion pattern string \"") << pattern << LOG4CPLUS_TEXT("\""); helpers::getLogLog().error(buf.str()); pos = pattern.length(); } } return r; } int PatternParser::extractPrecisionOption() { tstring opt = extractOption(); int r = 0; if (! opt.empty ()) r = std::atoi(LOG4CPLUS_TSTRING_TO_STRING(opt).c_str()); return r; } PatternConverterList PatternParser::parse() { pos = 0; while(pos < pattern.length()) { tchar const c = pattern[pos++]; switch (state) { case LITERAL_STATE : // In literal state, the last char is always a literal. if(pos == pattern.length()) { currentLiteral += c; continue; } if(c == ESCAPE_CHAR) { // peek at the next char. switch (pattern[pos]) { case ESCAPE_CHAR: currentLiteral += c; pos++; // move pointer break; default: if(! currentLiteral.empty ()) { list.push_back (std::unique_ptr( new LiteralPatternConverter(currentLiteral))); //getLogLog().debug("Parsed LITERAL converter: \"" // +currentLiteral+"\"."); } currentLiteral.resize(0); currentLiteral += c; // append % state = CONVERTER_STATE; formattingInfo.reset(); } } else { currentLiteral += c; } break; case CONVERTER_STATE: currentLiteral += c; switch (c) { case LOG4CPLUS_TEXT('-'): formattingInfo.leftAlign = true; break; case LOG4CPLUS_TEXT('.'): state = DOT_STATE; break; default: if(c >= LOG4CPLUS_TEXT('0') && c <= LOG4CPLUS_TEXT('9')) { formattingInfo.minLen = c - LOG4CPLUS_TEXT('0'); state = MIN_STATE; } else { finalizeConverter(c); } } // switch break; case MIN_STATE: currentLiteral += c; if (c >= LOG4CPLUS_TEXT('0') && c <= LOG4CPLUS_TEXT('9')) { formattingInfo.minLen = formattingInfo.minLen * 10 + (c - LOG4CPLUS_TEXT('0')); } else if(c == LOG4CPLUS_TEXT('.')) { state = DOT_STATE; } else { finalizeConverter(c); } break; case DOT_STATE: currentLiteral += c; if (c == LOG4CPLUS_TEXT('-')) formattingInfo.trimStart = false; else if(c >= LOG4CPLUS_TEXT('0') && c <= LOG4CPLUS_TEXT('9')) { formattingInfo.maxLen = c - LOG4CPLUS_TEXT('0'); state = MAX_STATE; } else { tostringstream buf; buf << LOG4CPLUS_TEXT("Error occured in position ") << pos << LOG4CPLUS_TEXT(".\n Was expecting digit, instead got char \"") << c << LOG4CPLUS_TEXT("\"."); helpers::getLogLog().error(buf.str()); state = LITERAL_STATE; } break; case MAX_STATE: currentLiteral += c; if (c >= LOG4CPLUS_TEXT('0') && c <= LOG4CPLUS_TEXT('9')) formattingInfo.maxLen = formattingInfo.maxLen * 10 + (c - LOG4CPLUS_TEXT('0')); else { finalizeConverter(c); state = LITERAL_STATE; } break; } // end switch } // end while if(! currentLiteral.empty ()) { list.push_back( std::unique_ptr( new LiteralPatternConverter(currentLiteral))); //getLogLog().debug("Parsed LITERAL converter: \""+currentLiteral+"\"."); } return std::move (list); } void PatternParser::finalizeConverter(tchar c) { PatternConverter* pc = nullptr; switch (c) { case LOG4CPLUS_TEXT('b'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::BASENAME_CONVERTER); //getLogLog().debug("BASENAME converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('c'): pc = new LoggerPatternConverter(formattingInfo, extractPrecisionOption()); //getLogLog().debug( LOG4CPLUS_TEXT("LOGGER converter.") ); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('d'): case LOG4CPLUS_TEXT('D'): { tstring dOpt = extractOption(); if(dOpt.empty ()) { dOpt = LOG4CPLUS_TEXT("%Y-%m-%d %H:%M:%S"); } bool use_gmtime = c == LOG4CPLUS_TEXT('d'); pc = new DatePatternConverter(formattingInfo, dOpt, use_gmtime); //if(use_gmtime) { // getLogLog().debug("GMT DATE converter."); //} //else { // getLogLog().debug("LOCAL DATE converter."); //} //formattingInfo.dump(getLogLog()); } break; case LOG4CPLUS_TEXT('E'): pc = new EnvPatternConverter(formattingInfo, extractOption()); //getLogLog().debug("Environment converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('F'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::FILE_CONVERTER); //getLogLog().debug("FILE NAME converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('h'): case LOG4CPLUS_TEXT('H'): { bool fqdn = (c == LOG4CPLUS_TEXT('H')); pc = new HostnamePatternConverter(formattingInfo, fqdn); // getLogLog().debug( LOG4CPLUS_TEXT("HOSTNAME converter.") ); // formattingInfo.dump(getLogLog()); } break; case LOG4CPLUS_TEXT('i'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::PROCESS_CONVERTER); //getLogLog().debug("PROCESS_CONVERTER converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('l'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::FULL_LOCATION_CONVERTER); //getLogLog().debug("FULL LOCATION converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('L'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::LINE_CONVERTER); //getLogLog().debug("LINE NUMBER converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('m'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::MESSAGE_CONVERTER); //getLogLog().debug("MESSAGE converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('M'): pc = new BasicPatternConverter ( formattingInfo, BasicPatternConverter::FUNCTION_CONVERTER); //getLogLog().debug("METHOD (function name) converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('n'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::NEWLINE_CONVERTER); //getLogLog().debug("MESSAGE converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('p'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::LOGLEVEL_CONVERTER); //getLogLog().debug("LOGLEVEL converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('r'): pc = new RelativeTimestampConverter (formattingInfo); //getLogLog().debug("RELATIVE converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('t'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::THREAD_CONVERTER); //getLogLog().debug("THREAD converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('T'): pc = new BasicPatternConverter (formattingInfo, BasicPatternConverter::THREAD2_CONVERTER); //getLogLog().debug("THREAD2 converter."); //formattingInfo.dump(getLogLog()); break; case LOG4CPLUS_TEXT('x'): pc = new NDCPatternConverter (formattingInfo, ndcMaxDepth); //getLogLog().debug("NDC converter."); break; case LOG4CPLUS_TEXT('X'): pc = new MDCPatternConverter (formattingInfo, extractOption ()); //getLogLog().debug("MDC converter."); break; default: tostringstream buf; buf << LOG4CPLUS_TEXT("Unexpected char [") << c << LOG4CPLUS_TEXT("] at position ") << pos << LOG4CPLUS_TEXT(" in conversion patterrn."); helpers::getLogLog().error(buf.str()); pc = new LiteralPatternConverter(currentLiteral); } list.push_back(std::unique_ptr(pc)); currentLiteral.resize(0); state = LITERAL_STATE; formattingInfo.reset(); } } // namespace pattern typedef pattern::PatternConverterList PatternConverterList; //////////////////////////////////////////////// // PatternLayout methods: //////////////////////////////////////////////// PatternLayout::PatternLayout(const tstring& pattern_) { init(pattern_, 0); } PatternLayout::PatternLayout(const helpers::Properties& properties) { unsigned ndcMaxDepth = 0; properties.getUInt (ndcMaxDepth, LOG4CPLUS_TEXT ("NDCMaxDepth")); bool hasPattern = properties.exists( LOG4CPLUS_TEXT("Pattern") ); bool hasConversionPattern = properties.exists( LOG4CPLUS_TEXT("ConversionPattern") ); if(hasPattern) { helpers::getLogLog().warn( LOG4CPLUS_TEXT("PatternLayout- the \"Pattern\" property has been") LOG4CPLUS_TEXT(" deprecated. Use \"ConversionPattern\" instead.")); } if(hasConversionPattern) { init(properties.getProperty( LOG4CPLUS_TEXT("ConversionPattern") ), ndcMaxDepth); } else if(hasPattern) { init(properties.getProperty( LOG4CPLUS_TEXT("Pattern") ), ndcMaxDepth); } else { helpers::getLogLog().error( LOG4CPLUS_TEXT ("ConversionPattern not specified in properties"), true); } } void PatternLayout::init(const tstring& pattern_, unsigned ndcMaxDepth) { pattern = pattern_; parsedPattern = pattern::PatternParser(pattern, ndcMaxDepth).parse(); // Let's validate that our parser didn't give us any NULLs. If it did, // we will convert them to a valid PatternConverter that does nothing so // at least we don't core. for (auto & pc : parsedPattern) { if (! pc) { helpers::getLogLog().error( LOG4CPLUS_TEXT("Parsed Pattern created a NULL PatternConverter")); pc.reset (new pattern::LiteralPatternConverter); } } if(parsedPattern.empty ()) { helpers::getLogLog().warn( LOG4CPLUS_TEXT("PatternLayout pattern is empty. Using default...")); parsedPattern.push_back ( std::unique_ptr( new pattern::BasicPatternConverter(pattern::FormattingInfo(), pattern::BasicPatternConverter::MESSAGE_CONVERTER))); } } PatternLayout::~PatternLayout() { } void PatternLayout::formatAndAppend(tostream& output, const spi::InternalLoggingEvent& event) { for (auto const & pc : parsedPattern) { pc->formatAndAppend(output, event); } } } // namespace log4cplus