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 log_level minimum_log_level = log_level::Debug;
184
185 void logger_tick() noexcept;
186 void gather_tick(bool last) noexcept;
187
188 template<log_level Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
189 void
190 log(typename cpu_counter_clock::time_point timestamp, Args &&...args) noexcept
191 {
192 if (Level >= minimum_log_level) {
193 // Add messages in the queue, block when full.
194 // * This reduces amount of instructions needed to be executed during logging.
195 // * Simplifies logged_fatal_message logic.
196 // * Will make sure everything gets logged.
197 // * Blocking is bad in a real time thread, so maybe count the number of times it is blocked.
198 auto message = message_queue.write<"logger_blocked">();
199
200 // dereference the message so that we get the polymorphic_optional, so this assignment will work correctly.
202 timestamp, std::forward<Args>(args)...);
203
204 if constexpr (Level >= log_level::Fatal) {
205 // Make sure everything including this message and counters are logged.
206 terminateOnFatalError((*message)->format());
207
208 } else if constexpr (Level >= log_level::Error) {
209 // Actually logging of tracing will only work when we cleanly unwind the stack and destruct all trace objects
210 // this will not work on fatal messages.
211 trace_record();
212 }
213 }
214 }
215
216private:
217 void write(std::string const &str) noexcept;
218 void writeToFile(std::string str) noexcept;
219 void writeToConsole(std::string str) noexcept;
220 void display_time_calibration() noexcept;
221 void display_counters() noexcept;
222 void display_trace_statistics() noexcept;
223};
224
225// The constructor of logger only starts the logging thread.
226// The ring buffer of the logger is triviality constructed and can be used before the logger's constructor is stared.
227inline logger_type logger = {};
228
229} // namespace tt
230
231#define tt_log(level, fmt, ...) \
232 do { \
233 ttlet _tt_log_timestamp = ::tt::cpu_counter_clock::now(); \
234 ::tt::logger.log<level, __FILE__, __LINE__, fmt>(_tt_log_timestamp __VA_OPT__(,) __VA_ARGS__); \
235 } while (false)
236
237#define tt_log_debug(fmt, ...) tt_log(::tt::log_level::Debug, fmt __VA_OPT__(,) __VA_ARGS__)
238#define tt_log_trace(fmt, ...) tt_log(::tt::log_level::Trace, fmt __VA_OPT__(,) __VA_ARGS__)
239#define tt_log_counter(fmt, ...) tt_log(::tt::log_level::Counter, fmt __VA_OPT__(,) __VA_ARGS__)
240#define tt_log_info(fmt, ...) tt_log(::tt::log_level::Info, fmt __VA_OPT__(,) __VA_ARGS__)
241#define tt_log_audit(fmt, ...) tt_log(::tt::log_level::Audit, fmt __VA_OPT__(,) __VA_ARGS__)
242#define tt_log_warning(fmt, ...) tt_log(::tt::log_level::Warning, fmt __VA_OPT__(,) __VA_ARGS__)
243#define tt_log_error(fmt, ...) tt_log(::tt::log_level::Error, fmt __VA_OPT__(,) __VA_ARGS__)
244#define tt_log_critical(fmt, ...) tt_log(::tt::log_level::Critical, fmt __VA_OPT__(,) __VA_ARGS__)
245#define tt_log_fatal(fmt, ...) \
246 tt_log(::tt::log_level::Fatal, fmt __VA_OPT__(,) __VA_ARGS__); \
247 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)