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