Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
logger.h
1// SPDX-FileCopyrightText: © 2024-2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <atomic>
7#include <filesystem>
8#include <mutex>
9#include <source_location>
10#include <vector>
11
12#include <fmt/format.h>
13
14// Logger architecture: Global API Injection Pattern
15//
16// Uses a variable template (log_api<>) that defaults to NullLogApi and is
17// explicitly specialized to SpdlogApi. The log_* function templates dispatch
18// through this variable template, so spdlog is never referenced in this
19// header -- all spdlog usage lives in logger.cpp.
20//
21// This is a compile-time form of dependency injection: no virtual dispatch,
22// no singletons, no passthrough antipattern. When the logger is not
23// initialized (logger_ == nullptr), SpdlogApi::log() returns immediately
24// after a single pointer check.
25//
26// Based on Ben Deane's pattern:
27// https://www.elbeno.com/blog/?p=1831
28
29namespace zrythm::utils
30{
31class Utf8String;
32
33enum class LoggerType
34{
35 GUI,
36 Test,
37};
38
39enum class LogLevel
40{
41 Trace = 0,
42 Debug = 1,
43 Info = 2,
44 Warning = 3,
45 Error = 4,
46 Critical = 5,
47};
48
49namespace detail
50{
51
53{
54 template <typename... FmtArgs>
55 void log (
56 std::source_location,
57 LogLevel,
58 fmt::format_string<FmtArgs...>,
59 FmtArgs &&...) const
60 {
61 }
62};
63
65{
66 ~SpdlogApi ();
67
68 template <typename... FmtArgs>
69 void log (
70 std::source_location loc,
71 LogLevel level,
72 fmt::format_string<FmtArgs...> fmt,
73 FmtArgs &&... args) const
74 {
75 if (!logger_.load (std::memory_order_acquire))
76 return;
77 auto msg = fmt::format (fmt, std::forward<FmtArgs> (args)...);
78 submit (loc, level, std::move (msg));
79 }
80
81 void init (LoggerType type);
82
83 // Opaque -- defined in logger.cpp, hides spdlog from this header.
84 struct Logger;
85 // Owned raw pointer: cleaned up by ~SpdlogApi() (defined in .cpp where
86 // Logger is complete). unique_ptr doesn't work here because the inline
87 // variable specialization triggers unique_ptr's destructor in every TU.
88 // Atomic for thread-safe init + safe static teardown.
89 std::atomic<Logger *> logger_{ nullptr };
90 std::once_flag init_flag_;
91
92private:
93 // Non-template bridge to spdlog -- defined in logger.cpp.
94 void submit (std::source_location loc, LogLevel level, std::string msg) const;
95};
96
97template <typename... Tags> inline auto log_api = NullLogApi{};
98
99template <> inline auto log_api<> = SpdlogApi{};
100
101} // namespace detail
102
103void
104init_logging (LoggerType type);
105bool
106is_logging_initialized ();
107
108std::vector<Utf8String>
109get_last_log_entries (size_t count);
110
111std::filesystem::path
112get_log_file_path ();
113
114template <typename... Args>
115void
116log_trace (
117 std::source_location loc,
118 fmt::format_string<Args...> fmt,
119 Args &&... args)
120{
121 detail::log_api<>.log (
122 loc, LogLevel::Trace, fmt, std::forward<Args> (args)...);
123}
124
125template <typename... Args>
126void
127log_debug (
128 std::source_location loc,
129 fmt::format_string<Args...> fmt,
130 Args &&... args)
131{
132 detail::log_api<>.log (
133 loc, LogLevel::Debug, fmt, std::forward<Args> (args)...);
134}
135
136template <typename... Args>
137void
138log_info (
139 std::source_location loc,
140 fmt::format_string<Args...> fmt,
141 Args &&... args)
142{
143 detail::log_api<>.log (loc, LogLevel::Info, fmt, std::forward<Args> (args)...);
144}
145
146template <typename... Args>
147void
148log_warning (
149 std::source_location loc,
150 fmt::format_string<Args...> fmt,
151 Args &&... args)
152{
153 detail::log_api<>.log (
154 loc, LogLevel::Warning, fmt, std::forward<Args> (args)...);
155}
156
157template <typename... Args>
158void
159log_error (
160 std::source_location loc,
161 fmt::format_string<Args...> fmt,
162 Args &&... args)
163{
164 detail::log_api<>.log (
165 loc, LogLevel::Error, fmt, std::forward<Args> (args)...);
166}
167
168template <typename... Args>
169void
170log_critical (
171 std::source_location loc,
172 fmt::format_string<Args...> fmt,
173 Args &&... args)
174{
175 detail::log_api<>.log (
176 loc, LogLevel::Critical, fmt, std::forward<Args> (args)...);
177}
178
179#define z_trace(...) \
180 ::zrythm::utils::log_trace (std::source_location::current (), __VA_ARGS__)
181#define z_debug(...) \
182 ::zrythm::utils::log_debug (std::source_location::current (), __VA_ARGS__)
183#define z_info(...) \
184 ::zrythm::utils::log_info (std::source_location::current (), __VA_ARGS__)
185#define z_warning(...) \
186 ::zrythm::utils::log_warning (std::source_location::current (), __VA_ARGS__)
187#define z_error(...) \
188 ::zrythm::utils::log_error (std::source_location::current (), __VA_ARGS__)
189#define z_critical(...) \
190 ::zrythm::utils::log_critical (std::source_location::current (), __VA_ARGS__)
191
192#define z_return_val_if_fail(cond, val) \
193 if (!(cond)) [[unlikely]] \
194 { \
195 z_error ("Assertion failed: {}", #cond); \
196 return val; \
197 }
198
199#define z_return_if_fail(cond) z_return_val_if_fail (cond, )
200
201#define z_return_val_if_reached(val) \
202 { \
203 z_error ("This code should not be reached"); \
204 return val; \
205 }
206
207#define z_return_if_reached() z_return_val_if_reached ()
208
209#define z_warn_if_fail(cond) \
210 if (!(cond)) [[unlikely]] \
211 { \
212 z_warning ("Assertion failed: {}", #cond); \
213 }
214
215#define z_warn_if_reached() z_warning ("This code should not be reached")
216
217}; // namespace zrythm::utils
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:37
String utilities.