blob: daef2db2dae5cbf76189e9f6627854e03dfd8563 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <log4cxx/logstring.h>
#include <log4cxx/pattern/colorstartpatternconverter.h>
#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_NS;
using namespace LOG4CXX_NS::pattern;
using namespace LOG4CXX_NS::spi;
using namespace LOG4CXX_NS::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;
bool hasParens = false;
size_t x = 0;
for(x = 0; x < sequence.length(); x++){
if( sequence[x] == '(' && !inParens ){
inParens = true;
hasParens = true;
continue;
}else if( sequence[x] == '(' && inParens ){
// Unbalanced parens - parse invalid
return LOG4CXX_STR("");
}
if( sequence[x] == ')' && inParens ){
hasParens = true;
inParens = false;
break;
}
if( inParens ){
strInParens.push_back(sequence[x]);
}
}
if( (x != (sequence.length() - 1) || inParens) && hasParens ){
// Unbalanced parens, or more data in the string than we expected - parse invalid
return LOG4CXX_STR("");
}
if(StringHelper::startsWith(sequence, LOG4CXX_STR("fg("))){
// Parse foreground
return colorToANSISequence(strInParens, true, pool);
}else if(StringHelper::startsWith(sequence, LOG4CXX_STR("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(std::make_unique<ColorPatternConverterPrivate>(LOG4CXX_STR("Color Start"),
LOG4CXX_STR("colorStart")))
{
}
PatternConverterPtr ColorStartPatternConverter::newInstance(
const std::vector<LogString>& /* options */)
{
static WideLife<PatternConverterPtr> instance = std::make_shared<ColorStartPatternConverter>();
return instance;
}
void ColorStartPatternConverter::format(
const LoggingEventPtr& event,
LogString& toAppendTo,
Pool& p) const
{
LOG4CXX_NS::LevelPtr lvl = event->getLevel();
switch (lvl->toInt())
{
case LOG4CXX_NS::Level::FATAL_INT:
toAppendTo.append(priv->m_fatalColor);
break;
case LOG4CXX_NS::Level::ERROR_INT:
toAppendTo.append(priv->m_errorColor);
break;
case LOG4CXX_NS::Level::WARN_INT:
toAppendTo.append(priv->m_warnColor);
break;
case LOG4CXX_NS::Level::INFO_INT:
toAppendTo.append(priv->m_infoColor);
break;
case LOG4CXX_NS::Level::DEBUG_INT:
toAppendTo.append(priv->m_debugColor);
break;
case LOG4CXX_NS::Level::TRACE_INT:
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 the color we are trying to parse is blank, clear our result
if(StringHelper::trim(color).empty() ||
StringHelper::equalsIgnoreCase(color,
LOG4CXX_STR("NONE"),
LOG4CXX_STR("none"))){
result->clear();
return;
}
if( StringHelper::startsWith(lower, LOG4CXX_STR("\\x1b")) ){
if( color[color.size() - 1] != 'm' ){
// In order for this to be a valid ANSI escape sequence,
// it must end with an 'm'. If it does not, reject.
return;
}
// We start with an escape sequence, copy the data over after the escape byte
result->clear();
result->append(LOG4CXX_STR("\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(LOG4CXX_STR("\x1b["));
LogString tmp;
for( size_t x = 0; x < color.size(); x++ ){
if(color[x] == '|' ){
LogString toAppend = convertSingleSequence(tmp, pool);
tmp.clear();
if(!toAppend.empty()){
result->push_back(';');
result->append(toAppend);
}
}else{
tmp.push_back(color[x]);
}
}
LogString toAppend = convertSingleSequence(tmp, pool);
tmp.clear();
if(!toAppend.empty()){
result->push_back(';');
result->append(toAppend);
}
result->append(LOG4CXX_STR("m"));
}
}