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 <date/tz.h>
20#include <fmt/format.h>
21#include <fmt/ostream.h>
22#include <string>
23#include <string_view>
24#include <tuple>
25#include <mutex>
26
27namespace tt {
28
29std::string getLastErrorMessage();
30
31[[noreturn]] void terminateOnFatalError(std::string &&message) noexcept;
32
33// Forward without including trace.hpp
34void trace_record() noexcept;
35
36enum class log_level : uint8_t {
38 Debug,
40 Info,
42 Trace,
44 Counter,
46 Audit,
48 Warning,
50 Error,
52 Critical,
54 Fatal,
55};
56
57constexpr char const *to_const_string(log_level level) noexcept
58{
59 switch (level) {
60 case log_level::Debug: return "DEBUG";
61 case log_level::Info: return "INFO";
62 case log_level::Trace: return "TRACE";
63 case log_level::Counter: return "COUNT";
64 case log_level::Audit: return "AUDIT";
65 case log_level::Warning: return "WARN";
66 case log_level::Error: return "ERROR";
67 case log_level::Critical: return "CRIT";
68 case log_level::Fatal: return "FATAL";
69 default: return "<unknown>";
70 }
71}
72
73inline int command_line_argument_to_log_level(std::string_view str) noexcept
74{
75 if (str == "debug") {
76 return static_cast<int>(log_level::Debug);
77 } else if (str == "info") {
78 return static_cast<int>(log_level::Info);
79 } else if (str == "audit") {
80 return static_cast<int>(log_level::Audit);
81 } else if (str == "warning") {
82 return static_cast<int>(log_level::Warning);
83 } else if (str == "error") {
84 return static_cast<int>(log_level::Error);
85 } else if (str == "critical") {
86 return static_cast<int>(log_level::Critical);
87 } else if (str == "fatal") {
88 return static_cast<int>(log_level::Fatal);
89 } else {
90 return -1;
91 }
92}
93
94constexpr bool operator<(log_level lhs, log_level rhs) noexcept
95{
96 return static_cast<int>(lhs) < static_cast<int>(rhs);
97}
98constexpr bool operator>(log_level lhs, log_level rhs) noexcept
99{
100 return rhs < lhs;
101}
102constexpr bool operator==(log_level lhs, log_level rhs) noexcept
103{
104 return static_cast<int>(lhs) == static_cast<int>(rhs);
105}
106constexpr bool operator!=(log_level lhs, log_level rhs) noexcept
107{
108 return !(lhs == rhs);
109}
110constexpr bool operator<=(log_level lhs, log_level rhs) noexcept
111{
112 return !(lhs > rhs);
113}
114constexpr bool operator>=(log_level lhs, log_level rhs) noexcept
115{
116 return !(lhs < rhs);
117}
118
120public:
121 log_message_base() noexcept = default;
122 virtual ~log_message_base() = default;
123
124 log_message_base(log_message_base const &) = delete;
126 log_message_base &operator=(log_message_base const &) = delete;
127 log_message_base &operator=(log_message_base &&) = delete;
128
129 virtual std::string format() const noexcept = 0;
130
131 static std::string cpu_utc_clock_as_iso8601(cpu_counter_clock::time_point const timestamp) noexcept;
132};
133
134template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Values>
136public:
137 static_assert(std::is_same_v<decltype(SourceFile)::value_type, char>, "SourceFile must be a basic_fixed_string<char>");
138 static_assert(std::is_same_v<decltype(Fmt)::value_type, char>, "Fmt must be a basic_fixed_string<char>");
139
140 template<typename... Args>
141 log_message(cpu_counter_clock::time_point timestamp, Args &&...args) noexcept :
142 _timestamp(timestamp), _what(std::forward<Args>(args)...)
143 {
144 }
145
146 std::string format() const noexcept override
147 {
148 ttlet local_timestring = log_message_base::cpu_utc_clock_as_iso8601(_timestamp);
149
150 if constexpr (Level == log_level::Trace || Level == log_level::Counter) {
151 return fmt::format("{} {:5} {}", local_timestring, to_const_string(Level), _what());
152 } else {
153 return fmt::format(
154 "{} {:5} {} ({}:{})", local_timestring, to_const_string(Level), _what(), SourceFile, SourceLine);
155 }
156 }
157
158private:
160 delayed_format<Fmt, Values...> _what;
161};
162
163template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
166
170 static constexpr size_t MAX_MESSAGE_SIZE = 224;
171 static constexpr size_t MESSAGE_ALIGNMENT = 256;
172 static constexpr size_t MAX_NR_MESSAGES = 4096;
173
176
178 message_queue_type message_queue;
179
180 hires_utc_clock::time_point next_gather_time = {};
181
182public:
183 logger_type() noexcept;
184
185 log_level minimum_log_level = log_level::Debug;
186
187 void logger_tick() noexcept;
188 void gather_tick(bool last) noexcept;
189
190 template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
191 void
192 log(typename cpu_counter_clock::time_point timestamp, Args &&...args) noexcept
193 {
194 if (Level >= minimum_log_level) {
195 // Add messages in the queue, block when full.
196 // * This reduces amount of instructions needed to be executed during logging.
197 // * Simplifies logged_fatal_message logic.
198 // * Will make sure everything gets logged.
199 // * Blocking is bad in a real time thread, so maybe count the number of times it is blocked.
200 auto message = message_queue.write<"logger_blocked">();
201
202 // dereference the message so that we get the polymorphic_optional, so this assignment will work correctly.
204 timestamp, std::forward<Args>(args)...);
205
206 if constexpr (Level >= log_level::Fatal) {
207 // Make sure everything including this message and counters are logged.
208 terminateOnFatalError((*message)->format());
209
210 } else if constexpr (Level >= log_level::Error) {
211 // Actually logging of tracing will only work when we cleanly unwind the stack and destruct all trace objects
212 // this will not work on fatal messages.
213 trace_record();
214 }
215 }
216 }
217
218private:
219 void write(std::string const &str) noexcept;
220 void writeToFile(std::string str) noexcept;
221 void writeToConsole(std::string str) noexcept;
222 void display_time_calibration() noexcept;
223 void display_counters() noexcept;
224 void display_trace_statistics() noexcept;
225};
226
227// The constructor of logger only starts the logging thread.
228// The ring buffer of the logger is triviality constructed and can be used before the logger's constructor is stared.
229inline logger_type logger = {};
230
231} // namespace tt
232
233#define tt_log(level, fmt, ...) \
234 do { \
235 ttlet _tt_log_timestamp = ::tt::cpu_counter_clock::now(); \
236 ::tt::logger.log<level, __FILE__, __LINE__, fmt>(_tt_log_timestamp __VA_OPT__(,) __VA_ARGS__); \
237 } while (false)
238
239#define tt_log_debug(fmt, ...) tt_log(::tt::log_level::Debug, fmt __VA_OPT__(,) __VA_ARGS__)
240#define tt_log_trace(fmt, ...) tt_log(::tt::log_level::Trace, fmt __VA_OPT__(,) __VA_ARGS__)
241#define tt_log_counter(fmt, ...) tt_log(::tt::log_level::Counter, fmt __VA_OPT__(,) __VA_ARGS__)
242#define tt_log_info(fmt, ...) tt_log(::tt::log_level::Info, fmt __VA_OPT__(,) __VA_ARGS__)
243#define tt_log_audit(fmt, ...) tt_log(::tt::log_level::Audit, fmt __VA_OPT__(,) __VA_ARGS__)
244#define tt_log_warning(fmt, ...) tt_log(::tt::log_level::Warning, fmt __VA_OPT__(,) __VA_ARGS__)
245#define tt_log_error(fmt, ...) tt_log(::tt::log_level::Error, fmt __VA_OPT__(,) __VA_ARGS__)
246#define tt_log_critical(fmt, ...) tt_log(::tt::log_level::Critical, fmt __VA_OPT__(,) __VA_ARGS__)
247#define tt_log_fatal(fmt, ...) \
248 tt_log(::tt::log_level::Fatal, fmt __VA_OPT__(,) __VA_ARGS__); \
249 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:119
Definition logger.hpp:135
Definition logger.hpp:169
Polymorphic optional.
Definition polymorphic_optional.hpp:26
scoped_write_operation write() noexcept
Definition wfree_message_queue.hpp:130
T operator>(T... args)