Skip to content

Commit 100db87

Browse files
committed
Refactor logging
1 parent b4443b9 commit 100db87

File tree

4 files changed

+163
-68
lines changed

4 files changed

+163
-68
lines changed

src/include/sndx/data/version.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@
66
#define SNDXLIB_MINOR 3
77
#define SNDXLIB_PATCH 1
88

9+
static_assert(std::is_integral_v<decltype(SNDXLIB_MAJOR)>);
10+
static_assert(std::is_integral_v<decltype(SNDXLIB_MINOR)>);
11+
static_assert(std::is_integral_v<decltype(SNDXLIB_PATCH)>);
12+
913
#define SNDX_AS_STRING(macro) #macro
1014

1115
#define DETAIL_SNDXLIB_VERSION(major, minor, patch) (SNDX_AS_STRING(major) "." SNDX_AS_STRING(minor) "." SNDX_AS_STRING(patch))
1216
#define SNDXLIB_VERSION DETAIL_SNDXLIB_VERSION(SNDXLIB_MAJOR, SNDXLIB_MINOR, SNDXLIB_PATCH)
1317

18+
static_assert(std::is_constructible_v<std::string, decltype(SNDXLIB_VERSION)>);
19+
1420
namespace sndx {
1521
struct Version {
1622
int major{}, minor{}, patch{};

src/include/sndx/platform/shared_lib.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cstdint>
34
#include <string>
45
#include <string_view>
56
#include <unordered_map>
Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,103 @@
11
#pragma once
22

3-
#include <iostream>
43
#include <atomic>
4+
#include <concepts>
5+
#include <format>
56
#include <functional>
7+
#include <string>
68
#include <string_view>
79

8-
#include <vector>
9-
#include <mutex>
10-
11-
#include <algorithm>
12-
1310
namespace sndx::utility {
11+
struct LogLevel {
12+
using T = intmax_t;
13+
14+
static constexpr T increment = 10;
15+
16+
static constexpr T Info = 0;
17+
static constexpr T Debug = Info - increment;
18+
static constexpr T Trace = Debug - increment;
19+
20+
static constexpr T Warning = Info + increment;
21+
static constexpr T Error = Warning + increment;
22+
23+
#ifdef _DEBUG
24+
static constexpr T Default = Debug;
25+
#else
26+
static constexpr T Default = Info;
27+
#endif
28+
[[nodiscard]]
29+
static constexpr std::string_view toString(T level) {
30+
if (level >= Error) {
31+
return "Error";
32+
}
33+
if (level >= Warning) {
34+
return "Warning";
35+
}
36+
if (level >= Info) {
37+
return "Info";
38+
}
39+
if (level >= Debug) {
40+
return "Debug";
41+
}
42+
return "Trace";
43+
}
44+
};
45+
using LogLevelT = LogLevel::T;
1446

15-
template <typename CharT = char>
16-
using Formatter = std::function<std::basic_string<CharT>(std::basic_string_view<CharT>)>;
17-
18-
template <typename CharT = char>
19-
std::basic_string<CharT> formatterNone(std::basic_string_view<CharT> str) { return std::basic_string<CharT>(str); }
20-
21-
template <typename CharT = char>
2247
class Logger {
48+
private:
49+
std::atomic<LogLevelT> m_level{ LogLevel::Default };
50+
2351
protected:
24-
std::basic_ostream<CharT> stream;
52+
virtual void log_impl(LogLevelT level, std::string&& str) = 0;
2553

2654
public:
27-
std::atomic_bool active;
2855

29-
Logger(std::basic_streambuf<CharT>* target) :
30-
active(false), stream(target) {}
56+
template <class... Args>
57+
void log(LogLevelT level, std::format_string<Args...> fmt, Args&&... args) {
58+
if (level >= m_level.load(std::memory_order_acquire)) {
59+
log_impl(level, std::format(fmt, std::forward<Args>(args)...));
60+
}
61+
}
3162

32-
virtual void log(std::basic_string_view<CharT> msg) {
33-
if (active) stream << msg;
63+
template <class... Args>
64+
void vlog(LogLevelT level, std::string_view fmt, Args&&... args) {
65+
if (level >= m_level.load(std::memory_order_acquire)) {
66+
log_impl(level, std::vformat(fmt, std::make_format_args(args...)));
67+
}
3468
}
3569

36-
bool setActive(bool newVal) {
37-
return active.exchange(newVal);
70+
auto setLevel(LogLevelT level) noexcept {
71+
return m_level.exchange(level, std::memory_order_acq_rel);
3872
}
3973

40-
template <typename T>
41-
Logger& operator<<(const T& msg) {
42-
if (active) {
43-
stream << msg;
44-
}
45-
return *this;
74+
[[nodiscard]]
75+
auto level() const noexcept {
76+
return m_level.load(std::memory_order_acquire);
4677
}
4778

4879
virtual ~Logger() = default;
49-
};
50-
51-
// Warning: operator<< is still raw logging
52-
template <typename CharT = char>
53-
class FormatLogger : public Logger<CharT> {
54-
public:
55-
Formatter<CharT> formatter;
5680

57-
FormatLogger(std::basic_streambuf<CharT>* target, Formatter<CharT> formatter = formatterNone<CharT>) :
58-
Logger<CharT>(target), formatter(formatter) {}
59-
60-
void log(std::basic_string_view<CharT> msg) {
61-
Logger<CharT>::log(formatter(msg));
62-
}
6381
};
6482

65-
// non-owning container
66-
template <typename CharT = char>
67-
class LoggingContext {
68-
protected:
69-
std::vector<Logger<CharT>*> loggers;
70-
std::mutex contextMtx;
71-
72-
public:
73-
using LoggerT = Logger<CharT>;
74-
75-
void addLogger(LoggerT& logger) {
76-
std::unique_lock lock(contextMtx);
77-
loggers.emplace_back(&logger);
78-
}
83+
template <std::invocable F>
84+
struct LazyArg {
85+
F fnc;
86+
};
87+
}
7988

80-
void activateLogger(LoggerT& logger) {
81-
logger.setActive(true);
82-
std::unique_lock lock(contextMtx);
83-
loggers.emplace_back(&logger);
84-
}
89+
#define SNDX_MAKE_LAZY(func) \
90+
sndx::utility::LazyArg{[&](){ return (func); }}
8591

86-
void removeLogger(LoggerT& logger) {
87-
std::unique_lock lock(contextMtx);
88-
loggers.erase(std::ranges::remove(loggers, &logger));
92+
namespace std {
93+
template <std::invocable F>
94+
struct formatter<sndx::utility::LazyArg<F>> {
95+
constexpr auto parse(std::format_parse_context& ctx) {
96+
return ctx.begin();
8997
}
9098

91-
void log(std::basic_string_view<CharT> msg) {
92-
std::unique_lock lock(contextMtx);
93-
for (auto& logger : loggers) {
94-
logger->log(msg);
95-
}
99+
auto format(const sndx::utility::LazyArg<F>& arg, std::format_context& ctx) const {
100+
return std::format_to(ctx.out(), "{}", arg.fnc());
96101
}
97102
};
98-
}
103+
}

src/tests/utility/logging.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "utility/logging.hpp"
2+
3+
#include <gtest/gtest.h>
4+
5+
#include "../mock_utils.hpp"
6+
7+
using namespace sndx::utility;
8+
9+
struct MockLogger : Logger {
10+
MOCK_METHOD(void, log_impl, (LogLevelT level, std::string&& str), (override));
11+
};
12+
13+
TEST(Logging, logLogs) {
14+
MockLogger logger{};
15+
16+
EXPECT_CALL(logger, log_impl(LogLevel::Error, testing::StrEq("Warning: 123")));
17+
18+
logger.log(LogLevel::Error, "{} {}", "Warning:", 123);
19+
}
20+
21+
TEST(Logging, vlogLogs) {
22+
MockLogger logger{};
23+
24+
EXPECT_CALL(logger, log_impl(LogLevel::Error, testing::StrEq("Warning: 123")));
25+
26+
std::string vfmt = "{}";
27+
vfmt += "{}";
28+
29+
logger.vlog(LogLevel::Error, vfmt, "Warning: ", 123);
30+
}
31+
32+
TEST(Logging, logLevelIsRespected) {
33+
MockLogger logger{};
34+
35+
EXPECT_CALL(logger, log_impl(5, testing::StrEq("banana")));
36+
EXPECT_CALL(logger, log_impl(6, testing::StrEq("apple")));
37+
38+
logger.setLevel(5);
39+
40+
logger.log(5, "banana");
41+
logger.log(6, "apple");
42+
logger.log(4, "square");
43+
}
44+
45+
TEST(Logging, lazyArgIsLazy) {
46+
MockLogger logger{};
47+
48+
logger.setLevel(0);
49+
50+
size_t i = 0;
51+
52+
auto func = [&i]() {
53+
++i;
54+
return "hi";
55+
};
56+
57+
EXPECT_CALL(logger, log_impl(1, testing::StrEq("hi")));
58+
59+
logger.log(1, "{}", LazyArg{func});
60+
logger.log(-1, "{}", LazyArg{func});
61+
62+
EXPECT_EQ(i, 1);
63+
}
64+
65+
TEST(Logging, makeLazyArgWorks) {
66+
MockLogger logger{};
67+
68+
logger.setLevel(0);
69+
70+
size_t i = 0;
71+
auto func = [&](size_t b) {
72+
i += b;
73+
return i;
74+
};
75+
76+
EXPECT_CALL(logger, log_impl(1, testing::StrEq("6")));
77+
78+
auto lazy = SNDX_MAKE_LAZY(func(func(3)));
79+
EXPECT_EQ(i, 0);
80+
81+
logger.log(1, "{}", lazy);
82+
EXPECT_EQ(i, 6);
83+
}

0 commit comments

Comments
 (0)