Logging Library
Contents
Introduction
Architecture
C API — Subsystem and Component Definitions
C API — Logging Macros
C API — Color Macros
C API — Filtering Functions
C API — Dynamic Registration
MicroPython API —
logsModuleMicroPython API — Registration
MicroPython API — Logging Functions
MicroPython API — Filtering
Log Output Format
Kconfig Options
Capacity Limits
Example — C Component
Example — MicroPython
Introduction
The logging library provides a unified, structured logging system for both C native code and MicroPython applications. All log messages pass through the same pipeline and appear in a consistent columnar format with timestamp, log type, OS context, source location, subsystem, component, and message fields.
Key features:
Structured output — fixed-width columns with configurable widths
Compile-time definitions — subsystems and components defined via macros, enabling per-module compile-time gating
Runtime filtering — enable/disable output by subsystem, component, log type, or header column at runtime
Terminal coloring — ANSI color support with per-subsystem and per-component colors
Dynamic registration — MicroPython code can register subsystems and components at runtime and produce log output identical to native C logs
Soft-reset safe — dynamic registrations are cleared on MicroPython soft reset, allowing clean re-registration
Architecture
┌──────────────────────┐ ┌──────────────────────┐
│ C source files │ │ MicroPython scripts │
│ __log_info(...) │ │ logs.info(...) │
└──────────┬───────────┘ └──────────┬───────────┘
│ │
│ compile-time │ runtime dynamic
│ subsys/comp IDs │ subsys/comp names
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ log pipeline (log_main.c) │
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ │
│ │ filter │→│ header │→│ output │ │
│ │ checks │ │ render │ │ serial │ │
│ └─────────┘ └──────────┘ └─────────┘ │
└──────────────────────────────────────────┘
The static registry holds subsystems and components defined at compile time
using __log_subsystem_def and __log_component_def. The dynamic registry
holds subsystems and components registered at runtime from MicroPython via
log_register_subsystem() and log_register_component(). Both registries
participate in the same filtering and output pipeline.
C API — Subsystem and Component Definitions
Each C source file selects its subsystem and component before including the log header:
#define __log_subsystem mysubsys
#define __log_component mycomp
#include "log_lib.h"
Subsystems and components are defined (typically once per compilation unit) using the following macros:
__log_subsystem_def(name, color, compile_flag, on)— define a subsystem.Parameter
Description
nameSubsystem identifier (used as a C token)
color- Color identifier:
default,red,green,yellow, blue,purple,cyan,white,black
compile_flag1to compile log calls,0to strip them at compile timeon1to enable output at startup,0to start disabled- Color identifier:
__log_component_def(subsystem, name, color, compile_flag, on)— define a component under an existing subsystem. Same parameters as above, with an additionalsubsystemparameter naming the parent.
// mydriver.c
#define __log_subsystem drivers
#define __log_component spi
#include "log_lib.h"
__log_subsystem_def(drivers, blue, 1, 1)
__log_component_def(drivers, spi, yellow, 1, 1)
C API — Logging Macros
All macros use printf-style format strings. Output is gated by the compile
flag and runtime filter state of the current subsystem and component.
Macro |
Log Type |
Description |
|---|---|---|
|
info |
General status and progress |
|
debug |
Detailed tracing information |
|
warn |
Recoverable issues, unusual conditions |
|
error |
Error conditions and failures |
|
output |
Direct standard output (no header) |
|
printf |
Continuation of a previous log line |
|
assert |
Fatal assertion (triggers crash if |
|
debug |
Convenience: logs |
__log_info("SPI transfer complete, %d bytes", len);
__log_debug("Register 0x%02X = 0x%02X", reg, val);
__log_warn("Retry %d/%d", attempt, max_retries);
__log_error("SPI timeout after %d ms", timeout_ms);
__log_assert(buf != NULL, "buffer must not be NULL");
C API — Color Macros
Inline color escapes can be embedded in log format strings. These are stripped
when SDK_LOG_LIB_TERMINAL_COLORING_ENABLE is disabled.
Macro |
Color |
|---|---|
|
Red |
|
Green |
|
Yellow |
|
Blue |
|
Purple |
|
Cyan |
|
White |
|
Black |
|
Terminal default |
__log_info("Status: "__green__"OK"__default__);
__log_error("Status: "__red__"FAIL"__default__" (code %d)", err);
String wrapper macros are also available: __red_str("text"),
__green_str("text"), etc. These automatically reset to the default color
after the string.
C API — Filtering Functions
All filtering functions work on both static (compile-time) and dynamic (runtime) registrations.
void log_filter_subsystem(name, state, silent)— enable (true) or disable (false) all output from a subsystem. Whensilentisfalse, a confirmation message is logged.void log_filter_component(subsys_name, comp_name, state, silent)— enable or disable a specific component.void log_filter_type(type_name, state, silent)— enable or disable a log type globally (e.g.,"debug","info").void log_filter_header(column_name, state, silent)— show or hide a header column (e.g.,"timestamp","func_name").void log_filter_header_reorder(name, new_order)— change the display order of a header column.void log_filter_list_stats()— print the current filter state for all subsystems, components, log types, and header columns.bool log_filter_subsystem_get_state(name)— query the current enable state of a subsystem.bool log_filter_component_get_state(subsys_name, comp_name)— query the current enable state of a component.void log_filter_save_state(p_state, new_state)— save the current filter state for a subsystem/component pair and set a new state. Useful for temporarily enabling output around a specific operation.void log_filter_restore_state(p_state)— restore a previously saved filter state.
C API — Dynamic Registration
These functions are the C-level API backing the MicroPython logs module.
They can also be called directly from C code that needs runtime registration.
int log_register_subsystem(name, color_name, enabled, silent)— register a dynamic subsystem. Returns0on success,-1if the name is a duplicate, clashes with a static subsystem, or the registry is full.int log_register_component(subsys_name, comp_name, color_name, enabled, silent)— register a dynamic component under an existing dynamic subsystem. Returns0on success,-1on failure.void log_dynamic_message(subsys_name, comp_name, p_type_info, msg)— push a message through the log pipeline using dynamic subsystem/component names.void log_dynamic_registry_clear()— clear all dynamic registrations. Called automatically on MicroPython soft reset to allow re-registration.
MicroPython API — logs Module
import logs
The logs module is a built-in C module enabled by
SDK_LOG_LIB_MPY_CMOD_ENABLE. It provides Python access to log registration,
message output, and filtering.
MicroPython API — Registration
Before logging, at least one subsystem and one component must be registered.
logs.register_subsystem(name, *, color='default', enabled=True, silent=True)Register a new dynamic subsystem.
Parameter
Type
Default
Description
namestr
(required)
Subsystem name (max 23 characters)
colorstr
'default'Display color for the subsystem column
enabledbool
TrueWhether the subsystem starts enabled
silentbool
TrueIf
False, logs a registration confirmationRaises
ValueErrorif the registry is full or the name is a duplicate.logs.register_component(subsystem, component, *, color='default', enabled=True, silent=True)Register a dynamic component under an existing subsystem.
Parameter
Type
Default
Description
subsystemstr
(required)
Parent subsystem name (must exist)
componentstr
(required)
Component name (max 23 characters)
colorstr
'default'Display color for the component column
enabledbool
TrueWhether the component starts enabled
silentbool
TrueIf
False, logs a registration confirmationRaises
ValueErrorif the subsystem is not found or the component is a duplicate.
MicroPython API — Logging Functions
All logging functions share the same signature:
logs.<level>(subsystem, component, message)
Function |
Log Type |
Typical Use |
|---|---|---|
|
info |
General status and progress |
|
debug |
Detailed tracing information |
|
warn |
Recoverable issues |
|
error |
Error conditions and failures |
|
output |
Direct output (no log-type decoration) |
All three arguments are required strings. The subsystem and component must have been previously registered.
logs.info("myapp", "network", "Connected to broker")
logs.debug("myapp", "sensors", "Raw value: {}".format(val))
logs.warn("myapp", "network", "Connection timeout, retrying...")
logs.error("myapp", "network", "Failed: {}".format(err))
logs.output("myapp", "sensors", "Temperature: 24.5°C")
MicroPython API — Filtering
logs.filter_subsystem(subsystem, state, *, silent=False)— enable or disable a subsystem. Works on both static and dynamic subsystems.logs.filter_component(subsystem, component, state, *, silent=False)— enable or disable a specific component.logs.filter_header(header_item, state, *, silent=False)— show or hide a header column. Valid names:"timestamp","log_type","os_info","subsystem","component","filename","line_num","func_name".logs.filter_log_type(log_type, state, *, silent=False)— enable or disable a log type globally. Valid names:"info","debug","warn","error","output","printf","assert".logs.header_reorder(header_item, new_order)— change the display position of a header column.logs.filter_stats()— print the current filter state for all subsystems, components, types, and header columns.
# Disable all debug output
logs.filter_log_type("debug", False)
# Hide the function name column
logs.filter_header("func_name", False)
# Disable a specific component
logs.filter_component("ctrl", "sensors", False)
# Print full filter status
logs.filter_stats()
Log Output Format
All log messages (C and MicroPython) appear in the same structured format:
|000:00:06-822| info |1:mp_task |mpy | ctrl | connection | Connected to MQTT broker
The default column layout:
Column |
Header Name |
Default Width |
Description |
|---|---|---|---|
Timestamp |
|
13 |
Time since boot |
Log type |
|
8 |
|
OS context |
|
12 |
Core ID and task name (e.g. |
Function |
|
15 |
Source function name (C) or |
Subsystem |
|
9 |
Registered subsystem name |
Component |
|
13 |
Registered component name |
Message |
— |
— |
The log message text |
Columns can be enabled/disabled, reordered, and resized via Kconfig and runtime
filter APIs. The filename and line_num columns are disabled by default.
Kconfig Options
All options are under the SDK_LOG_LIB_ENABLE menu.
Option |
Default |
Description |
|---|---|---|
|
y |
Master enable for the logging library |
|
4 |
Internal buffer size in KB |
|
y |
ANSI color output |
|
y |
Enable MicroPython |
Log types — each can be individually compiled in or out:
Option |
Default |
|---|---|
|
y |
|
y |
|
y |
|
y |
|
y |
|
y |
|
y |
|
y |
Header fields — each can be enabled/disabled and has a configurable width:
Field |
Enable Option |
Width Option |
Default Width |
|---|---|---|---|
Timestamp |
|
|
13 |
Log type |
|
|
8 |
OS context |
|
|
12 |
File name |
|
|
12 |
Line number |
|
|
6 |
Function |
|
|
15 |
Subsystem |
|
|
9 |
Component |
|
|
13 |
Line wrapping is optionally enabled with
SDK_LOG_LIB_TOTAL_LINE_WRAPPING_ENABLE (default: off) and
SDK_LOG_LIB_TOTAL_LINE_WIDTH (default: 86).
Capacity Limits
Dynamic registry (runtime registrations from MicroPython or C):
Resource |
Limit |
|---|---|
Dynamic subsystems |
8 |
Dynamic components (shared across all subsystems) |
32 |
Name length |
23 characters |
These limits are separate from compile-time static subsystems and components, which have no practical limit beyond available flash.
The dynamic registry is cleared on every MicroPython soft reset
(log_dynamic_registry_clear()), allowing modules to re-register cleanly.
Example — C Component
// spi_driver.c
#define __log_subsystem drivers
#define __log_component spi
#include "log_lib.h"
__log_subsystem_def(drivers, blue, 1, 1)
__log_component_def(drivers, spi, yellow, 1, 1)
esp_err_t spi_transfer(uint8_t* buf, size_t len) {
__log_debug("SPI transfer: %d bytes at %p", len, buf);
esp_err_t err = /* ... actual transfer ... */;
if (err != ESP_OK) {
__log_error("SPI transfer failed: %s", esp_err_to_name(err));
return err;
}
__log_info("SPI transfer complete: %d bytes", len);
return ESP_OK;
}
Output:
|000:00:01-234| debug |0:spi_task |spi_driver.c | drivers | spi | SPI transfer: 64 bytes at 0x3FC90000
|000:00:01-235| info |0:spi_task |spi_driver.c | drivers | spi | SPI transfer complete: 64 bytes
Example — MicroPython
import logs
# Register subsystem and components
logs.register_subsystem("myapp", color='cyan', enabled=True, silent=False)
logs.register_component("myapp", "main", color='green', enabled=True)
logs.register_component("myapp", "network", color='blue', enabled=True)
logs.register_component("myapp", "sensors", color='yellow', enabled=True)
# Log messages
logs.info("myapp", "main", "Application starting")
logs.info("myapp", "network", "Connecting to WiFi...")
logs.debug("myapp", "network", "SSID: MyNetwork, RSSI: -45")
logs.warn("myapp", "sensors", "Battery level low: 15%")
logs.error("myapp", "network", "MQTT connection lost")
# Runtime filtering
logs.filter_component("myapp", "sensors", False) # silence sensors
logs.filter_component("myapp", "sensors", True) # re-enable sensors
logs.filter_stats() # show all filter states
Output:
|000:00:06-100| info |1:mp_task |mpy | myapp | main | Application starting
|000:00:06-110| info |1:mp_task |mpy | myapp | network | Connecting to WiFi...
|000:00:06-120| debug |1:mp_task |mpy | myapp | network | SSID: MyNetwork, RSSI: -45
|000:00:06-130| warn |1:mp_task |mpy | myapp | sensors | Battery level low: 15%
|000:00:06-140| error |1:mp_task |mpy | myapp | network | MQTT connection lost