OXRecorder::OXLog

Centralized, level-based logging facility

Overview

OXLog is the single output channel for the firmware. Instead of calling Serial.print* directly, every part of the codebase routes messages through OXLog. This keeps the output logic in one place so behavior — log levels, timestamps, tags, and later alternate sinks such as a file or network target — can evolve without touching call sites.

Defined in include/OXLog.h, implemented in src/OXLog.cpp.

OXLog::Level enum class

Severity of a message, backed by uint8_t. Higher value means more verbose. A message is emitted only when its level is less than or equal to the configured level, so setting the level to Debug also shows Error, Warn, and Info.

ValueTagMeaning
NoneDisables all output.
ErrorEA failure that needs attention.
WarnWUnexpected but recoverable condition.
InfoINormal operational messages. Default level.
DebugDDiagnostic detail for development.
VerboseVHigh-volume tracing.

API reference

static void begin(unsigned long baud = 115200, Level level = Level::Info) static

Initializes the underlying serial transport and sets the active log level. Call once from setup() before emitting any messages; calls before begin() are silently ignored.

ParameterTypeDefaultMeaning
baudunsigned long115200Serial bit rate. Must match your monitor's baud setting.
levelLevelLevel::InfoInitial minimum severity emitted; messages below it are dropped. Change later with setLevel().

static void setLevel(Level level) static

Changes the minimum level emitted at runtime.

ParameterTypeMeaning
levelLevelNew minimum severity. Messages with a higher (more verbose) level than this are dropped.

static Level getLevel() static

Returns the currently configured level.

static void error(const char *fmt, ...) static

Emits at Error level. printf-style arguments; no trailing newline needed.

static void warn(const char *fmt, ...) static

Emits at Warn level.

static void info(const char *fmt, ...) static

Emits at Info level.

static void debug(const char *fmt, ...) static

Emits at Debug level.

static void verbose(const char *fmt, ...) static

Emits at Verbose level.

The logging methods are annotated with __attribute__((format(printf, 1, 2))), so the compiler checks your format string against its arguments just like a real printf.

ParameterTypeMeaning
fmtconst char *printf-style format string (e.g. "battery %d%%"). No trailing \n — one is added automatically.
...variadicValues substituted into the format specifiers, same rules as printf: %d int, %u unsigned, %s C-string, %lu unsigned long, %f double, %02x hex, etc.

File-backed logs

OXLog can also persist diagnostics to external storage. The device creates /log/yyyyMMdd.log, keeps the newest entries at the top of the file, and caps the file at a configurable size (200 KB by default). The implementation uses a small RAM staging buffer and rewrites through a temp file, so the ESP32-S3 does not need a 200 KB RAM allocation.

static bool beginFileLogging(OXFileManager &files, size_t maxBytes = 200 KB, uint32_t flushIntervalMs = 10000, const char *dir = "/log") static

Enables SD-backed logging. External storage must already be mounted. The current boot's staged log lines are flushed immediately.

static void poll() static

Cooperative flush pump. Call from loop(); it writes only when the buffer is dirty and the interval has elapsed.

static bool flushFileLog() static

Forces an immediate flush. readLog calls this before reading.

static bool readLatestLog(...)

Reads a chunk from the latest yyyyMMdd.log. Use offset and maxBytes to page through the log.

OXLog::begin();

if (files.beginExternal()) {
  OXLog::beginFileLogging(files);       // /log/yyyyMMdd.log, max 200 KB
}

void loop() {
  OXLog::poll();                        // periodic flush
}

Example

Try it

Paste the sketch below into src/main.cpp, then build and watch the serial monitor: pio run -e esp32-s3-super-mini -t upload && pio device monitor. You should see the four messages from the sample output appear.

A minimal sketch that brings up the logger and emits messages at several levels:

#include <Arduino.h>
#include "OXLog.h"

using OXRecorder::OXLog;

void setup() {
  // Bring up serial at 115200 and show Info and above.
  OXLog::begin(115200, OXLog::Level::Info);

  OXLog::info("Boot complete, firmware v%d.%d", 1, 0);
  OXLog::warn("Battery at %d%%", 18);
  OXLog::error("Sensor %s failed to init", "IMU");

  // Dropped at the default Info level — no output.
  OXLog::debug("raw adc = %d", 2048);

  // Raise verbosity at runtime to see Debug messages.
  OXLog::setLevel(OXLog::Level::Debug);
  OXLog::debug("now visible: adc = %d", 2048);
}

void loop() {
  static uint32_t counter = 0;
  OXLog::info("Loop count: %lu", counter++);
  delay(1000);
}

Sample output

[12][I] Boot complete, firmware v1.0
[12][W] Battery at 18%
[13][E] Sensor IMU failed to init
[13][D] now visible: adc = 2048
[1013][I] Loop count: 0
[2013][I] Loop count: 1

The number in the first bracket is millis() since boot; the letter is the level tag.