Przeglądaj źródła

Add class name and location to pattern_layout.
TODO: Test %C{1} (namespace limitation)
TODO: Test Free Function
TODO: Windows

Sam Jaffe 5 lat temu
rodzic
commit
64152cb581

+ 5 - 1
include/logger/level.h

@@ -9,7 +9,11 @@
 
 #include <string>
 
-#define log_here { __FILE__, __LINE__, __FUNCTION__ }
+#if defined (_MSC_VER)
+#define log_here { __FILE__, __LINE__, __FUNCTION__, __FUNCSIG__ }
+#else
+#define log_here { __FILE__, __LINE__, __FUNCTION__, __PRETTY_FUNCTION__ }
+#endif
 
 #define LIST_OF_LOGGING_LEVELS \
   X(trace) X(debug) X(info) X(warning) X(error) X(critical) X(fatal) X(none)

+ 1 - 0
include/logger/logpacket.h

@@ -9,6 +9,7 @@ namespace logging {
     char const * filename = "???";
     int line = 0;
     char const * function = "";
+    char const * pretty_function = "";
   };
   
   struct logpacket {

+ 51 - 5
src/loggers/pattern_layout_format.cxx

@@ -36,26 +36,70 @@ namespace logging { namespace {
       return to_string(lp.level, true);
     }
   };
-
+  
+  void erase_prefix(std::string & str, char const * prefix) {
+    size_t size = strlen(prefix);
+    if (str.find(prefix, 0, size) == 0) {
+      str.erase(0, size);
+    }
+  }
+  
+  void trim_upto_prefix_alpha(std::string & str, char const * prefix) {
+    size_t size = strlen(prefix);
+    for (size_t i = str.find(prefix); i != std::string::npos;
+         i = str.find(prefix, i + 1)) {
+      if (isalpha(str[i + size])) {
+        str.erase(0, i + size);
+        return;
+      }
+    }
+  }
+  
   struct location_info_gen : public format::generator_t {
-    enum { method, line, file } where;
+    enum { method, line, file, classname, location } where;
     constexpr static const decltype(where) format_codes[] = {
       ['F'] = file,
       ['M'] = method,
       ['L'] = line,
+      ['C'] = classname,
+      ['l'] = location
     };
 
     location_info_gen(char wh) : where(format_codes[wh]) {}
-    std::string str(logpacket const & lp) const override {
-      switch (where) {
+    static std::string str(logpacket const & lp, decltype(where) wh) {
+      switch (wh) {
         case file:
           return lp.info.filename;
         case line:
           return std::to_string(lp.info.line);
         case method:
           return lp.info.function;
+        case classname: {
+          std::string pf = lp.info.pretty_function;
+          std::string func = std::string(lp.info.function) + "(";
+          if (pf[pf.find(func)-1] == ' ') {
+            return "";
+          }
+          pf.erase(pf.find(func));
+          if (pf.rfind("::", pf.size()) != std::string::npos) {
+            pf.erase(pf.size() - 2);
+          }
+          // Erase virtual prefix
+          erase_prefix(pf, "virtual ");
+          trim_upto_prefix_alpha(pf, "> ");
+          trim_upto_prefix_alpha(pf, "&");
+          pf.erase(0, pf.find(" ") + 1);
+          return pf;
+        }
+        case location:
+          return str(lp, classname) + "::" + str(lp, method) + " (" +
+              str(lp, file) + ", " + str(lp, line) + ")";
       }
     }
+    
+    std::string str(logpacket const & lp) const override {
+      return str(lp, where);
+    }
   };
 
   struct message_gen : public format::generator_t {
@@ -110,7 +154,9 @@ namespace logging {
       return std::make_shared<message_gen>();
     } else if (is('t')) {
       return std::make_shared<literal_gen>("???");
-    } else if (is('F') || is('M') || is('L')) {
+//    } else if (is('r')) {
+//      // TODO: milliseconds from layout creation
+    } else if (is('F') || is('M') || is('L') || is('C') || is('l')) {
       return std::make_shared<location_info_gen>(*next);
     } else {
       std::string error_msg{"unknown format character: '"};

+ 11 - 0
test/pattern_layout_test.cxx

@@ -168,6 +168,17 @@ TEST(PatternLayoutTest, CanOutputCallingMethod) {
              testing::Eq("TestBody"));
 }
 
+TEST(PatternLayoutTest, CanOutputClassName) {
+  EXPECT_THAT(DoFormat("%C", getpkt("", log_here)),
+             testing::Eq("PatternLayoutTest_CanOutputClassName_Test"));
+}
+
+TEST(PatternLayoutTest, CanOutputLocation) {
+  auto message = DoFormat("%l", getpkt("", log_here));
+  std::string regex = "PatternLayoutTest_CanOutputLocation_Test::TestBody ..*pattern_layout_test.cxx, [1-9][0-9]*.";
+  EXPECT_THAT(message, testing::MatchesRegex(regex));
+}
+
 TEST(PatternLayoutTest, ThrowsOnUnknownToken) {
   using testing::Eq;
   EXPECT_THROW(GetPatternLayout("%q"),