Ultra-Lightweight and Highly Configurable Embedded Logging Framework

In embedded system development, logging functionality is an indispensable tool for debugging and diagnosis. However, traditional logging libraries often struggle to balance resource consumption and feature richness. EasyLogger is a C/C++ logging library that is popular for its extreme lightweight nature and high configurability, and it is highly recommended.

A recommendation for an extremely minimalist embedded logging library

Advanced techniques for embedded logging systems: The art of transitioning from recording to diagnosis

Project Overview
Project Address: https://github.com/armink/EasyLogger

EasyLogger is an open-source logging library designed specifically for resource-constrained embedded environments. It has gained wide recognition in the open-source community and has been practically validated in numerous mass production projects, proving to be quite effective.

Core Architecture Design
EasyLogger adopts a modular design, with core features including:

- Pure C language implementation, no external dependencies, cross-platform support

- Simple architecture with a single header file and source file, easy to integrate

- Zero dynamic memory allocation, all resources determined at compile time

- Plugin-based output, supporting multiple output backends

Intelligent Log Level Management
EasyLogger provides a complete logging level system and supports multi-level fine control:

/* Log level definitions, from low to high */
typedef enum {
    ELOG_LVL_ASSERT = 0,
    ELOG_LVL_ERROR,
    ELOG_LVL_WARN,
    ELOG_LVL_INFO,
    ELOG_LVL_DEBUG,
    ELOG_LVL_VERBOSE
} ElogLevel;

// Compile-time static setting of global log level
#define ELOG_OUTPUT_LVL                    ELOG_LVL_DEBUG

// Usage example
void network_communication(void) {
    elog_d("Network", "TCP connection established");
    elog_i("Network", "Received %d bytes", data_len);
    
    if (checksum_error) {
        elog_w("Network", "Checksum mismatch, retrying...");
    }
    
    if (connection_lost) {
        elog_e("Network", "Connection lost, code: 0x%08X", error_code);
    }
}

Tag Filtering System
This is one of the most distinctive features of EasyLogger, supporting independent log levels for different software modules:

// Set different log levels for different modules
void module_log_level_init(void) {
    // Network module: debug level, output all detailed information
    elog_set_filter_lvl("Network", ELOG_LVL_DEBUG);
    
    // File system module: warning level, focus only on exceptions
    elog_set_filter_lvl("FS", ELOG_LVL_WARN);
    
    // Sensor module: info level, focus on critical state changes
    elog_set_filter_lvl("Sensor", ELOG_LVL_INFO);
    
    // In production environments, debugging module logs can be completely turned off
    #ifdef PRODUCTION_MODE
    elog_set_filter_lvl("Debug", ELOG_LVL_ASSERT);
    #endif
}

// Dynamically adjust log levels at runtime
void on_debug_mode_changed(bool enable) {
    if (enable) {
        elog_set_filter_lvl("Network", ELOG_LVL_DEBUG);
        elog_set_filter_lvl("Algorithm", ELOG_LVL_VERBOSE);
    } else {
        elog_set_filter_lvl("Network", ELOG_LVL_INFO);
        elog_set_filter_lvl("Algorithm", ELOG_LVL_WARN);
    }
}

High-Performance Asynchronous Logging Mode
For applications with high real-time requirements, EasyLogger provides an asynchronous logging mode:

// Enable asynchronous mode configuration
#define ELOG_ASYNC_OUTPUT_ENABLE          1
#define ELOG_ASYNC_OUTPUT_BUF_SIZE        (1024)  // Asynchronous buffer size
#define ELOG_ASYNC_OUTPUT_TASK_STACK_SIZE (512)   // Asynchronous task stack size
#define ELOG_ASYNC_OUTPUT_TASK_PRIORITY   (5)     // Asynchronous task priority

// Asynchronous mode ensures log operations do not block the main thread
void critical_real_time_task(void) {
    // Critical operations for real-time tasks
    process_sensor_data();
    
    // Log writing to asynchronous buffer, return immediately
    elog_d("Control", "Motor speed: %d RPM", get_motor_speed());
    
    // Does not affect real-time performance
    update_control_algorithm();
}

Output Formatting Functionality
// Rich formatting support
elog_i("System", "Device status: %s, Voltage: %.2fV, Temp: %d°C", 
       status_str, voltage, temperature);

// Hexadecimal data dump - a tool for communication debugging
uint8_t can_frame[] = {0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF};
elog_hexdump("CAN", 7, can_frame);

// Output effect:
// I/CAN 0000: 12 34 56 78 AB CD EF                            .4Vx...

Additionally, there are many advanced features

Support for Multiple Output Backends
EasyLogger supports simultaneous output to multiple targets:

// Initialize multiple output plugins
void log_output_init(void) {
    // Output to serial port
    elog_uart_init();
    
    // Output to file system (e.g., SD card)
    elog_fs_init();
    
    // Output to network (via TCP/IP)
    elog_net_init();
    
    // Output to display
    elog_gui_init();
}

Thread Safety and Lock Mechanism
// Custom lock implementation to ensure multi-thread safety
static void elog_port_lock(void) {
    rt_enter_critical();
}

static void elog_port_unlock(void) {
    rt_exit_critical();
}

// Or use a simple lock mechanism in a non-RTOS environment
#ifdef NO_OS
static void elog_port_lock(void) {
    __disable_irq();
}

static void elog_port_unlock(void) {
    __enable_irq();
}
#endif

Resource Trimming: Remove Unnecessary Features via Macro Definitions
// Minimal configuration, suitable for extremely resource-constrained scenarios
#define ELOG_COLOR_ENABLE                 0
#define ELOG_TIME_ENABLE                  0  
#define ELOG_LINE_ENABLE                  0
#define ELOG_FUNCTION_ENABLE              0

Buffer Optimization: Adjust buffer size based on actual log volume
#define ELOG_LINE_BUF_SIZE                (512)  // Single line log buffer
#define ELOG_FILTER_TAG_MAX_LEN           (16)   // Maximum length of tags
#define ELOG_FILTER_KW_MAX_LEN            (16)   // Maximum length of keyword filters

EasyLogger achieves extreme lightweight through fine compile-time configuration and zero dynamic memory allocation. With advanced features such as tag filtering, asynchronous mode, and plugin systems, it meets the needs of complex applications. Its simple API design and comprehensive documentation significantly lower the integration and usage threshold.

Whether for simple 8-bit MCU projects or complex 32-bit embedded systems, EasyLogger provides the right logging solution. Its elegant architecture design and rich feature set make it an ideal choice for embedded developers when building reliable and maintainable systems.

Leave a Comment