Просмотр исходного кода

Add class info precision handling.

Sam Jaffe 5 лет назад
Родитель
Сommit
eeb0abcdbf
2 измененных файлов с 106 добавлено и 21 удалено
  1. 41 21
      src/loggers/pattern_layout_format.cxx
  2. 65 0
      test/pattern_layout_test.cxx

+ 41 - 21
src/loggers/pattern_layout_format.cxx

@@ -55,6 +55,40 @@ namespace logging { namespace {
     }
   }
   
+  struct class_info_gen : public format::generator_t {
+    size_t components;
+    class_info_gen(char const * token) : components(0) {
+      if (token && *token == '{') {
+        components = atol(token + 1);
+      }
+    }
+    std::string str(logpacket const & lp) const override {
+      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 ");
+      erase_prefix(pf, "static ");
+      trim_upto_prefix_alpha(pf, "> ");
+      trim_upto_prefix_alpha(pf, "&");
+      pf.erase(0, pf.find(" ") + 1);
+      for (size_t i = pf.rfind("::"), c = 1;
+           i != std::string::npos && c <= components;
+           i = pf.rfind("::", i-1), ++c) {
+        if (c == components) {
+          pf.erase(0, i + 2);
+        }
+      }
+      return pf;
+    }
+  };
+  
   struct location_info_gen : public format::generator_t {
     enum { method, line, file, classname, location } where;
     constexpr static const decltype(where) format_codes[] = {
@@ -74,23 +108,8 @@ namespace logging { namespace {
           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 classname:
+          return class_info_gen(0).str(lp);
         case location:
           return str(lp, classname) + "::" + str(lp, method) + " (" +
               str(lp, file) + ", " + str(lp, line) + ")";
@@ -154,10 +173,6 @@ namespace logging {
       return std::make_shared<message_gen>();
     } else if (is('t')) {
       return std::make_shared<literal_gen>("???");
-//    } 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: '"};
       throw unknown_format_specifier{error_msg + *next + "'"};
@@ -215,6 +230,11 @@ namespace logging {
         out.gen.push_back(std::make_shared<literal_gen>("%"));
       } else if (is('.') || is('-') || isdigit( *next )) {
         out.gen.push_back(parse_with_bounds(next));
+      } else if (is('C')) {
+        out.gen.push_back(std::make_shared<class_info_gen>(++next));
+        if (is('{')) next = std::strchr(next, '}');
+      } else if (is('F') || is('M') || is('L') || is('l')) {
+        out.gen.push_back(std::make_shared<location_info_gen>(*next));
       } else {
         out.gen.push_back(handle(next));
       }

+ 65 - 0
test/pattern_layout_test.cxx

@@ -168,11 +168,76 @@ TEST(PatternLayoutTest, CanOutputCallingMethod) {
              testing::Eq("TestBody"));
 }
 
+logging::logpacket packet() {
+  return getpkt("", log_here);
+}
+struct stub {
+  std::map<int, int> rval;
+  std::map<int, int> operator()(logging::logpacket & out) {
+    out = getpkt("", log_here);
+    return rval;
+  }
+  std::map<int, int> const & operator()(logging::logpacket & out) const {
+    out = getpkt("", log_here);
+    return rval;
+  }
+  logging::logpacket operator()() {
+    return getpkt("", log_here);
+  }
+  static logging::logpacket packet() {
+    return getpkt("", log_here);
+  }
+};
+namespace ns {
+  logging::logpacket packet() {
+    return getpkt("", log_here);
+  }
+  struct stub {
+    logging::logpacket operator()() {
+      return getpkt("", log_here);
+    }
+    static logging::logpacket packet() {
+      return getpkt("", log_here);
+    }
+  };
+}
+
 TEST(PatternLayoutTest, CanOutputClassName) {
   EXPECT_THAT(DoFormat("%C", getpkt("", log_here)),
              testing::Eq("PatternLayoutTest_CanOutputClassName_Test"));
 }
 
+TEST(PatternLayoutTest, GlobalNamespaceClassIsEmptyString) {
+  EXPECT_THAT(DoFormat("%C", packet()), testing::Eq(""));
+}
+
+TEST(PatternLayoutTest, NamespacesActLikeClassName) {
+  EXPECT_THAT(DoFormat("%C", ns::packet()), testing::Eq("ns"));
+}
+
+TEST(PatternLayoutTest, StaticMethodClassIsClass) {
+  EXPECT_THAT(DoFormat("%C", stub::packet()), testing::Eq("stub"));
+}
+
+TEST(PatternLayoutTest, NCMethodReturningComplexTypeReturnsCorrectClass) {
+  logging::logpacket pkt;
+  stub st{};
+  (void) st(pkt);
+  EXPECT_THAT(DoFormat("%C", pkt), testing::Eq("stub"));
+}
+
+TEST(PatternLayoutTest, ConstMethodReturningComplexTypeReturnsCorrectClass) {
+  logging::logpacket pkt;
+  stub const st{};
+  (void) st(pkt);
+  EXPECT_THAT(DoFormat("%C", pkt), testing::Eq("stub"));
+}
+
+TEST(PatternLayoutTest, CanLimitClassToNTokens) {
+  EXPECT_THAT(DoFormat("%C{1}", ns::stub{}()), testing::Eq("stub"));
+  EXPECT_THAT(DoFormat("%C{2}", ns::stub{}()), testing::Eq("ns::stub"));
+}
+
 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]*.";