Ver a proveniência

* load logger configuation from JSON config file

master
bergmann há 6 anos
ascendente
cometimento
13ae836670
12 ficheiros alterados com 303 adições e 30 eliminações
  1. +1
    -0
      .gitignore
  2. +3
    -0
      cmake/config.h.in
  3. +4
    -4
      include/cpplogging/manager/consumer/consumer.h
  4. +14
    -8
      include/cpplogging/manager/consumer/consumer_stream.h
  5. +16
    -0
      include/cpplogging/manager/manager.h
  6. +15
    -1
      src/CMakeLists.txt
  7. +2
    -5
      src/cpplogging/manager/consumer/consumer.cpp
  8. +10
    -4
      src/cpplogging/manager/consumer/consumer_stream.cpp
  9. +185
    -0
      src/cpplogging/manager/manager.cpp
  10. +17
    -8
      test/cpplogging/cpplogging_tests.cpp
  11. +36
    -0
      test/cpplogging/test_config.json
  12. +0
    -0
     

+ 1
- 0
.gitignore Ver ficheiro

@@ -0,0 +1 @@
build/

+ 3
- 0
cmake/config.h.in Ver ficheiro

@@ -0,0 +1,3 @@
#pragma once

#cmakedefine CPPLOGGING_HAS_NLOHMANN_JSON

+ 4
- 4
include/cpplogging/manager/consumer/consumer.h Ver ficheiro

@@ -21,11 +21,13 @@ namespace cpplogging
std::string _name; //!< name of the consumer
format_type _format; //!< parsed logging format

public:
/**
* @brief Get the default log format.
*/
static inline const std::string& default_format();

protected:
/**
* @brief Parse the given format and generate a pre-compiled list of strings.
*
@@ -49,13 +51,11 @@ namespace cpplogging
* @brief Constructor.
*
* @param[in] name Name of the consumer.
* @param[in] auto_register Automatically register the consumer.
* @param[in] format Format to use for logging.
*/
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.


+ 14
- 8
include/cpplogging/manager/consumer/consumer_stream.h Ver ficheiro

@@ -25,20 +25,26 @@ namespace cpplogging
/**
* @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.
*
* @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.


+ 16
- 0
include/cpplogging/manager/manager.h Ver ficheiro

@@ -3,11 +3,18 @@
#include <set>
#include <map>
#include <list>
#include <string>

#include <cpplogging/config.h>

#include "rule.h"
#include "logger_impl.h"
#include "consumer/consumer.h"

#ifdef CPPLOGGING_HAS_NLOHMANN_JSON
#define CPPLOGGING_HAS_LOAD_CONFIG
#endif

namespace cpplogging
{

@@ -148,6 +155,15 @@ namespace cpplogging
*/
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:
/**
* @brief Initialize the given logger instance.


+ 15
- 1
src/CMakeLists.txt Ver ficheiro

@@ -20,10 +20,18 @@ Option ( CPPLOGGING_NO_STRIP
"Do not strip debug symbols from binary."
OFF )

Find_Package ( nlohmann_json )
If ( nlohmann_json_FOUND )
Set ( CPPLOGGING_HAS_NLOHMANN_JSON true )
EndIf ( )

# Object Library ##################################################################################

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 )
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_INLINE_FILES ${CPPLOGGING_INCLUDE_DIR}/*.inl )
File ( GLOB_RECURSE CPPLOGGING_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
@@ -32,9 +40,15 @@ Add_Library ( cpplogging-objects
${CPPLOGGING_HEADER_FILES}
${CPPLOGGING_INLINE_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
PUBLIC
$<BUILD_INTERFACE:${CPPLOGGING_INCLUDE_DIR}>
$<BUILD_INTERFACE:${CPPLOGGING_GENERATED_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CPPLOGGING_INSTALL_DIR_INCLUDE}> )

# Static Library ##################################################################################
@@ -82,7 +96,7 @@ EndIf ( )

# Header
If ( CPPLOGGING_INSTALL_HEADER )
Install ( FILES ${CPPLOGGING_INCLUDE_DIR}/cpplogging.h
Install ( DIRECTORY ${CPPLOGGING_GENERATED_INCLUDE_DIR}/cpplogging
DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} )
Install ( DIRECTORY ${CPPLOGGING_INCLUDE_DIR}/cpplogging
DESTINATION ${CPPLOGGING_INSTALL_DIR_INCLUDE} )


+ 2
- 5
src/cpplogging/manager/consumer/consumer.cpp Ver ficheiro

@@ -3,13 +3,10 @@

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)
, _format (parse_format(format))
{
if (auto_register)
manager::register_consumer(*this);
}
{ }

consumer::~consumer()
{


+ 10
- 4
src/cpplogging/manager/consumer/consumer_stream.cpp Ver ficheiro

@@ -6,14 +6,20 @@

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)
, _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)
, _storage (std::move(stream))
{ }


+ 185
- 0
src/cpplogging/manager/manager.cpp Ver ficheiro

@@ -1,5 +1,15 @@
#include <fstream>

#include <cpplogging/manager/manager.h>
#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;

@@ -129,6 +139,181 @@ void manager::reset()
_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)
{
for (auto& rule : _rules)


+ 17
- 8
test/cpplogging/cpplogging_tests.cpp Ver ficheiro

@@ -17,8 +17,10 @@ struct consumer_mock
MOCK_CONST_METHOD1(write_entry, void (const log_entry_ptr_s& entry));

consumer_mock(const std::string& n) :
consumer(n, true)
{ }
consumer(n)
{
manager::register_consumer(*this);
}
};

MATCHER_P5(MatchLogData, level, sender, thread, name, message, "")
@@ -37,8 +39,8 @@ MATCHER_P5(MatchLogData, level, sender, thread, name, message, "")
TEST(LoggingTests, matcher_all)
{
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& l1 = logger::get("TestLogger");
@@ -54,8 +56,8 @@ TEST(LoggingTests, matcher_all)
TEST(LoggingTests, matcher_default)
{
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& l1 = logger::get("TestLogger");
@@ -71,8 +73,8 @@ TEST(LoggingTests, matcher_default)
TEST(LoggingTests, matcher_regex)
{
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& 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, error).sender((void*)0x24).message("test2") << " error";
}

#ifdef CPPLOGGING_HAS_LOAD_CONFIG
TEST(LoggingTests, load)
{
manager::load("./cpplogging/test_config.json");
}
#endif

+ 36
- 0
test/cpplogging/test_config.json Ver ficheiro

@@ -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"
}]
}

+ 0
- 0
Ver ficheiro


Carregando…
Cancelar
Guardar