27 for (
int i = 0; i < slotCount; i++)
28 delete slots[i].backend;
35 if (backend ==
nullptr ||
id == LOGGER_NONE || (
id & (
id - 1)) != 0)
41 for (
int i = 0; i < slotCount; i++)
43 if (slots[i].
id ==
id)
56 slots[slotCount].id = id;
57 slots[slotCount].backend = backend;
58 slots[slotCount].active =
false;
63uint32_t Logger::resolveActiveMask()
const
67 switch (logConfig.
mode)
70 case LogMode::UDP: mask = LOGGER_UDP;
break;
71 case LogMode::SerialGecko: mask = LOGGER_SERIAL;
break;
72 case LogMode::File: mask = LOGGER_FILE;
break;
74 default: mask = LOGGER_NONE;
break;
77 if (logConfig.mirrorToOSReport)
78 mask |= LOGGER_OSREPORT;
87 logConfig = newConfig;
88 uint32_t activeMask = resolveActiveMask();
96 for (
int i = 0; i < slotCount; i++)
98 bool wantActive = (slots[i].id & activeMask) != 0;
100 if (wantActive && !slots[i].active)
102 slots[i].active = slots[i].backend->init(logConfig);
103 activationFailed[i] = !slots[i].active;
105 else if (!wantActive && slots[i].active)
107 slots[i].backend->shutdown();
108 slots[i].active =
false;
110 else if (wantActive && slots[i].active)
114 slots[i].backend->shutdown();
115 slots[i].active = slots[i].backend->init(logConfig);
116 activationFailed[i] = !slots[i].active;
122 int osReportSlot = -1;
123 for (
int i = 0; i < slotCount; i++)
124 if (slots[i].
id == LOGGER_OSREPORT && slots[i].active)
127 if (osReportSlot >= 0)
129 for (
int i = 0; i < slotCount; i++)
131 if (!activationFailed[i])
135 int n = snprintf(msg,
sizeof(msg),
"[Logger] backend '%s' failed to activate (init() returned false)\n", slots[i].backend->name());
138 size_t len = (size_t)n <
sizeof(msg) ? (size_t)n :
sizeof(msg) - 1;
139 slots[osReportSlot].backend->write(LogLevel::Warning, msg, len);
154 uint32_t activeMask = resolveActiveMask();
155 if ((
id & activeMask) == 0)
158 for (
int i = 0; i < slotCount; i++)
160 if (slots[i].
id !=
id)
163 if (!slots[i].active)
164 slots[i].active =
true;
175 for (
int i = 0; i < slotCount; i++)
177 if (slots[i].
id == LOGGER_FILE && slots[i].active)
179 slots[i].backend->shutdown();
180 slots[i].active =
false;
185 for (
int i = 0; i < slotCount; i++)
189 slots[i].backend->shutdown();
190 slots[i].active =
false;
200 logConfig.level = level;
203static const char * LevelTag(
LogLevel level)
207 case LogLevel::Debug:
return "[DEBUG] ";
208 case LogLevel::Info:
return "[INFO] ";
209 case LogLevel::Warning:
return "[WARN] ";
210 case LogLevel::Error:
return "[ERROR] ";
217 if (!initialized || level == LogLevel::None)
224 if (level < logConfig.level)
231 if (!initialized || level < logConfig.level)
238 if (logConfig.includeSequenceNumber)
240 int n = snprintf(line + offset,
sizeof(line) - offset,
"%06u ", (
unsigned)(++sequence));
242 offset += (size_t)n < (
sizeof(line) - offset) ? (size_t)n : (
sizeof(line) - offset - 1);
245 if (logConfig.includeTimestamp)
248 int n = snprintf(line + offset,
sizeof(line) - offset,
"[%5u.%03u] ", (
unsigned)(ms / 1000), (
unsigned)(ms % 1000));
250 offset += (size_t)n < (
sizeof(line) - offset) ? (size_t)n : (
sizeof(line) - offset - 1);
253 if (logConfig.includeLevelTag)
255 size_t remaining =
sizeof(line) - offset;
256 size_t tagLen = strlen(LevelTag(level));
257 if (tagLen < remaining)
259 memcpy(line + offset, LevelTag(level), tagLen);
264 if (offset <
sizeof(line) - 1)
266 int n = vsnprintf(line + offset,
sizeof(line) - offset, fmt, args);
268 offset += (size_t)n < (
sizeof(line) - offset) ? (size_t)n : (
sizeof(line) - offset - 1);
274 if (offset == 0 || line[offset - 1] !=
'\n')
276 if (offset <
sizeof(line) - 1)
277 line[offset++] =
'\n';
279 line[offset - 1] =
'\n';
283 uint32_t activeMask = resolveActiveMask();
285 for (
int i = 0; i < slotCount; i++)
287 if (slots[i].active && (slots[i].
id & activeMask) != 0)
288 slots[i].backend->write(level, line, offset);
292void LogPrintf(
LogLevel level,
const char * fmt, ...)
294 if (!platform || !platform->getLogger())
299 platform->getLogger()->log(level, fmt, args);
303extern "C" void Log_Printf(
int level,
const char * fmt, ...)
305 if (!platform || !platform->getLogger())
310 platform->getLogger()->log(
static_cast<LogLevel>(level), fmt, 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...
void activateDeferred(LogBackendId id)
Called by a backend that deferred its own activation on some background precondition (eg.
void shutdown()
Shuts down every registered backend and forgets the config.
void setLevel(LogLevel level)
Runtime-adjusts the minimum severity without touching any other config field or re-touching backend i...
bool registerBackend(LogBackendId id, LoggingDriver *backend)
Registers a backend under the given single-bit LogBackendId, for later selection via LogConfig::mode ...
void init(const LogConfig &config)
Applies config, calling init()/shutdown() on registered backends as needed so only the ones actually ...
static const int MAX_BACKENDS
Maximum number of backends that can be registered at once.
Abstract backend a Logger fans a formatted line out to.
RAII lock guard - locks on construction, unlocks on destruction.
static uint32_t diffMillisecs(Ticks start, Ticks end)
LogLevel
Severity of a single log call.
LogBackendId
Bitmask identifying an individual backend.
@ OSReport
OSReport (Wii U) / SYS_Report (GC+Wii) - always safe, no hardware required.
@ Multi
fan out to every backend set in LogConfig::multiBackendMask
Full runtime configuration for the Logger.
uint32_t multiBackendMask
Only consulted when mode == LogMode::Multi.
LogMode mode
Which backend(s) receive log lines; see LogMode.