374 lines
9.3 KiB
C++
374 lines
9.3 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include "matador/logger/log_manager.hpp"
|
|
|
|
#include "matador/utils/os.hpp"
|
|
|
|
#ifdef _MSC_VER
|
|
#include <io.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
|
|
using namespace matador::logger;
|
|
|
|
namespace filehelper {
|
|
|
|
class std_stream_switcher
|
|
{
|
|
public:
|
|
explicit std_stream_switcher(FILE *str, const char* redirect)
|
|
: stream(str) {
|
|
fflush(stream);
|
|
fgetpos(stream, &pos);
|
|
fd = matador::os::dup(stream);
|
|
matador::os::freopen(redirect, "w+", stream);
|
|
}
|
|
~std_stream_switcher() {
|
|
fflush(stream);
|
|
#ifdef _MSC_VER
|
|
_dup2(fd, _fileno(stream));
|
|
_close(fd);
|
|
#else
|
|
dup2(fd, fileno(stream));
|
|
close(fd);
|
|
#endif
|
|
clearerr(stream);
|
|
fsetpos(stream, &pos);
|
|
}
|
|
|
|
private:
|
|
FILE *stream = nullptr;
|
|
int fd = 0;
|
|
fpos_t pos = {};
|
|
};
|
|
|
|
}
|
|
|
|
void validate_log_file_line(const std::string &filename, int line_index, const std::string &level, const std::string &src, const std::string &msg);
|
|
|
|
TEST_CASE("Test log file sink", "[logger][log][file_sink]") {
|
|
file_sink test("test.txt");
|
|
|
|
REQUIRE(matador::os::exists("test.txt"));
|
|
|
|
// UNIT_ASSERT_EQUAL("test.txt", test.path());
|
|
|
|
test.close();
|
|
|
|
if (::remove("test.txt") == -1) {
|
|
#ifdef _MSC_VER
|
|
char buf[1024];
|
|
strerror_s(buf, 1024, errno);
|
|
FAIL(buf);
|
|
#else
|
|
FAIL(strerror(errno));
|
|
#endif
|
|
}
|
|
|
|
REQUIRE(!matador::os::exists("test.txt"));
|
|
|
|
{
|
|
auto path = matador::os::build_path("mylog", "test.txt");
|
|
file_sink test2(path);
|
|
|
|
REQUIRE("mylog" == test2.path());
|
|
|
|
matador::os::chdir("mylog");
|
|
|
|
REQUIRE(matador::os::exists("test.txt"));
|
|
|
|
}
|
|
if (::remove("test.txt") == -1) {
|
|
#ifdef _MSC_VER
|
|
char buf[1024];
|
|
strerror_s(buf, 1024, errno);
|
|
FAIL(buf);
|
|
#else
|
|
FAIL(strerror(errno));
|
|
#endif
|
|
}
|
|
|
|
REQUIRE(!matador::os::exists("test.txt"));
|
|
|
|
matador::os::chdir("..");
|
|
|
|
matador::os::rmdir("mylog");
|
|
}
|
|
|
|
TEST_CASE("Test log rotating file sink", "[logger][log][rotate_file_sink]") {
|
|
const auto dir = matador::os::build_path("my", "log");
|
|
const auto path = matador::os::build_path(dir, "log.txt");
|
|
|
|
const auto logsink = create_rotating_file_sink(path, 30, 3);
|
|
|
|
matador::os::chdir(dir);
|
|
|
|
REQUIRE(matador::os::is_readable("log.txt"));
|
|
REQUIRE(matador::os::is_writable("log.txt"));
|
|
|
|
std::string line = "hello world and first line\n";
|
|
|
|
logsink->write(line.c_str(), line.size());
|
|
|
|
matador::os::chdir("my");
|
|
matador::os::chdir("log");
|
|
|
|
REQUIRE(!matador::os::exists("log.1.txt"));
|
|
|
|
logsink->write(line.c_str(), line.size());
|
|
|
|
REQUIRE(matador::os::exists("log.1.txt"));
|
|
|
|
logsink->close();
|
|
|
|
matador::os::remove("log.txt");
|
|
matador::os::remove("log.1.txt");
|
|
|
|
matador::os::chdir("..");
|
|
matador::os::chdir("..");
|
|
|
|
matador::os::rmpath(matador::os::build_path("my", "log"));
|
|
}
|
|
|
|
TEST_CASE("Test log level range", "[logger][level][range]") {
|
|
REQUIRE(log_level::LVL_INFO == log_manager::min_default_log_level());
|
|
REQUIRE(log_level::LVL_FATAL == log_manager::max_default_log_level());
|
|
|
|
default_min_log_level(log_level::LVL_DEBUG);
|
|
default_max_log_level(log_level::LVL_ERROR);
|
|
|
|
REQUIRE(log_level::LVL_DEBUG == log_manager::min_default_log_level());
|
|
REQUIRE(log_level::LVL_ERROR == log_manager::max_default_log_level());
|
|
|
|
log_level_range llr;
|
|
llr.min_level = log_level::LVL_DEBUG;
|
|
llr.max_level = log_level::LVL_TRACE;
|
|
log_domain ld("test", llr);
|
|
|
|
REQUIRE(log_level::LVL_DEBUG == ld.min_log_level());
|
|
REQUIRE(log_level::LVL_TRACE == ld.max_log_level());
|
|
|
|
ld.min_log_level(log_level::LVL_INFO);
|
|
ld.max_log_level(log_level::LVL_ERROR);
|
|
|
|
REQUIRE(log_level::LVL_INFO == ld.min_log_level());
|
|
REQUIRE(log_level::LVL_ERROR == ld.max_log_level());
|
|
}
|
|
|
|
TEST_CASE("Test basic logger functions", "[logger][basic]") {
|
|
auto logger = create_logger("test");
|
|
|
|
REQUIRE("test" == logger.source());
|
|
REQUIRE("default" == logger.domain());
|
|
|
|
auto logsink = create_file_sink("log.txt");
|
|
|
|
REQUIRE(matador::os::is_readable("log.txt"));
|
|
REQUIRE(matador::os::is_writable("log.txt"));
|
|
|
|
logsink->close();
|
|
|
|
if (::remove("log.txt") == -1) {
|
|
#ifdef _MSC_VER
|
|
char buf[1024];
|
|
strerror_s(buf, 1024, errno);
|
|
FAIL(buf);
|
|
#else
|
|
FAIL(strerror(errno));
|
|
#endif
|
|
}
|
|
|
|
REQUIRE(!matador::os::exists("log.txt"));
|
|
|
|
logger = create_logger("net", "system");
|
|
|
|
REQUIRE("net" == logger.source());
|
|
REQUIRE("system" == logger.domain());
|
|
|
|
logsink = create_file_sink("net.txt");
|
|
|
|
REQUIRE(matador::os::is_readable("net.txt"));
|
|
REQUIRE(matador::os::is_writable("net.txt"));
|
|
|
|
logsink->close();
|
|
|
|
matador::os::remove("net.txt");
|
|
|
|
REQUIRE(!matador::os::exists("net.txt"));
|
|
|
|
log_manager::instance().clear();
|
|
}
|
|
|
|
TEST_CASE("Test logging", "[logger][logging]") {
|
|
domain_min_log_level("default", log_level::LVL_FATAL);
|
|
domain_max_log_level("default", log_level::LVL_TRACE);
|
|
|
|
auto logger = create_logger("test");
|
|
|
|
REQUIRE("test" == logger.source());
|
|
REQUIRE("default" == logger.domain());
|
|
|
|
auto logsink = create_file_sink("log.txt");
|
|
|
|
REQUIRE(matador::os::is_readable("log.txt"));
|
|
REQUIRE(matador::os::is_writable("log.txt"));
|
|
|
|
add_log_sink(logsink);
|
|
|
|
logger.info("information");
|
|
logger.info("information %s", "important");
|
|
logger.warn("warning");
|
|
logger.warn("warning %s", "important");
|
|
logger.debug("debugging");
|
|
logger.debug("debugging %s", "important");
|
|
logger.trace("tracing something");
|
|
logger.trace("tracing something %s", "important");
|
|
logger.error("big error");
|
|
logger.error("big error %s", "important");
|
|
log(log_level::LVL_ERROR, "test", "global log test %d", 4711);
|
|
|
|
logsink->close();
|
|
|
|
validate_log_file_line("log.txt", 0, "INFO", "test", "information");
|
|
validate_log_file_line("log.txt", 1, "INFO", "test", "information important");
|
|
validate_log_file_line("log.txt", 2, "WARN", "test", "warning");
|
|
validate_log_file_line("log.txt", 3, "WARN", "test", "warning important");
|
|
validate_log_file_line("log.txt", 4, "DEBUG", "test", "debugging");
|
|
validate_log_file_line("log.txt", 5, "DEBUG", "test", "debugging important");
|
|
validate_log_file_line("log.txt", 6, "TRACE", "test", "tracing something");
|
|
validate_log_file_line("log.txt", 7, "TRACE", "test", "tracing something important");
|
|
validate_log_file_line("log.txt", 8, "ERROR", "test", "big error");
|
|
validate_log_file_line("log.txt", 9, "ERROR", "test", "big error important");
|
|
validate_log_file_line("log.txt", 10, "ERROR", "test", "global log test 4711");
|
|
|
|
matador::os::remove("log.txt");
|
|
|
|
REQUIRE(!matador::os::exists("log.txt"));
|
|
|
|
log_manager::instance().clear();
|
|
}
|
|
|
|
TEST_CASE("Test log stdout", "[logger][logging][stdout]") {
|
|
auto logger = create_logger("test");
|
|
|
|
REQUIRE("test" == logger.source());
|
|
REQUIRE("default" == logger.domain());
|
|
|
|
auto logsink = create_stdout_sink();
|
|
|
|
// Redirect stdout
|
|
{
|
|
filehelper::std_stream_switcher stdout_switcher(stdout, "stdout.txt");
|
|
add_log_sink(logsink);
|
|
logger.info("information");
|
|
}
|
|
|
|
logsink->close();
|
|
|
|
validate_log_file_line("stdout.txt", 0, "INFO", "test", "information");
|
|
|
|
matador::os::remove("stdout.txt");
|
|
|
|
REQUIRE(!matador::os::exists("stdout.txt"));
|
|
|
|
log_manager::instance().clear();
|
|
}
|
|
|
|
TEST_CASE("Test log stderr", "[logger][logging][stderr]") {
|
|
auto logger = create_logger("test");
|
|
|
|
REQUIRE("test" == logger.source());
|
|
REQUIRE("default" == logger.domain());
|
|
|
|
auto logsink = create_stderr_sink();
|
|
|
|
// Redirect stdout
|
|
{
|
|
filehelper::std_stream_switcher stdout_switcher(stderr, "stderr.txt");
|
|
add_log_sink(logsink);
|
|
logger.info("information");
|
|
}
|
|
|
|
logsink->close();
|
|
|
|
validate_log_file_line("stderr.txt", 0, "INFO", "test", "information");
|
|
|
|
matador::os::remove("stderr.txt");
|
|
|
|
REQUIRE(!matador::os::exists("stderr.txt"));
|
|
|
|
log_manager::instance().clear();
|
|
}
|
|
|
|
TEST_CASE("Test log levels", "[logger][levels]") {
|
|
std::stringstream out;
|
|
|
|
out << log_level::LVL_ERROR;
|
|
|
|
REQUIRE("ERROR" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_DEBUG;
|
|
|
|
REQUIRE("DEBUG" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_INFO;
|
|
|
|
REQUIRE("INFO" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_FATAL;
|
|
|
|
REQUIRE("FATAL" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_TRACE;
|
|
|
|
REQUIRE("TRACE" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_WARN;
|
|
|
|
REQUIRE("WARN" == out.str());
|
|
|
|
out.str("");
|
|
out.clear();
|
|
out << log_level::LVL_ALL;
|
|
|
|
REQUIRE("ALL" == out.str());
|
|
}
|
|
|
|
void validate_log_file_line(const std::string &filename, int line_index, const std::string &level, const std::string &src, const std::string &msg) {
|
|
std::ifstream logfile(filename);
|
|
|
|
REQUIRE(logfile.good());
|
|
|
|
std::string line;
|
|
int line_count = 0;
|
|
do {
|
|
if (!std::getline(logfile, line).good()) {
|
|
FAIL("couldn't find line");
|
|
}
|
|
} while (line_count++ < line_index);
|
|
|
|
const std::regex logline_regex(R"(^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d{3}.*\[([A-Z]+)\s*\] \[(.*)\]: (.*)$)");
|
|
std::smatch match;
|
|
|
|
REQUIRE(std::regex_match(line, match, logline_regex));
|
|
REQUIRE(4 == match.size());
|
|
|
|
REQUIRE(level == match[1].str());
|
|
REQUIRE(src ==match[2].str());
|
|
REQUIRE(msg == match[3].str());
|
|
} |