Ver código fonte

Merge all the layout things together

Sam Jaffe 6 anos atrás
pai
commit
787fa0c3d1

+ 19 - 0
include/logger/detail/appender.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include "logger/logger_fwd.h"
+
+namespace logging {
+  class appender {
+  public:
+    virtual ~appender() = default;
+    virtual void write(std::string const & msg) = 0;
+    virtual void flush() = 0;
+    
+    bool should_log(log_level ll) const {
+      return ll >= min_log_level;
+    }
+    
+  protected:
+    log_level min_log_level;
+  };
+}

+ 12 - 0
include/logger/detail/layout.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include "logger/logger_fwd.h"
+
+#include <string>
+
+namespace logging {
+  struct layout {
+    virtual ~layout() = default;
+    virtual std::string format(logpacket const & pkt) const = 0;
+  };
+}

+ 0 - 0
include/logger/detail/layout_impl.h


+ 18 - 13
include/logger/detail/logger_impl.h

@@ -1,19 +1,24 @@
 #pragma once
 
-#include "../logger_fwd.h"
+#include "logger/logger_fwd.h"
+
+#include <memory>
+#include <vector>
+#include <utility>
 
 namespace logging {
-  class logger_impl {
-  public:
-    virtual ~logger_impl() = default;
-    virtual void write(logpacket const & pkt) = 0;
-    virtual void flush() = 0;
-    
-    bool should_log(log_level ll) const {
-      return ll >= min_log_level;
-    }
-    
-  protected:
-    log_level min_log_level;
+  class appender;
+  class layout;
+
+  struct logger_impl {
+    using p_appender = std::shared_ptr<appender>;
+    using p_layout = std::shared_ptr<layout>;
+
+    bool should_log(log_level ll) const;
+    void write(logpacket const & pkt);
+    void flush();
+
+    std::vector<std::pair<p_appender, p_layout>> impls_;
+    log_level min_log_level_;
   };
 }

+ 6 - 3
include/logger/log_manager.h

@@ -15,9 +15,10 @@ namespace objects { namespace prototype {
 } }
 
 namespace logging {
+  class appender;
   class c_logger;
+  class layout;
   class logger;
-  class logger_impl;
   class properties;
   
   class manager {
@@ -38,6 +39,8 @@ namespace logging {
     std::unique_ptr<struct manager_impl> pimpl_;
   };
   
-  using impl_factory = objects::prototype::factory<
-      std::shared_ptr<logger_impl>, properties const &>;
+  using appenders = objects::prototype::factory<
+      std::shared_ptr<appender>, properties const &>;
+  using layouts = objects::prototype::factory<
+      std::shared_ptr<layout>, properties const &>;
 }

+ 12 - 8
logger.xcodeproj/project.pbxproj

@@ -8,11 +8,12 @@
 
 /* Begin PBXBuildFile section */
 		CD1CDE872252E5B900E5B6B2 /* properties.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE862252E5B900E5B6B2 /* properties.cxx */; };
-		CD1CDE892252E60900E5B6B2 /* file_logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE882252E60900E5B6B2 /* file_logger.cxx */; };
-		CD1CDE8B2252E61800E5B6B2 /* console_logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8A2252E61800E5B6B2 /* console_logger.cxx */; };
+		CD1CDE892252E60900E5B6B2 /* file_appender.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE882252E60900E5B6B2 /* file_appender.cxx */; };
+		CD1CDE8B2252E61800E5B6B2 /* console_appender.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8A2252E61800E5B6B2 /* console_appender.cxx */; };
 		CD1CDE8D22540D9B00E5B6B2 /* c_logger_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */; };
 		CD1CDE9022542CC500E5B6B2 /* log_manager_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */; };
 		CD1CDE9222543E7E00E5B6B2 /* test_properties.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */; };
+		CD1CDEAF22556B7E00E5B6B2 /* logger_impl.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDEAE22556B7E00E5B6B2 /* logger_impl.cxx */; };
 		CD29739B1D7B401F00E37217 /* logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD2973991D7B401F00E37217 /* logger.cxx */; };
 		CD3C80C01D6A2CA300ACC795 /* format.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80BE1D6A2CA300ACC795 /* format.cxx */; };
 		CD6F73EC225187BE0081ED74 /* logger in Headers */ = {isa = PBXBuildFile; fileRef = CD6F73EA225187A10081ED74 /* logger */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -66,12 +67,13 @@
 		0EB833481BBF45E600DDC844 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
 		0ECAC4AF1BC00AC500FDAE14 /* liblogging.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = liblogging.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD1CDE862252E5B900E5B6B2 /* properties.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = properties.cxx; sourceTree = "<group>"; };
-		CD1CDE882252E60900E5B6B2 /* file_logger.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = file_logger.cxx; sourceTree = "<group>"; };
-		CD1CDE8A2252E61800E5B6B2 /* console_logger.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = console_logger.cxx; sourceTree = "<group>"; };
+		CD1CDE882252E60900E5B6B2 /* file_appender.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = file_appender.cxx; sourceTree = "<group>"; };
+		CD1CDE8A2252E61800E5B6B2 /* console_appender.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = console_appender.cxx; sourceTree = "<group>"; };
 		CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = c_logger_test.cxx; sourceTree = "<group>"; };
 		CD1CDE8E22540DEA00E5B6B2 /* mock_logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mock_logger.h; sourceTree = "<group>"; };
 		CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log_manager_test.cxx; sourceTree = "<group>"; };
 		CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_properties.cxx; sourceTree = "<group>"; };
+		CD1CDEAE22556B7E00E5B6B2 /* logger_impl.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = logger_impl.cxx; sourceTree = "<group>"; };
 		CD2973991D7B401F00E37217 /* logger.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logger.cxx; sourceTree = "<group>"; };
 		CD3C80BE1D6A2CA300ACC795 /* format.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cxx; sourceTree = "<group>"; };
 		CD6F73EA225187A10081ED74 /* logger */ = {isa = PBXFileReference; lastKnownFileType = folder; name = logger; path = include/logger; sourceTree = "<group>"; };
@@ -133,8 +135,8 @@
 			isa = PBXGroup;
 			children = (
 				CD1CDE862252E5B900E5B6B2 /* properties.cxx */,
-				CD1CDE882252E60900E5B6B2 /* file_logger.cxx */,
-				CD1CDE8A2252E61800E5B6B2 /* console_logger.cxx */,
+				CD1CDE882252E60900E5B6B2 /* file_appender.cxx */,
+				CD1CDE8A2252E61800E5B6B2 /* console_appender.cxx */,
 			);
 			path = loggers;
 			sourceTree = "<group>";
@@ -144,6 +146,7 @@
 			children = (
 				CD1CDE812252E54100E5B6B2 /* loggers */,
 				CD2973991D7B401F00E37217 /* logger.cxx */,
+				CD1CDEAE22556B7E00E5B6B2 /* logger_impl.cxx */,
 				CD88E9642252D6C700927F40 /* common.h */,
 				CD88E9612252D67A00927F40 /* common.cxx */,
 				CD88E95D2252D3EF00927F40 /* c_logger.cxx */,
@@ -336,8 +339,9 @@
 				CD3C80C01D6A2CA300ACC795 /* format.cxx in Sources */,
 				CD88E95F2252D3EF00927F40 /* c_logger.cxx in Sources */,
 				CD1CDE872252E5B900E5B6B2 /* properties.cxx in Sources */,
-				CD1CDE8B2252E61800E5B6B2 /* console_logger.cxx in Sources */,
-				CD1CDE892252E60900E5B6B2 /* file_logger.cxx in Sources */,
+				CD1CDE8B2252E61800E5B6B2 /* console_appender.cxx in Sources */,
+				CD1CDEAF22556B7E00E5B6B2 /* logger_impl.cxx in Sources */,
+				CD1CDE892252E60900E5B6B2 /* file_appender.cxx in Sources */,
 				CD88E9632252D67A00927F40 /* common.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 55 - 34
src/log_manager.cxx

@@ -14,23 +14,70 @@
 #include "../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
 
 #include "logger/c_logger.h"
-#include "logger/detail/logger_impl.h"
 #include "logger/exception.h"
 #include "logger/logger.h"
 #include "logger/properties.h"
+#include "logger_impl.h"
 
-INSTANTIATE_PROTOTYPE_FACTORY_2(logging::impl_factory);
+INSTANTIATE_PROTOTYPE_FACTORY_2(logging::appenders);
+INSTANTIATE_PROTOTYPE_FACTORY_2(logging::layouts);
 
 using namespace logging;
 
 struct logging::manager_impl {
-  std::shared_ptr<logger_impl> get_logger(properties const & props);
+  using p_appender = std::shared_ptr<appender>;
+  using p_layout = std::shared_ptr<layout>;
+  using p_logger = std::shared_ptr<logger_impl>;
   
-  std::shared_ptr<logger_impl> default_logger;
-  std::unordered_map<std::string, std::shared_ptr<logger_impl>> appenders;
-  std::unordered_map<std::string, std::shared_ptr<logger_impl>> loggers;
+  void load_appender(std::string const & name, properties const & props);
+  void load_layout(std::string const & source, properties const & props);
+  p_logger get_logger(properties const & props);
+  
+  p_logger default_logger;
+  std::unordered_map<std::string, p_appender> appenders;
+  std::unordered_map<std::string, p_layout> layouts;
+  std::unordered_map<std::string, p_logger> loggers;
 };
 
+void manager_impl::load_appender(std::string const & name,
+                                 properties const & props) {
+  auto & factory = appenders::instance();
+  appenders.emplace(name, factory.get(name, props));
+}
+
+static bool is_layout(std::string const & str) {
+  return str.find("Layout") != std::string::npos;
+}
+
+void manager_impl::load_layout(std::string const & source,
+                               properties const & props) {
+  auto & factory = layouts::instance();
+  for (auto & layout : props.obj) {
+    if (is_layout(layout.first)) {
+      layouts.emplace(source, factory.get(layout.first, layout.second));
+      return;
+    }
+  }
+}
+
+std::shared_ptr<logger_impl>
+manager_impl::get_logger(properties const & props) {
+  auto out = std::make_shared<logger_impl>();
+  if (props.type == properties::OBJECT) {
+    // TODO include settings on the parent
+    std::string ref = props["ref"];
+    out->impls_.push_back({appenders.at(ref), layouts.at(ref)});
+  } else if (props.type == properties::ARRAY) {
+    for (auto & part : props.array) {
+      std::string ref = part["ref"];
+      out->impls_.push_back({appenders.at(ref), layouts.at(ref)});
+    }
+  } else {
+    throw invalid_property_type{"appenders cannot be a STRING"};
+  }
+  return out;
+}
+
 manager::manager() : pimpl_(new manager_impl) {}
 
 manager::~manager() {}
@@ -51,41 +98,15 @@ c_logger manager::c_get(std::string const & name) {
   return c_logger(name, pimpl_->loggers.at(name));
 }
 
-struct compound_logger : public logger_impl {
-  void write(logpacket const & pkt) override {
-    for (auto & log : values) { log->write(pkt); }
-  }
-  void flush() override {
-    for (auto & log : values) { log->flush(); }
-  }
-  std::vector<std::shared_ptr<logger_impl>> values;
-};
-
-std::shared_ptr<logger_impl>
-manager_impl::get_logger(properties const & props) {
-  if (props.type == properties::OBJECT) {
-    // TODO include settings on the parent
-    return appenders.at(props["ref"].value);
-  } else if (props.type == properties::ARRAY) {
-    auto out = std::make_shared<compound_logger>();
-    for (auto & part : props.array) {
-      out->values.push_back(get_logger(part));
-    }
-    return out;
-  } else {
-    throw invalid_property_type{"appenders cannot be a STRING"};
-  }
-}
-
 
 void manager::configure(properties const & props) {
-  auto & factory = impl_factory::instance();
   // TODO: Load logger_impl here
   auto & impls = props["configuration"]["appenders"];
   // TODO: Support multiple File loggers, etc.
   expects(impls.type == properties::OBJECT);
   for (auto & app : impls.obj) {
-    pimpl_->appenders.emplace(app.first, factory.get(app.first, app.second));
+    pimpl_->load_appender(app.first, app.second);
+    pimpl_->load_layout(app.first, app.second);
   }
 
   auto & configs = props["configuration"]["loggers"];

+ 29 - 0
src/logger_impl.cxx

@@ -0,0 +1,29 @@
+//
+//  logger_impl.cxx
+//  logging
+//
+//  Created by Sam Jaffe on 4/3/19.
+//
+
+#include "logger_impl.h"
+
+#include "logger/detail/appender.h"
+#include "logger/detail/layout.h"
+
+using namespace logging;
+
+bool logger_impl::should_log(log_level ll) const {
+  return ll >= min_log_level_;
+}
+
+void logger_impl::write(logpacket const & pkt) {
+  for (auto & pair : impls_) {
+    pair.first->write(pair.second->format(pkt));
+  }
+}
+
+void logger_impl::flush() {
+  for (auto & pair : impls_) {
+    pair.first->flush();
+  }
+}

+ 13 - 13
src/loggers/console_logger.cxx

@@ -10,7 +10,7 @@
 #include "../../../../paradigm/declarative/expect/include/expect/expect.hpp"
 #include "../../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
 
-#include "logger/detail/logger_impl.h"
+#include "logger/detail/appender.h"
 #include "logger/log_manager.h"
 #include "logger/properties.h"
 
@@ -25,40 +25,40 @@ static std::string validate_or_throw(logging::properties const& props) {
   return it->second.value;
 }
 
-class console_logger : public logger_impl {
+class console_appender : public appender {
 public:
-  static std::shared_ptr<logger_impl> create(properties const& props);
+  static std::shared_ptr<appender> create(properties const& props);
   
-  explicit console_logger(std::ostream& os);
+  explicit console_appender(std::ostream& os);
   
-  void write(logpacket const & pkt) override;
+  void write(std::string const & msg) override;
   
   void flush() override;
 private:
   std::ostream& out_;
 };
 
-std::shared_ptr<logger_impl> console_logger::create(properties const& props) {
+std::shared_ptr<appender> console_appender::create(properties const& props) {
   const std::string target = validate_or_throw(props);
   if (target == "SYSTEM_OUT") {
-    return std::make_shared<console_logger>(std::cout);
+    return std::make_shared<console_appender>(std::cout);
   } else if (target == "SYSTEM_ERR") {
-    return std::make_shared<console_logger>(std::cerr);
+    return std::make_shared<console_appender>(std::cerr);
   } else {
     throw std::logic_error{target + " is not a valid console"};
   }
 }
 
-console_logger::console_logger(std::ostream& os) : out_(os) {}
+console_appender::console_appender(std::ostream& os) : out_(os) {}
 
-void console_logger::write(logpacket const & pkt) {
-  out_ << pkt.message;
+void console_appender::write(std::string const & msg) {
+  out_ << msg;
 }
 
-void console_logger::flush() {
+void console_appender::flush() {
   out_.flush();
 }
 
 namespace {
-  bool _ = impl_factory::instance().bind("Console", console_logger::create);
+  bool _ = appenders::instance().bind("Console", console_appender::create);
 }

+ 9 - 9
src/loggers/file_logger.cxx

@@ -10,34 +10,34 @@
 #include "../../../../paradigm/declarative/expect/include/expect/expect.hpp"
 #include "../../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
 
-#include "logger/detail/logger_impl.h"
+#include "logger/detail/appender.h"
 #include "logger/log_manager.h"
 #include "logger/properties.h"
 
 using namespace logging;
 
-class file_logger : public logger_impl {
+class file_appender : public appender {
 public:
-  static std::shared_ptr<logger_impl> create(properties const&);
+  static std::shared_ptr<appender> create(properties const&);
   
-  explicit file_logger(const std::string& fname);
+  explicit file_appender(const std::string& fname);
   
   void flush() override;
 private:
   std::ofstream out_;
 };
 
-std::shared_ptr<logger_impl> file_logger::create(properties const&) {
-  return std::shared_ptr<logger_impl>(); // TODO
+std::shared_ptr<appender> file_appender::create(properties const&) {
+  return std::shared_ptr<appender>(); // TODO
 }
 
-file_logger::file_logger(const std::string& fname)
+file_appender::file_appender(const std::string& fname)
 : out_(fname) {}
 
-void file_logger::flush() {
+void file_appender::flush() {
   out_.flush();
 }
 
 namespace {
-  bool _ = impl_factory::instance().bind("File", file_logger::create);
+  bool _ = appenders::instance().bind("File", file_appender::create);
 }

+ 10 - 10
test/c_logger_test.cxx

@@ -21,12 +21,12 @@ struct t_logger : public c_logger {
 using CLoggerTest = LoggerTest;
 
 TEST_F(CLoggerTest, FlushesOnClose) {
-  EXPECT_CALL(*pimpl, flush()).Times(1);
+  EXPECT_CALL(*appender, flush()).Times(1);
   t_logger("", pimpl);
 }
 
 TEST_F(CLoggerTest, FlushesOnFlushCall) {
-  EXPECT_CALL(*pimpl, flush()).Times(2);
+  EXPECT_CALL(*appender, flush()).Times(2);
   t_logger("", pimpl).flush();
 }
 
@@ -34,7 +34,7 @@ TEST_F(CLoggerTest, LogsWithFmtCode) {
   using testing::_;
   using testing::Field;
   // TODO: Eq
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::message, "5"))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::message, "5"))).Times(1);
   t_logger("", pimpl).errorf("%d", 5);
 }
 
@@ -42,7 +42,7 @@ TEST_F(CLoggerTest, LogsWithFmtCode) {
 TEST_F(CLoggerTest, FmtLogHasNameInHeader) {
   using testing::_;
   using testing::Field;
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::logger, "TEST"))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::logger, "TEST"))).Times(1);
   t_logger("TEST", pimpl).errorf("%d", 5);
 }
 
@@ -50,27 +50,27 @@ TEST_F(CLoggerTest, FmtLogHasNameInHeader) {
 TEST_F(CLoggerTest, FmtLogHasLevelInHeader) {
   using testing::_;
   using testing::Field;
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::level, LERROR))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::level, LERROR))).Times(1);
   t_logger("TEST", pimpl).errorf("%d", 5);
 }
 
 TEST_F(CLoggerTest, LogsRawData) {
   using testing::_;
   using testing::Field;
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::message, "5"))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::message, "5"))).Times(1);
   t_logger("", pimpl).error("5");
 }
 
 TEST_F(CLoggerTest, DoesNotLogAboveLevel) {
   using testing::_;
-  pimpl->SetLogLevel(LFATAL);
-  EXPECT_CALL(*pimpl, write(_)).Times(0);
+  pimpl->min_log_level_ = LFATAL;
+  EXPECT_CALL(*appender, write(_)).Times(0);
   t_logger("", pimpl).errorf("%d", 5);
 }
 
 TEST_F(CLoggerTest, DoesNotRawLogAboveLevel) {
   using testing::_;
-  pimpl->SetLogLevel(LFATAL);
-  EXPECT_CALL(*pimpl, write(_)).Times(0);
+  pimpl->min_log_level_ = LFATAL;
+  EXPECT_CALL(*appender, write(_)).Times(0);
   t_logger("", pimpl).error("5");
 }

+ 20 - 12
test/log_manager_test.cxx

@@ -8,7 +8,6 @@
 #include "../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
 
 #include "logger/c_logger.h"
-#include "logger/detail/logger_impl.h"
 #include "logger/log_manager.h"
 #include "logger/properties.h"
 
@@ -21,20 +20,27 @@ public:
   void SetUp() override;
   void TearDown() override;
 protected:
-  std::shared_ptr<MockLoggerImpl> logger;
+  std::shared_ptr<MockAppender> appender;
+  std::shared_ptr<MockLayout> layout;
 private:
-  impl_factory::scoped_binding binding_;
+  appenders::scoped_binding abinding_;
+  layouts::scoped_binding lbinding_;
 };
 
 void LogManagerTest::SetUp() {
   auto GetMock = [this](properties const &) {
-    return logger = std::make_shared<MockLoggerImpl>();
+    return appender = std::make_shared<MockAppender>();
   };
-  binding_ = impl_factory::instance().bind_scoped("Mock", GetMock);
+  abinding_ = appenders::instance().bind_scoped("Mock", GetMock);
+  auto GetMockLayout = [this](properties const &) {
+    return layout = std::make_shared<MockLayout>();
+  };
+  lbinding_ = layouts::instance().bind_scoped("MockLayout", GetMockLayout);
 }
 
 void LogManagerTest::TearDown() {
-  binding_.reset();
+  abinding_.reset();
+  lbinding_.reset();
 }
 
 extern properties const MIN_PROPERTY_SCHEMA;
@@ -45,7 +51,7 @@ TEST_F(LogManagerTest, CanInjectMock) {
 
   using ::testing::NotNull;
 
-  EXPECT_THAT(logger, ::testing::NotNull());
+  EXPECT_THAT(appender, ::testing::NotNull());
 }
 
 TEST_F(LogManagerTest, CanFetchInjectedMock) {
@@ -55,8 +61,9 @@ TEST_F(LogManagerTest, CanFetchInjectedMock) {
   using ::testing::_;
   using ::testing::AnyNumber;
   using ::testing::Field;
-  EXPECT_CALL(*logger, flush()).Times(AnyNumber());
-  EXPECT_CALL(*logger, write(Field(&logpacket::message, "TEST MESSAGE")));
+  EXPECT_CALL(*appender, flush()).Times(AnyNumber());
+  EXPECT_CALL(*appender, write("TEST MESSAGE"));
+  EXPECT_CALL(*layout, format(_)).WillRepeatedly(ReturnMessage());
 
   c_logger l = mgr.c_get();
   l.error("TEST MESSAGE");
@@ -69,9 +76,10 @@ TEST_F(LogManagerTest, MultiplexMockLogsToMultipleImpls) {
   using ::testing::_;
   using ::testing::AnyNumber;
   using ::testing::Field;
-  EXPECT_CALL(*logger, flush()).Times(AnyNumber());
-  EXPECT_CALL(*logger, write(Field(&logpacket::message, "TEST MESSAGE"))).Times(2);
-  
+  EXPECT_CALL(*appender, flush()).Times(AnyNumber());
+  EXPECT_CALL(*appender, write("TEST MESSAGE")).Times(2);
+  EXPECT_CALL(*layout, format(_)).WillRepeatedly(ReturnMessage());
+
   c_logger l = mgr.c_get();
   l.error("TEST MESSAGE");
 }

+ 6 - 6
test/logger_test.cxx

@@ -19,30 +19,30 @@ struct t_logger : public logger {
 }
 
 TEST_F(LoggerTest, FlushesOnClose) {
-  EXPECT_CALL(*pimpl, flush()).Times(1);
+  EXPECT_CALL(*appender, flush()).Times(1);
   t_logger("", pimpl);
 }
 
 TEST_F(LoggerTest, FlushesOnFlushCall) {
-  EXPECT_CALL(*pimpl, flush()).Times(2);
+  EXPECT_CALL(*appender, flush()).Times(2);
   t_logger("", pimpl).flush();
 }
 
 TEST_F(LoggerTest, LogsWithBraceFmtCode) {
   using testing::Field;
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::message, "5"))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::message, "5"))).Times(1);
   t_logger("", pimpl).log(LERROR, "{}", 5);
 }
 
 TEST_F(LoggerTest, DoesNotLogAboveLevel) {
   using testing::_;
-  pimpl->SetLogLevel(LFATAL);
-  EXPECT_CALL(*pimpl, write(_)).Times(0);
+  pimpl->min_log_level_ = LFATAL;
+  EXPECT_CALL(*appender, write(_)).Times(0);
   t_logger("", pimpl).log(LERROR, "{}", 5);
 }
 
 TEST_F(LoggerTest, LogCurlyBraceLiteralByDoubling) {
   using testing::Field;
-  EXPECT_CALL(*pimpl, write(Field(&logpacket::message, "{}"))).Times(1);
+  EXPECT_CALL(*layout, format(Field(&logpacket::message, "{}"))).Times(1);
   t_logger("", pimpl).log(LERROR, "{{}}", 5);
 }

+ 28 - 6
test/mock_logger.h

@@ -10,6 +10,8 @@
 
 #include <gmock/gmock.h>
 
+#include "logger/detail/appender.h"
+#include "logger/detail/layout.h"
 #include "logger/detail/logger_impl.h"
 
 namespace logging {
@@ -29,24 +31,44 @@ namespace logging {
   }
 }
 
-struct MockLoggerImpl : public logging::logger_impl {
-  MockLoggerImpl() { SetLogLevel(logging::LTRACE); }
+struct MockAppender : public logging::appender {
+  MockAppender() { SetLogLevel(logging::LTRACE); }
   void SetLogLevel(logging::log_level ll) { min_log_level = ll; }
   MOCK_METHOD0(flush, void());
-  MOCK_METHOD1(write, void(logging::logpacket const &));
+  MOCK_METHOD1(write, void(std::string const &));
 };
 
+struct MockLayout : public logging::layout {
+  MOCK_CONST_METHOD1(format, std::string(logging::logpacket const &));
+};
+
+ACTION(ReturnMessage) {
+  return arg0.message;
+}
+
 struct LoggerTest : public testing::Test {
   void SetUp() override {
-    pimpl.reset(new MockLoggerImpl);
+    appender.reset(new MockAppender);
+    layout.reset(new MockLayout);
+    pimpl = std::make_shared<logging::logger_impl>();
+    pimpl->impls_.push_back({appender, layout});
+    
+    using testing::_;
     using testing::AnyNumber;
-    EXPECT_CALL(*pimpl, flush()).Times(AnyNumber());
+
+    EXPECT_CALL(*appender, write(_)).Times(AnyNumber());
+    EXPECT_CALL(*appender, flush()).Times(AnyNumber());
+    ON_CALL(*layout, format(_)).WillByDefault(ReturnMessage());
   }
   void TearDown() override {
     pimpl.reset();
+    layout.reset();
+    appender.reset();
   }
   
-  std::shared_ptr<MockLoggerImpl> pimpl;
+  std::shared_ptr<MockAppender> appender;
+  std::shared_ptr<MockLayout> layout;
+  std::shared_ptr<logging::logger_impl> pimpl;
 };
 
 #endif /* mock_logger_h */

+ 6 - 2
test/test_properties.cxx

@@ -27,7 +27,9 @@ extern properties const MULTIPLEX_PROPERTY_SCHEMA;
 properties const MIN_PROPERTY_SCHEMA{_property({
   {"configuration", _property({
     {"appenders", _property({
-      {"Mock", _property({})}
+      {"Mock", _property({
+        {"MockLayout", _value("")}
+      })}
     })},
     {"loggers", _property({
       {"root", _property({
@@ -42,7 +44,9 @@ properties const MIN_PROPERTY_SCHEMA{_property({
 properties const MULTIPLEX_PROPERTY_SCHEMA{_property({
   {"configuration", _property({
     {"appenders", _property({
-      {"Mock", _property({})}
+      {"Mock", _property({
+        {"MockLayout", _value("")}
+      })}
     })},
     {"loggers", _property({
       {"root", _property({