Unverified Commit 14ecdf51 authored by mplellison's avatar mplellison Committed by GitHub

Separated macro-based logging API from impl, renamed default impl. (#777)

The separation of the impl from the API means that it is more possible
to independently test variations of the API by changing the macro
definitions in log.h.

The default StringLogger impl is now called StringToStreamLogger, and
takes an optional ostream pointer on input - this makes it more
testable. It is assumed that std::cerr will be used here nearly all of
the time in practice.
parent 22509199
/* log.h
Michael Ellison, 27 May 2020
Logging API definitions
Logging macro definitions - use these to log messages in Pistache.
*/
#pragma once
#include <memory>
#include <sstream>
namespace Pistache {
namespace Log {
enum class Level {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
};
class StringLogger {
public:
virtual void log(Level level, const std::string &message) = 0;
virtual bool isEnabledFor(Level level) const = 0;
virtual ~StringLogger() {}
};
class DefaultStringLogger : public StringLogger {
public:
explicit DefaultStringLogger(Level level) : level_(level) {}
~DefaultStringLogger() override {}
void log(Level level, const std::string &message) override;
bool isEnabledFor(Level level) const override;
private:
Level level_;
};
} // namespace Log
} // namespace Pistache
#include <pistache/string_logger.h>
#ifndef PISTACHE_LOG_STRING_FATAL
#define PISTACHE_LOG_STRING_FATAL(logger, message) do {\
......@@ -120,7 +85,7 @@ private:
#ifndef PISTACHE_DEFAULT_STRING_LOGGER
#define PISTACHE_DEFAULT_STRING_LOGGER \
std::make_shared<::Pistache::Log::DefaultStringLogger>(::Pistache::Log::Level::WARN)
std::make_shared<::Pistache::Log::StringToStreamLogger>(::Pistache::Log::Level::WARN)
#endif
#ifndef PISTACHE_NULL_STRING_LOGGER
......
/* log.h
Michael Ellison, 27 May 2020
String logger definitions - to be used via the macros defined in log.h, or
passed into a Pistache library function as a logging endpoint.
*/
#pragma once
#include <memory>
#include <iostream>
namespace Pistache {
namespace Log {
enum class Level {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
};
class StringLogger {
public:
virtual void log(Level level, const std::string &message) = 0;
virtual bool isEnabledFor(Level level) const = 0;
virtual ~StringLogger() {}
};
class StringToStreamLogger : public StringLogger {
public:
explicit StringToStreamLogger(Level level, std::ostream *out = &std::cerr)
: level_(level), out_(out) {}
~StringToStreamLogger() override {}
void log(Level level, const std::string &message) override;
bool isEnabledFor(Level level) const override;
private:
Level level_;
std::ostream* out_;
};
} // namespace Log
} // namespace Pistache
/* log.cc
Michael Ellison, 27 May 2020
Implementation of the default logger
String logger implementations - to be used via the macros defined in log.h,
or passed into a Pistache library function as a logging endpoint.
*/
#include <iostream>
#include <pistache/log.h>
#include <pistache/string_logger.h>
namespace Pistache {
namespace Log {
void DefaultStringLogger::log(Level level, const std::string &message) {
if (isEnabledFor(level)) {
std::cerr << message << std::endl;
void StringToStreamLogger::log(Level level, const std::string &message) {
if (out_ && isEnabledFor(level)) {
(*out_) << message << std::endl;
}
}
bool DefaultStringLogger::isEnabledFor(Level level) const {
bool StringToStreamLogger::isEnabledFor(Level level) const {
return static_cast<int>(level) >= static_cast<int>(level_);
}
......
......@@ -34,7 +34,8 @@ pistache_test(stream_test)
pistache_test(reactor_test)
pistache_test(threadname_test)
pistache_test(optional_test)
pistache_test(logger_test)
pistache_test(log_api_test)
pistache_test(string_logger_test)
if (PISTACHE_USE_SSL)
......
......@@ -7,64 +7,6 @@
using namespace Pistache;
class TestStringLogger : public Log::StringLogger {
public:
void log(Log::Level level, const std::string &message) {
messages_.push_back(message);
levels_.push_back(level);
}
bool isEnabledFor(Log::Level level) const override {
switch(level) {
case Log::Level::FATAL:
case Log::Level::ERROR:
case Log::Level::WARN:
return true;
case Log::Level::INFO:
case Log::Level::DEBUG:
case Log::Level::TRACE:
default:
return false;
}
}
TestStringLogger(Log::Level level) : level_(level) {}
Log::Level level_;
std::vector<std::string> messages_;
std::vector<Log::Level> levels_;
};
// Test that isEnabledFor is called when using the PISTACHE_LOG_STRING_* macros.
TEST(logger_test, macros_guard_by_level) {
auto logger_subclass = std::make_shared<TestStringLogger>(Log::Level::WARN);
auto& actual_messages = logger_subclass->messages_;
auto& actual_levels = logger_subclass->levels_;
std::shared_ptr<::Pistache::Log::StringLogger> logger = logger_subclass;
PISTACHE_LOG_STRING_FATAL(logger, "test_message_1_fatal");
PISTACHE_LOG_STRING_ERROR(logger, "test_message_2_error");
PISTACHE_LOG_STRING_WARN(logger, "test_message_3_warn");
PISTACHE_LOG_STRING_INFO(logger, "test_message_4_info");
PISTACHE_LOG_STRING_DEBUG(logger, "test_message_5_debug");
PISTACHE_LOG_STRING_TRACE(logger, "test_message_6_trace");
std::vector<std::string> expected_messages;
expected_messages.push_back("test_message_1_fatal");
expected_messages.push_back("test_message_2_error");
expected_messages.push_back("test_message_3_warn");
std::vector<Log::Level> expected_levels;
expected_levels.push_back(Log::Level::FATAL);
expected_levels.push_back(Log::Level::ERROR);
expected_levels.push_back(Log::Level::WARN);
ASSERT_EQ(actual_messages, expected_messages);
ASSERT_EQ(actual_levels, expected_levels);
}
// Test that the PISTACHE_LOG_STRING_* macros guard against accessing a null logger.
TEST(logger_test, macros_guard_null_logger) {
PISTACHE_STRING_LOGGER_T logger = PISTACHE_NULL_STRING_LOGGER;
......@@ -76,7 +18,7 @@ TEST(logger_test, macros_guard_null_logger) {
PISTACHE_LOG_STRING_DEBUG(logger, "test_message_5_debug");
PISTACHE_LOG_STRING_TRACE(logger, "test_message_6_trace");
// Expect no death from accessing the default logger.
// Expect no death from accessing the null logger.
}
// Test that the PISTACHE_LOG_STRING_* macros access a default logger.
......@@ -90,7 +32,6 @@ TEST(logger_test, macros_access_default_logger) {
PISTACHE_LOG_STRING_DEBUG(logger, "test_message_5_debug");
PISTACHE_LOG_STRING_TRACE(logger, "test_message_6_trace");
// Expect no death from using the default handler. The only output of the
// Expect no death from using the default handler. The only output of the
// default logger is to stdout, so output cannot be confirmed by gtest.
}
#include <utility>
#include <vector>
#include <pistache/string_logger.h>
#include "gtest/gtest.h"
using namespace Pistache;
TEST(logger_test, logger_guards_by_level) {
std::stringstream ss;
std::shared_ptr<::Pistache::Log::StringLogger> logger =
std::make_shared<::Pistache::Log::StringToStreamLogger>(::Pistache::Log::Level::WARN, &ss);
logger->log(::Pistache::Log::Level::FATAL, "test_message_1_fatal");
logger->log(::Pistache::Log::Level::ERROR, "test_message_2_error");
logger->log(::Pistache::Log::Level::WARN, "test_message_3_warn");
logger->log(::Pistache::Log::Level::INFO, "test_message_4_info");
logger->log(::Pistache::Log::Level::DEBUG, "test_message_5_debug");
logger->log(::Pistache::Log::Level::TRACE, "test_message_6_trace");
logger->log(::Pistache::Log::Level::ERROR, "test_message_7_error");
logger->log(::Pistache::Log::Level::DEBUG, "test_message_8_debug");
logger->log(::Pistache::Log::Level::FATAL, "test_message_9_fatal");
std::string expected_string =
"test_message_1_fatal\n"
"test_message_2_error\n"
"test_message_3_warn\n"
"test_message_7_error\n"
"test_message_9_fatal\n";
ASSERT_EQ(ss.str(), expected_string);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment