Skip to content

UXLog

UXLog is a small logging facility: a subsystem name, a minimum level, and messages below that level dropped. os_log/syslog in shape.

#use <UXKit> // or #import "UXLog.xc"
UXLog* net = UXLog.forSubsystem((u8*)"net");
net.setMinLevel(UX_LOG_INFO);
net.debug((u8*)"opening socket"); // dropped — below the level
net.info((u8*)"connected to host"); // [INFO] net: connected to host
net.warn((u8*)"read timeout after 30s");
net.error((u8*)"giving up");

Output goes to stdout by default. A syslog sink is a per-backend addition.

UXLog.forSubsystem((u8*)"net") == UXLog.forSubsystem((u8*)"net"); // true

forSubsystem returns the same logger for a name, from a process-wide registry, as os_log’s subsystem registry does.

A level set in one place applies everywhere that subsystem logs, and a module deep in a call stack does not need to be handed a logger. Raising the verbosity of one subsystem takes one line, anywhere.

shared is the unnamed logger for code that has no subsystem of its own.

net.addMonitor(UXRegex.compile((u8*)"timeout"), &self.onTimeout);

Register a UXRegex and a callback, and the callback fires whenever a logged message matches. The matching uses the toolkit’s own regex engine.

With monitors, the program can react to its log as it is written. A test asserts that a particular message was produced; a diagnostic panel lights up when an error pattern appears; a retry counter increments without the networking code knowing anything is counting.

[WARN] net: read timeout after 30s
MONITOR saw: read timeout after 30s

The callback is a callback, so a monitor cannot keep its observer alive. When the observer is freed, its monitor stops firing. This is the same lifetime model as UXNotificationCenter.

Monitors search the message rather than requiring the pattern to match all of it. A pattern of "timeout" fires on "read timeout after 30s".

This suits a log watch. UXValidator’s regex rule works the other way: its pattern must describe the entire field. Both use the same engine.

forSubsystem · shared · setMinLevel · setStdout · debug / info / warn / error · log · addMonitor · removeMonitor · monitorCount · levelName

static UXLog* forSubsystem(u8* name)

The logger for a name, made on first use and shared after that. The name is kept, not copied.

static UXLog* shared(void)

The default logger.

void setMinLevel(i32 lvl)

UX_LOG_DEBUG, UX_LOG_INFO, UX_LOG_WARN, UX_LOG_ERROR. Messages below it are dropped, and monitors do not see them.

void setStdout(bool on)

Turn the stdout sink off. A logger with stdout off and a monitor attached is a silent watcher. This is useful in a test, where the monitor makes the assertion and the output would be noise.

void debug(u8* msg)
void info(u8* msg)
void warn(u8* msg)
void error(u8* msg)
void log(i32 lvl, u8* msg)

The general form. It takes a finished message with no format string, so build the message with UXStr first.

void addMonitor(UXRegex* pattern, callback cb void(u8* msg))
void removeMonitor(callback cb void(u8* msg))

Matched on the callback, so the same callback cannot be registered twice with different patterns and removed individually.

i32 monitorCount(void)
u8* levelName(i32 lvl)

"DEBUG", "INFO", "WARN", "ERROR": what the stdout sink prints in brackets.

[INFO] net: connected to host
[WARN] net: read timeout after 30s
MONITOR saw: read timeout after 30s
[ERROR] net: giving up
monitor fired 1 time(s), monitors=1
same logger: 1

The debug call, below the level, produced nothing. The program is website/site/examples/uxkit/toolbox.xc. The doc-examples gate compiles it, and the listing above is its output.

  • A plain class (not an Object subclass)