Forráskód Böngészése

Don't use globals like a dumbass

Sam Jaffe 6 éve
szülő
commit
b1adb209ad

+ 39 - 0
include/logger/log_manager.h

@@ -0,0 +1,39 @@
+//
+//  log_manager.hpp
+//  logging
+//
+//  Created by Sam Jaffe on 4/1/19.
+//
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+namespace objects { namespace prototype {
+  template <typename, typename...> class factory;
+} }
+
+namespace logging {
+  class logger;
+  class logger_impl;
+  
+  class manager {
+  public:
+    static manager & instance();
+    
+    logger get();
+    logger get(std::string const & name);
+    
+    void configure();
+    
+  private:
+    manager();
+    
+  private:
+    std::unique_ptr<struct manager_impl> pimpl;
+  };
+  
+  using impl_factory = objects::prototype::factory<
+      std::shared_ptr<logger_impl>>;
+}

+ 16 - 16
include/logger/logger.h

@@ -26,23 +26,23 @@
 #include "logger_fwd.h"
 #include "format.h"
 
-#define log_message( logger, level, ... ) logger.log( level, { __FILE__, __LINE__, STRING( FUNCTION ) }, __VA_ARGS__ )
+#define log_here { __FILE__, __LINE__, STRING( FUNCTION ) }
+#define log_message( logger, level, ... )   \
+  logger.log( level, log_here, __VA_ARGS__ )
 
 namespace logging {
   class logger_impl;
-  typedef std::function<logger_impl&(void)> _binding;
-  bool bind_logger_impl(_binding impl);
+  class manager;
     
   const char* level_header(log_level);
   
   class logger {
   public:
-    static logger& instance();
-    static logger& instance(std::string const & key);
-  public:
+    ~logger();
+
     template <typename... Args>
-    inline void log(log_level ll,
-                    std::string const & interp, Args && ...args) {
+    inline void log(log_level ll, std::string const & interp,
+                    Args && ...args) {
       log( ll, location_info{}, interp, std::forward<Args>(args)... );
     }
     
@@ -56,18 +56,18 @@ namespace logging {
     
     void flush();
     
-    ~logger();
+  protected:
+    logger(std::string const & name, std::shared_ptr<logger_impl> impl);
+    
   private:
+    friend class manager;
     bool should_log( log_level ) const;
     void log( log_level ll, location_info info, std::string const& );
-    
-    explicit logger(std::string const & name = "");
-    logger(logger const&);
-    logger& operator=(logger const&);
-    
+
+  private:
     log_level min_level_;
     std::string const logger_name_;
-    logger_impl& impl_;
+    std::shared_ptr<logger_impl> impl_;
   };
   
   class logger_impl {
@@ -76,7 +76,7 @@ namespace logging {
     virtual void write( logpacket const & pkt ) = 0;
     virtual void flush() = 0;
     virtual ~logger_impl() = default;
-  private:
+  protected:
     log_level min_log_level;
   };
 }

+ 4 - 0
logger.xcodeproj/project.pbxproj

@@ -14,6 +14,7 @@
 		CD6F740C225187FD0081ED74 /* logger_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD6F73FC225187E10081ED74 /* logger_test.cxx */; };
 		CD6F742E225189290081ED74 /* libcfmt_logger.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6F742D225189290081ED74 /* libcfmt_logger.dylib */; };
 		CD6F746C22518A2C0081ED74 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6F746B22518A2C0081ED74 /* GoogleMock.framework */; };
+		CD88E9572252BDFC00927F40 /* log_manager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD88E9552252BDFC00927F40 /* log_manager.cxx */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -66,6 +67,7 @@
 		CD6F742D225189290081ED74 /* libcfmt_logger.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libcfmt_logger.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD6F742F225189470081ED74 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CD6F746B22518A2C0081ED74 /* GoogleMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleMock.framework; path = "../../../gmock-xcode-master/build/Release/GoogleMock.framework"; sourceTree = "<group>"; };
+		CD88E9552252BDFC00927F40 /* log_manager.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log_manager.cxx; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -115,6 +117,7 @@
 			isa = PBXGroup;
 			children = (
 				CD2973991D7B401F00E37217 /* logger.cxx */,
+				CD88E9552252BDFC00927F40 /* log_manager.cxx */,
 				CD3C80BE1D6A2CA300ACC795 /* format.cxx */,
 			);
 			path = src;
@@ -295,6 +298,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD29739B1D7B401F00E37217 /* logger.cxx in Sources */,
+				CD88E9572252BDFC00927F40 /* log_manager.cxx in Sources */,
 				CD3C80C01D6A2CA300ACC795 /* format.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 39 - 0
src/log_manager.cxx

@@ -0,0 +1,39 @@
+//
+//  log_manager.cxx
+//  logging
+//
+//  Created by Sam Jaffe on 4/1/19.
+//
+
+#include "logger/log_manager.h"
+
+#include <memory>
+#include <unordered_map>
+
+#include "logger/logger.h"
+#include "../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
+
+INSTANTIATE_PROTOTYPE_FACTORY_2(logging::impl_factory);
+
+using namespace logging;
+
+struct logging::manager_impl {
+  std::shared_ptr<logger_impl> default_logger;
+  std::unordered_map<std::string, std::shared_ptr<logger_impl>> loggers;
+};
+
+manager::manager() : pimpl(new manager_impl) {}
+
+logger manager::get() {
+  return logger("", pimpl->default_logger);
+}
+
+logger manager::get(std::string const & name) {
+  auto it = pimpl->loggers.find(name);
+  if (it == pimpl->loggers.end()) {
+    auto & factory = impl_factory::instance();
+    it = pimpl->loggers.emplace(name, factory.get(name)).first;
+  }
+  return logger(name, it->second);
+}
+

+ 9 - 54
src/logger.cxx

@@ -13,7 +13,6 @@
 #include <map>
 #include <stdexcept>
 
-#include "expect/expect.h"
 #include "logger/logger.h"
 //#include "properties.hpp"
 
@@ -48,75 +47,31 @@ namespace {
 }
 
 namespace logging {
-  class multiple_bindings : public std::logic_error {
-    using std::logic_error::logic_error;
-  };
-  
-  //  extern logger_impl& _logger_impl_shared_instance();
-  extern logger_impl& (*_default_logger_impl)(void);
-  static _binding _impl_binding = nullptr;
-  
-  bool bind_logger_impl(_binding impl) {
-    expects(_impl_binding == nullptr, multiple_bindings,
-            "Only one logger implementation may be bound");
-    _impl_binding = impl;
-    return true;
-  }
-  
-  logger_impl& _i_get_shared_instance() {
-    if (_impl_binding == nullptr) {
-      return _default_logger_impl();
-    } else {
-      return _impl_binding();
-    }
-  }
-}
-
-namespace logging {
-  logger& logger::instance() {
-    static logger instance;
-    return instance;
-  }
-  
-  logger& logger::instance(std::string const & name) {
-    using p_logger = std::unique_ptr<logger>;
-    using store = std::map<std::string, p_logger>;
-    static store instances;
-    store::iterator it;
-    if ((it = instances.find(name)) != instances.end()) {
-      return *(it->second);
-    }
-    return *(instances.emplace(std::make_pair(name, p_logger(new logger(name)))).first->second);
-  }
-  
-  
-  logger::logger(std::string const & name)
-  : min_level_(LDEBUG)
-  , logger_name_(name)
-  , impl_(_i_get_shared_instance())
+  logger::logger(std::string const & name, std::shared_ptr<logger_impl> impl)
+  : min_level_(LDEBUG), logger_name_(name), impl_(impl)
   {
   }
   
   logger::~logger() {
-    flush();
+    if (impl_) impl_->flush();
   }
   
-  void logger::log(log_level ll, location_info info, std::string const & msg) {
-    impl_.write({ now( ), ll, info, logger_name_.c_str(), msg });
+  void logger::log(log_level ll, location_info info,
+                   std::string const & msg) {
+    impl_->write({ now( ), ll, info, logger_name_.c_str(), msg });
   }
   
   bool logger::should_log( log_level ll ) const {
-    return ll < min_level_ && impl_.should_log( ll );
+    return ll >= min_level_ && impl_->should_log( ll );
   }
   
   void logger::flush() {
-    impl_.flush();
+    impl_->flush();
   }
 }
 
 namespace logging {
-  
   bool logger_impl::should_log( log_level ll ) const {
-    return ll < min_log_level;
+    return ll >= min_log_level;
   }
 }

+ 13 - 16
test/logger_test.cxx

@@ -7,46 +7,43 @@
 
 #include <gmock/gmock.h>
 
-#include "logger/logger.hpp"
+#include "logger/logger.h"
 
 struct MockLoggerImpl : public logging::logger_impl {
+  MockLoggerImpl() {
+    min_log_level = logging::LTRACE;
+  }
   MOCK_METHOD0(flush, void());
   MOCK_METHOD1(write, void(logging::logpacket const &));
 };
 
 struct LoggerTest : public testing::Test {
-  static void SetUpTestCase();
-  static void TearDownTestCase();
   void SetUp() override;
   void TearDown() override;
 
-  static std::unique_ptr<MockLoggerImpl> pimpl;
+  std::shared_ptr<MockLoggerImpl> pimpl;
 };
-std::unique_ptr<MockLoggerImpl> LoggerTest::pimpl{};
 
 using namespace logging;
-void LoggerTest::SetUpTestCase() {
-  _binding bind([]() -> logger_impl& {
-    return *pimpl;
-  });
-  bind_logger_impl(bind);
-}
+using namespace testing;
+
+struct t_logger : public logger {
+  t_logger(std::string const & name, std::shared_ptr<logger_impl> impl)
+  : logger(name, impl) {}
+};
 
 void LoggerTest::SetUp() {
   pimpl.reset(new MockLoggerImpl);
+  EXPECT_CALL(*pimpl, flush()).Times(AnyNumber());
 }
 
 void LoggerTest::TearDown() {
   pimpl.reset();
 }
 
-TEST_F(LoggerTest, ImplCannotBeMadeTwice) {
-  EXPECT_ANY_THROW(bind_logger_impl(nullptr));
-}
-
 TEST_F(LoggerTest, LogsWithBraceFmtCode) {
   using testing::Field;
   EXPECT_CALL(*pimpl, write(Field(&logpacket::message, "5"))).Times(1);
-  logger & LOG = logger::instance( );
+  logger LOG = t_logger("", pimpl);
   LOG.log(LERROR, "{}", 5);
 }