HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
log.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 "time_stamp_count.hpp"
8#include "time_stamp_utc.hpp"
9#include "wfree_fifo.hpp"
10#include "atomic.hpp"
11#include "meta.hpp"
12#include "source_location.hpp"
13#include "architecture.hpp"
14#include "delayed_format.hpp"
15#include "fixed_string.hpp"
16#include "subsystem.hpp"
17#include "global_state.hpp"
18#include "URL.hpp"
19#include "unfair_mutex.hpp"
20#include "debugger.hpp"
21#include "format_check.hpp"
22#include "thread.hpp"
23#include <chrono>
24#include <format>
25#include <string>
26#include <string_view>
27#include <tuple>
28#include <mutex>
29#include <atomic>
30#include <memory>
31#include <thread>
32
33namespace hi::inline v1 {
34namespace detail {
35
37public:
38 hi_force_inline log_message_base() noexcept = default;
39 virtual ~log_message_base() = default;
40
41 [[nodiscard]] virtual std::string format() const noexcept = 0;
42 [[nodiscard]] virtual std::unique_ptr<log_message_base> make_unique_copy() const noexcept = 0;
43
44public:
45 static inline std::chrono::time_zone const *zone = nullptr;
46};
47
48template<global_state_type Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Values>
50public:
51 static_assert(std::popcount(to_underlying(Level)) == 1);
52 static_assert(
53 std::is_same_v<typename decltype(SourceFile)::value_type, char>,
54 "SourceFile must be a basic_fixed_string<char>");
55 static_assert(std::is_same_v<typename decltype(Fmt)::value_type, char>, "Fmt must be a basic_fixed_string<char>");
56
57 // clang-format off
58 static constexpr char const *log_level_name =
59 Level == global_state_type::log_fatal ? "fatal" :
60 Level == global_state_type::log_error ? "error" :
61 Level == global_state_type::log_warning ? "warning" :
62 Level == global_state_type::log_info ? "info" :
63 Level == global_state_type::log_debug ? "debug" :
64 Level == global_state_type::log_trace ? "trace" :
65 Level == global_state_type::log_audit ? "audit" :
66 Level == global_state_type::log_statistics ? "stats" :
67 "<unknown log level>";
68 // clang-format on
69
70 log_message(log_message const&) noexcept = default;
71 log_message& operator=(log_message const&) noexcept = default;
72
73 template<typename... Args>
74 hi_force_inline log_message(Args&&...args) noexcept :
75 _time_stamp(time_stamp_count::inplace_with_thread_id{}), _what(std::forward<Args>(args)...)
76 {
77 }
78
79 std::string format() const noexcept override
80 {
81 hilet utc_time_point = time_stamp_utc::make(_time_stamp);
82 hilet sys_time_point = std::chrono::clock_cast<std::chrono::system_clock>(utc_time_point);
83 hilet local_time_point = zone->to_local(sys_time_point);
84
85 hilet cpu_id = _time_stamp.cpu_id();
86 hilet thread_id = _time_stamp.thread_id();
87 hilet thread_name = get_thread_name(thread_id);
88
89 if constexpr (to_bool(Level & global_state_type::log_statistics)) {
90 return std::format("{} {}({}) {:5} {}\n", local_time_point, thread_name, cpu_id, log_level_name, _what());
91 } else {
92 return std::format(
93 "{} {}({}) {:5} {} ({}:{})\n",
94 local_time_point,
95 thread_name,
96 cpu_id,
97 log_level_name,
98 _what(),
99 URL::urlFromPath(SourceFile).filename(),
100 SourceLine);
101 }
102 }
103
104 [[nodiscard]] std::unique_ptr<log_message_base> make_unique_copy() const noexcept override
105 {
106 return std::make_unique<log_message>(*this);
107 }
108
109private:
110 time_stamp_count _time_stamp;
111 delayed_format<Fmt, Values...> _what;
112};
113
114} // namespace detail
115
116class log {
117public:
126 template<global_state_type Level, basic_fixed_string SourceFile, int SourceLine, basic_fixed_string Fmt, typename... Args>
127 hi_force_inline void add(Args&&...args) noexcept
128 {
129 static_assert(std::popcount(to_underlying(Level)) == 1);
130
131 hilet state = global_state.load(std::memory_order::relaxed);
132 if (not to_bool(state & Level)) {
133 return;
134 }
135
136 // Add messages in the queue, block when full.
137 // * This reduces amount of instructions needed to be executed during logging.
138 // * Simplifies logged_fatal_message logic.
139 // * Will make sure everything gets logged.
140 // * Blocking is bad in a real time thread, so maybe count the number of times it is blocked.
141
142 // Emplace a message directly on the queue.
144 std::forward<Args>(args)...);
145
146 if (to_bool(Level & global_state_type::log_fatal) or not to_bool(state & global_state_type::log_is_running)) {
147 // If the logger did not start we will log in degraded mode and log from the current thread.
148 // On fatal error we also want to log from the current thread.
149 [[unlikely]] flush();
150 }
151 }
152
157 hi_no_inline void flush() noexcept;
158
166 static bool start_subsystem(global_state_type log_level = global_state_type::log_level_default)
167 {
168 set_log_level(log_level);
169 return hi::start_subsystem(global_state_type::log_is_running, log::subsystem_init, log::subsystem_deinit);
170 }
171
175 static void stop_subsystem()
176 {
177 return hi::stop_subsystem(log::subsystem_deinit);
178 }
179
180private:
184 mutable unfair_mutex _mutex;
185
190 void write(std::string const& str) const noexcept;
191
194 static inline std::jthread _log_thread;
195
198 static void log_thread_main(std::stop_token stop_token) noexcept;
199
202 static void subsystem_deinit() noexcept;
203
209 static bool subsystem_init() noexcept;
210};
211
212inline log log_global;
213
216[[nodiscard]] std::string get_last_error_message() noexcept;
217
218} // namespace hi::inline v1
219
220#define hi_log(level, fmt, ...) \
221 hi_format_check(fmt __VA_OPT__(, ) __VA_ARGS__); \
222 ::hi::log_global.add<level, __FILE__, __LINE__, fmt>(__VA_ARGS__)
223
224#define hi_log_debug(fmt, ...) hi_log(::hi::global_state_type::log_debug, fmt __VA_OPT__(, ) __VA_ARGS__)
225#define hi_log_info(fmt, ...) hi_log(::hi::global_state_type::log_info, fmt __VA_OPT__(, ) __VA_ARGS__)
226#define hi_log_statistics(fmt, ...) hi_log(::hi::global_state_type::log_statistics, fmt __VA_OPT__(, ) __VA_ARGS__)
227#define hi_log_trace(fmt, ...) hi_log(::hi::global_state_type::log_trace, fmt __VA_OPT__(, ) __VA_ARGS__)
228#define hi_log_audit(fmt, ...) hi_log(::hi::global_state_type::log_audit, fmt __VA_OPT__(, ) __VA_ARGS__)
229#define hi_log_warning(fmt, ...) hi_log(::hi::global_state_type::log_warning, fmt __VA_OPT__(, ) __VA_ARGS__)
230#define hi_log_error(fmt, ...) hi_log(::hi::global_state_type::log_error, fmt __VA_OPT__(, ) __VA_ARGS__)
231#define hi_log_fatal(fmt, ...) \
232 hi_log(::hi::global_state_type::log_fatal, fmt __VA_OPT__(, ) __VA_ARGS__); \
233 hi_debug_abort()
234
235#define hi_log_info_once(name, fmt, ...) \
236 do { \
237 if (++global_counter<name> == 1) { \
238 hi_log(::hi::global_state_type::log_info, fmt __VA_OPT__(, ) __VA_ARGS__); \
239 } \
240 } while (false)
241
242#define hi_log_error_once(name, fmt, ...) \
243 do { \
244 if (++global_counter<name> == 1) { \
245 hi_log(::hi::global_state_type::log_error, fmt __VA_OPT__(, ) __VA_ARGS__); \
246 } \
247 } while (false)
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Functions and macros for handling architectural difference between compilers, CPUs and operating syst...
STL namespace.
Delayed formatting.
Definition delayed_format.hpp:19
example: ``` template<hi::basic_fixed_string Foo> class A { auto bar() { return std::string{Foo}; } }...
Definition fixed_string.hpp:33
Definition log.hpp:36
Definition log.hpp:49
Definition log.hpp:116
hi_no_inline void flush() noexcept
Flush all messages from the log_queue directly from this thread.
hi_force_inline void add(Args &&...args) noexcept
Log a message.
Definition log.hpp:127
static void stop_subsystem()
Stop the logger system.
Definition log.hpp:175
Since Window's 10 QueryPerformanceCounter() counts at only 10MHz which is too low to measure performa...
Definition time_stamp_count.hpp:29
Definition time_stamp_count.hpp:35
A wait-free multiple-producer/single-consumer fifo designed for absolute performance.
Definition wfree_fifo.hpp:35