cleanup
This commit is contained in:
431
1 software design/components/utils/logger/README.md
Normal file
431
1 software design/components/utils/logger/README.md
Normal 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.
|
||||
Reference in New Issue
Block a user