sandbox
Loading...
Searching...
No Matches
logger.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_UTILITY_LOGGER_HPP_
3#define LIBSBX_UTILITY_LOGGER_HPP_
4
5#include <iostream>
6#include <optional>
7#include <mutex>
8#include <deque>
9#include <filesystem>
10
11#include <fmt/format.h>
12
13#include <spdlog/logger.h>
14#include <spdlog/sinks/stdout_color_sinks.h>
15#include <spdlog/sinks/basic_file_sink.h>
16#include <spdlog/sinks/base_sink.h>
17
18#include <libsbx/utility/target.hpp>
19#include <libsbx/utility/string_literal.hpp>
20
21namespace sbx::utility {
22
23namespace detail {
24
25template<typename Mutex>
26class ring_buffer_sink final : public spdlog::sinks::base_sink<Mutex> {
27
28 using base = spdlog::sinks::base_sink<Mutex>;
29
30public:
31
32 struct log_line {
33 std::string text;
34 spdlog::level::level_enum level;
35 }; // struct log_line
36
37 explicit ring_buffer_sink(const std::size_t max_lines = 512)
38 : _max_lines{max_lines} {}
39
40 [[nodiscard]] auto lines() -> std::vector<log_line> {
41 auto lock = std::lock_guard<Mutex>{base::mutex_};
42
43 return {_lines.begin(), _lines.end()};
44 }
45
46 void clear() {
47 std::lock_guard<Mutex> lock(base::mutex_);
48
49 _lines.clear();
50 }
51
52protected:
53
54 void sink_it_(const spdlog::details::log_msg& msg) override {
55 auto formatted = spdlog::memory_buf_t{};
56 base::formatter_->format(msg, formatted);
57
58 auto lock = std::lock_guard<Mutex>{base::mutex_};
59 _lines.emplace_back(fmt::to_string(formatted), msg.level);
60
61 if (_lines.size() > _max_lines) {
62 _lines.pop_front();
63 }
64 }
65
66 void flush_() override {
67
68 }
69
70private:
71
72 std::size_t _max_lines;
73 std::deque<log_line> _lines;
74
75}; // class ring_buffer_sink
76
77using ring_buffer_sink_mt = ring_buffer_sink<std::mutex>;
78using ring_buffer_sink_st = ring_buffer_sink<spdlog::details::null_mutex>;
79
81
82 static auto create_logger() -> spdlog::logger {
83 auto sinks = std::vector<std::shared_ptr<spdlog::sinks::sink>>{};
84
85 sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_st>("./demo/logs/sbx.log", true));
86
87 if constexpr (utility::build_configuration_v == utility::build_configuration::debug) {
88 sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_st>());
89 }
90
91 sink = std::make_shared<ring_buffer_sink_st>();
92
93 sinks.push_back(sink);
94
95 auto logger = spdlog::logger{"logger", std::begin(sinks), std::end(sinks)};
96
97 logger.set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
98
99 if constexpr (build_configuration_v == build_configuration::debug) {
100 logger.set_level(spdlog::level::trace);
101 } else {
102 logger.set_level(spdlog::level::info);
103 }
104
105 return logger;
106 }
107
108 inline static auto sink = std::shared_ptr<ring_buffer_sink_st>{};
109 inline static auto logger = create_logger();
110
111}; // struct logger_instance
112
113inline auto instance() -> spdlog::logger& {
114 return logger_instance::logger;
115}
116
117inline auto sink() -> std::shared_ptr<ring_buffer_sink_st>& {
118 return logger_instance::sink;
119}
120
121} // namespace detail
122
123template<string_literal Tag>
124class logger {
125
126public:
127
128 template<typename... Args>
129 using format_string_type = spdlog::format_string_t<Args...>;
130
131 logger() = delete;
132
133 ~logger() = default;
134
135 template<typename... Args>
136 static auto trace(format_string_type<Args...> format, Args&&... args) -> void {
137 detail::instance().trace("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
138 }
139
140 template<typename Type>
141 static auto trace(const Type& value) -> void {
142 detail::instance().trace("[{}] : {}", Tag, value);
143 }
144
145 template<typename... Args>
146 static auto debug(format_string_type<Args...> format, Args&&... args) -> void {
147 detail::instance().debug("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
148 }
149
150 template<typename Type>
151 static auto debug(const Type& value) -> void {
152 detail::instance().debug("[{}] : {}", Tag, value);
153 }
154
155 template<typename... Args>
156 static auto info(format_string_type<Args...> format, Args&&... args) -> void {
157 detail::instance().info("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
158 }
159
160 template<typename Type>
161 static auto info(const Type& value) -> void {
162 detail::instance().info("[{}] : {}", Tag, value);
163 }
164
165 template<typename... Args>
166 static auto warn(format_string_type<Args...> format, Args&&... args) -> void {
167 detail::instance().warn("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
168 }
169
170 template<typename Type>
171 static auto warn(const Type& value) -> void {
172 detail::instance().warn("[{}] : {}", Tag, value);
173 }
174
175 template<typename... Args>
176 static auto error(format_string_type<Args...> format, Args&&... args) -> void {
177 detail::instance().error("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
178 }
179
180 template<typename Type>
181 static auto error(const Type& value) -> void {
182 detail::instance().error("[{}] : {}", Tag, value);
183 }
184
185 template<typename... Args>
186 static auto critical(format_string_type<Args...> format, Args&&... args) -> void {
187 detail::instance().critical("[{}] : {}", Tag, fmt::format(format, std::forward<Args>(args)...));
188 }
189
190 template<typename Type>
191 static auto critical(const Type& value) -> void {
192 detail::instance().critical("[{}] : {}", Tag, value);
193 }
194
195}; // class logger
196
197} // namespace sbx::utility
198
199// [NOTE] KAJ 2024-01-19 : Enable formatting to underlying type for all enums
200template<typename Type>
201requires (std::is_enum_v<Type>)
202struct fmt::formatter<Type> : public fmt::formatter<std::underlying_type_t<Type>> {
203
204 using base_type = fmt::formatter<std::underlying_type_t<Type>>;
205
206 template<typename FormatContext>
207 auto format(const Type& value, FormatContext& context) const -> decltype(auto) {
208 return base_type::format(static_cast<std::underlying_type_t<Type>>(value), context);
209 }
210
211}; // struct fmt::formatter
212
213// [NOTE] KAJ 2025-05-27 : Enable formatting for optionals
214template<typename Type>
215struct fmt::formatter<std::optional<Type>> : public fmt::formatter<Type> {
216
217 using base_type = fmt::formatter<Type>;
218
219 template<typename FormatContext>
220 auto format(const std::optional<Type>& value, FormatContext& context) -> decltype(auto) {
221 if (value) {
222 return base_type::format(*value, context);
223 }
224
225 return fmt::format_to(context.out(), "[empty optional]");
226 }
227
228}; // struct fmt::formatter<Type>
229
230template<>
231struct fmt::formatter<std::filesystem::path> : public fmt::formatter<std::filesystem::path::string_type> {
232
233 using base_type = fmt::formatter<std::filesystem::path::string_type>;
234
235 template<typename FormatContext>
236 auto format(const std::filesystem::path& value, FormatContext& context) -> decltype(auto) {
237 return fmt::format_to(context.out(), "{}", value.string());
238 }
239
240}; // struct fmt::formatter<Type>
241
242
243#endif // LIBSBX_UTILITY_LOGGER_HPP_
Definition: logger.hpp:124
Definition: logger.hpp:202
Definition: logger.hpp:80