This commit is contained in:
2026-01-19 16:19:41 +01:00
commit edd3e96591
301 changed files with 36763 additions and 0 deletions

View File

@@ -0,0 +1,431 @@
# ASF Logger Module
## Overview
The ASF Logger module provides a comprehensive C++ wrapper for ESP-IDF logging functionality. It offers a clean, efficient, and feature-rich logging interface that abstracts the underlying ESP-IDF logging mechanism while providing enhanced formatting, filtering, and configuration options.
## Features
- **Multiple Log Levels**: Verbose, Debug, Info, Warning, Error
- **ISO 8601 Timestamps**: Precise timestamping with millisecond accuracy
- **Unique Message IDs**: Track and identify specific log messages
- **Color-Coded Output**: Visual distinction between log levels
- **Configurable Filtering**: Runtime log level adjustment
- **Low Overhead**: Minimal performance impact with compile-time optimizations
- **Thread-Safe**: Built on ESP-IDF's thread-safe logging system
- **Easy Integration**: Simple namespace-based API with convenient macros
## Architecture
### Design Philosophy
The logger is implemented as a namespace with free functions rather than a class-based approach for several reasons:
1. **Zero Overhead**: No object instantiation or virtual function calls
2. **Compile-Time Efficiency**: Header-only interface with minimal includes
3. **Easy Integration**: Simple include without complex initialization
4. **Memory Efficient**: No per-instance memory overhead
5. **Thread-Safe**: Stateless design with minimal shared state
### Output Format
The logger produces output in the following format:
```
ISO_TIMESTAMP : TAG[LEVEL] : ID : Message
```
Example:
```
2025-01-21T10:30:45.123Z : GPIO_WRAPPER[INFO] : 1001 : GPIO wrapper initialized
2025-01-21T10:30:45.124Z : UART_WRAPPER[ERROR] : 2001 : Failed to configure UART port 0
```
### Class Diagram
```
┌─────────────────────────────────────┐
│ asf::logger │
│ (namespace) │
├─────────────────────────────────────┤
│ + initialize(config): void │
│ + setLogLevel(level): void │
│ + getLogLevel(): LogLevel │
│ + enableTimestamp(enable): void │
│ + enableColor(enable): void │
│ + enableId(enable): void │
│ + getIsoTimestamp(buf, size): char* │
│ + log(tag, id, level, fmt, ...): void│
│ + logVerbose(tag, id, fmt, ...): void│
│ + logDebug(tag, id, fmt, ...): void │
│ + logInfo(tag, id, fmt, ...): void │
│ + logWarning(tag, id, fmt, ...): void│
│ + logError(tag, id, fmt, ...): void │
│ + getDefaultConfig(): LoggerConfig │
│ + logLevelToString(level): char* │
│ + logLevelToColor(level): char* │
└─────────────────────────────────────┘
```
### Enumerations
#### LogLevel
- `VERBOSE`: Most detailed logging (level 0)
- `DEBUG`: Debug information (level 1)
- `INFO`: General information (level 2)
- `WARNING`: Warning messages (level 3)
- `ERROR`: Error messages (level 4)
- `NONE`: No logging (level 5)
### Configuration Structure
```cpp
struct LoggerConfig {
LogLevel minLevel; // Minimum log level to display
bool enableTimestamp; // Enable ISO timestamp in output
bool enableColor; // Enable color output
bool enableId; // Enable unique ID in output
uint32_t maxMessageLength; // Maximum message length
};
```
## Usage Examples
### Basic Usage
```cpp
#include "logger.hpp"
static const char* TAG = "MY_MODULE";
void myFunction() {
// Initialize logger with default configuration
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
asf::logger::initialize(config);
// Log messages with different levels
asf::logger::logInfo(TAG, 1001, "Module initialized successfully");
asf::logger::logWarning(TAG, 1002, "Configuration parameter missing, using default: %d", 42);
asf::logger::logError(TAG, 1003, "Failed to connect to server: %s", "timeout");
}
```
### Using Convenience Macros
```cpp
#include "logger.hpp"
static const char* TAG = "SENSOR_MODULE";
void sensorTask() {
// Short form macros for easier usage
ASF_LOGI(TAG, 2001, "Sensor task started");
ASF_LOGD(TAG, 2002, "Reading sensor value: %d", sensorValue);
ASF_LOGW(TAG, 2003, "Sensor value out of range: %d", sensorValue);
ASF_LOGE(TAG, 2004, "Sensor communication failed");
// Long form macros
ASF_LOG_INFO(TAG, 2005, "Sensor calibration complete");
ASF_LOG_ERROR(TAG, 2006, "Critical sensor failure detected");
}
```
### Custom Configuration
```cpp
#include "logger.hpp"
void setupCustomLogger() {
asf::logger::LoggerConfig config = {};
config.minLevel = asf::logger::LogLevel::DEBUG;
config.enableTimestamp = true;
config.enableColor = false; // Disable colors for file output
config.enableId = true;
config.maxMessageLength = 512; // Longer messages
asf::logger::initialize(config);
// Runtime configuration changes
asf::logger::setLogLevel(asf::logger::LogLevel::WARNING);
asf::logger::enableColor(true);
}
```
### Performance-Critical Code
```cpp
#include "logger.hpp"
static const char* TAG = "PERFORMANCE";
void performanceCriticalFunction() {
// Check log level before expensive operations
if (asf::logger::getLogLevel() <= asf::logger::LogLevel::DEBUG) {
// Only format expensive debug info if debug logging is enabled
char debugInfo[256];
formatExpensiveDebugInfo(debugInfo, sizeof(debugInfo));
ASF_LOGD(TAG, 3001, "Debug info: %s", debugInfo);
}
// Error logging is always fast
ASF_LOGE(TAG, 3002, "Critical error occurred");
}
```
### Module-Specific Logging
```cpp
// gpio_wrapper.cpp
#include "logger.hpp"
static const char* TAG = "GPIO_WRAPPER";
class Gpio {
public:
bool configure(uint32_t pin, GpioMode mode) {
ASF_LOGI(TAG, 4001, "Configuring GPIO pin %lu", pin);
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 4002, "Invalid GPIO pin: %lu", pin);
return false;
}
ASF_LOGD(TAG, 4003, "GPIO pin %lu configured successfully", pin);
return true;
}
};
```
## API Reference
### Initialization Functions
- **initialize(config)**: Initialize logger with configuration
- **getDefaultConfig()**: Get default logger configuration
### Configuration Functions
- **setLogLevel(level)**: Set minimum log level
- **getLogLevel()**: Get current minimum log level
- **enableTimestamp(enable)**: Enable/disable timestamp
- **enableColor(enable)**: Enable/disable color output
- **enableId(enable)**: Enable/disable message ID
### Logging Functions
- **log(tag, id, level, format, ...)**: Main logging function
- **logVerbose(tag, id, format, ...)**: Log verbose message
- **logDebug(tag, id, format, ...)**: Log debug message
- **logInfo(tag, id, format, ...)**: Log info message
- **logWarning(tag, id, format, ...)**: Log warning message
- **logError(tag, id, format, ...)**: Log error message
### Utility Functions
- **getIsoTimestamp(buffer, size)**: Get ISO 8601 timestamp
- **logLevelToString(level)**: Convert log level to string
- **logLevelToColor(level)**: Convert log level to color code
### Convenience Macros
#### Long Form Macros
- `ASF_LOG_VERBOSE(tag, id, format, ...)`
- `ASF_LOG_DEBUG(tag, id, format, ...)`
- `ASF_LOG_INFO(tag, id, format, ...)`
- `ASF_LOG_WARNING(tag, id, format, ...)`
- `ASF_LOG_ERROR(tag, id, format, ...)`
#### Short Form Macros
- `ASF_LOGV(tag, id, format, ...)`
- `ASF_LOGD(tag, id, format, ...)`
- `ASF_LOGI(tag, id, format, ...)`
- `ASF_LOGW(tag, id, format, ...)`
- `ASF_LOGE(tag, id, format, ...)`
## Message ID Guidelines
To maintain consistency and avoid conflicts, follow these guidelines for message IDs:
### ID Ranges by Module
- **1000-1999**: Core system modules
- **2000-2999**: Hardware abstraction layer (HAL)
- **3000-3999**: Application layer
- **4000-4999**: Communication modules
- **5000-5999**: User interface modules
- **6000-6999**: Test and debug modules
### ID Ranges by Severity
Within each module range:
- **x001-x199**: Info messages
- **x200-x399**: Debug messages
- **x400-x599**: Warning messages
- **x600-x799**: Error messages
- **x800-x999**: Verbose messages
### Example ID Assignment
```cpp
// GPIO Wrapper (HAL module, range 2000-2999)
static const uint32_t GPIO_INIT_SUCCESS = 2001; // Info
static const uint32_t GPIO_PIN_CONFIGURED = 2002; // Info
static const uint32_t GPIO_DEBUG_STATE = 2201; // Debug
static const uint32_t GPIO_INVALID_PIN = 2601; // Error
static const uint32_t GPIO_CONFIG_FAILED = 2602; // Error
```
## Performance Considerations
### Compile-Time Optimizations
The logger is designed for minimal overhead:
1. **Level Filtering**: Messages below the minimum level are filtered at runtime
2. **Macro Efficiency**: Macros provide zero-overhead abstraction
3. **String Formatting**: Only performed when message will be output
4. **Memory Usage**: Fixed buffer sizes prevent dynamic allocation
### Runtime Performance
- **Fast Level Check**: O(1) log level comparison
- **Efficient Formatting**: Uses stack-based buffers
- **Minimal Function Calls**: Direct ESP-IDF integration
- **Thread-Safe**: No locking overhead (ESP-IDF handles synchronization)
### Memory Usage
- **Static Configuration**: ~20 bytes of static memory
- **Stack Usage**: ~400 bytes per log call (configurable)
- **No Heap Allocation**: All operations use stack memory
- **Flash Usage**: ~2KB for complete implementation
## Integration with ESP-IDF Wrappers
### Replacing ESP-IDF Logging
Replace ESP-IDF logging calls in your wrappers:
```cpp
// Before (ESP-IDF logging)
ESP_LOGI(TAG, "GPIO wrapper initialized");
ESP_LOGE(TAG, "Failed to configure GPIO pin %lu: %s", pin, esp_err_to_name(ret));
// After (ASF logging)
ASF_LOGI(TAG, 1001, "GPIO wrapper initialized");
ASF_LOGE(TAG, 1002, "Failed to configure GPIO pin %lu: %s", pin, esp_err_to_name(ret));
```
### Consistent Error Reporting
```cpp
bool Gpio::configure(uint32_t pin, GpioMode mode) {
ASF_LOGI(TAG, 2001, "Configuring GPIO pin %lu", pin);
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2002, "Invalid GPIO pin: %lu", pin);
return false;
}
esp_err_t ret = gpio_config(&config);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2003, "Failed to configure GPIO pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2004, "GPIO pin %lu configured successfully", pin);
return true;
}
```
## Testing
The logger includes comprehensive unit tests covering:
- **Initialization and Configuration**: Default and custom configurations
- **Log Level Management**: Setting and getting log levels
- **Message Formatting**: Timestamp, color, and ID formatting
- **Null Parameter Handling**: Graceful handling of invalid inputs
- **Performance Testing**: High-frequency logging scenarios
- **Macro Functionality**: All convenience macros
- **Edge Cases**: Long messages, buffer limits, etc.
### Running Tests
```bash
# Build and run logger tests
idf.py build
idf.py flash monitor
```
## Dependencies
- ESP-IDF logging system (`esp_log.h`)
- ESP-IDF timer (`esp_timer.h`)
- Standard C library (`cstdio`, `cstring`, `ctime`)
- POSIX time functions (`sys/time.h`)
## Thread Safety
The ASF Logger is thread-safe because:
1. **ESP-IDF Integration**: Uses ESP-IDF's thread-safe logging system
2. **Minimal Shared State**: Only configuration is shared between threads
3. **Atomic Operations**: Configuration changes are atomic
4. **Stack-Based Buffers**: Each thread uses its own stack space
## Limitations
- **Message Length**: Limited by configured maximum message length (default: 256 characters)
- **ID Range**: 32-bit unsigned integer range (0 to 4,294,967,295)
- **Color Support**: Depends on terminal/console color support
- **Timestamp Accuracy**: Limited by system clock resolution
## Migration Guide
### From ESP-IDF Logging
1. Include the ASF logger header:
```cpp
#include "logger.hpp"
```
2. Replace ESP-IDF logging calls:
```cpp
// Old
ESP_LOGI(TAG, "Message");
ESP_LOGE(TAG, "Error: %d", error);
// New
ASF_LOGI(TAG, 1001, "Message");
ASF_LOGE(TAG, 1002, "Error: %d", error);
```
3. Initialize the logger in your main function:
```cpp
void app_main() {
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
asf::logger::initialize(config);
// Your application code
}
```
### Message ID Assignment
Create a header file for your module's message IDs:
```cpp
// module_log_ids.hpp
#pragma once
namespace MyModule {
namespace LogIds {
// Info messages (1001-1199)
static const uint32_t INIT_SUCCESS = 1001;
static const uint32_t CONFIG_LOADED = 1002;
// Error messages (1600-1799)
static const uint32_t INIT_FAILED = 1601;
static const uint32_t CONFIG_ERROR = 1602;
}
}
```
This ASF Logger provides a robust, efficient, and feature-rich logging solution that enhances the ESP-IDF logging system while maintaining compatibility and performance.