This is an automated email from the ASF dual-hosted git repository. rmiddleton pushed a commit to branch LOGCXX-566 in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit c138073c45d773b412559df544b74786ebdc36a0 Author: Robert Middleton <[email protected]> AuthorDate: Sat Dec 3 23:16:36 2022 -0500 Converting colors to escape sequences kinda works --- src/main/cpp/colorstartpatternconverter.cpp | 190 ++++++++++++++++++++- src/main/cpp/patternlayout.cpp | 46 ++++- .../log4cxx/pattern/colorstartpatternconverter.h | 13 +- src/main/include/log4cxx/pattern/patternparser.h | 3 +- src/main/include/log4cxx/patternlayout.h | 3 + 5 files changed, 243 insertions(+), 12 deletions(-) diff --git a/src/main/cpp/colorstartpatternconverter.cpp b/src/main/cpp/colorstartpatternconverter.cpp index 02a8d779..c58ad02c 100644 --- a/src/main/cpp/colorstartpatternconverter.cpp +++ b/src/main/cpp/colorstartpatternconverter.cpp @@ -20,6 +20,7 @@ #include <log4cxx/spi/loggingevent.h> #include <log4cxx/spi/location/locationinfo.h> #include <log4cxx/helpers/stringhelper.h> +#include <log4cxx/private/patternconverter_priv.h> using namespace log4cxx; using namespace log4cxx::pattern; @@ -28,9 +29,122 @@ using namespace log4cxx::helpers; IMPLEMENT_LOG4CXX_OBJECT(ColorStartPatternConverter) +#define priv static_cast<ColorPatternConverterPrivate*>(m_priv.get()) + +static LogString colorToANSISequence(const LogString& color, bool isForeground, Pool& pool){ + int numberToConvert = 0; + + if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("BLACK"), LOG4CXX_STR("black"))){ + numberToConvert = 30; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("RED"), LOG4CXX_STR("red"))){ + numberToConvert = 31; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("GREEN"), LOG4CXX_STR("green"))){ + numberToConvert = 32; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("YELLOW"), LOG4CXX_STR("yellow"))){ + numberToConvert = 33; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("BLUE"), LOG4CXX_STR("blue"))){ + numberToConvert = 34; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("MAGENTA"), LOG4CXX_STR("magenta"))){ + numberToConvert = 35; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("CYAN"), LOG4CXX_STR("cyan"))){ + numberToConvert = 36; + }else if(StringHelper::equalsIgnoreCase(color, LOG4CXX_STR("WHITE"), LOG4CXX_STR("white"))){ + numberToConvert = 37; + } + + if( numberToConvert == 0 ){ + return LOG4CXX_STR(""); + } + LogString ret; + if( isForeground == false ){ + numberToConvert += 10; + } + StringHelper::toString(numberToConvert, pool, ret); + return ret; +} + +static LogString graphicsModeToANSISequence(const LogString& graphicsMode, Pool& pool){ + int numberToConvert = 0; + + if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("BOLD"), LOG4CXX_STR("bold"))){ + numberToConvert = 1; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("DIM"), LOG4CXX_STR("dim"))){ + numberToConvert = 2; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("ITALIC"), LOG4CXX_STR("italic"))){ + numberToConvert = 3; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("UNDERLINE"), LOG4CXX_STR("underline"))){ + numberToConvert = 4; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("BLINKING"), LOG4CXX_STR("blinking"))){ + numberToConvert = 5; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("INVERSE"), LOG4CXX_STR("inverse"))){ + numberToConvert = 7; + }else if(StringHelper::equalsIgnoreCase(graphicsMode, LOG4CXX_STR("STRIKETHROUGH"), LOG4CXX_STR("strikethrough"))){ + numberToConvert = 9; + } + + if( numberToConvert == 0 ){ + return LOG4CXX_STR(""); + } + LogString ret; + StringHelper::toString(numberToConvert, pool, ret); + return ret; +} + +static LogString convertSingleSequence(const LogString& sequence, Pool& pool){ + LogString strInParens; + bool inParens = false; + size_t x = 0; + + for(x = 0; x < sequence.length(); x++){ + if( sequence[x] == '(' && !inParens ){ + inParens = true; + continue; + }else if( sequence[x] == '(' && inParens ){ + // Unbalanced parens - parse invalid + return LOG4CXX_STR(""); + } + + if( sequence[x] == ')' && inParens ){ + inParens = false; + break; + } + + if( inParens ){ + strInParens.push_back(sequence[x]); + } + } + + if( x != (sequence.length() - 1) || inParens ){ + // Unbalanced parens, or more data in the string than we expected - parse invalid + return LOG4CXX_STR(""); + } + + if(StringHelper::startsWith(sequence, "fg(")){ + // Parse foreground + return colorToANSISequence(strInParens, true, pool); + }else if(StringHelper::startsWith(sequence, "bg(")){ + return colorToANSISequence(strInParens, false, pool); + }else{ + return graphicsModeToANSISequence(sequence, pool); + } +} + +struct ColorStartPatternConverter::ColorPatternConverterPrivate : public PatternConverterPrivate +{ + ColorPatternConverterPrivate( const LogString& name, const LogString& style ) : + PatternConverterPrivate( name, style ){} + + LogString m_fatalColor; + LogString m_errorColor; + LogString m_warnColor; + LogString m_infoColor; + LogString m_debugColor; + LogString m_traceColor; +}; + ColorStartPatternConverter::ColorStartPatternConverter() : - LoggingEventPatternConverter(LOG4CXX_STR("Color Start"), - LOG4CXX_STR("colorStart")) + LoggingEventPatternConverter(std::make_unique<ColorPatternConverterPrivate>(LOG4CXX_STR("Color Start"), + LOG4CXX_STR("colorStart"))) { } @@ -52,30 +166,90 @@ void ColorStartPatternConverter::format( switch (lvl->toInt()) { case log4cxx::Level::FATAL_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[35m")); //magenta + toAppendTo.append(priv->m_fatalColor); break; case log4cxx::Level::ERROR_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[91m")); //red + toAppendTo.append(priv->m_errorColor); break; case log4cxx::Level::WARN_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[33m")); //yellow + toAppendTo.append(priv->m_warnColor); break; case log4cxx::Level::INFO_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[32m")); //green + toAppendTo.append(priv->m_infoColor); break; case log4cxx::Level::DEBUG_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[36m")); //cyan + toAppendTo.append(priv->m_debugColor); break; case log4cxx::Level::TRACE_INT: - toAppendTo.append(LOG4CXX_STR("\x1B[34m")); //blue + toAppendTo.append(priv->m_traceColor); break; default: break; } } + +void ColorStartPatternConverter::setFatalColor(const LogString& color){ + parseColor(color, &(priv->m_fatalColor)); +} + +void ColorStartPatternConverter::setErrorColor(const LogString& color){ + parseColor(color, &(priv->m_errorColor)); +} + +void ColorStartPatternConverter::setWarnColor(const LogString& color){ + parseColor(color, &(priv->m_warnColor)); +} + +void ColorStartPatternConverter::setInfoColor(const LogString& color){ + parseColor(color, &(priv->m_infoColor)); +} + +void ColorStartPatternConverter::setDebugColor(const LogString& color){ + parseColor(color, &(priv->m_debugColor)); +} + +void ColorStartPatternConverter::setTraceColor(const LogString& color){ + parseColor(color, &(priv->m_traceColor)); +} + +void ColorStartPatternConverter::parseColor(const LogString& color, LogString* result){ + LogString lower = StringHelper::toLowerCase(color); + Pool pool; + + if( StringHelper::startsWith(lower, "\\x1b") ){ + // We start with an escape sequence, copy the data over after the escape byte + result->clear(); + result->append("\x1b"); + for( size_t x = 4; x < color.size(); x++ ){ + result->push_back(color[x]); + } + }else{ + // We do not start with an escape sequence: try to parse color + // Escape sequence information: + // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 + // https://en.wikipedia.org/wiki/ANSI_escape_code + result->clear(); + result->append("\x1b["); + LogString tmp; + for( size_t x = 0; x < color.size(); x++ ){ + if(color[x] == '|' || + x == color.size() ){ + LogString toAppend = convertSingleSequence(tmp, pool); + tmp.clear(); + if(!toAppend.empty()){ + result->push_back(';'); + result->append(toAppend); + } + }else{ + tmp.push_back(color[x]); + } + } + result->append("m"); + } +} diff --git a/src/main/cpp/patternlayout.cpp b/src/main/cpp/patternlayout.cpp index 4d119f80..e1b5e244 100644 --- a/src/main/cpp/patternlayout.cpp +++ b/src/main/cpp/patternlayout.cpp @@ -74,6 +74,13 @@ struct PatternLayout::PatternLayoutPrivate * Field widths and alignment corresponding to pattern converters. */ FormattingInfoList patternFields; + + LogString m_fatalColor = LOG4CXX_STR("\x1B[35m"); //magenta + LogString m_errorColor = LOG4CXX_STR("\x1B[31m"); //red + LogString m_warnColor = LOG4CXX_STR("\x1B[33m"); //yellow + LogString m_infoColor = LOG4CXX_STR("\x1B[32m"); //green + LogString m_debugColor = LOG4CXX_STR("\x1B[36m"); //cyan; + LogString m_traceColor = LOG4CXX_STR("\x1B[34m"); //blue; }; IMPLEMENT_LOG4CXX_OBJECT(PatternLayout) @@ -129,6 +136,30 @@ void PatternLayout::setOption(const LogString& option, const LogString& value) LOG4CXX_STR("conversionpattern"))) { m_priv->conversionPattern = OptionConverter::convertSpecialChars(value); + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("ERRORCOLOR"), + LOG4CXX_STR("errorcolor"))){ + m_priv->m_errorColor = value; + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("FATALCOLOR"), + LOG4CXX_STR("fatalcolor"))){ + m_priv->m_fatalColor = value; + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("WARNCOLOR"), + LOG4CXX_STR("warncolor"))){ + m_priv->m_warnColor = value; + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("INFOCOLOR"), + LOG4CXX_STR("infocolor"))){ + m_priv->m_infoColor = value; + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("DEBUGCOLOR"), + LOG4CXX_STR("debugcolor"))){ + m_priv->m_debugColor = value; + }else if(StringHelper::equalsIgnoreCase(option, + LOG4CXX_STR("TRACECOLOR"), + LOG4CXX_STR("tracecolor"))){ + m_priv->m_traceColor = value; } } @@ -168,7 +199,7 @@ void PatternLayout::activateOptions(Pool&) } #define RULES_PUT(spec, cls) \ - specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR(spec)), (PatternConstructor) cls ::newInstance)) + specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR(spec)), cls ::newInstance)) log4cxx::pattern::PatternMap PatternLayout::getFormatSpecifiers() @@ -180,7 +211,7 @@ log4cxx::pattern::PatternMap PatternLayout::getFormatSpecifiers() RULES_PUT("C", ClassNamePatternConverter); RULES_PUT("class", ClassNamePatternConverter); - RULES_PUT("Y", ColorStartPatternConverter); + specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR("Y")), std::bind(&PatternLayout::createColorStartPatternConverter, this, std::placeholders::_1))); RULES_PUT("y", ColorEndPatternConverter); RULES_PUT("d", DatePatternConverter); @@ -231,7 +262,18 @@ LogString PatternLayout::getConversionPattern() const return m_priv->conversionPattern; } +pattern::PatternConverterPtr PatternLayout::createColorStartPatternConverter(const std::vector<LogString>& options){ + std::shared_ptr<ColorStartPatternConverter> colorPatternConverter = std::make_shared<ColorStartPatternConverter>(); + + colorPatternConverter->setErrorColor(m_priv->m_errorColor); + colorPatternConverter->setFatalColor(m_priv->m_fatalColor); + colorPatternConverter->setWarnColor(m_priv->m_warnColor); + colorPatternConverter->setInfoColor(m_priv->m_infoColor); + colorPatternConverter->setDebugColor(m_priv->m_debugColor); + colorPatternConverter->setTraceColor(m_priv->m_traceColor); + return colorPatternConverter; +} diff --git a/src/main/include/log4cxx/pattern/colorstartpatternconverter.h b/src/main/include/log4cxx/pattern/colorstartpatternconverter.h index 3ce700ab..c84d0619 100644 --- a/src/main/include/log4cxx/pattern/colorstartpatternconverter.h +++ b/src/main/include/log4cxx/pattern/colorstartpatternconverter.h @@ -35,6 +35,8 @@ namespace pattern class LOG4CXX_EXPORT ColorStartPatternConverter : public LoggingEventPatternConverter { + struct ColorPatternConverterPrivate; + public: DECLARE_LOG4CXX_PATTERN(ColorStartPatternConverter) BEGIN_LOG4CXX_CAST_MAP() @@ -44,7 +46,6 @@ class LOG4CXX_EXPORT ColorStartPatternConverter ColorStartPatternConverter(); - /** * Obtains an instance of pattern converter. * @param options options, may be null. @@ -58,6 +59,16 @@ class LOG4CXX_EXPORT ColorStartPatternConverter void format(const spi::LoggingEventPtr& event, LogString& toAppendTo, helpers::Pool& p) const override; + + void setFatalColor(const LogString& color); + void setErrorColor(const LogString& color); + void setWarnColor(const LogString& color); + void setInfoColor(const LogString& color); + void setDebugColor(const LogString& color); + void setTraceColor(const LogString& color); + + private: + void parseColor(const LogString& color, LogString* result); }; } diff --git a/src/main/include/log4cxx/pattern/patternparser.h b/src/main/include/log4cxx/pattern/patternparser.h index 2c328ae3..66aea74e 100644 --- a/src/main/include/log4cxx/pattern/patternparser.h +++ b/src/main/include/log4cxx/pattern/patternparser.h @@ -21,6 +21,7 @@ #include <map> #include <vector> +#include <functional> #include <log4cxx/helpers/class.h> #include <log4cxx/pattern/patternconverter.h> #include <log4cxx/pattern/formattinginfo.h> @@ -30,7 +31,7 @@ namespace log4cxx namespace pattern { -typedef PatternConverterPtr (*PatternConstructor)(const std::vector<LogString>& options); +typedef std::function<PatternConverterPtr(const std::vector<LogString>& options)> PatternConstructor; typedef std::map<LogString, PatternConstructor> PatternMap; diff --git a/src/main/include/log4cxx/patternlayout.h b/src/main/include/log4cxx/patternlayout.h index f7b844d8..3e605cb9 100644 --- a/src/main/include/log4cxx/patternlayout.h +++ b/src/main/include/log4cxx/patternlayout.h @@ -416,6 +416,9 @@ class LOG4CXX_EXPORT PatternLayout : public Layout protected: virtual log4cxx::pattern::PatternMap getFormatSpecifiers(); + + private: + pattern::PatternConverterPtr createColorStartPatternConverter(const std::vector<LogString>& options); }; LOG4CXX_PTR_DEF(PatternLayout);
