mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 00:11:55 +07:00
simple logger module
This commit is contained in:
@@ -7,3 +7,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "_core.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
||||
@@ -9,3 +9,16 @@
|
||||
#include "../base/_all.hpp"
|
||||
#include "../math/_all.hpp"
|
||||
#include "../utils/_all.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class debug;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename ModuleT >
|
||||
ModuleT& the() {
|
||||
return modules::instance<ModuleT>();
|
||||
}
|
||||
}
|
||||
|
||||
134
headers/enduro2d/core/debug.hpp
Normal file
134
headers/enduro2d/core/debug.hpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_core.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class debug final : public module<debug> {
|
||||
public:
|
||||
enum class level : u8 {
|
||||
trace,
|
||||
warning,
|
||||
error,
|
||||
fatal
|
||||
};
|
||||
|
||||
class sink : private e2d::noncopyable {
|
||||
public:
|
||||
virtual ~sink() noexcept = default;
|
||||
virtual bool on_message(level lvl, str_view text) noexcept = 0;
|
||||
};
|
||||
using sink_uptr = std::unique_ptr<sink>;
|
||||
public:
|
||||
debug();
|
||||
~debug();
|
||||
|
||||
template < typename T, typename... Args >
|
||||
T& add_sink(Args&&... args);
|
||||
sink& add_sink(sink_uptr sink);
|
||||
|
||||
template < typename T, typename... Args >
|
||||
T& add_sink_ex(level min_lvl, Args&&... args);
|
||||
sink& add_sink_ex(level min_lvl, sink_uptr sink);
|
||||
|
||||
void set_min_level(level lvl) noexcept;
|
||||
level min_level() const noexcept;
|
||||
|
||||
template < typename... Args >
|
||||
void log(level lvl, str_view fmt, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
void trace(str_view fmt, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
void warning(str_view fmt, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
void error(str_view fmt, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
void fatal(str_view fmt, Args&&... args);
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
level min_level_ = level::trace;
|
||||
vector<std::pair<level, sink_uptr>> sinks_;
|
||||
};
|
||||
|
||||
class debug_file_sink final : public debug::sink {
|
||||
public:
|
||||
debug_file_sink(str_view path);
|
||||
bool on_message(debug::level lvl, str_view text) noexcept final;
|
||||
private:
|
||||
str path_;
|
||||
};
|
||||
|
||||
class debug_console_sink final : public debug::sink {
|
||||
public:
|
||||
bool on_message(debug::level lvl, str_view text) noexcept final;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename T, typename... Args >
|
||||
T& debug::add_sink(Args&&... args) {
|
||||
return add_sink_ex<T>(level::trace, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline debug::sink& debug::add_sink(sink_uptr sink) {
|
||||
return add_sink_ex(level::trace, std::move(sink));
|
||||
}
|
||||
|
||||
template < typename T, typename... Args >
|
||||
T& debug::add_sink_ex(level min_lvl, Args&&... args) {
|
||||
return static_cast<T&>(add_sink_ex(
|
||||
min_lvl,
|
||||
std::make_unique<T>(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
inline debug::sink& debug::add_sink_ex(level min_lvl, sink_uptr sink) {
|
||||
E2D_ASSERT(sink);
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
sinks_.push_back(std::make_pair(min_lvl, std::move(sink)));
|
||||
return *sinks_.back().second;
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void debug::log(level lvl, str_view fmt, Args&&... args) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
if ( lvl >= min_level_ && !sinks_.empty() ) {
|
||||
str text = strings::rformat(fmt, std::forward<Args>(args)...);
|
||||
for ( const auto& pair : sinks_ ) {
|
||||
if ( lvl >= pair.first && pair.second ) {
|
||||
pair.second->on_message(lvl, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void debug::trace(str_view fmt, Args&&... args) {
|
||||
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void debug::warning(str_view fmt, Args&&... args) {
|
||||
log(level::warning, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void debug::error(str_view fmt, Args&&... args) {
|
||||
log(level::error, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void debug::fatal(str_view fmt, Args&&... args) {
|
||||
log(level::fatal, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
89
sources/enduro2d/core/debug.cpp
Normal file
89
sources/enduro2d/core/debug.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/core/debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
const char* level_to_str(debug::level lvl) noexcept {
|
||||
switch ( lvl ) {
|
||||
case debug::level::trace: return "trace";
|
||||
case debug::level::warning: return "warning";
|
||||
case debug::level::error: return "error";
|
||||
case debug::level::fatal: return "fatal";
|
||||
default:
|
||||
E2D_ASSERT(false);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
str log_text_format(debug::level lvl, str_view text) {
|
||||
return strings::rformat(
|
||||
"[%0](%1) -> %2\n",
|
||||
level_to_str(lvl), time::now_ms(), text);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// debug
|
||||
//
|
||||
|
||||
debug::debug() {
|
||||
}
|
||||
|
||||
debug::~debug() {
|
||||
}
|
||||
|
||||
void debug::set_min_level(level lvl) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
min_level_ = lvl;
|
||||
}
|
||||
|
||||
debug::level debug::min_level() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return min_level_;
|
||||
}
|
||||
|
||||
//
|
||||
// debug_file_sink
|
||||
//
|
||||
|
||||
debug_file_sink::debug_file_sink(str_view path)
|
||||
: path_(path) {
|
||||
make_write_file(path, false);
|
||||
}
|
||||
|
||||
bool debug_file_sink::on_message(debug::level lvl, str_view text) noexcept {
|
||||
try {
|
||||
auto file = make_write_file(path_, true);
|
||||
str log_text = log_text_format(lvl, text);
|
||||
return file && output_sequence(*file)
|
||||
.write(log_text.c_str(), log_text.length())
|
||||
.success();
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// debug_console_sink
|
||||
//
|
||||
|
||||
bool debug_console_sink::on_message(debug::level lvl, str_view text) noexcept {
|
||||
try {
|
||||
str log_text = log_text_format(lvl, text);
|
||||
const std::ptrdiff_t rprintf = std::printf("%s", log_text.c_str());
|
||||
return rprintf >= 0
|
||||
&& math::numeric_cast<std::size_t>(rprintf) == log_text.length();
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
untests/sources/untests_core/debug.cpp
Normal file
70
untests/sources/untests_core/debug.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_core.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
class test_sink final : public debug::sink {
|
||||
public:
|
||||
str on_message_acc;
|
||||
|
||||
bool on_message(debug::level lvl, str_view text) noexcept final {
|
||||
E2D_UNUSED(lvl);
|
||||
on_message_acc.append(text.cbegin(), text.cend());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("debug"){
|
||||
{
|
||||
debug d;
|
||||
test_sink& s = d.add_sink<test_sink>();
|
||||
REQUIRE(s.on_message_acc.empty());
|
||||
d.trace("h");
|
||||
d.warning("e");
|
||||
REQUIRE(s.on_message_acc == "he");
|
||||
d.set_min_level(debug::level::error);
|
||||
d.trace("el");
|
||||
d.warning("lo");
|
||||
REQUIRE(s.on_message_acc == "he");
|
||||
d.error("ll");
|
||||
REQUIRE(s.on_message_acc == "hell");
|
||||
d.fatal("o");
|
||||
REQUIRE(s.on_message_acc == "hello");
|
||||
}
|
||||
{
|
||||
modules::initialize<debug>();
|
||||
test_sink& s1 = the<debug>().add_sink_ex<test_sink>(debug::level::warning);
|
||||
test_sink& s2 = the<debug>().add_sink_ex<test_sink>(debug::level::error);
|
||||
REQUIRE(s1.on_message_acc.empty());
|
||||
REQUIRE(s2.on_message_acc.empty());
|
||||
the<debug>().trace("w");
|
||||
REQUIRE(s1.on_message_acc.empty());
|
||||
REQUIRE(s2.on_message_acc.empty());
|
||||
the<debug>().warning("w");
|
||||
REQUIRE(s1.on_message_acc == "w");
|
||||
REQUIRE(s2.on_message_acc.empty());
|
||||
the<debug>().error("q");
|
||||
REQUIRE(s1.on_message_acc == "wq");
|
||||
REQUIRE(s2.on_message_acc == "q");
|
||||
the<debug>().fatal("e");
|
||||
REQUIRE(s1.on_message_acc == "wqe");
|
||||
REQUIRE(s2.on_message_acc == "qe");
|
||||
the<debug>().set_min_level(debug::level::fatal);
|
||||
the<debug>().error("r");
|
||||
REQUIRE(s1.on_message_acc == "wqe");
|
||||
REQUIRE(s2.on_message_acc == "qe");
|
||||
the<debug>().fatal("r");
|
||||
REQUIRE(s1.on_message_acc == "wqer");
|
||||
REQUIRE(s2.on_message_acc == "qer");
|
||||
modules::shutdown<debug>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user