blob: e4918942583f6dfe35291044b279f82058222bdc [file] [log] [blame]
/***************************************************************************
patternparser.cpp - class PatternParser
-------------------
begin : mer avr 30 2003
copyright : (C) 2003 by Michael CATANZARITI
email : mcatan@free.fr
***************************************************************************/
/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* *
* This software is published under the terms of the Apache Software *
* License version 1.1, a copy of which has been included with this *
* distribution in the LICENSE.txt file. *
***************************************************************************/
#include <log4cxx/helpers/patternparser.h>
#include <log4cxx/helpers/dateformat.h>
#include <log4cxx/helpers/absolutetimedateformat.h>
#include <log4cxx/helpers/iso8601dateformat.h>
#include <log4cxx/helpers/datetimedateformat.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/level.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;
#define ESCAPE_CHAR _T('%')
enum ParserState
{
LITERAL_STATE,
CONVERTER_STATE,
MINUS_STATE,
DOT_STATE,
MIN_STATE,
MAX_STATE,
FULL_LOCATION_CONVERTER,
//METHOD_LOCATION_CONVERTER = 1001;
CLASS_LOCATION_CONVERTER,
LINE_LOCATION_CONVERTER,
FILE_LOCATION_CONVERTER,
RELATIVE_TIME_CONVERTER,
THREAD_CONVERTER,
LEVEL_CONVERTER,
NDC_CONVERTER,
MESSAGE_CONVERTER,
};
PatternParser::PatternParser(const tstring& pattern)
: pattern(pattern), patternLength(pattern.length()), state(LITERAL_STATE), i(0)
{
}
void PatternParser::addToList(PatternConverterPtr& pc)
{
if(head == 0)
{
head = tail = pc;
}
else
{
tail->next = pc;
tail = pc;
}
}
tstring PatternParser::extractOption()
{
if((i < patternLength) && (pattern.at(i) == _T('{')))
{
int end = pattern.find(_T('}'), i);
if (end > i)
{
tstring r = pattern.substr(i + 1, end - (i + 1));
i = end+1;
return r;
}
}
return tstring();
}
int PatternParser::extractPrecisionOption()
{
tstring opt = extractOption();
int r = 0;
if(!opt.empty())
{
r = (int)ttol(opt.c_str());
if(r <= 0)
{
LogLog::error(
_T("Precision option (") + opt + _T(") isn't a positive integer."));
r = 0;
}
}
return r;
}
PatternConverterPtr PatternParser::parse()
{
char c;
i = 0;
while(i < patternLength)
{
c = pattern.at(i++);
switch(state)
{
case LITERAL_STATE:
// In literal state, the last char is always a literal.
if(i == patternLength)
{
currentLiteral << c;
continue;
}
if(c == ESCAPE_CHAR)
{
// peek at the next char.
switch(pattern.at(i))
{
case ESCAPE_CHAR:
currentLiteral << c;
i++; // move pointer
break;
case _T('n'):
currentLiteral << std::endl;
i++; // move pointer
break;
default:
// test if currentLiteral is not empty
if(currentLiteral.tellp() > std::streamoff(0))
{
PatternConverterPtr patternConverter(new LiteralPatternConverter(
currentLiteral.str()));
addToList(patternConverter);
//LogLog.debug("Parsed LITERAL converter: \""
// +currentLiteral+"\".");
}
currentLiteral.str(_T(""));
currentLiteral << c; // append %
state = CONVERTER_STATE;
formattingInfo.reset();
}
}
else
{
currentLiteral << c;
}
break;
case CONVERTER_STATE:
currentLiteral << c;
switch(c)
{
case _T('-'):
formattingInfo.leftAlign = true;
break;
case _T('.'):
state = DOT_STATE;
break;
default:
if(c >= _T('0') && c <= _T('9'))
{
formattingInfo.min = c - _T('0');
state = MIN_STATE;
}
else
finalizeConverter(c);
} // switch
break;
case MIN_STATE:
currentLiteral << c;
if(c >= _T('0') && c <= _T('9'))
formattingInfo.min = formattingInfo.min*10 + (c - _T('0'));
else if(c == _T('.'))
state = DOT_STATE;
else
{
finalizeConverter(c);
}
break;
case DOT_STATE:
currentLiteral << c;
if(c >= _T('0') && c <= _T('9'))
{
formattingInfo.max = c - _T('0');
state = MAX_STATE;
}
else {
LOGLOG_ERROR(_T("Error occured in position ") << i
<< _T(".\n Was expecting digit, instead got char \"") << c << _T("\"."));
state = LITERAL_STATE;
}
break;
case MAX_STATE:
currentLiteral << c;
if(c >= _T('0') && c <= _T('9'))
formattingInfo.max = formattingInfo.max*10 + (c - _T('0'));
else
{
finalizeConverter(c);
state = LITERAL_STATE;
}
break;
} // switch
} // while
// test if currentLiteral is not empty
if(currentLiteral.tellp() > std::streamoff(0))
{
PatternConverterPtr patternConverter(
new LiteralPatternConverter(currentLiteral.str()));
addToList(patternConverter);
//LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
}
return head;
}
void PatternParser::finalizeConverter(TCHAR c)
{
PatternConverterPtr pc;
switch(c)
{
case _T('c'):
pc = new CategoryPatternConverter(formattingInfo,
extractPrecisionOption());
//LogLog::debug(_T("CATEGORY converter."));
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('d'):
{
tstring dateFormatStr;
DateFormat * df = 0;
tstring dOpt = extractOption();
if(!dOpt.empty())
{
dateFormatStr = dOpt;
}
else
{
dateFormatStr = AbsoluteTimeDateFormat::ISO8601_DATE_FORMAT;
}
if(StringHelper::equalsIgnoreCase(dateFormatStr,
AbsoluteTimeDateFormat::ISO8601_DATE_FORMAT))
df = new ISO8601DateFormat();
else if(StringHelper::equalsIgnoreCase(dateFormatStr,
AbsoluteTimeDateFormat::ABS_TIME_DATE_FORMAT))
df = new AbsoluteTimeDateFormat();
else if(StringHelper::equalsIgnoreCase(dateFormatStr,
AbsoluteTimeDateFormat::DATE_AND_TIME_DATE_FORMAT))
df = new DateTimeDateFormat();
else
{
df = new DateFormat(dateFormatStr);
}
pc = new DatePatternConverter(formattingInfo, df);
//LogLog.debug("DATE converter {"+dateFormatStr+"}.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
}
case _T('F'):
pc = new LocationPatternConverter(formattingInfo,
FILE_LOCATION_CONVERTER);
//LogLog.debug("File name converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('l'):
pc = new LocationPatternConverter(formattingInfo,
FULL_LOCATION_CONVERTER);
//LogLog.debug("Location converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('L'):
pc = new LocationPatternConverter(formattingInfo,
LINE_LOCATION_CONVERTER);
//LogLog.debug("LINE NUMBER converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('m'):
pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
//LogLog.debug("MESSAGE converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('p'):
{
pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
//LogLog.debug("LEVEL converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
}
break;
case _T('r'):
pc = new BasicPatternConverter(formattingInfo,
RELATIVE_TIME_CONVERTER);
//LogLog.debug("RELATIVE time converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
case _T('t'):
pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
//LogLog.debug("THREAD converter.");
//formattingInfo.dump();
currentLiteral.str(_T(""));
break;
/*case 'u':
if(i < patternLength) {
char cNext = pattern.charAt(i);
if(cNext >= '0' && cNext <= '9') {
pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
LogLog.debug("USER converter ["+cNext+"].");
formattingInfo.dump();
currentLiteral.setLength(0);
i++;
}
else
LogLog.error("Unexpected char" +cNext+" at position "+i);
}
break;*/
case _T('x'):
pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
//LogLog.debug("NDC converter.");
currentLiteral.str(_T(""));
break;
case _T('X'):
{
tstring xOpt = extractOption();
pc = new MDCPatternConverter(formattingInfo, xOpt);
currentLiteral.str(_T(""));
break;
}
default:
LOGLOG_ERROR(_T("Unexpected char [") << c << _T("] at position ") << i
<<_T(" in conversion patterrn."));
pc = new LiteralPatternConverter(currentLiteral.str());
currentLiteral.str(_T(""));
}
addConverter(pc);
}
void PatternParser::addConverter(PatternConverterPtr& pc)
{
currentLiteral.str(_T(""));
// Add the pattern converter to the list.
addToList(pc);
// Next pattern is assumed to be a literal.
state = LITERAL_STATE;
// Reset formatting info
formattingInfo.reset();
}
// ---------------------------------------------------------------------
// PatternConverters
// ---------------------------------------------------------------------
PatternParser::BasicPatternConverter::BasicPatternConverter(const FormattingInfo& formattingInfo, int type)
: PatternConverter(formattingInfo), type(type)
{
}
void PatternParser::BasicPatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
switch(type)
{
case RELATIVE_TIME_CONVERTER:
sbuf << (event.getTimeStamp() - LoggingEvent::getStartTime());
break;
case THREAD_CONVERTER:
sbuf << event.getThreadId();
break;
case LEVEL_CONVERTER:
sbuf << event.getLevel().toString();
break;
case NDC_CONVERTER:
sbuf << event.getNDC();
break;
case MESSAGE_CONVERTER:
sbuf << event.getRenderedMessage();
break;
}
}
PatternParser::LiteralPatternConverter::LiteralPatternConverter(const tstring& value)
: literal(value)
{
}
void PatternParser::LiteralPatternConverter::format(tostringstream& sbuf, const spi::LoggingEvent& e)
{
sbuf << literal;
}
void PatternParser::LiteralPatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
sbuf << literal;
}
PatternParser::DatePatternConverter::DatePatternConverter(const FormattingInfo& formattingInfo, DateFormat * df)
: PatternConverter(formattingInfo), df(df)
{
}
PatternParser::DatePatternConverter::~DatePatternConverter()
{
delete df;
}
void PatternParser::DatePatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
df->format(sbuf, event.getTimeStamp());
}
PatternParser::MDCPatternConverter::MDCPatternConverter(const FormattingInfo& formattingInfo, const tstring& key)
: PatternConverter(formattingInfo), key(key)
{
}
void PatternParser::MDCPatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
sbuf << event.getNDC();
}
PatternParser::LocationPatternConverter::LocationPatternConverter(const FormattingInfo& formattingInfo, int type)
: PatternConverter(formattingInfo), type(type)
{
}
void PatternParser::LocationPatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
switch(type)
{
case FULL_LOCATION_CONVERTER:
if (event.getFile() != 0)
{
sbuf << event.getFile() << _T("(") << event.getLine() << _T(")");
}
break;
case LINE_LOCATION_CONVERTER:
sbuf << event.getLine();
break;
case FILE_LOCATION_CONVERTER:
if (event.getFile() != 0)
{
USES_CONVERSION;
sbuf << A2T(event.getFile());
}
break;
}
}
PatternParser::CategoryPatternConverter::CategoryPatternConverter(const FormattingInfo& formattingInfo, int precision)
: PatternConverter(formattingInfo), precision(precision)
{
}
void PatternParser::CategoryPatternConverter::convert(tostream& sbuf, const spi::LoggingEvent& event)
{
const tstring& n = event.getLoggerName();
if(precision <= 0)
{
sbuf << n;
}
else
{
tstring::size_type len = n.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 category name ends with a dot.
tstring::size_type end = len -1 ;
for(int i = precision; i > 0; i--)
{
end = n.rfind(_T('.'), end-1);
if(end == tstring::npos)
{
sbuf << n;
return;
}
}
sbuf << n.substr(end+1, len - (end+1));
}
}