/*
* PatternLayout.cpp
*
* Copyright 2002, Bastiaan Bakker. All rights reserved.
*
* See the COPYING file for the terms of usage and distribution.
*/
#include "PortabilityImpl.hh"
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/NDC.hh>
#include <log4cpp/TimeStamp.hh>
#include <log4cpp/FactoryParams.hh>
#include <memory>
#ifdef LOG4CPP_HAVE_SSTREAM
#include <sstream>
#else
#include <strstream>
#endif
#include <iomanip>
#include <ctime>
#include <cstdlib>
#include "Localtime.hh"
#ifdef LOG4CPP_HAVE_INT64_T
#ifdef LOG4CPP_HAVE_STDINT_H
#include <stdint.h>
#endif // LOG4CPP_HAVE_STDINT_H
#ifdef LOG4CPP_MISSING_INT64_OSTREAM_OP
/* workaround suggested at:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q168440
*/
#include <stdio.h>
std::ostream& operator<<(std::ostream& os, int64_t i) {
char buf[20];
::sprintf(buf,"%I64d", i);
return os << buf;
}
#endif // LOG4CPP_MISSING_INT64_OSTREAM_OP
#endif // LOG4CPP_HAVE_INT64_T
namespace log4cpp {
struct StringLiteralComponent : public PatternLayout::PatternComponent {
StringLiteralComponent(const std::string& literal) :
_literal(literal) {
}
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << _literal;
}
private:
std::string _literal;
};
struct CategoryNameComponent : public PatternLayout::PatternComponent {
CategoryNameComponent(std::string specifier) {
if (specifier == "") {
_precision = -1;
} else {
#ifdef LOG4CPP_HAVE_SSTREAM
std::istringstream s(specifier);
#else
std::istrstream s(specifier.c_str());
#endif
s >> _precision;
}
}
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
if (_precision == -1) {
out << event.categoryName;
} else {
std::string::size_type begin = std::string::npos;
for(int i = 0; i < _precision; i++) {
begin = event.categoryName.rfind('.', begin - 2);
if (begin == std::string::npos) {
begin = 0;
break;
}
begin++;
}
if (begin == std::string::npos) {
begin = 0;
}
out << event.categoryName.substr(begin);
}
}
private:
int _precision;
};
struct MessageComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << event.message;
}
};
struct NDCComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << event.ndc;
}
};
struct PriorityComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << Priority::getPriorityName(event.priority);
}
};
struct ThreadNameComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << event.threadName;
}
};
struct ProcessorTimeComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << std::clock();
}
};
struct TimeStampComponent : public PatternLayout::PatternComponent {
static const char* const FORMAT_ISO8601;
static const char* const FORMAT_ABSOLUTE;
static const char* const FORMAT_DATE;
TimeStampComponent(std::string timeFormat) {
if ((timeFormat == "") || (timeFormat == "ISO8601")) {
timeFormat = FORMAT_ISO8601;
} else if (timeFormat == "ABSOLUTE") {
timeFormat = FORMAT_ABSOLUTE;
} else if (timeFormat == "DATE") {
timeFormat = FORMAT_DATE;
}
std::string::size_type pos = timeFormat.find("%l");
if (pos == std::string::npos) {
_printMillis = false;
_timeFormat1 = timeFormat;
} else {
_printMillis = true;
_timeFormat1 = timeFormat.substr(0, pos);
_timeFormat2 = timeFormat.substr(pos + 2);
}
}
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
struct std::tm currentTime;
std::time_t t = event.timeStamp.getSeconds();
localtime(&t, ¤tTime);
char formatted[100];
std::string timeFormat;
if (_printMillis) {
std::ostringstream formatStream;
formatStream << _timeFormat1
<< std::setw(3) << std::setfill('0')
<< event.timeStamp.getMilliSeconds()
<< _timeFormat2;
timeFormat = formatStream.str();
} else {
timeFormat = _timeFormat1;
}
std::strftime(formatted, sizeof(formatted), timeFormat.c_str(), ¤tTime);
out << formatted;
}
private:
std::string _timeFormat1;
std::string _timeFormat2;
bool _printMillis;
};
const char* const TimeStampComponent::FORMAT_ISO8601 = "%Y-%m-%d %H:%M:%S,%l";
const char* const TimeStampComponent::FORMAT_ABSOLUTE = "%H:%M:%S,%l";
const char* const TimeStampComponent::FORMAT_DATE = "%d %b %Y %H:%M:%S,%l";
struct SecondsSinceEpochComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
out << event.timeStamp.getSeconds();
}
};
struct MillisSinceEpochComponent : public PatternLayout::PatternComponent {
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
#ifdef LOG4CPP_HAVE_INT64_T
int64_t t = event.timeStamp.getSeconds() -
TimeStamp::getStartTime().getSeconds();
t *= 1000;
t += event.timeStamp.getMilliSeconds() -
TimeStamp::getStartTime().getMilliSeconds();
out << t;
#else
double t = event.timeStamp.getSeconds() -
TimeStamp::getStartTime().getSeconds();
t *= 1000;
t += event.timeStamp.getMilliSeconds() -
TimeStamp::getStartTime().getMilliSeconds();
out << std::setiosflags(std::ios::fixed)
<< std::setprecision(0) << t;
#endif
}
};
struct FormatModifierComponent : public PatternLayout::PatternComponent {
FormatModifierComponent(PatternLayout::PatternComponent* component,
size_t minWidth, size_t maxWidth, bool alignLeft) :
_component(component) ,
_minWidth(minWidth),
_maxWidth(maxWidth),
_alignLeft(alignLeft) {
}
virtual ~FormatModifierComponent() {
delete _component;
}
virtual void append(std::ostringstream& out, const LoggingEvent& event) {
std::ostringstream s;
_component->append(s, event);
std::string msg = s.str();
if (_maxWidth > 0 && _maxWidth < msg.length()) {
msg.erase(_maxWidth);
}
size_t fillCount = _minWidth - msg.length();
if (_minWidth > msg.length()) {
if (_alignLeft) {
out << msg << std::string(fillCount, ' ');
} else {
out << std::string(fillCount, ' ') << msg;
}
} else {
out << msg;
}
}
private:
PatternLayout::PatternComponent* _component;
size_t _minWidth;
size_t _maxWidth;
bool _alignLeft;
};
const char* PatternLayout::DEFAULT_CONVERSION_PATTERN = "%m%n";
const char* PatternLayout::SIMPLE_CONVERSION_PATTERN = "%p - %m%n";
const char* PatternLayout::BASIC_CONVERSION_PATTERN = "%R %p %c %x: %m%n";
const char* PatternLayout::TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
PatternLayout::PatternLayout() {
try {
setConversionPattern(DEFAULT_CONVERSION_PATTERN);
} catch(ConfigureFailure&) {
}
}
PatternLayout::~PatternLayout() {
clearConversionPattern();
}
void PatternLayout::clearConversionPattern() {
for(ComponentVector::const_iterator i = _components.begin();
i != _components.end(); ++i) {
delete (*i);
}
_components.clear();
_conversionPattern = "";
}
void PatternLayout::setConversionPattern(const std::string& conversionPattern) {
#ifdef LOG4CPP_HAVE_SSTREAM
std::istringstream conversionStream(conversionPattern);
#else
std::istrstream conversionStream(conversionPattern.c_str());
#endif
std::string literal;
char ch;
PatternLayout::PatternComponent* component = NULL;
int minWidth = 0;
size_t maxWidth = 0;
clearConversionPattern();
while (conversionStream.get(ch)) {
if (ch == '%') {
// readPrefix;
{
char ch2;
conversionStream.get(ch2);
if ((ch2 == '-') || ((ch2 >= '0') && (ch2 <= '9'))) {
conversionStream.putback(ch2);
conversionStream >> minWidth;
conversionStream.get(ch2);
}
if (ch2 == '.') {
conversionStream >> maxWidth;
} else {
conversionStream.putback(ch2);
}
}
if (!conversionStream.get(ch)) {
std::ostringstream msg;
msg << "unterminated conversion specifier in '" << conversionPattern << "' at index " << conversionStream.tellg();
throw ConfigureFailure(msg.str());
}
std::string specPostfix = "";
// read postfix
{
char ch2;
if (conversionStream.get(ch2)) {
if (ch2 == '{') {
while(conversionStream.get(ch2) && (ch2 != '}'))
specPostfix += ch2;
} else {
conversionStream.putback(ch2);
}
}
}
switch (ch) {
case '%':
literal += ch;
break;
case 'm':
component = new MessageComponent();
break;
case 'n':
{
std::ostringstream endline;
endline << std::endl;
literal += endline.str();
}
break;
case 'c':
component = new CategoryNameComponent(specPostfix);
break;
case 'd':
component = new TimeStampComponent(specPostfix);
break;
case 'p':
component = new PriorityComponent();
break;
case 'r':
component = new MillisSinceEpochComponent();
break;
case 'R':
component = new SecondsSinceEpochComponent();
break;
case 't':
component = new ThreadNameComponent();
break;
case 'u':
component = new ProcessorTimeComponent();
break;
case 'x':
component = new NDCComponent();
break;
default:
std::ostringstream msg;
msg << "unknown conversion specifier '" << ch << "' in '" << conversionPattern << "' at index " << conversionStream.tellg();
throw ConfigureFailure(msg.str());
}
if (component) {
if (!literal.empty()) {
_components.push_back(new StringLiteralComponent(literal));
literal = "";
}
if ((minWidth != 0) || (maxWidth != 0)) {
component = new FormatModifierComponent(component, std::abs(minWidth), maxWidth, minWidth < 0);
minWidth = maxWidth = 0;
}
_components.push_back(component);
component = NULL;
}
} else {
literal += ch;
}
}
if (!literal.empty()) {
_components.push_back(new StringLiteralComponent(literal));
}
_conversionPattern = conversionPattern;
}
std::string PatternLayout::getConversionPattern() const {
return _conversionPattern;
}
std::string PatternLayout::format(const LoggingEvent& event) {
std::ostringstream message;
for(ComponentVector::const_iterator i = _components.begin();
i != _components.end(); ++i) {
(*i)->append(message, event);
}
return message.str();
}
std::LOG4CPP_UNIQUE_PTR<Layout> create_pattern_layout(const FactoryParams& params)
{
std::string pattern;
params.get_for("pattern layout").optional("pattern", pattern);
std::LOG4CPP_UNIQUE_PTR<Layout> result(new PatternLayout);
PatternLayout* l = static_cast<PatternLayout*>(result.get());
if (pattern.empty() || pattern == "default")
return result;
if (pattern == "simple")
{
l->setConversionPattern(PatternLayout::SIMPLE_CONVERSION_PATTERN);
return result;
}
if (pattern == "basic")
{
l->setConversionPattern(PatternLayout::BASIC_CONVERSION_PATTERN);
return result;
}
if (pattern == "ttcc")
{
l->setConversionPattern(PatternLayout::TTCC_CONVERSION_PATTERN);
return result;
}
l->setConversionPattern(pattern);
return result;
}
}