libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
Logger.h
1/****************************************************************************
2* Platform Abstraction Layer
3* Daryl Borth 2026
4* Logger.h
5*
6* Multi-backend debug logging. Platform-agnostic - this header never
7* includes a platform SDK header. Each platform's Platform::init() wires
8* up whichever LoggingDriver implementations it supports and hands them
9* to a single Logger instance.
10*
11* Zero-overhead when disabled: build with -DLOGGING_ENABLED=0 (or define
12* it before this header is first included) and every LOG_*() call site
13* compiles to nothing - no string literal in .rodata, no branch, no call.
14***************************************************************************/
15#pragma once
16
17#include <stdarg.h>
18#include <stddef.h>
19#include <stdint.h>
20
24#ifndef LOGGING_ENABLED
25#define LOGGING_ENABLED 0
26#endif
27
32#define LOG_LEVEL_DEBUG 0
33#define LOG_LEVEL_INFO 1
34#define LOG_LEVEL_WARNING 2
35#define LOG_LEVEL_ERROR 3
36
37#ifdef __cplusplus
38
39#include "Mutex.h"
40
44enum class LogLevel : uint8_t
45{
46 Debug,
47 Info,
48 Warning,
49 Error,
50 None
51};
52
53static_assert(static_cast<uint8_t>(LogLevel::Debug) == LOG_LEVEL_DEBUG, "LOG_LEVEL_DEBUG out of sync with LogLevel");
54static_assert(static_cast<uint8_t>(LogLevel::Info) == LOG_LEVEL_INFO, "LOG_LEVEL_INFO out of sync with LogLevel");
55static_assert(static_cast<uint8_t>(LogLevel::Warning) == LOG_LEVEL_WARNING, "LOG_LEVEL_WARNING out of sync with LogLevel");
56static_assert(static_cast<uint8_t>(LogLevel::Error) == LOG_LEVEL_ERROR, "LOG_LEVEL_ERROR out of sync with LogLevel");
57
62enum class LogMode : uint8_t
63{
65 UDP,
66 SerialGecko,
67 File,
69};
70
76enum LogBackendId : uint32_t
77{
78 LOGGER_NONE = 0,
79 LOGGER_OSREPORT = 1u << 0,
80 LOGGER_UDP = 1u << 1,
81 LOGGER_SERIAL = 1u << 2,
82 LOGGER_FILE = 1u << 3,
83};
84
87enum class LogFlushPolicy : uint8_t
88{
92};
93
101{
102 LogMode mode = LogMode::File;
103 LogLevel level = LogLevel::Info;
104
107 uint32_t multiBackendMask = LOGGER_OSREPORT;
108
113 bool mirrorToOSReport = true;
114
115 // ---- UDP ----
117 const char * targetIp = "192.168.1.100";
119 uint16_t targetPort = 4405;
120 bool nonBlocking = true;
121
122 // ---- Serial / USB Gecko ----
126
130 uint32_t serialBaudRate = 115200;
131
132 // ---- SD / file ----
134 char filePath[1024] = "sd:/debug.log";
138 uint32_t flushEveryNWrites = 16;
139
140 // ---- Formatting ----
141 bool includeLevelTag = true;
143 bool includeTimestamp = true;
144};
145
153{
154public:
155 virtual ~LoggingDriver() = default;
156
163 virtual bool init(const LogConfig & config) = 0;
164
167 virtual void shutdown() = 0;
168
171 virtual void write(LogLevel level, const char * line, size_t len) = 0;
172
174 virtual const char * name() const = 0;
175};
176
190class Logger
191{
192public:
193 Logger();
194 ~Logger();
195
196 Logger(const Logger &) = delete;
197 Logger & operator=(const Logger &) = delete;
198
205 void init(const LogConfig & config);
206
210
215 void shutdown();
216
225 bool registerBackend(LogBackendId id, LoggingDriver * backend);
226
229 void setLevel(LogLevel level);
231 LogLevel getLevel() const { return logConfig.level; }
232
238 void log(LogLevel level, const char * fmt, va_list args);
239
241 static const int MAX_BACKENDS = 8;
242
243private:
244 struct Slot
245 {
246 LogBackendId id = LOGGER_NONE;
247 LoggingDriver * backend = nullptr;
248 bool active = false;
249 };
250
253 uint32_t resolveActiveMask() const;
254
255 Slot slots[MAX_BACKENDS];
256 int slotCount = 0;
257 LogConfig logConfig;
258 Mutex lock;
259 uint32_t sequence = 0;
260 bool initialized = false;
261};
262
263#endif // __cplusplus
264
265/****************************************************************************
266* C bridge
267*
268* LogPrintf() (below) is a C++ function taking a `LogLevel` argument, so
269* its mangled name and argument type aren't callable from C.
270***************************************************************************/
271#ifdef __cplusplus
272extern "C" {
273#endif
274
279#ifdef __cplusplus
280void LogPrintf(LogLevel level, const char * fmt, ...);
281#endif
282
286void Log_Printf(int level, const char * fmt, ...);
287
288#ifdef __cplusplus
289} // extern "C"
290#endif
291
292#if LOGGING_ENABLED
293 #ifdef __cplusplus
294 #define LOG_DEBUG(fmt, ...) LogPrintf(LogLevel::Debug, fmt, ##__VA_ARGS__)
295 #define LOG_INFO(fmt, ...) LogPrintf(LogLevel::Info, fmt, ##__VA_ARGS__)
296 #define LOG_WARN(fmt, ...) LogPrintf(LogLevel::Warning, fmt, ##__VA_ARGS__)
297 #define LOG_ERROR(fmt, ...) LogPrintf(LogLevel::Error, fmt, ##__VA_ARGS__)
298 #else
299 #define LOG_DEBUG(fmt, ...) Log_Printf(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
300 #define LOG_INFO(fmt, ...) Log_Printf(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
301 #define LOG_WARN(fmt, ...) Log_Printf(LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
302 #define LOG_ERROR(fmt, ...) Log_Printf(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
303 #endif
304#else
311#define LOG_DEBUG(fmt, ...) ((void)0)
312#define LOG_INFO(fmt, ...) ((void)0)
313#define LOG_WARN(fmt, ...) ((void)0)
314#define LOG_ERROR(fmt, ...) ((void)0)
315#endif
316
318#define LOG(fmt, ...) LOG_INFO(fmt, ##__VA_ARGS__)
void log(LogLevel level, const char *fmt, va_list args)
Formats fmt/args into a fixed stack buffer and fans it out to every backend selected by the current c...
Definition Logger.cpp:215
void activateDeferred(LogBackendId id)
Called by a backend that deferred its own activation on some background precondition (eg.
Definition Logger.cpp:147
void shutdown()
Shuts down every registered backend and forgets the config.
Definition Logger.cpp:170
void setLevel(LogLevel level)
Runtime-adjusts the minimum severity without touching any other config field or re-touching backend i...
Definition Logger.cpp:197
bool registerBackend(LogBackendId id, LoggingDriver *backend)
Registers a backend under the given single-bit LogBackendId, for later selection via LogConfig::mode ...
Definition Logger.cpp:31
void init(const LogConfig &config)
Applies config, calling init()/shutdown() on registered backends as needed so only the ones actually ...
Definition Logger.cpp:83
static const int MAX_BACKENDS
Maximum number of backends that can be registered at once.
Definition Logger.h:241
LogLevel getLevel() const
Definition Logger.h:231
Abstract backend a Logger fans a formatted line out to.
Definition Logger.h:153
virtual bool init(const LogConfig &config)=0
Opens whatever handle/socket/file this backend needs, using only fields from config (nothing hardcode...
virtual void write(LogLevel level, const char *line, size_t len)=0
Writes one already-formatted, newline-terminated line.
virtual void shutdown()=0
Releases whatever init() acquired.
virtual const char * name() const =0
Short identifier for diagnostics (eg. "UDP", "File").
A simple mutual-exclusion lock.
Definition Mutex.h:14
LogLevel
Severity of a single log call.
Definition Logger.h:45
LogFlushPolicy
How LoggerFile flushes writes to storage.
Definition Logger.h:88
LogMode
Which single backend is active when LogConfig::mode isn't Multi.
Definition Logger.h:63
LogBackendId
Bitmask identifying an individual backend.
Definition Logger.h:77
@ Immediate
fflush() after every write - safest against a crash/power loss, slowest
Definition Logger.h:89
@ Never
rely on the C library / fclose() at shutdown - fastest, least crash-safe
Definition Logger.h:91
@ EveryNWrites
fflush() every LogConfig::flushEveryNWrites writes
Definition Logger.h:90
@ OSReport
OSReport (Wii U) / SYS_Report (GC+Wii) - always safe, no hardware required.
Definition Logger.h:64
@ Multi
fan out to every backend set in LogConfig::multiBackendMask
Definition Logger.h:68
Full runtime configuration for the Logger.
Definition Logger.h:101
bool mirrorToOSReport
Regardless of mode/multiBackendMask, also mirror every line to the console OS default backend (OSRepo...
Definition Logger.h:113
uint32_t multiBackendMask
Only consulted when mode == LogMode::Multi.
Definition Logger.h:107
LogFlushPolicy flushPolicy
How often the file backend flushes to storage.
Definition Logger.h:136
uint16_t targetPort
Destination UDP port for the UDP backend.
Definition Logger.h:119
int geckoChannel
EXI channel (GC/Wii): 0 = memory card slot A, 1 = memory card slot B (where a USB Gecko is normally i...
Definition Logger.h:125
bool includeTimestamp
prefix each line with the time since process start as "[secs.millis]"
Definition Logger.h:143
LogLevel level
Minimum severity that is logged.
Definition Logger.h:103
char filePath[1024]
Path of the log file for the file backend. Each platform's init() points this at debug....
Definition Logger.h:134
const char * targetIp
Destination IPv4 address for the UDP backend.
Definition Logger.h:117
uint32_t flushEveryNWrites
Flush interval in writes; only used when flushPolicy is LogFlushPolicy::EveryNWrites.
Definition Logger.h:138
LogMode mode
Which backend(s) receive log lines; see LogMode.
Definition Logger.h:102
bool nonBlocking
socket is always created non-blocking; kept for clarity/future use
Definition Logger.h:120
bool includeLevelTag
prefix each line with "[DEBUG] "/"[INFO] "/etc.
Definition Logger.h:141
bool includeSequenceNumber
prefix each line with a monotonic call counter, useful for spotting dropped UDP packets
Definition Logger.h:142
uint32_t serialBaudRate
Informational only on GC/Wii (EXI has a fixed protocol rate); used as the requested baud rate for a W...
Definition Logger.h:130