| @@ -0,0 +1 @@ | |||||
| build/ | |||||
| @@ -0,0 +1,3 @@ | |||||
| #pragma once | |||||
| #cmakedefine CPPLOGGING_HAS_NLOHMANN_JSON | |||||
| @@ -21,11 +21,13 @@ namespace cpplogging | |||||
| std::string _name; //!< name of the consumer | std::string _name; //!< name of the consumer | ||||
| format_type _format; //!< parsed logging format | format_type _format; //!< parsed logging format | ||||
| public: | |||||
| /** | /** | ||||
| * @brief Get the default log format. | * @brief Get the default log format. | ||||
| */ | */ | ||||
| static inline const std::string& default_format(); | static inline const std::string& default_format(); | ||||
| protected: | |||||
| /** | /** | ||||
| * @brief Parse the given format and generate a pre-compiled list of strings. | * @brief Parse the given format and generate a pre-compiled list of strings. | ||||
| * | * | ||||
| @@ -49,13 +51,11 @@ namespace cpplogging | |||||
| * @brief Constructor. | * @brief Constructor. | ||||
| * | * | ||||
| * @param[in] name Name of the consumer. | * @param[in] name Name of the consumer. | ||||
| * @param[in] auto_register Automatically register the consumer. | |||||
| * @param[in] format Format to use for logging. | * @param[in] format Format to use for logging. | ||||
| */ | */ | ||||
| consumer( | consumer( | ||||
| const std::string& name, | |||||
| bool auto_register, | |||||
| const std::string& format = default_format()); | |||||
| const std::string& name, | |||||
| const std::string& format = default_format()); | |||||
| /** | /** | ||||
| * @breif Destructor. | * @breif Destructor. | ||||
| @@ -25,20 +25,26 @@ namespace cpplogging | |||||
| /** | /** | ||||
| * @brief Constructor. | * @brief Constructor. | ||||
| * | * | ||||
| * @param[in] name Name of the consumer. | |||||
| * @param[in] stream Stream to write data to. | |||||
| * @param[in] auto_register Automatically register the consumer. | |||||
| * @param[in] name Name of the consumer. | |||||
| * @param[in] stream Stream to write data to. | |||||
| * @param[in] format Format to use for logging. | |||||
| */ | */ | ||||
| consumer_stream(const std::string& name, std::ostream& stream, bool auto_register); | |||||
| consumer_stream( | |||||
| const std::string& name, | |||||
| std::ostream& stream, | |||||
| const std::string& format = default_format()); | |||||
| /** | /** | ||||
| * @brief Constructor. | * @brief Constructor. | ||||
| * | * | ||||
| * @param[in] name Name of the consumer. | |||||
| * @param[in] stream Stream to write data to. | |||||
| * @param[in] auto_register Automatically register the consumer. | |||||
| * @param[in] name Name of the consumer. | |||||
| * @param[in] stream Stream to write data to. | |||||
| * @param[in] format Format to use for logging. | |||||
| */ | */ | ||||
| consumer_stream(const std::string& name, stream_ptr_u&& stream, bool auto_register); | |||||
| consumer_stream( | |||||
| const std::string& name, | |||||
| stream_ptr_u&& stream, | |||||
| const std::string& format = default_format()); | |||||
| /** | /** | ||||
| * @brief Consume a log entry. | * @brief Consume a log entry. | ||||
| @@ -3,11 +3,18 @@ | |||||
| #include <set> | #include <set> | ||||
| #include <map> | #include <map> | ||||
| #include <list> | #include <list> | ||||
| #include <string> | |||||
| #include <cpplogging/config.h> | |||||
| #include "rule.h" | #include "rule.h" | ||||
| #include "logger_impl.h" | #include "logger_impl.h" | ||||
| #include "consumer/consumer.h" | #include "consumer/consumer.h" | ||||
| #ifdef CPPLOGGING_HAS_NLOHMANN_JSON | |||||
| #define CPPLOGGING_HAS_LOAD_CONFIG | |||||
| #endif | |||||
| namespace cpplogging | namespace cpplogging | ||||
| { | { | ||||
| @@ -148,6 +155,15 @@ namespace cpplogging | |||||
| */ | */ | ||||
| static void reset(); | static void reset(); | ||||
| #ifdef CPPLOGGING_HAS_LOAD_CONFIG | |||||
| /** | |||||
| * @brief Load current configuration from file. | |||||
| * | |||||
| * @param[in] filename Filename to load configuration from. | |||||
| */ | |||||
| static void load(const std::string& filename); | |||||
| #endif | |||||
| private: | private: | ||||
| /** | /** | ||||
| * @brief Initialize the given logger instance. | * @brief Initialize the given logger instance. | ||||
| @@ -20,10 +20,18 @@ Option ( CPPLOGGING_NO_STRIP | |||||
| "Do not strip debug symbols from binary." | "Do not strip debug symbols from binary." | ||||
| OFF ) | OFF ) | ||||
| Find_Package ( nlohmann_json ) | |||||
| If ( nlohmann_json_FOUND ) | |||||
| Set ( CPPLOGGING_HAS_NLOHMANN_JSON true ) | |||||
| EndIf ( ) | |||||
| # Object Library ################################################################################## | # Object Library ################################################################################## | ||||
| Set ( CMAKE_POSITION_INDEPENDENT_CODE ON ) | Set ( CMAKE_POSITION_INDEPENDENT_CODE ON ) | ||||
| Set ( CPPLOGGING_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated/include ) | |||||
| Set ( CPPLOGGING_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | Set ( CPPLOGGING_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | ||||
| Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config.h.in | |||||
| ${CPPLOGGING_GENERATED_INCLUDE_DIR}/cpplogging/config.h ) | |||||
| File ( GLOB_RECURSE CPPLOGGING_HEADER_FILES ${CPPLOGGING_INCLUDE_DIR}/*.h ) | File ( GLOB_RECURSE CPPLOGGING_HEADER_FILES ${CPPLOGGING_INCLUDE_DIR}/*.h ) | ||||
| File ( GLOB_RECURSE CPPLOGGING_INLINE_FILES ${CPPLOGGING_INCLUDE_DIR}/*.inl ) | File ( GLOB_RECURSE CPPLOGGING_INLINE_FILES ${CPPLOGGING_INCLUDE_DIR}/*.inl ) | ||||
| File ( GLOB_RECURSE CPPLOGGING_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | File ( GLOB_RECURSE CPPLOGGING_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | ||||
| @@ -32,9 +40,15 @@ Add_Library ( cpplogging-objects | |||||
| ${CPPLOGGING_HEADER_FILES} | ${CPPLOGGING_HEADER_FILES} | ||||
| ${CPPLOGGING_INLINE_FILES} | ${CPPLOGGING_INLINE_FILES} | ||||
| ${CPPLOGGING_SOURCE_FILES} ) | ${CPPLOGGING_SOURCE_FILES} ) | ||||
| If ( CPPLOGGING_HAS_NLOHMANN_JSON ) | |||||
| Target_Link_Libraries ( cpplogging-objects | |||||
| PRIVATE | |||||
| nlohmann_json::nlohmann_json ) | |||||
| EndIf ( ) | |||||
| Target_Include_Directories ( cpplogging-objects | Target_Include_Directories ( cpplogging-objects | ||||
| PUBLIC | PUBLIC | ||||
| $<BUILD_INTERFACE:${CPPLOGGING_INCLUDE_DIR}> | $<BUILD_INTERFACE:${CPPLOGGING_INCLUDE_DIR}> | ||||
| $<BUILD_INTERFACE:${CPPLOGGING_GENERATED_INCLUDE_DIR}> | |||||
| $<INSTALL_INTERFACE:${CPPLOGGING_INSTALL_DIR_INCLUDE}> ) | $<INSTALL_INTERFACE:${CPPLOGGING_INSTALL_DIR_INCLUDE}> ) | ||||
| # Static Library ################################################################################## | # Static Library ################################################################################## | ||||
| @@ -82,7 +96,7 @@ EndIf ( ) | |||||
| # Header | # Header | ||||
| If ( CPPLOGGING_INSTALL_HEADER ) | If ( CPPLOGGING_INSTALL_HEADER ) | ||||
| Install ( FILES ${CPPLOGGING_INCLUDE_DIR}/cpplogging.h | |||||
| Install ( DIRECTORY ${CPPLOGGING_GENERATED_INCLUDE_DIR}/cpplogging | |||||
| DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} ) | DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} ) | ||||
| Install ( DIRECTORY ${CPPLOGGING_INCLUDE_DIR}/cpplogging | Install ( DIRECTORY ${CPPLOGGING_INCLUDE_DIR}/cpplogging | ||||
| DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} ) | DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} ) | ||||
| @@ -3,13 +3,10 @@ | |||||
| using namespace ::cpplogging; | using namespace ::cpplogging; | ||||
| consumer::consumer(const std::string& name, bool auto_register, const std::string& format) | |||||
| consumer::consumer(const std::string& name, const std::string& format) | |||||
| : _name (name) | : _name (name) | ||||
| , _format (parse_format(format)) | , _format (parse_format(format)) | ||||
| { | |||||
| if (auto_register) | |||||
| manager::register_consumer(*this); | |||||
| } | |||||
| { } | |||||
| consumer::~consumer() | consumer::~consumer() | ||||
| { | { | ||||
| @@ -6,14 +6,20 @@ | |||||
| using namespace ::cpplogging; | using namespace ::cpplogging; | ||||
| consumer_stream::consumer_stream(const std::string& name, std::ostream& stream, bool auto_register) | |||||
| : consumer (name, auto_register) | |||||
| consumer_stream::consumer_stream( | |||||
| const std::string& name, | |||||
| std::ostream& stream, | |||||
| const std::string& format) | |||||
| : consumer (name, format) | |||||
| , _stream (stream) | , _stream (stream) | ||||
| , _storage (nullptr) | , _storage (nullptr) | ||||
| { } | { } | ||||
| consumer_stream::consumer_stream(const std::string& name, stream_ptr_u&& stream, bool auto_register) | |||||
| : consumer (name, auto_register) | |||||
| consumer_stream::consumer_stream( | |||||
| const std::string& name, | |||||
| stream_ptr_u&& stream, | |||||
| const std::string& format) | |||||
| : consumer (name, format) | |||||
| , _stream (*stream) | , _stream (*stream) | ||||
| , _storage (std::move(stream)) | , _storage (std::move(stream)) | ||||
| { } | { } | ||||
| @@ -1,5 +1,15 @@ | |||||
| #include <fstream> | |||||
| #include <cpplogging/manager/manager.h> | #include <cpplogging/manager/manager.h> | ||||
| #include <cpplogging/manager/manager.inl> | #include <cpplogging/manager/manager.inl> | ||||
| #include <cpplogging/manager/matcher/matcher_all.h> | |||||
| #include <cpplogging/manager/matcher/matcher_regex.h> | |||||
| #include <cpplogging/manager/matcher/matcher_default_logger.h> | |||||
| #include <cpplogging/manager/consumer/consumer_stream.h> | |||||
| #ifdef CPPLOGGING_HAS_NLOHMANN_JSON | |||||
| #include <nlohmann/json.hpp> | |||||
| #endif | |||||
| using namespace ::cpplogging; | using namespace ::cpplogging; | ||||
| @@ -129,6 +139,181 @@ void manager::reset() | |||||
| _consumer.clear(); | _consumer.clear(); | ||||
| } | } | ||||
| #ifdef CPPLOGGING_HAS_LOAD_CONFIG | |||||
| matcher_ptr_u load_matcher(const ::nlohmann::json& jMatchers, const ::nlohmann::json& jName) | |||||
| { | |||||
| if (!jName.is_string()) | |||||
| throw std::invalid_argument("expected matcher value to be an string"); | |||||
| auto& jMatcher = jMatchers[jName.get<std::string>()]; | |||||
| if (!jMatcher.is_object()) | |||||
| throw std::invalid_argument("expected matcher to be an object"); | |||||
| auto& jType = jMatcher["type"]; | |||||
| if (!jType.is_string()) | |||||
| throw std::invalid_argument("expected 'matcher.type' to be an string"); | |||||
| /* match all cosumers/loggers */ | |||||
| auto type = jType.get<std::string>(); | |||||
| if (type == "all") | |||||
| { | |||||
| return std::make_unique<matcher_all>(); | |||||
| } | |||||
| /* match the default logger */ | |||||
| else if (type == "default_logger") | |||||
| { | |||||
| return std::make_unique<matcher_default_logger>(); | |||||
| } | |||||
| /* regex */ | |||||
| else if (type == "regex") | |||||
| { | |||||
| /* read regex value */ | |||||
| auto& jValue = jMatcher["value"]; | |||||
| if (!jValue.is_string()) | |||||
| throw std::invalid_argument("expected 'matcher.value' to be an string"); | |||||
| auto regex = jValue.get<std::string>(); | |||||
| /* read regex invert */ | |||||
| bool invert = false; | |||||
| auto& jInvert = jMatcher["invert"]; | |||||
| if (!jValue.empty() && !jValue.is_boolean()) | |||||
| throw std::invalid_argument("expected 'matcher.invert' to be an bool"); | |||||
| invert = jInvert.get<bool>(); | |||||
| return std::make_unique<matcher_regex>(regex, invert); | |||||
| } | |||||
| /* invalid type */ | |||||
| else | |||||
| { | |||||
| using namespace ::std; | |||||
| throw std::invalid_argument("unknown matcher type: "s + type); | |||||
| } | |||||
| } | |||||
| log_level load_log_level(const ::nlohmann::json& jLogLevel, log_level default_level) | |||||
| { | |||||
| if (jLogLevel.empty()) | |||||
| return default_level; | |||||
| if (!jLogLevel.is_string()) | |||||
| throw std::invalid_argument("expected rule log level to be an string"); | |||||
| auto tmp = jLogLevel.get<std::string>(); | |||||
| if (tmp == "debug") | |||||
| return log_level::debug; | |||||
| else if (tmp == "info") | |||||
| return log_level::info; | |||||
| else if (tmp == "warn") | |||||
| return log_level::warn; | |||||
| else if (tmp == "error") | |||||
| return log_level::error; | |||||
| else | |||||
| { | |||||
| using namespace ::std; | |||||
| throw std::invalid_argument("invalid log level: "s + tmp); | |||||
| } | |||||
| } | |||||
| void manager::load(const std::string& filename) | |||||
| { | |||||
| using namespace ::nlohmann; | |||||
| /* load the json object */ | |||||
| std::ifstream ifs(filename); | |||||
| json root; | |||||
| ifs >> root; | |||||
| /* get root nodes */ | |||||
| auto rules = root["rules"]; | |||||
| auto matchers = root["matchers"]; | |||||
| auto consumers = root["consumers"]; | |||||
| /* check root nodes */ | |||||
| if (rules.empty() || !rules.is_array()) | |||||
| throw std::invalid_argument("expected 'rules' to be an json array"); | |||||
| if (matchers.empty() || !matchers.is_object()) | |||||
| throw std::invalid_argument("expected 'matchers' to be an json object"); | |||||
| if (consumers.empty() || !consumers.is_object()) | |||||
| throw std::invalid_argument("expected 'consumers' to be an json object"); | |||||
| /* read consumers */ | |||||
| for (auto& c : consumers.items()) | |||||
| { | |||||
| auto name = c.key(); | |||||
| auto obj = c.value(); | |||||
| if (!obj.is_object()) | |||||
| throw std::invalid_argument("expected 'consumer' to be an json object"); | |||||
| /* type */ | |||||
| auto& jType = obj["type"]; | |||||
| if (!jType.is_string()) | |||||
| throw std::invalid_argument("expected 'consumer.type' to be an string"); | |||||
| /* format */ | |||||
| auto& jFormat = obj["format"]; | |||||
| auto format = consumer::default_format(); | |||||
| if (jFormat.empty()) | |||||
| { | |||||
| if (!jFormat.is_string()) | |||||
| throw std::invalid_argument("expected 'consumer.format' to be an string"); | |||||
| format = jFormat.get<std::string>(); | |||||
| } | |||||
| /* log to file */ | |||||
| auto type = jType.get<std::string>(); | |||||
| if (type == "file") | |||||
| { | |||||
| auto& jFilename = obj["filename"]; | |||||
| if (!jFilename.is_string()) | |||||
| throw std::invalid_argument("expected 'consumer.filename' to be an string"); | |||||
| auto fn = jFilename.get<std::string>(); | |||||
| register_consumer(std::make_unique<consumer_stream>( | |||||
| name, | |||||
| std::make_unique<std::ofstream>(fn), | |||||
| format)); | |||||
| } | |||||
| /* log to stdout */ | |||||
| else if (type == "stdout") | |||||
| { | |||||
| register_consumer(std::make_unique<consumer_stream>( | |||||
| name, | |||||
| std::cout, | |||||
| format)); | |||||
| } | |||||
| /* log to stderr */ | |||||
| else if (type == "stderr") | |||||
| { | |||||
| register_consumer(std::make_unique<consumer_stream>( | |||||
| name, | |||||
| std::cerr, | |||||
| format)); | |||||
| } | |||||
| /* invalid consumer type */ | |||||
| else | |||||
| { | |||||
| using namespace ::std; | |||||
| throw std::invalid_argument("unknown consumer type: "s + type); | |||||
| } | |||||
| } | |||||
| /* read rules */ | |||||
| for (auto& r : rules) | |||||
| { | |||||
| define_rule( | |||||
| load_matcher(matchers, r["logger"]), | |||||
| load_matcher(matchers, r["consumer"]), | |||||
| load_log_level(r["min"], log_level::debug), | |||||
| load_log_level(r["max"], log_level::error)); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| logger_impl& manager::init_logger(logger_impl& logger) | logger_impl& manager::init_logger(logger_impl& logger) | ||||
| { | { | ||||
| for (auto& rule : _rules) | for (auto& rule : _rules) | ||||
| @@ -17,8 +17,10 @@ struct consumer_mock | |||||
| MOCK_CONST_METHOD1(write_entry, void (const log_entry_ptr_s& entry)); | MOCK_CONST_METHOD1(write_entry, void (const log_entry_ptr_s& entry)); | ||||
| consumer_mock(const std::string& n) : | consumer_mock(const std::string& n) : | ||||
| consumer(n, true) | |||||
| { } | |||||
| consumer(n) | |||||
| { | |||||
| manager::register_consumer(*this); | |||||
| } | |||||
| }; | }; | ||||
| MATCHER_P5(MatchLogData, level, sender, thread, name, message, "") | MATCHER_P5(MatchLogData, level, sender, thread, name, message, "") | ||||
| @@ -37,8 +39,8 @@ MATCHER_P5(MatchLogData, level, sender, thread, name, message, "") | |||||
| TEST(LoggingTests, matcher_all) | TEST(LoggingTests, matcher_all) | ||||
| { | { | ||||
| LoggingReset loggingReset; | LoggingReset loggingReset; | ||||
| consumer_stream c0("TestConsumer1", std::cout, false); | |||||
| consumer_stream c1("TestConsumer2", std::cout, false); | |||||
| consumer_stream c0("TestConsumer1", std::cout); | |||||
| consumer_stream c1("TestConsumer2", std::cout); | |||||
| auto& l0 = logger::get(); | auto& l0 = logger::get(); | ||||
| auto& l1 = logger::get("TestLogger"); | auto& l1 = logger::get("TestLogger"); | ||||
| @@ -54,8 +56,8 @@ TEST(LoggingTests, matcher_all) | |||||
| TEST(LoggingTests, matcher_default) | TEST(LoggingTests, matcher_default) | ||||
| { | { | ||||
| LoggingReset loggingReset; | LoggingReset loggingReset; | ||||
| consumer_stream c0("TestConsumer1", std::cout, false); | |||||
| consumer_stream c1("TestConsumer2", std::cout, false); | |||||
| consumer_stream c0("TestConsumer1", std::cout); | |||||
| consumer_stream c1("TestConsumer2", std::cout); | |||||
| auto& l0 = logger::get(); | auto& l0 = logger::get(); | ||||
| auto& l1 = logger::get("TestLogger"); | auto& l1 = logger::get("TestLogger"); | ||||
| @@ -71,8 +73,8 @@ TEST(LoggingTests, matcher_default) | |||||
| TEST(LoggingTests, matcher_regex) | TEST(LoggingTests, matcher_regex) | ||||
| { | { | ||||
| LoggingReset loggingReset; | LoggingReset loggingReset; | ||||
| consumer_stream c0("TestConsumer1", std::cout, false); | |||||
| consumer_stream c1("TestConsumer2", std::cout, false); | |||||
| consumer_stream c0("TestConsumer1", std::cout); | |||||
| consumer_stream c1("TestConsumer2", std::cout); | |||||
| auto& l0 = logger::get(); | auto& l0 = logger::get(); | ||||
| auto& l1 = logger::get("TestLogger"); | auto& l1 = logger::get("TestLogger"); | ||||
| @@ -125,3 +127,10 @@ TEST(LoggingTests, log_base) | |||||
| cpplogging_log(l1, warn ).sender((void*)0x23).message("test2") << " warn"; | cpplogging_log(l1, warn ).sender((void*)0x23).message("test2") << " warn"; | ||||
| cpplogging_log(l1, error).sender((void*)0x24).message("test2") << " error"; | cpplogging_log(l1, error).sender((void*)0x24).message("test2") << " error"; | ||||
| } | } | ||||
| #ifdef CPPLOGGING_HAS_LOAD_CONFIG | |||||
| TEST(LoggingTests, load) | |||||
| { | |||||
| manager::load("./cpplogging/test_config.json"); | |||||
| } | |||||
| #endif | |||||
| @@ -0,0 +1,36 @@ | |||||
| { | |||||
| "consumers": { | |||||
| "c_stdout": { | |||||
| "type": "stdout", | |||||
| "format": "[${runtime:-016.6f}] ${message}" | |||||
| }, | |||||
| "c_stderr": { | |||||
| "type": "stderr", | |||||
| "format": "[${runtime:-016.6f}] ${message}" | |||||
| }, | |||||
| "c_file": { | |||||
| "type": "file", | |||||
| "filename": "test_file.log", | |||||
| "format": "[${runtime:-016.6f}] ${message}" | |||||
| } | |||||
| }, | |||||
| "matchers": { | |||||
| "m_all": { | |||||
| "type": "all" | |||||
| }, | |||||
| "m_default_logger": { | |||||
| "type": "default_logger" | |||||
| }, | |||||
| "m_regex": { | |||||
| "type": "regex", | |||||
| "value": "abc.*?xyz", | |||||
| "invert": true | |||||
| } | |||||
| }, | |||||
| "rules": [{ | |||||
| "consumer": "m_all", | |||||
| "logger": "m_default_logger", | |||||
| "min": "info", | |||||
| "max": "error" | |||||
| }] | |||||
| } | |||||