Browse Source

Clean up the Pattern Layout code.

Sam Jaffe 6 years ago
parent
commit
2671384a04

+ 9 - 4
src/loggers/pattern_layout.h

@@ -14,12 +14,20 @@
 namespace logging {
   struct logpacket;
   class format {
+  public:
+    struct generator_t {
+      virtual ~generator_t() = default;
+      virtual std::string str(logpacket const & lp) const = 0;
+      virtual void write(logpacket const & lp, std::ostream & os) const {
+        os << str(lp);
+      }
+    };
   public:
     enum class token_id {
       DATE, PRIORITY, CATEGORY, MESSAGE, STRING, NEWLINE
     };
     
-    using generator = std::function<void(logpacket const &, std::ostream &)>;
+    using generator = std::shared_ptr<generator_t>;
     
     static format parse_format_string(std::string const &);
     void process(logpacket const & pkt, std::ostream & os) const;
@@ -27,7 +35,4 @@ namespace logging {
   private:
     std::vector<generator> gen;
   };
-  
-  using string_generator = std::function<std::string(logpacket const &)>;
-  string_generator get_date_formatter(std::string fmt);
 }

+ 59 - 32
src/loggers/pattern_layout_date.cxx

@@ -14,28 +14,52 @@
 #include "logger/message.h"
 #include "pattern_layout.h"
 
+static std::string fmt_time(struct timeval round, char const * const fmt) {
+  struct tm time;
+  // Supports localtime when requested, but you can't really test that
+  if (strstr(fmt, "%z") || strstr(fmt, "%Z")) {
+    localtime_r(&round.tv_sec, &time);
+  } else {
+    gmtime_r(&round.tv_sec, &time);
+  }
+  char buf[64] = {'\0'};
+  std::strftime(buf, sizeof(buf), fmt, &time);
+  return buf;
+}
+
+static std::string fmt_time_with_milis(struct timeval round,
+                                       std::string const & fmt) {
+  char buf[64] = {'\0'};
+  snprintf(buf, sizeof(buf), fmt.c_str(), round.tv_usec/1000);
+  return fmt_time(round, buf);
+}
+
+namespace {
+  struct time_gen : public logging::format::generator_t {
+    enum resolution { seconds, milis };
+    time_gen(std::string const & pre, resolution res)
+    : predef_format(pre), res(res) {}
+    std::string str(logging::logpacket const & lp) const override {
+      switch (res) {
+        case seconds:
+          return fmt_time(lp.time, predef_format.c_str());
+        case milis:
+          return fmt_time_with_milis(lp.time, predef_format);
+      }
+    }
+    std::string predef_format;
+    resolution res;
+  };
+}
+
 namespace logging {
-  std::string fmt_time(struct timeval round, char const * const fmt) {
-    struct tm time;
-    // Supports localtime when requested, but you can't really test that
-    if (strstr(fmt, "%z") || strstr(fmt, "%Z")) {
-      localtime_r(&round.tv_sec, &time);
-    } else {
-      gmtime_r(&round.tv_sec, &time);
+  format::generator parse_date_format_string(char const * str) {
+    char const * const end = strchr(str, '}');
+    if (end == nullptr) {
+      std::string error_msg{"expected date-format specifier to terminate"};
+      throw format_parsing_exception{error_msg};
     }
-    char buf[64] = {'\0'};
-    std::strftime(buf, sizeof(buf), fmt, &time);
-    return buf;
-  }
-  
-  std::string fmt_time_with_milis(struct timeval round,
-                                  std::string const & fmt) {
-    char buf[64] = {'\0'};
-    snprintf(buf, sizeof(buf), fmt.c_str(), round.tv_usec/1000);
-    return fmt_time(round, buf);
-  }
-  
-  string_generator get_date_formatter(std::string fmt) {
+    std::string fmt(str, end);
     if (fmt.find("%_ms") != std::string::npos) {
       size_t pos = 0;
       while ((pos = fmt.find("%", pos)) != std::string::npos) {
@@ -43,22 +67,25 @@ namespace logging {
         pos += 2;
       }
       fmt.replace(fmt.find("%%_ms"), 5, "%.03d");
-      return [=](logpacket const & lp) {
-        return fmt_time_with_milis(lp.time, fmt);
-      };
+      return std::make_shared<time_gen>(fmt, time_gen::milis);
     } else {
-      return [=](logpacket const & lp) {
-        return fmt_time(lp.time, fmt.c_str());
-      };
+      return std::make_shared<time_gen>(fmt, time_gen::seconds);
     }
   }
   
-  string_generator parse_date_format_string(char const * str) {
-    char const * const end = strchr(str, '}');
-    if (end == nullptr) {
-      std::string error_msg{"expected date-format specifier to terminate"};
-      throw format_parsing_exception{error_msg};
+#define is( chr ) *next == chr
+#define is_string( str ) ! strncmp(next, str, strlen(str))
+  format::generator date_token(char const * next) {
+    std::string predef_format = "%%Y-%%m-%%d %%H:%%M:%%S,%.03d";
+    if (is_string("{ISO8601}")) {
+      predef_format = "%%Y-%%m-%%dT%%H:%%M:%%S.%.03dZ";
+    } else if (is_string("{ABSOLUTE}")) {
+      predef_format = "%%H:%%M:%%S,%.03d";
+    } else if (is_string("{DATE}")) {
+      predef_format = "%%d %%b %%Y %%H:%%M:%%S,%.03d";
+    } else if (is('{')) {
+      return parse_date_format_string(next+1);
     }
-    return get_date_formatter(std::string(str, end));
+    return std::make_shared<time_gen>(predef_format, time_gen::milis);
   }
 }

+ 71 - 48
src/loggers/pattern_layout_format.cxx

@@ -21,41 +21,74 @@
 #include "logger/message.h"
 #include "pattern_layout.h"
 
-namespace logging {
-  std::string fmt_time_with_milis(struct timeval, std::string const &);
-  string_generator parse_date_format_string(char const *);
+namespace logging { namespace {
+  struct log_handle_gen : public format::generator_t {
+    std::string str(logpacket const & lp) const override {
+      return lp.logger;
+    }
+    void write(logpacket const & lp, std::ostream & os) const override {
+      os << lp.logger;
+    }
+  };
+
+  struct level_gen : public format::generator_t {
+    std::string str(logpacket const & lp) const override {
+      return to_string(lp.level, true);
+    }
+  };
+
+  struct message_gen : public format::generator_t {
+    std::string str(logpacket const & lp) const override {
+      return lp.message.str();
+    }
+  };
   
+  struct literal_gen : public format::generator_t {
+    literal_gen(std::string const & value) : value(value) {}
+    std::string str(logpacket const & lp) const override {
+      return value;
+    }
+    void write(logpacket const & lp, std::ostream & os) const override {
+      os << value;
+    }
+    std::string value;
+  };
+  
+  struct bounds_gen : public format::generator_t {
+    bounds_gen(format::generator impl, bool is_left, int min, size_t max)
+    : impl(impl), is_left(is_left), min(min), max(max) {}
+    std::string str(logpacket const & lp) const override {
+      return impl->str(lp);
+    }
+    void write(logpacket const & lp, std::ostream & os) const override {
+      auto align = is_left ? std::left : std::right;
+      std::string str = impl->str(lp);
+      if (str.length() > max) {
+        str.erase(str.begin()+max, str.end());
+      }
+      os << align << std::setw(min) << str;
+    }
+    format::generator impl;
+    bool is_left;
+    int min;
+    size_t max;
+  };
+} }
+
+namespace logging {
 #define is( chr ) *next == chr
 #define is_string( str ) ! strncmp(next, str, strlen(str))
-  string_generator date_token(char const * next) {
-    std::string predef_format = "%%Y-%%m-%%d %%H:%%M:%%S,%.03d";
-    if (is_string("{ISO8601}")) {
-      predef_format = "%%Y-%%m-%%dT%%H:%%M:%%S.%.03dZ";
-    } else if (is_string("{ABSOLUTE}")) {
-      predef_format = "%%H:%%M:%%S,%.03d";
-    } else if (is_string("{DATE}")) {
-      predef_format = "%%d %%b %%Y %%H:%%M:%%S,%.03d";
-    } else if (is('{')) {
-      return parse_date_format_string(next+1);
-    }
-    return [=](logpacket const & lp ){
-      return fmt_time_with_milis(lp.time, predef_format);
-    };
-  }
+  format::generator date_token(char const * next);
 
-  string_generator string_token(std::string str) {
-    return [=](logpacket const &){ return str; };
-  }
-  
-  string_generator handle( char const * & next ) {
+  format::generator handle( char const * & next ) {
     if (is('c')) {
-      return [](logpacket const & lp){ return lp.logger; };
+      return std::make_shared<log_handle_gen>();
     } else if (is('p')) {
-      return [](logpacket const & lp){ return to_string(lp.level, true); };
+      return std::make_shared<level_gen>();
     } else if (is('m')) {
-      return [](logpacket const & lp){ return lp.message.str(); };
+      return std::make_shared<message_gen>();
     } else if (is('t')) {
-      return [](logpacket const & lp){ return "???"; };
+      return std::make_shared<literal_gen>("???");
     } else {
       std::string error_msg{"unknown format character: '"};
       throw unknown_format_specifier{error_msg + *next + "'"};
@@ -77,24 +110,12 @@ namespace logging {
   
   format::generator parse_with_bounds(char const * & next) {
     bool const is_left = *next == '-';
-    auto align = is_left ? std::left : std::right;
     if (is_left) ++next;
     
     auto bounds = get_bounds(next);
-    string_generator gen = handle(next);
     
-    return [=](logpacket const & lp, std::ostream & out) {
-      std::string str = gen(lp);
-      if (str.length() > bounds.second)
-        str.erase(str.begin()+bounds.second, str.end());
-      out << align << std::setw(bounds.first) << str;
-    };
-  }
-  
-  format::generator convert( string_generator gen ) {
-    return [=](logpacket const & lp, std::ostream & out) {
-      out << gen( lp );
-    };
+    bounds_gen gen{handle(next), is_left, bounds.first, bounds.second};
+    return std::make_shared<bounds_gen>(gen);
   }
   
   format format::parse_format_string( std::string const & fmt ) {
@@ -111,33 +132,35 @@ namespace logging {
       }
       
       if (curr < next-1) {
-        out.gen.push_back(convert(string_token({curr, next - 1})));
+        std::string lit{curr, next - 1};
+        out.gen.push_back(std::make_shared<literal_gen>(lit));
       }
 
       if (is('d')) {
         ++next;
-        out.gen.push_back(convert(date_token(next)));
+        out.gen.push_back(date_token(next));
         if (is('{')) next = std::strchr(next, '}');
       } else if (is('n')) {
-        out.gen.push_back(convert(string_token(NEWLINE_TOKEN)));
+        out.gen.push_back(std::make_shared<literal_gen>(NEWLINE_TOKEN));
       } else if (is('%')) {
-        out.gen.push_back(convert(string_token("%")));
+        out.gen.push_back(std::make_shared<literal_gen>("%"));
       } else if (is('.') || is('-') || isdigit( *next )) {
         out.gen.push_back(parse_with_bounds(next));
       } else {
-        out.gen.push_back(convert(handle(next)));
+        out.gen.push_back(handle(next));
       }
       curr = ++next;
     }
     if (curr < end) {
-      out.gen.push_back(convert(string_token({curr, end})));
+      std::string lit{curr, end};
+      out.gen.push_back(std::make_shared<literal_gen>(lit));
     }
     return out;
   }
 #undef is
   
   void format::process(logpacket const & pkt, std::ostream & os) const {
-    for (auto func : gen) { func(pkt, os); }
+    for (auto func : gen) { func->write(pkt, os); }
   }
   
   std::string format::process(logpacket const & pkt) const {