00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00037 #include "blocxx/BLOCXX_config.h"
00038 #include "blocxx/LogMessagePatternFormatter.hpp"
00039 #include "blocxx/String.hpp"
00040 #include "blocxx/LogMessage.hpp"
00041 #include "blocxx/StringBuffer.hpp"
00042 #include "blocxx/IntrusiveCountableBase.hpp"
00043 #include "blocxx/Format.hpp"
00044 #include "blocxx/ExceptionIds.hpp"
00045 #include "blocxx/DateTime.hpp"
00046 #include "blocxx/ThreadImpl.hpp"
00047
00048 #include <vector>
00049 #include <cstdlib>
00050
00051 extern "C"
00052 {
00053 #include <errno.h>
00054 }
00055
00056 namespace BLOCXX_NAMESPACE
00057 {
00058
00059 BLOCXX_DEFINE_EXCEPTION_WITH_ID(LogMessagePatternFormatter);
00060
00061 namespace
00062 {
00063
00064 enum EJustificationFlag
00065 {
00066 E_RIGHT_JUSTIFY,
00067 E_LEFT_JUSTIFY
00068 };
00069
00070 struct Formatting
00071 {
00072 int minWidth;
00073 int maxWidth;
00074 EJustificationFlag justification;
00075
00076 static const int NO_MIN_WIDTH = -1;
00077 static const int NO_MAX_WIDTH = 0x7FFFFFFF;
00078
00079 Formatting()
00080 : minWidth(NO_MIN_WIDTH)
00081 , maxWidth(NO_MAX_WIDTH)
00082 , justification(E_RIGHT_JUSTIFY)
00083 {}
00084 };
00085
00086 }
00087
00089 class LogMessagePatternFormatter::Converter : public IntrusiveCountableBase
00090 {
00091 public:
00092 Converter()
00093 {}
00094
00095 Converter(const Formatting& formatting)
00096 : m_formatting(formatting)
00097 {}
00098
00099 virtual ~Converter() {}
00100
00101 virtual void formatMessage(const LogMessage& message, StringBuffer& output) const
00102 {
00103 if ((m_formatting.minWidth == Formatting::NO_MIN_WIDTH) && (m_formatting.maxWidth == Formatting::NO_MAX_WIDTH))
00104 {
00105 convert(message, output);
00106 }
00107 else
00108 {
00109 StringBuffer buf;
00110 convert(message, buf);
00111
00112 if (buf.length() == 0)
00113 {
00114 if (m_formatting.minWidth > 0)
00115 {
00116 output.append(&(std::vector<char>(size_t(m_formatting.minWidth), ' ')[0]), m_formatting.minWidth);
00117 }
00118 return;
00119 }
00120
00121 int len = buf.length();
00122 if (len > m_formatting.maxWidth)
00123 {
00124 if (m_formatting.justification == E_LEFT_JUSTIFY)
00125 {
00126 buf.truncate(m_formatting.maxWidth);
00127 output += buf;
00128 }
00129 else
00130 {
00131 output += buf.releaseString().substring(len - m_formatting.maxWidth);
00132 }
00133 }
00134 else if (len < m_formatting.minWidth)
00135 {
00136 if (m_formatting.justification == E_LEFT_JUSTIFY)
00137 {
00138 output += buf;
00139 output.append(&(std::vector<char>(size_t(m_formatting.minWidth - len), ' ')[0]), m_formatting.minWidth - len);
00140 }
00141 else
00142 {
00143 output.append(&(std::vector<char>(size_t(m_formatting.minWidth - len), ' ')[0]), m_formatting.minWidth - len);
00144 output += buf;
00145 }
00146 }
00147 else
00148 {
00149 output += buf;
00150 }
00151 }
00152 }
00153
00154 virtual void convert(const LogMessage& message, StringBuffer& output) const = 0;
00155
00156 private:
00157 Formatting m_formatting;
00158
00159 };
00160
00162 const String LogMessagePatternFormatter::STR_DEFAULT_MESSAGE_PATTERN("%r [%t] %p %c - %m");
00163
00165 LogMessagePatternFormatter::~LogMessagePatternFormatter()
00166 {
00167 }
00168
00170 void
00171 LogMessagePatternFormatter::formatMessage(const LogMessage& message, StringBuffer& output) const
00172 {
00173 typedef Array<ConverterRef>::const_iterator iter_t;
00174 iter_t end(m_patternConverters.end());
00175 for (iter_t i(m_patternConverters.begin()); i != end; ++i)
00176 {
00177 (*i)->formatMessage(message, output);
00178 }
00179 }
00180
00182 namespace
00183 {
00184
00185 typedef LogMessagePatternFormatter::Converter Converter;
00186 typedef LogMessagePatternFormatter::ConverterRef ConverterRef;
00187
00189 class MessageConverter : public Converter
00190 {
00191 public:
00192 MessageConverter(const Formatting& formatting)
00193 : Converter(formatting)
00194 {}
00195
00196 virtual void convert(const LogMessage &message, StringBuffer &output) const
00197 {
00198 output += message.message;
00199 }
00200 };
00201
00202 String CDATA_START("<![CDATA[");
00203 String CDATA_END("]]>");
00204 String CDATA_PSEUDO_END("]]>");
00205 String CDATA_EMBEDDED_END(CDATA_END + CDATA_PSEUDO_END + CDATA_START);
00206
00208 class XMLMessageConverter : public Converter
00209 {
00210 public:
00211 XMLMessageConverter(const Formatting& formatting)
00212 : Converter(formatting)
00213 {}
00214
00215 virtual void convert(const LogMessage &message, StringBuffer &output) const
00216 {
00217 output += CDATA_START;
00218 const String& msg(message.message);
00219 if (!msg.empty())
00220 {
00221 size_t end = msg.indexOf(CDATA_END);
00222 if (end == String::npos)
00223 {
00224 output += msg;
00225 }
00226
00227 size_t start(0);
00228 while (end != String::npos)
00229 {
00230 output.append(&msg[start], end - start);
00231 output += CDATA_EMBEDDED_END;
00232 start = end + CDATA_END.length();
00233 if (start < msg.length())
00234 {
00235 end = msg.indexOf(CDATA_END, start);
00236 }
00237 else
00238 {
00239 break;
00240 }
00241 }
00242 }
00243 output += CDATA_END;
00244 }
00245 };
00246
00248 class LiteralConverter : public Converter
00249 {
00250 public:
00251 LiteralConverter(const String& literal)
00252 : m_literal(literal)
00253 {}
00254
00255 virtual void convert(const LogMessage &message, StringBuffer &output) const
00256 {
00257 output += m_literal;
00258 }
00259
00260 private:
00261 String m_literal;
00262 };
00263
00265 class ThreadConverter : public Converter
00266 {
00267 public:
00268 ThreadConverter(const Formatting& formatting)
00269 : Converter(formatting)
00270 {}
00271
00272 virtual void convert(const LogMessage &message, StringBuffer &output) const
00273 {
00274 output += ThreadImpl::thread_t_ToUInt64(ThreadImpl::currentThread());
00275 }
00276 };
00277
00279 class ComponentConverter : public Converter
00280 {
00281 public:
00282 ComponentConverter(const Formatting& formatting, int precision)
00283 : Converter(formatting)
00284 , m_precision(precision)
00285 {}
00286
00287 virtual void convert(const LogMessage &message, StringBuffer &output) const
00288 {
00289 if (m_precision <= 0)
00290 {
00291 output += message.component;
00292 }
00293 else
00294 {
00295 const String& component(message.component);
00296 size_t len(component.length());
00297 size_t end(len - 1);
00298 for (int i = m_precision; i > 0; --i)
00299 {
00300 end = component.lastIndexOf('.', end - 1);
00301 if (end == String::npos)
00302 {
00303 output += component;
00304 return;
00305 }
00306 }
00307 output += component.substring(end + 1, len - (end + 1));
00308 }
00309 }
00310
00311 private:
00312 int m_precision;
00313 };
00314
00316 class FileLocationConverter : public Converter
00317 {
00318 public:
00319 FileLocationConverter(const Formatting& formatting)
00320 : Converter(formatting)
00321 {}
00322
00323 virtual void convert(const LogMessage &message, StringBuffer &output) const
00324 {
00325 if (message.filename != 0)
00326 {
00327 output += message.filename;
00328 }
00329 }
00330 };
00331
00333 class FullLocationConverter : public Converter
00334 {
00335 public:
00336 FullLocationConverter(const Formatting& formatting)
00337 : Converter(formatting)
00338 {}
00339
00340 virtual void convert(const LogMessage &message, StringBuffer &output) const
00341 {
00342 if (message.filename != 0)
00343 {
00344 output += message.filename;
00345 output += '(';
00346 output += message.fileline;
00347 output += ')';
00348 }
00349 }
00350 };
00351
00353 class LineLocationConverter : public Converter
00354 {
00355 public:
00356 LineLocationConverter(const Formatting& formatting)
00357 : Converter(formatting)
00358 {}
00359
00360 virtual void convert(const LogMessage &message, StringBuffer &output) const
00361 {
00362 output += message.fileline;
00363 }
00364 };
00365
00367 class MethodLocationConverter : public Converter
00368 {
00369 public:
00370 MethodLocationConverter(const Formatting& formatting)
00371 : Converter(formatting)
00372 {}
00373
00374 virtual void convert(const LogMessage &message, StringBuffer &output) const
00375 {
00376 if (message.methodname != 0)
00377 {
00378 output += message.methodname;
00379 }
00380 }
00381 };
00382
00384 class CategoryConverter : public Converter
00385 {
00386 public:
00387 CategoryConverter(const Formatting& formatting)
00388 : Converter(formatting)
00389 {}
00390
00391 virtual void convert(const LogMessage &message, StringBuffer &output) const
00392 {
00393 output += message.category;
00394 }
00395 };
00396
00398 class RelativeTimeConverter : public Converter
00399 {
00400 public:
00401 RelativeTimeConverter(const Formatting& formatting)
00402 : Converter(formatting)
00403 {}
00404
00405 virtual void convert(const LogMessage &message, StringBuffer &output) const
00406 {
00407 output += getRelativeTime();
00408 }
00409
00410 private:
00411 static UInt64 getRelativeTime()
00412 {
00413 return getNowMillis() - startMillis;
00414 }
00415
00416 static UInt64 startMillis;
00417 public:
00418 static UInt64 getNowMillis()
00419 {
00420 DateTime now;
00421 now.setToCurrent();
00422 return UInt64(now.get()) * 1000 + (now.getMicrosecond() / 1000);
00423 }
00424 };
00425
00426 UInt64 RelativeTimeConverter::startMillis(RelativeTimeConverter::getNowMillis());
00427
00429 enum EParserState
00430 {
00431 E_LITERAL_STATE,
00432 E_CONVERTER_STATE,
00433 E_DOT_STATE,
00434 E_MIN_STATE,
00435 E_MAX_STATE
00436 };
00437
00439 class DateConverter : public Converter
00440 {
00441 public:
00442 DateConverter(const Formatting& formatting, const String& format)
00443 : Converter(formatting)
00444 , m_format(format)
00445 {
00446 size_t pos = m_format.indexOf("%Q");
00447 if (pos != String::npos)
00448 {
00449
00450 m_format = m_format.substring(0, pos) + '%' + m_format.substring(pos);
00451 }
00452 }
00453
00454 virtual void convert(const LogMessage &message, StringBuffer &output) const
00455 {
00456 char buf[255];
00457
00458 DateTime now;
00459 now.setToCurrent();
00460 struct tm nowTm;
00461 now.toLocal(nowTm);
00462
00463 size_t len = ::strftime(buf, sizeof(buf), m_format.c_str(), &nowTm);
00464
00465 buf[len] = '\0';
00466
00467
00468 char* p = strstr(buf, "%Q");
00469 if (p != NULL)
00470 {
00471 *p = '\0';
00472 output += buf;
00473 long deciMillis = now.getMicrosecond() / 1000;
00474 String strMillis(deciMillis);
00475
00476 switch (strMillis.length())
00477 {
00478 case 1:
00479 output += '0';
00480 case 2:
00481 output += '0';
00482 }
00483 output += strMillis;
00484 output += p+2;
00485 }
00486 else
00487 {
00488 output += buf;
00489 }
00490 }
00491
00492 static const char* const ISO8601_DATE_FORMAT;
00493 static const char* const ISO8601_PATTERN;
00494 static const char* const ABSOLUTE_DATE_FORMAT;
00495 static const char* const ABSOLUTE_PATTERN;
00496 static const char* const DATE_DATE_FORMAT;
00497 static const char* const DATE_PATTERN;
00498
00499 private:
00500 String m_format;
00501 };
00502
00503 const char* const DateConverter::ISO8601_DATE_FORMAT = "ISO8601";
00504 const char* const DateConverter::ISO8601_PATTERN = "%Y-%m-%d %H:%M:%S,%Q";
00505 const char* const DateConverter::ABSOLUTE_DATE_FORMAT = "ABSOLUTE";
00506 const char* const DateConverter::ABSOLUTE_PATTERN = "%H:%M:%S,%Q";
00507 const char* const DateConverter::DATE_DATE_FORMAT = "DATE";
00508 const char* const DateConverter::DATE_PATTERN = "%d %b %Y %H:%M:%S,%Q";
00509
00511 class Parser
00512 {
00513 public:
00514 Parser(const String& pattern_)
00515 : i(0)
00516 , state(E_LITERAL_STATE)
00517 , pattern(pattern_)
00518 {}
00519
00521 void parse(Array<ConverterRef>& converters)
00522 {
00523 char c;
00524 size_t patternLength(pattern.length());
00525
00526 while (i < patternLength)
00527 {
00528 c = pattern[i];
00529 ++i;
00530 switch (state)
00531 {
00532 case E_LITERAL_STATE:
00533 {
00534 if (i == patternLength)
00535 {
00536 literal += c;
00537 continue;
00538 }
00539
00540 else if (c == '%')
00541 {
00542 switch (pattern[i])
00543 {
00544 case '%':
00545 literal += c;
00546 ++i;
00547 break;
00548 case 'n':
00549 literal += '\n';
00550 ++i;
00551 break;
00552 default:
00553 if (literal.length() > 0)
00554 {
00555 converters.push_back(ConverterRef(new LiteralConverter(literal.toString())));
00556 literal.reset();
00557 }
00558 literal += c;
00559 state = E_CONVERTER_STATE;
00560 formatting = Formatting();
00561 }
00562 }
00563
00564 else if (c == '\\')
00565 {
00566 switch (pattern[i])
00567 {
00568 case 'n':
00569 literal += '\n';
00570 ++i;
00571 break;
00572
00573 case '\\':
00574 literal += '\\';
00575 ++i;
00576 break;
00577
00578 case 'r':
00579 literal += '\r';
00580 ++i;
00581 break;
00582
00583 case 't':
00584 literal += '\t';
00585 ++i;
00586 break;
00587
00588 case 'x':
00589 {
00590 if (i + 1 > patternLength)
00591 {
00592 literal += "\\x";
00593 ++i;
00594 break;
00595 }
00596
00597 char* begin = &pattern[i+1];
00598 char* end(0);
00599 errno = 0;
00600 int hexNumber = std::strtol(begin, &end, 16);
00601 if (end == begin || errno == ERANGE || hexNumber > CHAR_MAX)
00602 {
00603 literal += "\\x";
00604 ++i;
00605 break;
00606 }
00607 literal += static_cast<char>(hexNumber);
00608 i += (end - begin) + 1;
00609 }
00610 break;
00611
00612 default:
00613 literal += '\\';
00614 break;
00615 }
00616 }
00617 else
00618 {
00619 literal += c;
00620 }
00621 }
00622 break;
00623
00624 case E_CONVERTER_STATE:
00625 {
00626 literal += c;
00627 switch (c)
00628 {
00629 case '-':
00630 formatting.justification = E_LEFT_JUSTIFY;
00631 break;
00632 case '.':
00633 state = E_DOT_STATE;
00634 break;
00635 default:
00636 if (isdigit(c))
00637 {
00638 formatting.minWidth = c - '0';
00639 state = E_MIN_STATE;
00640 }
00641 else
00642 {
00643 converters.push_back(finalizeConverter(c));
00644 }
00645 }
00646 }
00647 break;
00648 case E_MIN_STATE:
00649 {
00650 literal += c;
00651 if (isdigit(c))
00652 {
00653 formatting.minWidth = formatting.minWidth * 10 + (c - '0');
00654 }
00655 else if (c == '.')
00656 {
00657 state = E_DOT_STATE;
00658 }
00659 else
00660 {
00661 converters.push_back(finalizeConverter(c));
00662 }
00663 }
00664 break;
00665 case E_DOT_STATE:
00666 {
00667 literal += c;
00668 if (isdigit(c))
00669 {
00670 formatting.maxWidth = c - '0';
00671 state = E_MAX_STATE;
00672 }
00673 else
00674 {
00675 BLOCXX_THROW_ERR(LogMessagePatternFormatterException,
00676 Format("Invalid pattern \"%1\" in position %2. Was expecting a digit, instead got char %3.",
00677 pattern, i, c).c_str(),
00678 LogMessagePatternFormatter::E_INVALID_PATTERN_NO_DIGIT_AFTER_DOT);
00679 }
00680 }
00681 break;
00682 case E_MAX_STATE:
00683 {
00684 literal += c;
00685 if (isdigit(c))
00686 {
00687 formatting.maxWidth = formatting.maxWidth * 10 + (c - '0');
00688 }
00689 else
00690 {
00691 converters.push_back(finalizeConverter(c));
00692 state = E_LITERAL_STATE;
00693 }
00694 }
00695 break;
00696 }
00697 }
00698
00699
00700 if (literal.length() > 0)
00701 {
00702 converters.push_back(ConverterRef(new LiteralConverter(literal.toString())));
00703 }
00704 }
00705
00707 String getOption()
00708 {
00709
00710 if ((i < pattern.length()) && (pattern[i] == '{'))
00711 {
00712 size_t end = pattern.indexOf('}', i);
00713 if (end > i)
00714 {
00715 String rv = pattern.substring(i + 1, end - (i + 1));
00716 i = end + 1;
00717 return rv;
00718 }
00719 }
00720
00721 return String();
00722 }
00723
00725 int getPrecision()
00726 {
00727
00728 String opt = getOption();
00729 int rv = 0;
00730 if (!opt.empty())
00731 {
00732 try
00733 {
00734 rv = opt.toUInt32();
00735 }
00736 catch (StringConversionException& e)
00737 {
00738 BLOCXX_THROW_ERR(LogMessagePatternFormatterException,
00739 Format("Invalid pattern \"%1\" in position %2. A positive integer is required for precision option (%3).",
00740 pattern, i, opt).c_str(),
00741 LogMessagePatternFormatter::E_INVALID_PATTERN_PRECISION_NOT_AN_INTEGER);
00742 }
00743 }
00744 return rv;
00745 }
00746
00748 ConverterRef finalizeConverter(char c)
00749 {
00750
00751 ConverterRef rv;
00752 switch (c)
00753 {
00754 case 'c':
00755 {
00756 rv = new ComponentConverter(formatting, getPrecision());
00757 }
00758 break;
00759
00760 case 'd':
00761 {
00762 String dateFormat;
00763 String dateOpt = getOption();
00764 if (dateOpt.empty())
00765 {
00766 dateFormat = DateConverter::ISO8601_DATE_FORMAT;
00767 }
00768 else
00769 {
00770 dateFormat = dateOpt;
00771 }
00772
00773
00774 if (dateFormat.equalsIgnoreCase(DateConverter::ISO8601_DATE_FORMAT))
00775 {
00776 dateFormat = DateConverter::ISO8601_PATTERN;
00777 }
00778 else if (dateFormat.equalsIgnoreCase(DateConverter::ABSOLUTE_DATE_FORMAT))
00779 {
00780 dateFormat = DateConverter::ABSOLUTE_PATTERN;
00781 }
00782 else if (dateFormat.equalsIgnoreCase(DateConverter::DATE_DATE_FORMAT))
00783 {
00784 dateFormat = DateConverter::DATE_PATTERN;
00785 }
00786
00787 rv = new DateConverter(formatting, dateFormat);
00788 }
00789 break;
00790
00791 case 'F':
00792 {
00793 rv = new FileLocationConverter(formatting);
00794 }
00795 break;
00796
00797 case 'l':
00798 {
00799 rv = new FullLocationConverter(formatting);
00800 }
00801 break;
00802
00803 case 'L':
00804 {
00805 rv = new LineLocationConverter(formatting);
00806 }
00807 break;
00808
00809 case 'M':
00810 {
00811 rv = new MethodLocationConverter(formatting);
00812 }
00813 break;
00814
00815 case 'm':
00816 {
00817 rv = new MessageConverter(formatting);
00818 }
00819 break;
00820
00821 case 'e':
00822 {
00823 rv = new XMLMessageConverter(formatting);
00824 }
00825 break;
00826
00827 case 'p':
00828 {
00829 rv = new CategoryConverter(formatting);
00830 }
00831 break;
00832
00833 case 'r':
00834 {
00835 rv = new RelativeTimeConverter(formatting);
00836 }
00837 break;
00838
00839 case 't':
00840 {
00841 rv = new ThreadConverter(formatting);
00842 }
00843 break;
00844 #if 0 // don't support these for now.
00845 case 'x':
00846 {
00847
00848 }
00849 break;
00850
00851 case 'X':
00852 {
00853
00854 }
00855 break;
00856 #endif
00857 default:
00858 {
00859 BLOCXX_THROW_ERR(LogMessagePatternFormatterException,
00860 Format("Invalid pattern \"%1\" in position %2. Unsupported conversion (%3).",
00861 pattern, i, c).c_str(),
00862 LogMessagePatternFormatter::E_INVALID_PATTERN_UNSUPPORTED_CONVERSION);
00863
00864 }
00865 break;
00866 }
00867
00868 literal.reset();
00869 state = E_LITERAL_STATE;
00870 formatting = Formatting();
00871 return rv;
00872 }
00873
00874 private:
00875 size_t i;
00876 EParserState state;
00877 StringBuffer literal;
00878 Formatting formatting;
00879 String pattern;
00880 };
00881
00882
00883 }
00884
00886 LogMessagePatternFormatter::LogMessagePatternFormatter(const String& pattern)
00887 {
00888 Parser parser(pattern);
00889 parser.parse(m_patternConverters);
00890 }
00891
00892 }
00893
00894
00895
00896
00897