diff --git a/compendium/ConfigurationAdmin/src/CMLogger.cpp b/compendium/ConfigurationAdmin/src/CMLogger.cpp index 318df9934..8a31ad614 100644 --- a/compendium/ConfigurationAdmin/src/CMLogger.cpp +++ b/compendium/ConfigurationAdmin/src/CMLogger.cpp @@ -128,5 +128,27 @@ namespace cppmicroservices currLogger->Log(sr, level, message, ex); } } + + std::shared_ptr + CMLogger::getLogger(const std::string& name) const + { + auto currLogger = std::atomic_load(&logService); + if (currLogger) + { + return currLogger->getLogger(name); + } + return nullptr; + } + + std::shared_ptr + CMLogger::getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const + { + auto currLogger = std::atomic_load(&logService); + if (currLogger) + { + return currLogger->getLogger(bundle, name); + } + return nullptr; + } } // namespace cmimpl } // namespace cppmicroservices diff --git a/compendium/ConfigurationAdmin/src/CMLogger.hpp b/compendium/ConfigurationAdmin/src/CMLogger.hpp index b6c9615b0..68a5fff1c 100644 --- a/compendium/ConfigurationAdmin/src/CMLogger.hpp +++ b/compendium/ConfigurationAdmin/src/CMLogger.hpp @@ -61,6 +61,8 @@ namespace cppmicroservices logservice::SeverityLevel level, std::string const& message, const std::exception_ptr ex) override; + [[nodiscard]] std::shared_ptr getLogger(const std::string& name) const override; + [[nodiscard]] std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const override; // methods from the cppmicroservices::ServiceTrackerCustomizer interface std::shared_ptr AddingService( diff --git a/compendium/ConfigurationAdmin/test/Mocks.hpp b/compendium/ConfigurationAdmin/test/Mocks.hpp index 399af9246..031b2df2b 100644 --- a/compendium/ConfigurationAdmin/test/Mocks.hpp +++ b/compendium/ConfigurationAdmin/test/Mocks.hpp @@ -58,6 +58,10 @@ namespace cppmicroservices cppmicroservices::logservice::SeverityLevel, std::string const&, std::exception_ptr const)); + MOCK_CONST_METHOD1(getLogger, + std::shared_ptr(const std::string&)); + MOCK_CONST_METHOD2(getLogger, + std::shared_ptr(const cppmicroservices::Bundle&, const std::string&)); }; /** @@ -86,6 +90,16 @@ namespace cppmicroservices std::exception_ptr const) override { } + [[nodiscard]] std::shared_ptr + getLogger(const std::string&) const override + { + return nullptr; + } + [[nodiscard]] std::shared_ptr + getLogger(const cppmicroservices::Bundle&, const std::string&) const override + { + return nullptr; + } }; /** diff --git a/compendium/ConfigurationAdmin/test/TestCMLogger.cpp b/compendium/ConfigurationAdmin/test/TestCMLogger.cpp index ca1e894b3..9f51504a5 100644 --- a/compendium/ConfigurationAdmin/test/TestCMLogger.cpp +++ b/compendium/ConfigurationAdmin/test/TestCMLogger.cpp @@ -32,6 +32,7 @@ #include "../src/CMLogger.hpp" #include "Mocks.hpp" +#include using cppmicroservices::logservice::LogService; using cppmicroservices::logservice::SeverityLevel; @@ -103,6 +104,9 @@ namespace cppmicroservices EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_WARNING, testing::_)).Times(1); EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_ERROR, testing::_, testing::_)) .Times(1); + EXPECT_CALL(*(mockLogger.get()), getLogger("test")).Times(1); + const cppmicroservices::Bundle mockBundle; + EXPECT_CALL(*(mockLogger.get()), getLogger(mockBundle, "test")).Times(1); // exercise methods on instance of CMLogger CMLogger logger(bundleContext); logger.Log(SeverityLevel::LOG_DEBUG, "some sample debug message"); @@ -115,6 +119,8 @@ namespace cppmicroservices SeverityLevel::LOG_ERROR, "some sample error message with service reference", std::make_exception_ptr(std::runtime_error("error occured"))); + auto resultLogger = logger.getLogger("test"); + auto resultBundleLogger = logger.getLogger(mockBundle, "test"); reg.Unregister(); }); } diff --git a/compendium/DeclarativeServices/src/SCRLogger.cpp b/compendium/DeclarativeServices/src/SCRLogger.cpp index c6de6a3c4..eb3359d82 100644 --- a/compendium/DeclarativeServices/src/SCRLogger.cpp +++ b/compendium/DeclarativeServices/src/SCRLogger.cpp @@ -126,5 +126,28 @@ namespace cppmicroservices currLogger->Log(sr, level, message, ex); } } + + std::shared_ptr + SCRLogger::getLogger(const std::string& name) const + { + auto currLogger = std::atomic_load(&logService); + if (currLogger) + { + return currLogger->getLogger(name); + } + return nullptr; + } + + std::shared_ptr + SCRLogger::getLogger(const cppmicroservices::Bundle& bundle, const std::string& name) const + { + auto currLogger = std::atomic_load(&logService); + if (currLogger) + { + return currLogger->getLogger(bundle, name); + } + return nullptr; + } + } // namespace scrimpl } // namespace cppmicroservices diff --git a/compendium/DeclarativeServices/src/SCRLogger.hpp b/compendium/DeclarativeServices/src/SCRLogger.hpp index 4076d6c8e..78fe0fd74 100644 --- a/compendium/DeclarativeServices/src/SCRLogger.hpp +++ b/compendium/DeclarativeServices/src/SCRLogger.hpp @@ -59,6 +59,8 @@ namespace cppmicroservices logservice::SeverityLevel level, std::string const& message, const std::exception_ptr ex) override; + [[nodiscard]] std::shared_ptr getLogger(const std::string& name) const override; + [[nodiscard]] std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, const std::string& name) const override; // methods from the cppmicroservices::ServiceTrackerCustomizer interface std::shared_ptr AddingService( diff --git a/compendium/DeclarativeServices/test/gtest/Mocks.hpp b/compendium/DeclarativeServices/test/gtest/Mocks.hpp index eb0fea5e3..442d2a2fc 100644 --- a/compendium/DeclarativeServices/test/gtest/Mocks.hpp +++ b/compendium/DeclarativeServices/test/gtest/Mocks.hpp @@ -143,6 +143,11 @@ namespace cppmicroservices cppmicroservices::logservice::SeverityLevel, std::string const&, std::exception_ptr const)); + MOCK_CONST_METHOD1(getLogger, + std::shared_ptr(const std::string&)); + MOCK_CONST_METHOD2(getLogger, + std::shared_ptr(const cppmicroservices::Bundle&, const std::string&)); + }; #ifdef _MSC_VER @@ -175,6 +180,16 @@ namespace cppmicroservices std::exception_ptr const) override { } + [[nodiscard]] std::shared_ptr + getLogger(const std::string&) const override + { + return nullptr; + } + [[nodiscard]] std::shared_ptr + getLogger(const cppmicroservices::Bundle&, const std::string&) const override + { + return nullptr; + } }; class MockComponentManager : public ComponentManager diff --git a/compendium/DeclarativeServices/test/gtest/SCRLoggerTest.cpp b/compendium/DeclarativeServices/test/gtest/SCRLoggerTest.cpp index 0c3651910..0d6b07e2c 100644 --- a/compendium/DeclarativeServices/test/gtest/SCRLoggerTest.cpp +++ b/compendium/DeclarativeServices/test/gtest/SCRLoggerTest.cpp @@ -85,7 +85,7 @@ namespace cppmicroservices logger.Log(dummyRef, SeverityLevel::LOG_DEBUG, "sample log message", - std::make_exception_ptr(std::runtime_error("error occured"))); + std::make_exception_ptr(std::runtime_error("error occured"))); }); } @@ -102,6 +102,10 @@ namespace cppmicroservices EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_WARNING, testing::_)).Times(1); EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_ERROR, testing::_, testing::_)) .Times(1); + EXPECT_CALL(*(mockLogger.get()), getLogger("test")).Times(1); + const cppmicroservices::Bundle mockBundle; + EXPECT_CALL(*(mockLogger.get()), getLogger(mockBundle, "test")).Times(1); + // exercise methods on instance of SCRLogger cppmicroservices::scrimpl::SCRLogger logger(bundleContext); logger.Log(SeverityLevel::LOG_DEBUG, "some sample debug message"); @@ -114,6 +118,8 @@ namespace cppmicroservices SeverityLevel::LOG_ERROR, "some sample error message with service reference", std::make_exception_ptr(std::runtime_error("error occured"))); + auto resultLogger = logger.getLogger("test"); + auto resultBundleLogger = logger.getLogger(mockBundle, "test"); }); } diff --git a/compendium/LogService/CMakeLists.txt b/compendium/LogService/CMakeLists.txt index 35b705aea..e2bf862dc 100755 --- a/compendium/LogService/CMakeLists.txt +++ b/compendium/LogService/CMakeLists.txt @@ -1,5 +1,7 @@ set(_public_headers include/cppmicroservices/logservice/LogService.hpp + include/cppmicroservices/logservice/LoggerFactory.hpp + include/cppmicroservices/logservice/Logger.hpp ) set(_version "1.0.0") diff --git a/compendium/LogService/include/cppmicroservices/logservice/LogService.hpp b/compendium/LogService/include/cppmicroservices/logservice/LogService.hpp index b3315ff5c..4254ce2bd 100644 --- a/compendium/LogService/include/cppmicroservices/logservice/LogService.hpp +++ b/compendium/LogService/include/cppmicroservices/logservice/LogService.hpp @@ -23,6 +23,7 @@ #define CPPMICROSERVICES_LOG_SERVICE_H__ #include "cppmicroservices/ServiceReferenceBase.h" +#include "cppmicroservices/logservice/LoggerFactory.hpp" #include #include @@ -69,7 +70,7 @@ namespace cppmicroservices * * @remarks This class is thread safe. */ - class LogService + class LogService : public LoggerFactory { public: virtual ~LogService() = default; @@ -115,6 +116,7 @@ namespace cppmicroservices std::string const& message, const std::exception_ptr ex) = 0; + }; } // namespace logservice diff --git a/compendium/LogService/include/cppmicroservices/logservice/Logger.hpp b/compendium/LogService/include/cppmicroservices/logservice/Logger.hpp new file mode 100644 index 000000000..c1ec02330 --- /dev/null +++ b/compendium/LogService/include/cppmicroservices/logservice/Logger.hpp @@ -0,0 +1,320 @@ +#ifndef CPPMICROSERVICES_LOGGER_H_ +#define CPPMICROSERVICES_LOGGER_H_ + +#include "cppmicroservices/ServiceReferenceBase.h" + +#include +#include +#include +#include + +namespace cppmicroservices::logservice +{ + /** + \defgroup gr_logservice Logger + + \brief Groups Logger class related symbols. + */ + /** + * \addtogroup gr_logservice + * @{ + */ + enum class LogLevel + { + Audit, // This log level is used for information that must always be logged. + Error, // This log level is used for information about an error situation. + Warn, // This log level is used for information about a failure or unwanted situation that is not blocking. + Info, // This log level is used for information about normal operation. + Debug, // This log level is used for detailed output for debugging operations. + Trace // This log level is used for large volume of output for tracing operations. + }; + /** @}*/ + + /** + * \ingroup MicroService + * \ingroup gr_logservice + * + * Provides methods for bundles to write messages to the log. + * Logger interface defines several methods for each of the defined LogLevels. + * + * @remarks This class is thread-safe. + */ + + class Logger + { + public: + virtual ~Logger() = default; + + /** + * Logs a message at LogLevel.Audit level. + * @param message The message to log. + */ + virtual void audit(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Audit level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void audit(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Audit level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void audit(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Audit level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void audit(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Audit level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void audit(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Audit level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void audit(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param message The message to log. + */ + virtual void debug(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void debug(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void debug(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void debug(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void debug(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Debug level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void debug(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param message The message to log. + */ + virtual void error(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void error(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void error(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void error(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void error(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Error level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void error(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param message The message to log. + */ + virtual void info(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void info(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void info(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void info(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void info(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Info level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void info(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param message The message to log. + */ + virtual void trace(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void trace(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void trace(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void trace(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void trace(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Trace level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void trace(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param message The message to log. + */ + virtual void warn(std::string const& message) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param format The format of the message to log. + * @param arg The argument to format into the message. + */ + virtual void warn(std::string const& format, std::string const& arg) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param format The format of the message to log. + * @param arg1 The first argument to format into the message. + * @param arg2 The second argument to format into the message. + */ + virtual void warn(std::string const& format, std::string const& arg1, std::string const& arg2) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param message The message to log. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void warn(std::string const& message, const std::exception_ptr ex) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + */ + virtual void warn(std::string const& message, ServiceReferenceBase const& sr) = 0; + + /** + * Logs a message at LogLevel.Warn level. + * @param message The message to log. + * @param sr The ServiceReferenceBase object of the service that this message is associated with or an + * invalid object. + * @param ex The exception that reflects the condition or nullptr. + */ + virtual void warn(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) = 0; + }; +} // namespace cppmicroservices::logservice + +#endif // CPPMICROSERVICES_LOG_SERVICE_H__ diff --git a/compendium/LogService/include/cppmicroservices/logservice/LoggerFactory.hpp b/compendium/LogService/include/cppmicroservices/logservice/LoggerFactory.hpp new file mode 100644 index 000000000..9d23ed34d --- /dev/null +++ b/compendium/LogService/include/cppmicroservices/logservice/LoggerFactory.hpp @@ -0,0 +1,77 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + =============================================================================*/ +#ifndef CPPMICROSERVICES_LOGGER_FACTORY_H_ +#define CPPMICROSERVICES_LOGGER_FACTORY_H_ +#include "cppmicroservices/logservice/Logger.hpp" +#include "cppmicroservices/Bundle.h" + +#include +#include +#include +#include + +namespace cppmicroservices::logservice +{ + /** + \defgroup gr_logservice LoggerFactory + + \brief Groups LoggerFactory class related symbols. + */ + + /** + * \ingroup MicroService + * \ingroup gr_logservice + * + * LoggerFactory is the service interface that allows a bundle to obtain a Logger. + * It provides methods to obtain Logger; then a bundle can start logging messages to the Log Service by calling one of the Logger methods. + * + * @remarks This class is thread safe. + */ + + class LoggerFactory + { + public: + + inline static const std::string ROOT_LOGGER_NAME = "ROOT"; + + virtual ~LoggerFactory() = default; + + /** + * Return the Logger named with the specified name. + * @param name The name to use for the logger name. + * + * If the name parameter is equal to ROOT_LOGGER_NAME, then the root logger is returned. + */ + [[nodiscard]] virtual std::shared_ptr getLogger(std::string const& name = ROOT_LOGGER_NAME) const = 0; + + /** + * Return the Logger for the specified bundle, named with the specified name. + * @param bundle The bundle associated with the Logger. + * @param name The name to use for the logger name. + * + * If the name parameter is equal to ROOT_LOGGER_NAME, then the root logger is returned. + */ + [[nodiscard]] virtual std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, std::string const& name = ROOT_LOGGER_NAME) const = 0; + }; +} + +#endif diff --git a/compendium/LogServiceImpl/CMakeLists.txt b/compendium/LogServiceImpl/CMakeLists.txt index e5bfc9390..2b0d11e30 100644 --- a/compendium/LogServiceImpl/CMakeLists.txt +++ b/compendium/LogServiceImpl/CMakeLists.txt @@ -1,11 +1,15 @@ # sources and headers set(_srcs src/LogServiceImpl.cpp + src/LoggerFactoryImpl.cpp + src/LoggerImpl.cpp ) set(_hdrs src/Activator.hpp src/LogServiceImpl.hpp + src/LoggerFactoryImpl.hpp + src/LoggerImpl.hpp ) set(_link_libraries ) diff --git a/compendium/LogServiceImpl/src/Activator.cpp b/compendium/LogServiceImpl/src/Activator.cpp index de3619147..945dd5f9f 100644 --- a/compendium/LogServiceImpl/src/Activator.cpp +++ b/compendium/LogServiceImpl/src/Activator.cpp @@ -1,4 +1,5 @@ #include "Activator.hpp" +#include "LoggerFactoryImpl.hpp" #include "LogServiceImpl.hpp" namespace cppmicroservices @@ -9,10 +10,13 @@ namespace cppmicroservices { void Activator::Start(cppmicroservices::BundleContext bc) - { - auto svc - = std::make_shared("cppmicroservices::logservice"); - bc.RegisterService(std::move(svc)); + { + const cppmicroservices::Bundle bundle = bc.GetBundle(); + const std::string bsn = bundle.GetSymbolicName(); + const std::string logger_name = "LogService." + bsn; + auto svc + = std::make_shared(logger_name); + bc.RegisterService(std::move(svc)); } void diff --git a/compendium/LogServiceImpl/src/LogServiceImpl.cpp b/compendium/LogServiceImpl/src/LogServiceImpl.cpp index 78b00d3d0..073378fb3 100644 --- a/compendium/LogServiceImpl/src/LogServiceImpl.cpp +++ b/compendium/LogServiceImpl/src/LogServiceImpl.cpp @@ -6,110 +6,92 @@ #include "cppmicroservices/ServiceReference.h" #include "LogServiceImpl.hpp" +#include "LoggerFactoryImpl.hpp" +#include "LoggerImpl.hpp" -namespace cppmicroservices -{ - namespace logservice - { - std::string - GetExceptionMessage(std::exception_ptr const& ex) - { - std::string message = "\nException logged: "; - if (ex) - { - std::ostringstream stream; - try - { - std::rethrow_exception(ex); - } - catch (std::exception const& e) - { - message += std::string(typeid(e).name()) + " : " + e.what(); - } - } - else - { - message += "none"; - } - - return message; - } - - std::string - GetServiceReferenceInfo(ServiceReferenceBase const& sr) - { - std::ostringstream stream; - stream << "\nServiceReference: " << sr; - return stream.str(); - } - +namespace cppmicroservices::logservice +{ LogServiceImpl::LogServiceImpl(std::string const& loggerName) { - auto sink = std::make_shared(); - m_Logger = std::make_shared(loggerName, std::move(sink)); - m_Logger->set_pattern("[%T] [%P:%t] %n (%^%l%$): %v"); - m_Logger->set_level(spdlog::level::trace); - } - + logger = getLogger(loggerName); + } + void LogServiceImpl::Log(SeverityLevel level, std::string const& message) { - switch (level) - { - case SeverityLevel::LOG_DEBUG: - { - m_Logger->debug(message); - break; - } - case SeverityLevel::LOG_INFO: - { - m_Logger->info(message); - break; - } - case SeverityLevel::LOG_WARNING: - { - m_Logger->warn(message); - break; - } - case SeverityLevel::LOG_ERROR: - { - m_Logger->error(message); - break; - } - } - } + + auto currLogger = std::atomic_load(&logger); + if (!currLogger) + { + return; + } + + logImpl(level, message); + } void LogServiceImpl::Log(SeverityLevel level, std::string const& message, const std::exception_ptr ex) { - std::string full_message = message; - full_message = message + GetExceptionMessage(ex); - LogServiceImpl::Log(level, full_message); - } + auto currLogger = std::atomic_load(&logger); + if (!currLogger) + { + return; + } - void - LogServiceImpl::Log(ServiceReferenceBase const& sr, SeverityLevel level, std::string const& message) + logImpl(level, message, ex); + } + + void + LogServiceImpl::Log(ServiceReferenceBase const& sr, SeverityLevel level, std::string const& message) + { + auto currLogger = std::atomic_load(&logger); + if (!currLogger) + { + return; + } + + logImpl(level, message, sr); + } + + void + LogServiceImpl::Log(ServiceReferenceBase const& sr, + SeverityLevel level, + std::string const& message, + const std::exception_ptr ex) + { + + auto currLogger = std::atomic_load(&logger); + if(!currLogger) + { + return; + } + + logImpl(level, message, sr, ex); + } + + std::shared_ptr + LogServiceImpl::getLogger(std::string const& name) const { - std::string full_message = message; - full_message = message + GetServiceReferenceInfo(sr); - LogServiceImpl::Log(level, full_message); + std::shared_ptr lf = std::make_shared(); + return lf->getLogger(name); } - void - LogServiceImpl::Log(ServiceReferenceBase const& sr, - SeverityLevel level, - std::string const& message, - const std::exception_ptr ex) + std::shared_ptr + LogServiceImpl::getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const { - std::string full_message = message; - full_message = message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex); - LogServiceImpl::Log(level, full_message); + std::shared_ptr lf = std::make_shared(); + return lf->getLogger(bundle, name); } void LogServiceImpl::AddSink(spdlog::sink_ptr& sink) { - m_Logger->sinks().push_back(sink); + auto currLogger = std::atomic_load(&logger); + if (!currLogger) + { + return; + } + std::shared_ptr logimpl = std::dynamic_pointer_cast(currLogger); + logimpl->AddSink(sink); } - } // namespace logservice -} // namespace cppmicroservices +} // namespace cppmicroservices::logservice diff --git a/compendium/LogServiceImpl/src/LogServiceImpl.hpp b/compendium/LogServiceImpl/src/LogServiceImpl.hpp index fb17144cf..270d21ed6 100644 --- a/compendium/LogServiceImpl/src/LogServiceImpl.hpp +++ b/compendium/LogServiceImpl/src/LogServiceImpl.hpp @@ -21,6 +21,10 @@ =============================================================================*/ #include "cppmicroservices/logservice/LogService.hpp" +#include "cppmicroservices/logservice/Logger.hpp" +#include "cppmicroservices/logservice/LoggerFactory.hpp" +#include +#include namespace sinks { @@ -33,6 +37,8 @@ namespace spdlog using sink_ptr = std::shared_ptr; } // namespace spdlog +using namespace cppmicroservices; + namespace cppmicroservices { namespace logservice @@ -84,6 +90,38 @@ namespace cppmicroservices std::string const& message, const std::exception_ptr ex) override; + [[nodiscard]] std::shared_ptr getLogger(std::string const& name) const override; + [[nodiscard]] std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const override; + + template + void logImpl(SeverityLevel level, Args&&... args) + { + auto currLogger = std::atomic_load(&logger); + if (!currLogger) + { + return; + } + + switch (level) + { + case SeverityLevel::LOG_DEBUG: + currLogger->debug(std::forward(args)...); + break; + case SeverityLevel::LOG_INFO: + currLogger->info(std::forward(args)...); + break; + case SeverityLevel::LOG_WARNING: + currLogger->warn(std::forward(args)...); + break; + case SeverityLevel::LOG_ERROR: + currLogger->error(std::forward(args)...); + break; + default: + currLogger->trace(std::forward(args)...); + break; + } + } + /** * Registers a sink to the logger for introspection of contents. This is not a publicly available * function and should only be used for testing. This is NOT thread-safe. @@ -91,7 +129,7 @@ namespace cppmicroservices void AddSink(spdlog::sink_ptr& sink); private: - std::shared_ptr<::spdlog::logger> m_Logger; + std::shared_ptr logger; }; } // namespace logservice } // namespace cppmicroservices diff --git a/compendium/LogServiceImpl/src/LoggerFactoryImpl.cpp b/compendium/LogServiceImpl/src/LoggerFactoryImpl.cpp new file mode 100644 index 000000000..dbbfb7daa --- /dev/null +++ b/compendium/LogServiceImpl/src/LoggerFactoryImpl.cpp @@ -0,0 +1,21 @@ +#include "LoggerFactoryImpl.hpp" +#include "LoggerImpl.hpp" + +namespace cppmicroservices::logservice +{ + std::shared_ptr + LoggerFactoryImpl::getLogger(const std::string& name) const + { + std::lock_guard lock(mutex); + return std::make_shared(name); + } + + std::shared_ptr + LoggerFactoryImpl::getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const + { + std::lock_guard lock(mutex); + return std::make_shared(bundle, name); + } + +} + diff --git a/compendium/LogServiceImpl/src/LoggerFactoryImpl.hpp b/compendium/LogServiceImpl/src/LoggerFactoryImpl.hpp new file mode 100644 index 000000000..fa79f4045 --- /dev/null +++ b/compendium/LogServiceImpl/src/LoggerFactoryImpl.hpp @@ -0,0 +1,31 @@ +#include "cppmicroservices/logservice/LoggerFactory.hpp" +#include + +namespace cppmicroservices::logservice +{ + class LoggerFactoryImpl final : public LoggerFactory + { + public: + LoggerFactoryImpl() = default; + ~LoggerFactoryImpl() override = default; + + // Copy constructor + LoggerFactoryImpl(const LoggerFactoryImpl& other) = delete; + + // Copy assignment operator + LoggerFactoryImpl& operator=(const LoggerFactoryImpl& other) = delete; + + // Move constructor + LoggerFactoryImpl(LoggerFactoryImpl&& other) noexcept = delete; + + // Move assignment operator + LoggerFactoryImpl& operator=(LoggerFactoryImpl&& other) noexcept = delete; + + + [[nodiscard]] std::shared_ptr getLogger(std::string const& name) const override; + [[nodiscard]] std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, std::string const& name) const override; + + private: + mutable std::mutex mutex; // Mutex for synchronization + }; +} // namespace cppmicroservices::logservice diff --git a/compendium/LogServiceImpl/src/LoggerImpl.cpp b/compendium/LogServiceImpl/src/LoggerImpl.cpp new file mode 100644 index 000000000..13d60d03d --- /dev/null +++ b/compendium/LogServiceImpl/src/LoggerImpl.cpp @@ -0,0 +1,311 @@ +#include + +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/spdlog.h" + +#include "cppmicroservices/ServiceReference.h" + +#include "LoggerImpl.hpp" + +namespace cppmicroservices::logservice +{ + inline std::string + GetExceptionMessage(std::exception_ptr const& ex) + { + std::string message = "\nException logged: "; + if (ex) + { + try + { + std::rethrow_exception(ex); + } + catch (std::exception const& e) + { + message += std::string(typeid(e).name()) + " : " + e.what(); + } + } + else + { + message += "none"; + } + + return message; + } + + inline std::string + GetServiceReferenceInfo(ServiceReferenceBase const& sr) + { + std::ostringstream stream; + stream << "\nServiceReference: " << sr; + return stream.str(); + } + + LoggerImpl::LoggerImpl(std::string const& loggerName) + { + auto sink = std::make_shared(); + m_Logger = std::make_shared(loggerName, std::move(sink)); + m_Logger->set_pattern("[%T] [%P:%t] %n (%^%l%$): %v"); + m_Logger->set_level(spdlog::level::trace); + } + + LoggerImpl::LoggerImpl(const cppmicroservices::Bundle& bundle, std::string const& loggerName) + { + auto sink = std::make_shared(); + const std::string logger_name = bundle.GetSymbolicName() + "." + loggerName; + m_Logger = std::make_shared(logger_name, std::move(sink)); + m_Logger->set_pattern("[%T] [%P:%t] %n (%^%l%$): %v"); + m_Logger->set_level(spdlog::level::trace); + } + + void + LoggerImpl::audit(std::string const& message) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message); + } + + void + LoggerImpl::audit(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(format, arg); + } + + void + LoggerImpl::audit(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(format, arg1, arg2); + } + + void + LoggerImpl::audit(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::audit(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::audit(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + + void + LoggerImpl::debug(std::string const& message) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(message); + } + + void + LoggerImpl::debug(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(format, arg); + } + + void + LoggerImpl::debug(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(format, arg1, arg2); + } + + void + LoggerImpl::debug(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::debug(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::debug(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::debug); + m_Logger->debug(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + + void + LoggerImpl::error(std::string const& message) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(message); + } + + void + LoggerImpl::error(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(format, arg); + } + + void + LoggerImpl::error(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(format, arg1, arg2); + } + + void + LoggerImpl::error(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::error(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::error(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::err); + m_Logger->error(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + + void + LoggerImpl::info(std::string const& message) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(message); + } + + void + LoggerImpl::info(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(format, arg); + } + + void + LoggerImpl::info(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(format, arg1, arg2); + } + + void + LoggerImpl::info(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::info(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::info(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::info); + m_Logger->info(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + + void + LoggerImpl::trace(std::string const& message) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message); + } + + void + LoggerImpl::trace(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(format, arg); + } + + void + LoggerImpl::trace(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(format, arg1, arg2); + } + + void + LoggerImpl::trace(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::trace(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::trace(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::trace); + m_Logger->trace(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + + void + LoggerImpl::warn(std::string const& message) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(message); + } + + void + LoggerImpl::warn(std::string const& format, std::string const& arg) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(format, arg); + } + + void + LoggerImpl::warn(std::string const& format, std::string const& arg1, std::string const& arg2) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(format, arg1, arg2); + } + + void + LoggerImpl::warn(std::string const& message, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(message + GetExceptionMessage(ex)); + } + void + LoggerImpl::warn(std::string const& message, ServiceReferenceBase const& sr) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(message + GetServiceReferenceInfo(sr)); + } + + void + LoggerImpl::warn(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) + { + m_Logger->set_level(spdlog::level::warn); + m_Logger->warn(message + GetServiceReferenceInfo(sr) + GetExceptionMessage(ex)); + } + void + LoggerImpl::AddSink(spdlog::sink_ptr& sink) + { + m_Logger->sinks().push_back(sink); + } + +} // namespace cppmicroservices::logservice diff --git a/compendium/LogServiceImpl/src/LoggerImpl.hpp b/compendium/LogServiceImpl/src/LoggerImpl.hpp new file mode 100644 index 000000000..e50501fdf --- /dev/null +++ b/compendium/LogServiceImpl/src/LoggerImpl.hpp @@ -0,0 +1,101 @@ +#include "cppmicroservices/logservice/Logger.hpp" +#include "cppmicroservices/Bundle.h" + +namespace sinks +{ + class sink; +} + +namespace spdlog +{ + class logger; + using sink_ptr = std::shared_ptr; +} // namespace spdlog + +namespace cppmicroservices::logservice +{ + + class LoggerImpl final : public Logger + { + public: + LoggerImpl(std::string const& loggerName); + LoggerImpl(const cppmicroservices::Bundle& bundle, std::string const& loggerName); + ~LoggerImpl() override = default; + LoggerImpl() = default; + // Copy constructor + LoggerImpl(const LoggerImpl& other) = default; + + // Copy assignment operator + LoggerImpl& operator=(const LoggerImpl& other) = default; + + // Move constructor + LoggerImpl(LoggerImpl&& other) noexcept = default; + + // Move assignment operator + LoggerImpl& operator=(LoggerImpl&& other) noexcept = default; + + + void audit(const std::string& message) override; + void audit(std::string const& format, std::string const& arg) override; + void audit(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void audit(std::string const& message, const std::exception_ptr ex) override; + void audit(std::string const& message, ServiceReferenceBase const& sr) override; + void audit(std::string const& message, + ServiceReferenceBase const& sr, + const std::exception_ptr ex) override; + + + void debug(std::string const& message) override; + void debug(std::string const& format, std::string const& arg) override; + void debug(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void debug(std::string const& message, const std::exception_ptr ex) override; + void debug(std::string const& message, ServiceReferenceBase const& sr) override; + void debug(std::string const& message, ServiceReferenceBase const& sr, const std::exception_ptr ex) override; + + + void error(std::string const& message) override; + void error(std::string const& format, std::string const& arg) override; + void error(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void error(std::string const& message, const std::exception_ptr ex) override; + void error(std::string const& message, ServiceReferenceBase const& sr) override; + void error(std::string const& message, + ServiceReferenceBase const& sr, + const std::exception_ptr ex) override; + + void info(std::string const& message) override; + void info(std::string const& format, std::string const& arg) override; + void info(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void info(std::string const& message, const std::exception_ptr ex) override; + void info(std::string const& message, ServiceReferenceBase const& sr) override; + void info(std::string const& message, + ServiceReferenceBase const& sr, + const std::exception_ptr ex) override; + + void trace(std::string const& message) override; + void trace(std::string const& format, std::string const& arg) override; + void trace(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void trace(std::string const& message, const std::exception_ptr ex) override; + void trace(std::string const& message, ServiceReferenceBase const& sr) override; + void trace(std::string const& message, + ServiceReferenceBase const& sr, + const std::exception_ptr ex) override; + + void warn(std::string const& message) override; + void warn(std::string const& format, std::string const& arg) override; + void warn(std::string const& format, std::string const& arg1, std::string const& arg2) override; + void warn(std::string const& message, const std::exception_ptr ex) override; + void warn(std::string const& message, ServiceReferenceBase const& sr) override; + void warn(std::string const& message, + ServiceReferenceBase const& sr, + const std::exception_ptr ex) override; + + /** + * Registers a sink to the logger for introspection of contents. This is not a publicly available + * function and should only be used for testing. This is NOT thread-safe. + */ + void AddSink(spdlog::sink_ptr& sink); + + private: + std::shared_ptr<::spdlog::logger> m_Logger; + }; +} // namespace cppmicroservices::logservice diff --git a/compendium/LogServiceImpl/test/CMakeLists.txt b/compendium/LogServiceImpl/test/CMakeLists.txt index c476915e5..afaa0b9a0 100644 --- a/compendium/LogServiceImpl/test/CMakeLists.txt +++ b/compendium/LogServiceImpl/test/CMakeLists.txt @@ -41,15 +41,20 @@ endif() set(_logservice_tests TestLogService.cpp + TestLoggerFactory.cpp main.cpp ) set(_logservice_additional_srcs ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LogServiceImpl.cpp + ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LoggerFactoryImpl.cpp + ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LoggerImpl.cpp ) set(_logservice_additional_hdrs ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LogServiceImpl.hpp + ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LoggerFactoryImpl.hpp + ${CppMicroServices_SOURCE_DIR}/compendium/LogServiceImpl/src/LoggerImpl.hpp ) #----------------------------------------------------------------------------- diff --git a/compendium/LogServiceImpl/test/TestLogService.cpp b/compendium/LogServiceImpl/test/TestLogService.cpp index fa423318e..c200434b4 100644 --- a/compendium/LogServiceImpl/test/TestLogService.cpp +++ b/compendium/LogServiceImpl/test/TestLogService.cpp @@ -165,15 +165,22 @@ TEST_F(LogServiceImplTests, InvalidLoggerUsage) { auto logger = GetLogger(); + ASSERT_NO_THROW(logger->Log(ls::SeverityLevel::LOG_INFO, "Test invalid exception_ptr", nullptr)); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid exception_ptr(\\n)" + exception_preamble + "none")); + ASSERT_NO_THROW(logger->Log(static_cast(-1), "Test invalid negative severity level")); - EXPECT_FALSE(ContainsRegex(log_preamble + "Test invalid negative severity level")); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid negative severity level")); + + logger->Log(static_cast(-1), "Test invalid negative severity level"); + EXPECT_TRUE(ContainsRegex("trace")); ASSERT_NO_THROW(logger->Log(static_cast(std::numeric_limits::max()), "Test invalid maximum severity level")); - EXPECT_FALSE(ContainsRegex(log_preamble + "Test invalid maximum severity level")); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid maximum severity level")); - ASSERT_NO_THROW(logger->Log(ls::SeverityLevel::LOG_INFO, "Test invalid exception_ptr", nullptr)); - EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid exception_ptr(\\n)" + exception_preamble + "none")); + logger->Log(static_cast(std::numeric_limits::max()), + "Test invalid maximum severity level"); + EXPECT_TRUE(ContainsRegex("trace")); ASSERT_NO_THROW(logger->Log(cppmicroservices::ServiceReferenceU {}, ls::SeverityLevel::LOG_INFO, diff --git a/compendium/LogServiceImpl/test/TestLoggerFactory.cpp b/compendium/LogServiceImpl/test/TestLoggerFactory.cpp new file mode 100644 index 000000000..9564fc2dc --- /dev/null +++ b/compendium/LogServiceImpl/test/TestLoggerFactory.cpp @@ -0,0 +1,224 @@ +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "LoggerFactoryImpl.hpp" +#include "LoggerImpl.hpp" + +#include "cppmicroservices/logservice/LoggerFactory.hpp" +#include "cppmicroservices/logservice/Logger.hpp" + +namespace ls = cppmicroservices::logservice; + +static const std::string sinkFormat = "[%T] [%P:%t] %n (%^%l%$): %v"; +static const std::string log_preamble("\\[([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})\\] " + "\\[([0-9]{1,9}):([0-9]{1,9})\\] cppmicroservices::testing::logger " + "\\((debug|trace|info|warning|error|critical)\\): "); +static const std::string svcRef_preamble("ServiceReference: "); +static const std::string exception_preamble("Exception logged: "); + +class LoggerImplTests : public ::testing::Test +{ + public: + LoggerImplTests() + { + std::shared_ptr lf = std::make_shared(); + _impl = lf->getLogger("cppmicroservices::testing::logger"); + _logimpl = std::dynamic_pointer_cast(_impl); + } + + void + SetUp() override + { + _sink = std::make_shared(oss); + _sink->set_pattern(sinkFormat); + _logimpl->AddSink(_sink); + } + + void + TearDown() override + { + _sink.reset(); + _logimpl.reset(); + } + + bool + ContainsRegex(std::string const& regex) + { + std::string text = oss.str(); + std::smatch m; + bool found = std::regex_search(text, m, std::regex(regex)); + oss.str(""); + return found; + } + + std::shared_ptr + GetLogger() + { + return _impl; + } + std::ostringstream& + GetStream() + { + return oss; + } + + private: + std::ostringstream oss; + spdlog::sink_ptr _sink; + std::shared_ptr _impl; + std::shared_ptr _logimpl; +}; + +TEST_F(LoggerImplTests, ProperLoggerUsage) +{ + std::shared_ptr logger = GetLogger(); + + logger->audit("Hello!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hello!")); + + logger->audit("Hello {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hello World!")); + + logger->audit("Hello {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hello World!")); + + logger->audit("Test audit message", cppmicroservices::ServiceReferenceU {}); + EXPECT_TRUE( + ContainsRegex(log_preamble + "Test audit message(\\n)" + svcRef_preamble + "Invalid service reference")); + + logger->audit("Test audit message", + std::make_exception_ptr(std::runtime_error("uh oh"))); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test audit message(\\n)" + exception_preamble + "(.)+ uh oh")); + + logger->audit("Test audit message", cppmicroservices::ServiceReferenceU {}, std::make_exception_ptr(std::runtime_error("uh oh"))); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test audit message(\\n)" + svcRef_preamble + "Invalid service reference(\\n)" + exception_preamble + "(.)+ uh oh")); + + logger->debug("Hola!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola!")); + + logger->debug("Hola {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->debug("Hola {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->info("Hola!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola!")); + + logger->info("Hola {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->info("Hola {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->error("Hola!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola!")); + + logger->error("Hola {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->error("Hola {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->error("Test error message", cppmicroservices::ServiceReferenceU {}); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test error message(\\n)" + svcRef_preamble + "Invalid service reference")); + + logger->warn("Hola!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola!")); + + logger->warn("Hola {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->warn("Hola {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->warn("Test warning message", + std::make_exception_ptr(std::runtime_error("uh oh"))); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test warning message(\\n)" + exception_preamble + "(.)+ uh oh")); + + logger->trace("Hola!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola!")); + + logger->trace("Hola {}", "World!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->trace("Hola {}{}", "World", "!"); + EXPECT_TRUE(ContainsRegex(log_preamble + "Hola World!")); + + logger->trace("Test trace message", + std::make_exception_ptr(std::runtime_error("uh oh"))); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test trace message(\\n)" + exception_preamble + "(.)+ uh oh")); + + logger->trace("Test trace message", cppmicroservices::ServiceReferenceU {}); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test trace message(\\n)" + svcRef_preamble + "Invalid service reference")); + + logger->trace("Test trace message", cppmicroservices::ServiceReferenceU {}, std::make_exception_ptr(std::runtime_error("uh oh"))); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test trace message(\\n)" + svcRef_preamble + "Invalid service reference(\\n)" + exception_preamble + "(.)+ uh oh")); + +} + +TEST_F(LoggerImplTests, InvalidLoggerUsage) +{ + std::shared_ptr logger = GetLogger(); + + std::exception_ptr exp = nullptr; + EXPECT_NO_THROW(logger->info("Test invalid exception_ptr", exp)); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid exception_ptr(\\n)" + exception_preamble + "none")); + + EXPECT_NO_THROW(logger->info("Test invalid ServiceReferenceBase object", cppmicroservices::ServiceReferenceU {}, + nullptr)); + EXPECT_TRUE(ContainsRegex(log_preamble + "Test invalid ServiceReferenceBase object(\\n)" + svcRef_preamble + + "Invalid service reference(\\n)" + exception_preamble + "none")); +} + +TEST_F(LoggerImplTests, ThreadSafety) +{ + std::shared_ptr logger = GetLogger(); + auto& oss = GetStream(); + + int const iterations = 100; + std::vector threads; + for (int i = 0; i < iterations; i++) + { + threads.emplace_back( + [&logger]() + { logger->info("Test concurrent log calls"); + }); + } + + for (auto& thread : threads) + { + thread.join(); + } + + std::regex regexp(log_preamble + "Test concurrent log calls"); + std::string stream(oss.str()); + auto regex_iter_end = std::sregex_iterator(); + + auto regex_iter_begin = std::sregex_iterator(stream.begin(), stream.end(), regexp); + std::ptrdiff_t num_found = std::distance(regex_iter_begin, regex_iter_end); + ASSERT_TRUE(num_found == iterations); +} + diff --git a/framework/src/bundle/CoreBundleContext.cpp b/framework/src/bundle/CoreBundleContext.cpp index 77ca3f130..bb1733423 100644 --- a/framework/src/bundle/CoreBundleContext.cpp +++ b/framework/src/bundle/CoreBundleContext.cpp @@ -29,6 +29,7 @@ US_MSVC_DISABLE_WARNING(4355) #include "cppmicroservices/BundleInitialization.h" #include "cppmicroservices/Constants.h" #include "cppmicroservices/FrameworkFactory.h" +#include "cppmicroservices/GetBundleContext.h" #include "cppmicroservices/util/FileSystem.h" #include "cppmicroservices/util/String.h" @@ -87,7 +88,6 @@ namespace cppmicroservices , workingDir(ref_any_cast(frameworkProperties.at(Constants::FRAMEWORK_WORKING_DIR))) , listeners(this) , services(this) - , logger(std::make_shared()) , serviceHooks(this) , bundleHooks(this) , bundleRegistry(this) @@ -174,7 +174,7 @@ namespace cppmicroservices bundleRegistry.Load(); - logger->Open(); + logger = std::make_shared(GetBundleContext()); std::string execPath; try diff --git a/framework/src/util/CFRLogger.cpp b/framework/src/util/CFRLogger.cpp index 1f2bac539..1bfe5b825 100644 --- a/framework/src/util/CFRLogger.cpp +++ b/framework/src/util/CFRLogger.cpp @@ -21,14 +21,22 @@ =============================================================================*/ #include "CFRLogger.h" -#include "CoreBundleContext.h" #include "cppmicroservices/GetBundleContext.h" namespace cppmicroservices { namespace cfrimpl { - CFRLogger::CFRLogger() : serviceTracker(), logService(nullptr) {} + CFRLogger::CFRLogger() : serviceTracker(), logService(nullptr) { } + + CFRLogger::CFRLogger(cppmicroservices::BundleContext context) + : cfrContext(std::move(context)) + ,serviceTracker( + std::make_unique>(cfrContext, + this)) + ,logService(nullptr) { + serviceTracker->Open(); + } CFRLogger::~CFRLogger() { @@ -119,21 +127,26 @@ namespace cppmicroservices } } - void - CFRLogger::Open() + std::shared_ptr + CFRLogger::getLogger(const std::string& name) const + { + auto currLogger = std::atomic_load(&logService); + if (currLogger) + { + return currLogger->getLogger(name); + } + return nullptr; + } + + std::shared_ptr + CFRLogger::getLogger(const cppmicroservices::Bundle& bundle, const std::string& name) const { - auto l = this->Lock(); - US_UNUSED(l); - cfrContext = GetBundleContext(); - if (!cfrContext) - { - return; - } - serviceTracker - = std::make_unique>( - cfrContext, - this); - serviceTracker->Open(); + auto currLogger = std::atomic_load(&logService); + if(currLogger) + { + return currLogger->getLogger(bundle, name); + } + return nullptr; } void @@ -147,6 +160,5 @@ namespace cppmicroservices serviceTracker.reset(); } } - } // namespace cfrimpl } // namespace cppmicroservices diff --git a/framework/src/util/CFRLogger.h b/framework/src/util/CFRLogger.h index 5b77fbaf1..965f9482c 100644 --- a/framework/src/util/CFRLogger.h +++ b/framework/src/util/CFRLogger.h @@ -53,6 +53,7 @@ namespace cppmicroservices { public: CFRLogger(); + explicit CFRLogger(cppmicroservices::BundleContext context); CFRLogger(CFRLogger const&) = delete; CFRLogger(CFRLogger&&) = delete; CFRLogger& operator=(CFRLogger const&) = delete; @@ -69,6 +70,8 @@ namespace cppmicroservices logservice::SeverityLevel level, std::string const& message, const std::exception_ptr ex) override; + [[nodiscard]] std::shared_ptr getLogger(const std::string& name) const override; + [[nodiscard]] std::shared_ptr getLogger(const cppmicroservices::Bundle& bundle, const std::string& name) const override; // methods from the cppmicroservices::ServiceTrackerCustomizer interface std::shared_ptr AddingService( @@ -79,7 +82,6 @@ namespace cppmicroservices std::shared_ptr const& service) override; // methods for the CFRLogger class - void Open(); void Close(); private: diff --git a/framework/test/gtest/CFRLoggerTest.cpp b/framework/test/gtest/CFRLoggerTest.cpp new file mode 100644 index 000000000..c0d69f15d --- /dev/null +++ b/framework/test/gtest/CFRLoggerTest.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include + +#include +#include +#include "../../src/util/CFRLogger.h" +#include "cppmicroservices/logservice/LogService.hpp" +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/Framework.h" +#include "cppmicroservices/FrameworkEvent.h" +#include "cppmicroservices/FrameworkFactory.h" + +using cppmicroservices::logservice::LogService; +using cppmicroservices::logservice::SeverityLevel; + +namespace cppmicroservices +{ + namespace cfrimpl + { + /** + * This class is used in tests where the logger is required and the test + * needs to verify what is sent to the logger + */ + class MockLogger : public cppmicroservices::logservice::LogService + { + public: + MOCK_METHOD2(Log, void(cppmicroservices::logservice::SeverityLevel, std::string const&)); + MOCK_METHOD3(Log, + void(cppmicroservices::logservice::SeverityLevel, + std::string const&, + std::exception_ptr const)); + MOCK_METHOD3(Log, + void(cppmicroservices::ServiceReferenceBase const&, + cppmicroservices::logservice::SeverityLevel, + std::string const&)); + MOCK_METHOD4(Log, + void(cppmicroservices::ServiceReferenceBase const&, + cppmicroservices::logservice::SeverityLevel, + std::string const&, + std::exception_ptr const)); + MOCK_CONST_METHOD1(getLogger, std::shared_ptr(std::string const&)); + MOCK_CONST_METHOD2(getLogger, + std::shared_ptr(cppmicroservices::Bundle const&, + std::string const&)); + }; + } +} + +namespace cppmicroservices +{ + namespace cfrimpl + { + // The fixture for testing class CFRLogger. + class CFRLoggerTest : public ::testing::Test + { + protected: + CFRLoggerTest() : framework(cppmicroservices::FrameworkFactory().NewFramework()) {} + virtual ~CFRLoggerTest() = default; + + virtual void + SetUp() + { + framework.Start(); + } + + virtual void + TearDown() + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } + + cppmicroservices::Framework& + GetFramework() + { + return framework; + } + + private: + cppmicroservices::Framework framework; + }; + + TEST_F(CFRLoggerTest, VerifyWithoutLoggerService) + { + cppmicroservices::cfrimpl::CFRLogger logger; + cppmicroservices::ServiceReferenceU dummyRef; + // check that calling log method is safe even if a LogService is unavailable + EXPECT_NO_THROW({ + logger.Log(SeverityLevel::LOG_DEBUG, "sample log message"); + logger.Log(SeverityLevel::LOG_DEBUG, + "sample log message", + std::make_exception_ptr(std::runtime_error("error occured"))); + logger.Log(dummyRef, SeverityLevel::LOG_DEBUG, "sample log message"); + logger.Log(dummyRef, + SeverityLevel::LOG_DEBUG, + "sample log message", + std::make_exception_ptr(std::runtime_error("error occured"))); + }); + } + + TEST_F(CFRLoggerTest, VerifyWithLoggerService) + { + EXPECT_NO_THROW({ + // Register a mock logger implementaion + auto mockLogger = std::make_shared(); + auto bundleContext = GetFramework().GetBundleContext(); + + auto reg = bundleContext.RegisterService(mockLogger); + // set expectations + EXPECT_CALL(*(mockLogger.get()), Log(SeverityLevel::LOG_DEBUG, testing::_)).Times(1); + EXPECT_CALL(*(mockLogger.get()), Log(SeverityLevel::LOG_ERROR, testing::_, testing::_)).Times(1); + EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_WARNING, testing::_)).Times(1); + EXPECT_CALL(*(mockLogger.get()), Log(testing::_, SeverityLevel::LOG_ERROR, testing::_, testing::_)) + .Times(1); + EXPECT_CALL(*(mockLogger.get()), getLogger("test")).Times(1); + const cppmicroservices::Bundle mockBundle; + EXPECT_CALL(*(mockLogger.get()), getLogger(mockBundle, "test")).Times(1); + + cppmicroservices::cfrimpl::CFRLogger logger(bundleContext); + logger.Log(SeverityLevel::LOG_DEBUG, "test"); + logger.Log(SeverityLevel::LOG_ERROR, + "some sample error message", + std::make_exception_ptr(std::runtime_error("error occured"))); + cppmicroservices::ServiceReferenceU dummyRef; + logger.Log(dummyRef, SeverityLevel::LOG_WARNING, "some sample warning message"); + logger.Log(dummyRef, + SeverityLevel::LOG_ERROR, + "some sample error message with service reference", + std::make_exception_ptr(std::runtime_error("error occured"))); + auto resultLogger = logger.getLogger("test"); + auto resultBundleLogger = logger.getLogger(mockBundle, "test"); + }); + } + } +} + + + + diff --git a/framework/test/gtest/CMakeLists.txt b/framework/test/gtest/CMakeLists.txt index 0381dc521..8cd6ec5ad 100644 --- a/framework/test/gtest/CMakeLists.txt +++ b/framework/test/gtest/CMakeLists.txt @@ -40,6 +40,7 @@ set(_gtest_tests BundleManifestTest.cpp BundleValidationTest.cpp BundleVersionTest.cpp + CFRLoggerTest.cpp InvalidBundleTest.cpp GlobalServiceTrackerTest.cpp LDAPExprTest.cpp @@ -97,6 +98,7 @@ set(_additional_srcs ../util/TestUtilFrameworkListener.cpp ../util/TestUtils.cpp ../util/ImportTestBundles.cpp + ../../src/util/CFRLogger.cpp $ ) @@ -146,8 +148,6 @@ if(UNIX AND NOT APPLE) target_link_libraries(${us_gtest_test_exe_name} rt) endif() - - # tests can be configured to run using add_test or GTEST_ADD_TEST (https://cmake.org/cmake/help/v3.0/module/FindGTest.html) # GTEST_ADD_TEST will run a seprate EXE for each test and test fixture in the EXE. # At this time, there isn't a need to use GTEST_ADD_TEST. diff --git a/framework/test/gtest/FrameworkTest.cpp b/framework/test/gtest/FrameworkTest.cpp index 213949dd1..70f437ace 100644 --- a/framework/test/gtest/FrameworkTest.cpp +++ b/framework/test/gtest/FrameworkTest.cpp @@ -87,6 +87,11 @@ namespace cppmicroservices::logservice::SeverityLevel, std::string const&, std::exception_ptr const)); + MOCK_CONST_METHOD1(getLogger, + std::shared_ptr(const std::string&)); + MOCK_CONST_METHOD2(getLogger, + std::shared_ptr(const cppmicroservices::Bundle&, const std::string&)); + }; } // namespace