Log FrameworkΒΆ
OverviewΒΆ
The Log Framework provides a unified logging interface for the Nexus embedded platform. It supports multiple log levels, multiple output backends, module-level filtering, and both synchronous and asynchronous modes.
FeaturesΒΆ
Multiple Log Levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
Multiple Backends: Console (stdout), UART, Memory (for testing)
Module Filtering: Per-module log levels with wildcard support
Customizable Format: Timestamp, level, module, file, line, function
Sync/Async Modes: Non-blocking async mode for real-time systems
Thread-Safe: Safe for multi-task environments
Compile-Time Optimization: Remove low-level logs at compile time
Resource Configurable: Static allocation support for constrained systems
Quick StartΒΆ
Basic usage:
#define LOG_MODULE "app"
#include "log/log.h"
void app_init(void)
{
// Initialize with default config
log_init(NULL);
// Use convenience macros
LOG_TRACE("Detailed trace info");
LOG_DEBUG("Debug value: %d", 42);
LOG_INFO("Application started");
LOG_WARN("Resource usage at 80%%");
LOG_ERROR("Failed to open file: %s", "config.txt");
LOG_FATAL("Critical system failure");
// Cleanup
log_deinit();
}
Log LevelsΒΆ
Log levels are ordered from most verbose (TRACE) to least verbose (FATAL):
Level |
Value |
Description |
|---|---|---|
LOG_LEVEL_TRACE |
0 |
Most detailed tracing info |
LOG_LEVEL_DEBUG |
1 |
Debug information |
LOG_LEVEL_INFO |
2 |
General information |
LOG_LEVEL_WARN |
3 |
Warning messages |
LOG_LEVEL_ERROR |
4 |
Error messages |
LOG_LEVEL_FATAL |
5 |
Fatal error messages |
LOG_LEVEL_NONE |
6 |
Disable all logging |
ConfigurationΒΆ
Custom configuration:
log_config_t config = {
.level = LOG_LEVEL_DEBUG, // Filter out TRACE
.format = "[%T] [%L] [%M] %m", // Custom format
.async_mode = false, // Synchronous mode
.buffer_size = 0, // Not used in sync mode
.max_msg_len = 256, // Max message length
.color_enabled = true // Enable ANSI colors
};
log_init(&config);
Format TokensΒΆ
The format pattern supports the following tokens:
Token |
Description |
Example |
|---|---|---|
|
Timestamp (milliseconds) |
|
|
Time (HH:MM:SS) |
|
|
Level name (full) |
|
|
Level name (short) |
|
|
Module name |
|
|
File name |
|
|
Function name |
|
|
Line number |
|
|
Message content |
|
|
ANSI color code |
|
|
ANSI color reset |
|
|
Literal percent sign |
|
Default format: [%T] [%L] [%M] %m
BackendsΒΆ
Console BackendΒΆ
Outputs to stdout, suitable for Native platform debugging.
log_backend_t* console = log_backend_console_create();
log_backend_register(console);
// When done
log_backend_unregister("console");
log_backend_console_destroy(console);
UART BackendΒΆ
Outputs to UART serial port, suitable for embedded targets.
// Initialize UART first
hal_uart_config_t uart_cfg = {
.baudrate = 115200,
.wordlen = HAL_UART_WORDLEN_8,
.stopbits = HAL_UART_STOPBITS_1,
.parity = HAL_UART_PARITY_NONE,
.flowctrl = HAL_UART_FLOWCTRL_NONE
};
hal_uart_init(HAL_UART_0, &uart_cfg);
// Create and register UART backend
log_backend_t* uart = log_backend_uart_create(HAL_UART_0);
log_backend_register(uart);
// When done
log_backend_unregister("uart");
log_backend_uart_destroy(uart);
hal_uart_deinit(HAL_UART_0);
Memory BackendΒΆ
Outputs to memory buffer, suitable for testing and debugging.
log_backend_t* memory = log_backend_memory_create(4096);
log_backend_register(memory);
// Read buffer contents
char buf[256];
size_t len = log_backend_memory_read(memory, buf, sizeof(buf));
// Clear buffer
log_backend_memory_clear(memory);
// When done
log_backend_unregister("memory");
log_backend_memory_destroy(memory);
Multiple BackendsΒΆ
Messages are delivered to all registered backends:
log_init(NULL);
// Register multiple backends
log_backend_t* console = log_backend_console_create();
log_backend_register(console);
log_backend_t* memory = log_backend_memory_create(4096);
log_backend_register(memory);
// Message goes to both backends
LOG_INFO("Message to console and memory");
Module FilteringΒΆ
Set different log levels for different modules:
log_init(NULL);
log_set_level(LOG_LEVEL_INFO); // Global level: INFO
// Enable DEBUG for HAL modules (wildcard)
log_module_set_level("hal.*", LOG_LEVEL_DEBUG);
// Only show WARN and above for network module
log_module_set_level("network", LOG_LEVEL_WARN);
// Get effective level for a module
log_level_t level = log_module_get_level("hal.gpio"); // Returns DEBUG
log_level_t level2 = log_module_get_level("app"); // Returns INFO (global)
// Clear module-specific level
log_module_clear_level("network");
// Clear all module-specific levels
log_module_clear_all();
Asynchronous ModeΒΆ
Async mode enables non-blocking log writes:
log_config_t config = {
.level = LOG_LEVEL_DEBUG,
.format = "[%T] [%L] %m",
.async_mode = true, // Enable async
.buffer_size = 4096, // Async buffer size
.max_msg_len = 128,
.async_queue_size = 32, // Queue depth
.async_policy = LOG_ASYNC_POLICY_DROP_OLDEST // Policy when full
};
log_init(&config);
// Log writes return immediately
for (int i = 0; i < 100; i++) {
LOG_INFO("Async message %d", i);
}
// Wait for all messages to be processed
log_async_flush();
// Check pending message count
size_t pending = log_async_pending();
Buffer full policies:
Policy |
Description |
|---|---|
|
Drop oldest message when full |
|
Drop newest message when full |
|
Block until space available |
Backend-Level FilteringΒΆ
Each backend can have its own minimum level:
log_init(NULL);
log_set_level(LOG_LEVEL_TRACE); // Global: allow all
// Console shows all messages
log_backend_t* console = log_backend_console_create();
console->min_level = LOG_LEVEL_TRACE;
log_backend_register(console);
// UART only shows WARN and above
log_backend_t* uart = log_backend_uart_create(HAL_UART_0);
uart->min_level = LOG_LEVEL_WARN;
log_backend_register(uart);
LOG_DEBUG("Only to console");
LOG_WARN("To console and UART");
Runtime ReconfigurationΒΆ
Log settings can be changed at runtime:
// Change log level
log_set_level(LOG_LEVEL_DEBUG);
// Change format
log_set_format("[%l] %m");
// Change max message length
log_set_max_msg_len(64);
// Enable/disable backends
log_backend_enable("uart", false);
log_backend_enable("uart", true);
Compile-Time ConfigurationΒΆ
Compile-time level filtering:
Remove low-level logs at compile time to reduce code size:
# CMakeLists.txt
add_definitions(-DLOG_COMPILE_LEVEL=LOG_LEVEL_INFO)
This completely removes LOG_TRACE() and LOG_DEBUG() calls from the binary.
Static allocation mode:
Disable dynamic memory allocation:
add_definitions(-DLOG_USE_STATIC_ALLOC=1)
Configuration macros:
Macro |
Default |
Description |
|---|---|---|
|
|
Default log level |
|
|
Max message length |
|
|
Max backend count |
|
|
Max module filter count |
|
|
Compile-time level |
|
|
Static allocation mode |
Custom BackendΒΆ
Create a custom backend by implementing the backend interface:
static log_status_t my_backend_write(void* ctx, const char* msg, size_t len)
{
// Custom output logic (file, network, etc.)
return LOG_OK;
}
static log_backend_t my_backend = {
.name = "custom",
.init = NULL, // Optional
.write = my_backend_write, // Required
.flush = NULL, // Optional
.deinit = NULL, // Optional
.ctx = NULL, // Custom context
.min_level = LOG_LEVEL_INFO,
.enabled = true
};
log_backend_register(&my_backend);
Thread SafetyΒΆ
The log framework is thread-safe in multi-task environments:
Uses OSAL Mutex to protect shared state
Minimizes lock hold time
Async mode uses lock-free queue
DependenciesΒΆ
OSAL: OS Abstraction Layer (Mutex, Queue, Task)
HAL: Hardware Abstraction Layer (UART)
API ReferenceΒΆ
See Log Framework API Reference for complete API documentation.