diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e1d9a9..08b25f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ add_library(webfused-static STATIC src/webfused/daemon.c src/webfused/config/config.c src/webfused/config/factory.c + src/webfused/log/logger.c ) add_executable(webfused @@ -105,6 +106,7 @@ pkg_check_modules(GMOCK gmock) add_executable(alltests test/test_config.cc + test/test_log.cc ) target_include_directories(alltests PRIVATE diff --git a/src/webfused/log/log.h b/src/webfused/log/log.h new file mode 100644 index 0000000..08a3b54 --- /dev/null +++ b/src/webfused/log/log.h @@ -0,0 +1,58 @@ +#ifndef WFD_LOG_H +#define WFD_LOG_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef WFD_LOGLEVEL +#define WFD_LOGLEVEL WFD_LOGLEVEL_ALL +#endif + +#define WFD_LOGLEVEL_FATAL 1 +#define WFD_LOGLEVEL_ERROR 3 +#define WFD_LOGLEVEL_WARN 4 +#define WFD_LOGLEVEL_INFO 5 +#define WFD_LOGLEVEL_DEBUG 7 +#define WFD_LOGLEVEL_ALL 8 + +#define WFD_FATAL(...) \ + WFD_LOG(WFD_LOGLEVEL_FATAL, __VA_ARGS__) + +#define WFD_ERROR(...) \ + WFD_LOG(WFD_LOGLEVEL_ERROR, __VA_ARGS__) + +#define WFD_WARN(...) \ + WFD_LOG(WFD_LOGLEVEL_WARN, __VA_ARGS__) + +#define WFD_INFO(...) \ + WFD_LOG(WFD_LOGLEVEL_INFO, __VA_ARGS__) + +#define WFD_DEBUG(...) \ + WFD_LOG(WFD_LOGLEVEL_DEBUG, __VA_ARGS__) + +#define WFD_LOG(level, ...) \ + do { \ + if (WFD_LOGLEVEL >= (level)) { \ + wfd_log((level), __VA_ARGS__); \ + } \ + } while (0) + +extern void +wfd_log( + int level, + char const * format, + ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/logger.c b/src/webfused/log/logger.c new file mode 100644 index 0000000..2c15fbe --- /dev/null +++ b/src/webfused/log/logger.c @@ -0,0 +1,86 @@ +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +#include + +struct wfd_logger +{ + int level; + wfd_logger_log_fn * log; + wfd_logger_onclose_fn * onclose; + void * user_data; +}; + +static void wfd_logger_default_log( + void * user_data, + int level, + char const * format, + va_list args); + +static struct wfd_logger g_wfd_logger = +{ + .level = WFD_LOGLEVEL_ALL, + .log = &wfd_logger_default_log, + .onclose = NULL, + .user_data = NULL +}; + +void +wfd_logger_init( + int level, + wfd_logger_log_fn * log, + wfd_logger_onclose_fn * onclose, + void * user_data) +{ + wfd_logger_close(); + + g_wfd_logger.level = level; + g_wfd_logger.log = log; + g_wfd_logger.onclose = onclose; + g_wfd_logger.user_data = user_data; +} + +void +wfd_logger_close(void) +{ + if (NULL != g_wfd_logger.onclose) + { + g_wfd_logger.onclose(g_wfd_logger.user_data); + } + + g_wfd_logger.level = WFD_LOGLEVEL_ALL; + g_wfd_logger.log = &wfd_logger_default_log; + g_wfd_logger.onclose = NULL; + g_wfd_logger.user_data = NULL; +} + +void +wfd_log( + int level, + char const * format, + ...) +{ + if (g_wfd_logger.level >= level) + { + va_list args; + va_start(args, format); + g_wfd_logger.log( + g_wfd_logger.user_data, + level, + format, + args); + va_end(args); + } +} + +static void wfd_logger_default_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + (void) user_data; + (void) level; + (void) format; + (void) args; +} diff --git a/src/webfused/log/logger.h b/src/webfused/log/logger.h new file mode 100644 index 0000000..2944b9c --- /dev/null +++ b/src/webfused/log/logger.h @@ -0,0 +1,40 @@ +#ifndef WFD_LOGGER_H +#define WFD_LOGGER_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void +wfd_logger_log_fn( + void * user_data, + int level, + char const * format, + va_list args); + +typedef void +wfd_logger_onclose_fn( + void * user_data); + +extern void +wfd_logger_init( + int level, + wfd_logger_log_fn * log, + wfd_logger_onclose_fn * onclose, + void * user_data); + +extern void +wfd_logger_close(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/test_log.cc b/test/test_log.cc new file mode 100644 index 0000000..57397f0 --- /dev/null +++ b/test/test_log.cc @@ -0,0 +1,152 @@ +#include +#include + +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +using ::testing::_; +using ::testing::StrEq; + +namespace +{ + +class ILogger +{ +public: + virtual ~ILogger() = default; + virtual void log(int level, char const * format, va_list args) = 0; + virtual void onclose() = 0; +}; + +class MockLogger: public ILogger +{ +public: + MOCK_METHOD3(log, void(int level, char const * format, va_list args)); + MOCK_METHOD0(onclose, void(void)); + + void * getUserData() + { + ILogger * logger = this; + return reinterpret_cast(logger); + } +}; + +} + +extern "C" +{ + +static void MockLogger_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + ILogger * logger = reinterpret_cast(user_data); + logger->log(level, format, args); +} + +static void MockLogger_onclose( + void * user_data) +{ + ILogger * logger = reinterpret_cast(user_data); + logger->onclose(); +} + +} + +TEST(log, fatal) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_FATAL, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_FATAL("too bad"); + wfd_logger_close(); +} + +TEST(log, error) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_ERROR("too bad"); + wfd_logger_close(); +} + +TEST(log, warn) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +} + +TEST(log, info) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_INFO, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_INFO("too bad"); + wfd_logger_close(); +} + +TEST(log, debug) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_DEBUG, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_DEBUG("too bad"); + wfd_logger_close(); +} + +TEST(log, respect_loglevel) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_DEBUG("too bad"); + wfd_logger_close(); +} + +TEST(log, log_same_loglevel) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &MockLogger_log, &MockLogger_onclose, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +} + +TEST(log, omit_onclose_if_nullptr) +{ + MockLogger logger; + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(0); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &MockLogger_log, nullptr, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +}