HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
logger.hpp
1// Copyright Take Vos 2019-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "counters.hpp"
8#include "cpu_counter_clock.hpp"
9#include "hires_utc_clock.hpp"
10#include "polymorphic_optional.hpp"
11#include "wfree_message_queue.hpp"
12#include "atomic.hpp"
13#include "meta.hpp"
14#include "format.hpp"
15#include "source_location.hpp"
16#include "os_detect.hpp"
17#include "delayed_format.hpp"
18#include "fixed_string.hpp"
19#include "system_status.hpp"
20#include <date/tz.h>
21#include <fmt/format.h>
22#include <fmt/ostream.h>
23#include <string>
24#include <string_view>
25#include <tuple>
26#include <mutex>
27#include <atomic>
28
29namespace tt {
30
31enum class log_level : uint8_t {
32 debug = to_log_level(system_status_type::log_level_debug),
33 info = to_log_level(system_status_type::log_level_info),
34 statistics = to_log_level(system_status_type::log_level_statistics),
35 trace = to_log_level(system_status_type::log_level_trace),
36 audit = to_log_level(system_status_type::log_level_audit),
37 warning = to_log_level(system_status_type::log_level_warning),
38 error = to_log_level(system_status_type::log_level_error),
39 fatal = to_log_level(system_status_type::log_level_fatal)
40};
41
42[[nodiscard]] constexpr log_level operator&(log_level const &lhs, log_level const &rhs) noexcept
43{
44 return static_cast<log_level>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
45}
46
47[[nodiscard]] constexpr log_level operator|(log_level const &lhs, log_level const &rhs) noexcept
48{
49 return static_cast<log_level>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
50}
51
52constexpr log_level &operator|=(log_level &lhs, log_level const &rhs) noexcept
53{
54 lhs = lhs | rhs;
55 return lhs;
56}
57
60[[nodiscard]] constexpr log_level make_log_level(log_level user_level) noexcept
61{
62 auto r = log_level{};
63
64 switch (user_level) {
65 using enum log_level;
66 case debug: r |= log_level::debug; [[fallthrough]];
67 case info: r |= info; [[fallthrough]];
68 case warning:
69 r |= statistics;
70 r |= warning;
71 [[fallthrough]];
72 case error:
73 r |= trace;
74 r |= error;
75 r |= fatal;
76 r |= audit;
77 break;
78 default: tt_no_default();
79 }
80
81 return r;
82}
83
84constexpr char const *to_const_string(log_level level) noexcept
85{
86 if (level >= log_level::fatal) {
87 return "fatal";
88 } else if (level >= log_level::error) {
89 return "error";
90 } else if (level >= log_level::warning) {
91 return "warning";
92 } else if (level >= log_level::audit) {
93 return "audit";
94 } else if (level >= log_level::trace) {
95 return "trace";
96 } else if (level >= log_level::statistics) {
97 return "statistics";
98 } else if (level >= log_level::info) {
99 return "info";
100 } else if (level >= log_level::debug) {
101 return "debug";
102 } else {
103 return "none";
104 }
105}
106
107inline void system_status_set_log_level(log_level level) noexcept
108{
109 return system_status_set_log_level(static_cast<uint8_t>(level));
110}
111
112inline int command_line_argument_to_log_level(std::string_view str)
113{
114 if (str == "debug") {
115 return static_cast<int>(make_log_level(log_level::debug));
116
117 } else if (str == "info") {
118 return static_cast<int>(make_log_level(log_level::info));
119
120 } else if (str == "warning") {
121 return static_cast<int>(make_log_level(log_level::warning));
122
123 } else if (str == "error") {
124 return static_cast<int>(make_log_level(log_level::error));
125
126 } else {
127 throw parse_error("Unknown log level '{}'", str);
128 }
129}
130
132public:
133 log_message_base() noexcept = default;
134 virtual ~log_message_base() = default;
135
136 log_message_base(log_message_base const &) = delete;
138 log_message_base &operator=(log_message_base const &) = delete;
139 log_message_base &operator=(log_message_base &&) = delete;
140
141 virtual std::string format() const noexcept = 0;
142
143 static std::string cpu_utc_clock_as_iso8601(cpu_counter_clock::time_point const timestamp) noexcept;
144};
145
146template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Values>
148public:
149 static_assert(std::is_same_v<decltype(SourceFile)::value_type, char>, "SourceFile must be a basic_fixed_string<char>");
150 static_assert(std::is_same_v<decltype(Fmt)::value_type, char>, "Fmt must be a basic_fixed_string<char>");
151
152 template<typename... Args>
153 log_message(Args &&...args) noexcept :
154 _timestamp(::tt::cpu_counter_clock::now()), _what(std::forward<Args>(args)...)
155 {
156 }
157
158 std::string format() const noexcept override
159 {
160 ttlet local_timestring = log_message_base::cpu_utc_clock_as_iso8601(_timestamp);
161
162 if constexpr (static_cast<bool>(Level & log_level::statistics)) {
163 return fmt::format("{} {:5} {}\n", local_timestring, to_const_string(Level), _what());
164 } else {
165 return fmt::format("{} {:5} {} ({}:{})\n", local_timestring, to_const_string(Level), _what(), SourceFile, SourceLine);
166 }
167 }
168
169private:
171 delayed_format<Fmt, Values...> _what;
172};
173
174template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
177
178static constexpr size_t MAX_MESSAGE_SIZE = 224;
179static constexpr size_t MAX_NR_MESSAGES = 4096;
180
183
186inline log_queue_type log_queue;
187
188std::string get_last_error_message();
189
190// Forward without including trace.hpp
191void trace_record() noexcept;
192
195tt_no_inline void logger_flush() noexcept;
196
199tt_no_inline void logger_deinit() noexcept;
200
206tt_no_inline void logger_init() noexcept;
207
212inline bool logger_start()
213{
214 return system_status_start_subsystem(system_status_type::logger, logger_init, logger_deinit);
215}
216
225template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
226void log(Args &&...args) noexcept
227{
228 ttlet status = system_status.load(std::memory_order::relaxed);
229
230 if (!static_cast<bool>(to_log_level(status) & static_cast<uint8_t>(Level))) [[likely]] {
231 return;
232 }
233
234 // Add messages in the queue, block when full.
235 // * This reduces amount of instructions needed to be executed during logging.
236 // * Simplifies logged_fatal_message logic.
237 // * Will make sure everything gets logged.
238 // * Blocking is bad in a real time thread, so maybe count the number of times it is blocked.
239 //auto message = ;
240
241 // Emplace a message directly on the queue.
242 log_queue.write<"logger_blocked">()->emplace<log_message<Level, SourceFile, SourceLine, Fmt, forward_value_t<Args>...>>(
243 std::forward<Args>(args)...);
244
245 if (static_cast<bool>(Level & log_level::fatal) || !logger_start()) {
246 [[unlikely]] logger_flush();
247 }
248
249 if constexpr (static_cast<bool>(Level & log_level::fatal)) {
251
252 } else if constexpr (static_cast<bool>(Level & log_level::error)) {
253 // Actually logging of tracing will only work when we cleanly unwind the stack and destruct all trace objects.
254 trace_record();
255 }
256}
257
258} // namespace tt
259
260#define tt_log(level, fmt, ...) \
261 do { \
262 ::tt::log<level, __FILE__, __LINE__, fmt>(__VA_ARGS__); \
263 } while (false)
264
265#define tt_log_debug(fmt, ...) tt_log(::tt::log_level::debug, fmt __VA_OPT__(, ) __VA_ARGS__)
266#define tt_log_info(fmt, ...) tt_log(::tt::log_level::info, fmt __VA_OPT__(, ) __VA_ARGS__)
267#define tt_log_statistics(fmt, ...) tt_log(::tt::log_level::statistics, fmt __VA_OPT__(, ) __VA_ARGS__)
268#define tt_log_trace(fmt, ...) tt_log(::tt::log_level::trace, fmt __VA_OPT__(, ) __VA_ARGS__)
269#define tt_log_audit(fmt, ...) tt_log(::tt::log_level::audit, fmt __VA_OPT__(, ) __VA_ARGS__)
270#define tt_log_warning(fmt, ...) tt_log(::tt::log_level::warning, fmt __VA_OPT__(, ) __VA_ARGS__)
271#define tt_log_error(fmt, ...) tt_log(::tt::log_level::error, fmt __VA_OPT__(, ) __VA_ARGS__)
272#define tt_log_fatal(fmt, ...) \
273 tt_log(::tt::log_level::fatal, fmt __VA_OPT__(, ) __VA_ARGS__); \
274 tt_unreachable()
Delayed formatting.
Definition delayed_format.hpp:19
example: ``` template<tt::basic_fixed_string Foo> class A { auto bar() { return std::string{Foo}; } }...
Definition fixed_string.hpp:29
Definition logger.hpp:131
Definition logger.hpp:147
Polymorphic optional.
Definition polymorphic_optional.hpp:26
Definition wfree_message_queue.hpp:75
scoped_write_operation write() noexcept
Definition wfree_message_queue.hpp:130
T terminate(T... args)