LOG_DEBUG, LOG_INFO, LOG_WARN, and LOG_ERROR (and LOG, an alias for LOG_INFO) are printf-style macros usable from anywhere in the app, C++ or C, with no platform-specific code:
Zero cost when disabled. Logging is off unless you build with -DLOGGING_ENABLED=1 (for example CFLAGS += -DLOGGING_ENABLED=1). In a default build every LOG_*() call expands to nothing: no format string is placed in the binary, no arguments are evaluated (so don't rely on side effects inside a log call), and the Logger isn't created or initialized at all - platform->getLogger() returns nullptr.
Backends. A Logger fans each formatted line out to registered LoggingDriver backends. Each platform registers the ones it supports:
| Backend | LogMode / mask bit | GameCube | Wii | Wii U |
|---|---|---|---|---|
| Console debug output | OSReport / LOGGER_OSREPORT | SYS_Report | SYS_Report | OSReport |
| UDP | UDP / LOGGER_UDP | - | Yes | Yes |
| Serial | SerialGecko / LOGGER_SERIAL | USB Gecko (EXI) | USB Gecko (EXI) | USB serial adapter, if the optional usbserial module's header is available at build time |
| File | File / LOGGER_FILE | Yes | Yes | Yes |
mirrorToOSReport (on by default) copies every line here in addition to the selected backend(s).includeSequenceNumber makes dropped packets easy to spot.debug.log at the root of the first mounted storage device (SD, then USB on Wii; SD on Wii U; the SD2SP2 card on GameCube). LogFlushPolicy picks the trade-off: Immediate flushes every line (safest against a crash or power loss), EveryNWrites flushes every N lines, and Never leaves it to the C library and shutdown (fastest).Configuration. Everything is set through a LogConfig; nothing is hardcoded in a backend.
| Field | Default | Meaning |
|---|---|---|
mode | File | OSReport, UDP, SerialGecko, File, or Multi |
multiBackendMask | LOGGER_OSREPORT | With Multi, the OR of LOGGER_* backends to use, e.g. LOGGER_UDP \| LOGGER_FILE |
level | Info | Minimum severity: Debug, Info, Warning, Error, or None (log nothing) |
mirrorToOSReport | true | Always also write to the console debug output |
targetIp / targetPort | 192.168.1.100 / 4405 | UDP destination |
geckoChannel | 1 | EXI channel for a USB Gecko (0 = memory card slot A, 1 = slot B) |
serialBaudRate | 115200 | Baud rate requested from a Wii U USB serial adapter |
filePath | sd:/debug.log | Log file path |
flushPolicy / flushEveryNWrites | Immediate / 16 | File flushing behavior |
includeLevelTag | true | Prefix lines with [DEBUG], [INFO], [WARN], [ERROR] |
includeSequenceNumber | false | Prefix lines with a running call counter |
Runtime behavior.
Logger::init() can be called again at any time - for example from a settings menu - and reconciles against what's already running: backends no longer selected are shut down, newly selected ones are started, and ones that stay selected are reopened against the new configuration. setLevel() changes just the minimum severity without touching any backend.Logger::registerBackend() accepts your own LoggingDriver under a unique single-bit LogBackendId; a Logger holds up to 8 backends.drivers/Trace.h is a small diagnostic layer on top of the logger for finding where a thread gets stuck without disturbing timing. Each thread records the name of the phase it is in - in plain RAM, with no I/O and no locks - plus a short ring of the most recent phase changes across all threads. Nothing is written while things are healthy. A watchdog thread reports any thread that has sat in one phase too long, together with the recent history and every other thread's phase, and a phase that finished but was slow is reported when it ends.
Like LOG_*(), it compiles to nothing unless LOGGING_ENABLED is set, so it costs release builds no thread and no memory.
TRACE_THREAD(name) - names the calling thread; call it first thing in the thread.TRACE_AT(where) - the calling thread is now in phase where; use "idle" while waiting for work so the thread is never reported as stalled. Pass string literals only: the pointer itself is what gets stored.TRACE_LOG(...) - logs one milestone line tagged with the thread's name.TRACE_STATE_FN(fn) - registers a function that describes application state (queues, flags) for stall reports. It runs on the watchdog thread with no locks held, so it may only read plain variables.TRACE_WATCHDOG() - starts the stall detector thread.Reports go through LOG_*(), which serializes on the logger's mutex, and the file backend holds that mutex while it writes to the SD card. If the stall you are chasing is the SD card itself, a report may not get out - use the UDP backend for those runs.