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