cleanup sw req

This commit is contained in:
2026-02-01 19:47:53 +01:00
parent 0bdbcb1657
commit 304371c6b8
608 changed files with 47798 additions and 0 deletions

View File

@@ -0,0 +1,601 @@
# ASF Logger - Architecture and Design Document
## Table of Contents
1. [Overview](#overview)
2. [Architecture Design](#architecture-design)
3. [UML Diagrams](#uml-diagrams)
4. [Design Patterns](#design-patterns)
5. [Component Structure](#component-structure)
6. [API Design](#api-design)
7. [Performance Analysis](#performance-analysis)
8. [Integration Guide](#integration-guide)
9. [Testing Strategy](#testing-strategy)
10. [Future Enhancements](#future-enhancements)
## Overview
The ASF Logger is a high-performance, low-overhead logging wrapper for ESP-IDF that provides structured, traceable logging with unique message identifiers. It abstracts the underlying ESP-IDF logging mechanism while adding enhanced formatting, filtering, and configuration capabilities.
### Key Design Goals
- **Zero Overhead Abstraction**: Minimal performance impact over direct ESP-IDF calls
- **Structured Logging**: Consistent format with timestamps, tags, levels, and unique IDs
- **Easy Integration**: Simple namespace-based API with convenient macros
- **Thread Safety**: Built on ESP-IDF's thread-safe logging infrastructure
- **Compile-Time Efficiency**: Header-only interface with minimal dependencies
- **Memory Efficiency**: No dynamic allocation, fixed memory footprint
## Architecture Design
### High-Level Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ GPIO Wrapper │ UART Wrapper │ I2C Wrapper │ Other Modules │
├─────────────────────────────────────────────────────────────┤
│ ASF Logger │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Config │ │ Formatter │ │ Convenience │ │
│ │ Manager │ │ Engine │ │ Macros │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ESP-IDF Log System │
├─────────────────────────────────────────────────────────────┤
│ Hardware Layer │
└─────────────────────────────────────────────────────────────┘
```
### Component Architecture
The ASF Logger consists of three main functional components:
1. **Configuration Manager**: Handles logger settings and runtime configuration
2. **Message Formatter**: Formats log messages with timestamps, colors, and IDs
3. **API Layer**: Provides the public interface with functions and macros
## UML Diagrams
### Class Diagram
```mermaid
classDiagram
namespace asf_logger {
class LogLevel {
<<enumeration>>
VERBOSE : 0
DEBUG : 1
INFO : 2
WARNING : 3
ERROR : 4
NONE : 5
}
class LoggerConfig {
+LogLevel minLevel
+bool enableTimestamp
+bool enableColor
+bool enableId
+uint32_t maxMessageLength
}
class Logger {
<<namespace>>
-LoggerConfig s_config
-COLOR_RESET : char*
-COLOR_VERBOSE : char*
-COLOR_DEBUG : char*
-COLOR_INFO : char*
-COLOR_WARNING : char*
-COLOR_ERROR : char*
+initialize(config: LoggerConfig) void
+setLogLevel(level: LogLevel) void
+getLogLevel() LogLevel
+enableTimestamp(enable: bool) void
+enableColor(enable: bool) void
+enableId(enable: bool) void
+getIsoTimestamp(buffer: char*, size: size_t) char*
+log(tag: char*, id: uint32_t, level: LogLevel, format: char*, ...) void
+logVerbose(tag: char*, id: uint32_t, format: char*, ...) void
+logDebug(tag: char*, id: uint32_t, format: char*, ...) void
+logInfo(tag: char*, id: uint32_t, format: char*, ...) void
+logWarning(tag: char*, id: uint32_t, format: char*, ...) void
+logError(tag: char*, id: uint32_t, format: char*, ...) void
+getDefaultConfig() LoggerConfig
+logLevelToString(level: LogLevel) char*
+logLevelToColor(level: LogLevel) char*
}
Logger --> LoggerConfig : uses
Logger --> LogLevel : uses
}
class ESP_IDF_Log {
<<external>>
+ESP_LOGV(tag, format, ...)
+ESP_LOGD(tag, format, ...)
+ESP_LOGI(tag, format, ...)
+ESP_LOGW(tag, format, ...)
+ESP_LOGE(tag, format, ...)
}
Logger --> ESP_IDF_Log : delegates to
```
### Sequence Diagram - Logging Flow
```mermaid
sequenceDiagram
participant App as Application
participant Macro as ASF_LOGI Macro
participant Logger as asf::logger
participant Formatter as Message Formatter
participant ESP as ESP-IDF Log
participant Output as Console/UART
App->>Macro: ASF_LOGI(TAG, ID, format, args)
Macro->>Logger: logInfo(TAG, ID, format, args)
alt Log level check
Logger->>Logger: Check if INFO >= minLevel
alt Level allowed
Logger->>Formatter: Format message with timestamp, color, ID
Formatter->>Logger: Formatted message string
Logger->>ESP: ESP_LOGI("ASF", formatted_message)
ESP->>Output: Write to console/UART
else Level filtered
Logger-->>App: Return (no output)
end
end
```
### State Diagram - Logger Configuration
```mermaid
stateDiagram-v2
[*] --> Uninitialized
Uninitialized --> Initialized : initialize(config)
state Initialized {
[*] --> DefaultConfig
DefaultConfig --> CustomConfig : setLogLevel() / enableTimestamp() / etc.
CustomConfig --> DefaultConfig : initialize(defaultConfig)
state "Runtime Configuration" as Runtime {
[*] --> LevelFiltering
LevelFiltering --> TimestampEnabled
TimestampEnabled --> ColorEnabled
ColorEnabled --> IdEnabled
IdEnabled --> LevelFiltering
}
DefaultConfig --> Runtime
CustomConfig --> Runtime
}
Initialized --> [*] : Application Exit
```
### Component Diagram
```mermaid
graph TB
subgraph "ASF Logger Component"
subgraph "Public API"
A[Logger Functions]
B[Convenience Macros]
C[Configuration API]
end
subgraph "Core Implementation"
D[Message Formatter]
E[Level Filter]
F[Timestamp Generator]
G[Color Manager]
end
subgraph "Configuration"
H[Static Config]
I[Runtime Settings]
end
end
subgraph "External Dependencies"
J[ESP-IDF Log System]
K[POSIX Time Functions]
L[Standard C Library]
end
A --> D
A --> E
B --> A
C --> H
C --> I
D --> F
D --> G
D --> J
F --> K
D --> L
E --> H
```
## Design Patterns
### 1. Namespace Pattern
The logger uses a namespace-based design instead of a class-based approach:
**Benefits:**
- Zero instantiation overhead
- No virtual function calls
- Simple include and use
- Thread-safe by design (minimal shared state)
```cpp
namespace asf {
namespace logger {
void logInfo(const char* tag, uint32_t id, const char* format, ...);
}
}
```
### 2. Configuration Object Pattern
Configuration is encapsulated in a structure for easy management:
```cpp
struct LoggerConfig {
LogLevel minLevel;
bool enableTimestamp;
bool enableColor;
bool enableId;
uint32_t maxMessageLength;
};
```
### 3. Template Specialization for Performance
The logger uses compile-time optimizations where possible:
```cpp
// Macros provide compile-time string concatenation
#define ASF_LOGI(tag, id, format, ...) \
asf::logger::logInfo(tag, id, format, ##__VA_ARGS__)
```
### 4. Facade Pattern
The logger acts as a facade over the ESP-IDF logging system, providing a simplified interface:
```cpp
// Complex ESP-IDF logging
ESP_LOGI(TAG, "Message with %d parameters", value);
// Simplified ASF logging
ASF_LOGI(TAG, 1001, "Message with %d parameters", value);
```
## Component Structure
### File Organization
```
components/utils/logger/
├── CMakeLists.txt # Build configuration
├── README.md # User documentation
├── ARCHITECTURE.md # This document
├── com/ # Core implementation
│ ├── logger.hpp # Public API header
│ └── logger.cpp # Implementation
├── test/ # Unit tests
│ ├── CMakeLists.txt
│ └── test_logger.cpp
└── example/ # Usage examples
└── gpio_wrapper_example.cpp
```
### Header Dependencies
```mermaid
graph TD
A[logger.hpp] --> B[cstdint]
A --> C[cstdarg]
A --> D[esp_log.h]
A --> E[esp_timer.h]
F[logger.cpp] --> A
F --> G[cstdio]
F --> H[cstring]
F --> I[ctime]
F --> J[sys/time.h]
```
## API Design
### Function Hierarchy
```
asf::logger namespace
├── Configuration Functions
│ ├── initialize(config)
│ ├── setLogLevel(level)
│ ├── getLogLevel()
│ ├── enableTimestamp(enable)
│ ├── enableColor(enable)
│ └── enableId(enable)
├── Core Logging Functions
│ ├── log(tag, id, level, format, ...)
│ ├── logVerbose(tag, id, format, ...)
│ ├── logDebug(tag, id, format, ...)
│ ├── logInfo(tag, id, format, ...)
│ ├── logWarning(tag, id, format, ...)
│ └── logError(tag, id, format, ...)
├── Utility Functions
│ ├── getIsoTimestamp(buffer, size)
│ ├── logLevelToString(level)
│ ├── logLevelToColor(level)
│ └── getDefaultConfig()
└── Convenience 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, ...)
├── 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 Flow Architecture
```mermaid
flowchart TD
A[Application Code] --> B{Macro Call}
B --> C[Level Check]
C --> D{Level >= MinLevel?}
D -->|No| E[Return - No Output]
D -->|Yes| F[Format Message]
F --> G[Add Timestamp]
G --> H[Add Color Codes]
H --> I[Add Message ID]
I --> J[Call ESP-IDF Log]
J --> K[Output to Console/UART]
style A fill:#e1f5fe
style E fill:#ffebee
style K fill:#e8f5e8
```
## Performance Analysis
### Memory Usage
| Component | Flash (bytes) | RAM (bytes) | Stack (bytes/call) |
|-----------|---------------|-------------|-------------------|
| Core Implementation | ~2048 | ~20 (static config) | ~400 |
| Macros | ~0 | ~0 | ~0 |
| Per Log Call | ~0 | ~0 | ~400 |
| **Total** | **~2KB** | **~20B** | **~400B** |
### Performance Characteristics
```mermaid
graph LR
subgraph "Performance Metrics"
A[Level Check: O(1)]
B[Message Format: O(n)]
C[Timestamp: O(1)]
D[ESP-IDF Call: O(1)]
end
subgraph "Optimization Techniques"
E[Early Level Filtering]
F[Stack-based Buffers]
G[Compile-time Macros]
H[Minimal Function Calls]
end
A --> E
B --> F
C --> G
D --> H
```
### Timing Analysis
| Operation | Typical Time (μs) | Notes |
|-----------|------------------|-------|
| Level Check | < 0.1 | Simple integer comparison |
| Message Formatting | 10-50 | Depends on message complexity |
| Timestamp Generation | 5-10 | System call overhead |
| ESP-IDF Output | 100-1000 | UART/Console output speed |
## Integration Guide
### Step-by-Step Integration
1. **Include Header**
```cpp
#include "logger.hpp"
```
2. **Initialize Logger**
```cpp
void app_main() {
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.minLevel = asf::logger::LogLevel::DEBUG;
asf::logger::initialize(config);
}
```
3. **Define Module Constants**
```cpp
// module_log_ids.hpp
static const char* TAG = "MY_MODULE";
namespace MyModuleLogIds {
static const uint32_t INIT_SUCCESS = 1001;
static const uint32_t CONFIG_ERROR = 1601;
}
```
4. **Replace Existing Logging**
```cpp
// Before
ESP_LOGI(TAG, "Module initialized");
// After
ASF_LOGI(TAG, MyModuleLogIds::INIT_SUCCESS, "Module initialized");
```
### Integration Patterns
#### Pattern 1: Module-Specific Wrapper
```cpp
class ModuleLogger {
private:
static const char* TAG;
public:
static void info(uint32_t id, const char* format, ...) {
va_list args;
va_start(args, format);
asf::logger::logInfo(TAG, id, format, args);
va_end(args);
}
};
```
#### Pattern 2: Macro Wrapper
```cpp
#define MODULE_LOGI(id, format, ...) \
ASF_LOGI("MODULE_NAME", id, format, ##__VA_ARGS__)
```
#### Pattern 3: Template Wrapper
```cpp
template<const char* ModuleTag>
class TypedLogger {
public:
static void info(uint32_t id, const char* format, ...) {
// Implementation
}
};
```
## Testing Strategy
### Test Categories
```mermaid
mindmap
root((ASF Logger Tests))
Unit Tests
Configuration Tests
Level Filtering Tests
Message Formatting Tests
Utility Function Tests
Integration Tests
ESP-IDF Integration
Multi-threaded Usage
Performance Tests
System Tests
Memory Usage Tests
Real-world Scenarios
Error Handling Tests
```
### Test Coverage Matrix
| Component | Unit Tests | Integration Tests | Performance Tests |
|-----------|------------|------------------|------------------|
| Configuration Manager | | | |
| Message Formatter | | | |
| Level Filtering | | | |
| Timestamp Generation | | | |
| Color Management | | | |
| Macro Interface | | | |
| Error Handling | | | |
### Automated Testing Pipeline
```mermaid
flowchart LR
A[Code Commit] --> B[Build Tests]
B --> C[Unit Tests]
C --> D[Integration Tests]
D --> E[Performance Tests]
E --> F[Memory Tests]
F --> G{All Pass?}
G -->|Yes| H[Deploy]
G -->|No| I[Report Failure]
I --> J[Fix Issues]
J --> A
```
## Future Enhancements
### Planned Features
1. **Log Rotation**
- File-based logging with rotation
- Configurable file sizes and retention
2. **Remote Logging**
- Network-based log transmission
- Log aggregation support
3. **Structured Logging**
- JSON format support
- Key-value pair logging
4. **Performance Monitoring**
- Built-in performance metrics
- Log frequency analysis
5. **Advanced Filtering**
- Tag-based filtering
- Runtime filter configuration
### Architecture Evolution
```mermaid
timeline
title ASF Logger Evolution
section Phase 1 (Current)
Basic Logging : Core functionality
: ESP-IDF integration
: Message formatting
section Phase 2 (Next)
Enhanced Features : File logging
: Network logging
: JSON support
section Phase 3 (Future)
Advanced Analytics : Performance monitoring
: Log analysis
: Machine learning integration
```
### Extensibility Points
The logger is designed with several extensibility points:
1. **Custom Formatters**: Plugin architecture for message formatting
2. **Output Backends**: Support for multiple output destinations
3. **Filter Plugins**: Custom filtering logic
4. **Compression**: Log compression for storage efficiency
## Conclusion
The ASF Logger provides a robust, efficient, and extensible logging solution for ESP-IDF applications. Its namespace-based design ensures minimal overhead while providing rich functionality for structured logging with unique message identification.
The architecture balances performance, usability, and maintainability, making it suitable for both development and production environments. The comprehensive testing strategy and clear integration patterns ensure reliable operation across various use cases.
Future enhancements will focus on advanced features while maintaining the core design principles of simplicity, performance, and reliability.

View File

@@ -0,0 +1,5 @@
idf_component_register(
SRCS "com/logger.cpp"
INCLUDE_DIRS "com"
REQUIRES log esp_timer
)

View File

@@ -0,0 +1,689 @@
# ASF Logger - UML Diagrams and Visual Documentation
## Table of Contents
1. [System Overview](#system-overview)
2. [Class Diagrams](#class-diagrams)
3. [Sequence Diagrams](#sequence-diagrams)
4. [State Diagrams](#state-diagrams)
5. [Component Diagrams](#component-diagrams)
6. [Deployment Diagrams](#deployment-diagrams)
7. [Activity Diagrams](#activity-diagrams)
## System Overview
### High-Level System Architecture
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ GPIO │ │ UART │ │ I2C │ │ Other │ │
│ │ Wrapper │ │ Wrapper │ │ Wrapper │ │ Modules │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ ASF Logger │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ Configuration │ │ Message │ │ API Layer │ │
│ │ Manager │ │ Formatter │ │ ┌─────────────────────────────┐ │ │
│ │ │ │ │ │ │ Convenience Macros │ │ │
│ │ • Log Levels │ │ • Timestamps │ │ │ ASF_LOGI, ASF_LOGE, etc. │ │ │
│ │ • Color Config │ │ • Color Codes │ │ └─────────────────────────────┘ │ │
│ │ • Buffer Size │ │ • Message IDs │ │ ┌─────────────────────────────┐ │ │
│ │ • Runtime Ctrl │ │ • Format String │ │ │ Core Functions │ │ │
│ └─────────────────┘ └─────────────────┘ │ │ logInfo, logError, etc. │ │ │
│ │ └─────────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ ESP-IDF Log System │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ Log Levels │ │ Tag Filtering │ │ Output Routing │ │
│ │ │ │ │ │ │ │
│ │ • ESP_LOG_NONE │ │ • Per-tag level │ │ • UART Console │ │
│ │ • ESP_LOG_ERROR │ │ • Wildcard tags │ │ • JTAG Debug │ │
│ │ • ESP_LOG_WARN │ │ • Runtime ctrl │ │ • Custom outputs │ │
│ │ • ESP_LOG_INFO │ │ │ │ │ │
│ │ • ESP_LOG_DEBUG │ │ │ │ │ │
│ │ • ESP_LOG_VERBOSE│ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ Hardware Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ UART │ │ JTAG │ │ Other I/O │ │
│ │ Controller │ │ Interface │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Class Diagrams
### Core Logger Class Structure
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ asf::logger namespace │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ LogLevel (enum class) │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ + VERBOSE : uint8_t = 0 │ │
│ │ + DEBUG : uint8_t = 1 │ │
│ │ + INFO : uint8_t = 2 │ │
│ │ + WARNING : uint8_t = 3 │ │
│ │ + ERROR : uint8_t = 4 │ │
│ │ + NONE : uint8_t = 5 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ LoggerConfig (struct) │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ + minLevel : LogLevel │ │
│ │ + enableTimestamp : bool │ │
│ │ + enableColor : bool │ │
│ │ + enableId : bool │ │
│ │ + maxMessageLength : uint32_t │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Logger Functions (namespace) │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Static Data: │ │
│ │ - s_config : LoggerConfig │ │
│ │ - COLOR_RESET : const char* │ │
│ │ - COLOR_VERBOSE : const char* │ │
│ │ - COLOR_DEBUG : const char* │ │
│ │ - COLOR_INFO : const char* │ │
│ │ - COLOR_WARNING : const char* │ │
│ │ - COLOR_ERROR : const char* │ │
│ │ │ │
│ │ Configuration Functions: │ │
│ │ + initialize(config: LoggerConfig) : void │ │
│ │ + setLogLevel(level: LogLevel) : void │ │
│ │ + getLogLevel() : LogLevel │ │
│ │ + enableTimestamp(enable: bool) : void │ │
│ │ + enableColor(enable: bool) : void │ │
│ │ + enableId(enable: bool) : void │ │
│ │ + getDefaultConfig() : LoggerConfig │ │
│ │ │ │
│ │ Core Logging Functions: │ │
│ │ + log(tag: char*, id: uint32_t, level: LogLevel, │ │
│ │ format: char*, ...) : void │ │
│ │ + logVerbose(tag: char*, id: uint32_t, │ │
│ │ format: char*, ...) : void │ │
│ │ + logDebug(tag: char*, id: uint32_t, │ │
│ │ format: char*, ...) : void │ │
│ │ + logInfo(tag: char*, id: uint32_t, │ │
│ │ format: char*, ...) : void │ │
│ │ + logWarning(tag: char*, id: uint32_t, │ │
│ │ format: char*, ...) : void │ │
│ │ + logError(tag: char*, id: uint32_t, │ │
│ │ format: char*, ...) : void │ │
│ │ │ │
│ │ Utility Functions: │ │
│ │ + getIsoTimestamp(buffer: char*, size: size_t) : char* │ │
│ │ + logLevelToString(level: LogLevel) : char* │ │
│ │ + logLevelToColor(level: LogLevel) : char* │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Convenience Macros │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Long Form: │ │
│ │ • 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: │ │
│ │ • 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, ...) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Relationship with ESP-IDF
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ ASF Logger Dependencies │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ asf::logger │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ uses │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ESP-IDF Log System │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ + ESP_LOGV(tag, format, ...) │ │
│ │ + ESP_LOGD(tag, format, ...) │ │
│ │ + ESP_LOGI(tag, format, ...) │ │
│ │ + ESP_LOGW(tag, format, ...) │ │
│ │ + ESP_LOGE(tag, format, ...) │ │
│ │ + esp_log_level_set(tag, level) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ uses │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ System Libraries │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Standard C Library: │ │
│ │ • cstdio (printf family) │ │
│ │ • cstring (string operations) │ │
│ │ • ctime (time formatting) │ │
│ │ • cstdarg (variadic functions) │ │
│ │ │ │
│ │ POSIX Functions: │ │
│ │ • sys/time.h (gettimeofday) │ │
│ │ │ │
│ │ ESP-IDF Specific: │ │
│ │ • esp_timer.h (high resolution timing) │ │
│ │ • esp_log.h (logging infrastructure) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Sequence Diagrams
### Basic Logging Sequence
```
Application ASF_LOGI asf::logger Message ESP-IDF Console
Code Macro ::logInfo Formatter Log Sys Output
│ │ │ │ │ │
│ Log Call │ │ │ │ │
├────────────► │ │ │ │
│ │ logInfo() │ │ │ │
│ ├─────────────► │ │ │
│ │ │ Level Check │ │ │
│ │ ├─────────────┤ │ │
│ │ │ │ │ │
│ │ │ Level OK? │ │ │
│ │ ├─────────────┤ │ │
│ │ │ │ │ │
│ │ │ Format Msg │ │ │
│ │ ├─────────────► │ │
│ │ │ │ Formatted │ │
│ │ │ │ Message │ │
│ │ │ ◄─────────────┤ │
│ │ │ │ │ │
│ │ │ ESP_LOGI() │ │ │
│ │ ├─────────────────────────────► │
│ │ │ │ │ Output │
│ │ │ │ ├────────────►
│ │ │ │ │ │
│ │ Return │ │ │ │
│ ◄─────────────┤ │ │ │
│ Return │ │ │ │ │
◄────────────┤ │ │ │ │
│ │ │ │ │ │
```
### Configuration Change Sequence
```
Application asf::logger Configuration ESP-IDF
Code Functions Manager Log System
│ │ │ │
│ setLogLevel │ │ │
├─────────────► │ │
│ │ Update Config │ │
│ ├───────────────► │
│ │ │ Set ESP Level│
│ │ ├──────────────►
│ │ │ │
│ │ Config Updated│ │
│ ◄───────────────┤ │
│ Return │ │ │
◄─────────────┤ │ │
│ │ │ │
│ │ │ │
│ Next Log │ │ │
├─────────────► │ │
│ │ Check Level │ │
│ ├───────────────► │
│ │ New Level │ │
│ ◄───────────────┤ │
│ │ │ │
│ Log Output │ │ │
◄─────────────┤ │ │
│ │ │ │
```
### Error Handling Sequence
```
Application ASF_LOGE asf::logger Message ESP-IDF Console
Code Macro ::logError Formatter Log Sys Output
│ │ │ │ │ │
│ Error Log │ │ │ │ │
├────────────► │ │ │ │
│ │ logError() │ │ │ │
│ ├─────────────► │ │ │
│ │ │ Level Check │ │ │
│ │ ├─────────────┤ │ │
│ │ │ (Always OK │ │ │
│ │ │ for ERROR) │ │ │
│ │ │ │ │ │
│ │ │ Format Msg │ │ │
│ │ ├─────────────► │ │
│ │ │ │ Add Error │ │
│ │ │ │ Color Code │ │
│ │ │ │ Add Timestamp│ │
│ │ │ │ Add Message │ │
│ │ │ │ ID │ │
│ │ │ │ │ │
│ │ │ │ Formatted │ │
│ │ │ │ Error Msg │ │
│ │ │ ◄─────────────┤ │
│ │ │ │ │ │
│ │ │ ESP_LOGE() │ │ │
│ │ ├─────────────────────────────► │
│ │ │ │ │ Red Error │
│ │ │ │ │ Output │
│ │ │ │ ├────────────►
│ │ │ │ │ │
│ │ Return │ │ │ │
│ ◄─────────────┤ │ │ │
│ Return │ │ │ │ │
◄────────────┤ │ │ │ │
│ │ │ │ │ │
```
## State Diagrams
### Logger State Machine
```
┌─────────────────────────────────────────────────────────┐
│ Logger State Machine │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ [Initial State] │
│ Uninitialized │
│ │
│ • No configuration loaded │
│ • Default ESP-IDF logging active │
│ • ASF functions not available │
└─────────────────────────────────────────────────────────┘
│ initialize(config)
┌─────────────────────────────────────────────────────────┐
│ Initialized │
│ │
│ • Configuration loaded │
│ • ESP-IDF log level set │
│ • ASF functions active │
│ • Ready for logging │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ Level Filter │ │ Message Format │ │ Runtime Config │
│ │ │ │ │ │
│ • Check min level │ │ • Add timestamp │ │ • Change log level │
│ • Allow/block msg │ │ • Add color codes │ │ • Toggle features │
│ • Early return │ │ • Add message ID │ │ • Update settings │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
│ │ │
│ │ │
└─────────────────────────┼─────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Output Ready │
│ │
│ • Message formatted │
│ • Ready for ESP-IDF output │
│ • All processing complete │
└─────────────────────────────────────────────────────────┘
│ ESP_LOG*() call
┌─────────────────────────────────────────────────────────┐
│ Message Output │
│ │
│ • Sent to ESP-IDF log system │
│ • Routed to console/UART │
│ • Visible to user │
└─────────────────────────────────────────────────────────┘
```
### Configuration State Transitions
```
┌─────────────────────────────────────────────────────────┐
│ Configuration States │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Default Config │
│ │
│ minLevel = INFO │
│ enableTimestamp = true │
│ enableColor = true │
│ enableId = true │
│ maxMessageLength = 256 │
└─────────────────────────────────────────────────────────┘
│ Runtime changes
┌─────────────────────────────────────────────────────────┐
│ Custom Config │
│ │
│ User-modified settings: │
│ • setLogLevel() │
│ • enableTimestamp() │
│ • enableColor() │
│ • enableId() │
└─────────────────────────────────────────────────────────┘
│ initialize(defaultConfig)
┌─────────────────────────────────────────────────────────┐
│ Reset to Default │
│ │
│ All settings restored to default values │
└─────────────────────────────────────────────────────────┘
```
## Component Diagrams
### Internal Component Structure
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ ASF Logger Component │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Public Interface │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Core API │ │ Convenience │ │ Configuration │ │ │
│ │ │ Functions │ │ Macros │ │ API │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • logInfo() │ │ • ASF_LOGI() │ │ • initialize() │ │ │
│ │ │ • logError() │ │ • ASF_LOGE() │ │ • setLogLevel() │ │ │
│ │ │ • logDebug() │ │ • ASF_LOGD() │ │ • enableColor() │ │ │
│ │ │ • logWarning() │ │ • ASF_LOGW() │ │ • getLogLevel() │ │ │
│ │ │ • logVerbose() │ │ • ASF_LOGV() │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Internal Components │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Configuration │ │ Message │ │ Utility │ │ │
│ │ │ Manager │ │ Formatter │ │ Functions │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • Static config │ │ • Timestamp gen │ │ • Level to string │ │ │
│ │ │ • Level filter │ │ • Color codes │ │ • Level to color │ │ │
│ │ │ • Runtime ctrl │ │ • Message ID │ │ • Default config │ │ │
│ │ │ • Validation │ │ • Format string │ │ • Validation │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ External Dependencies │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ ESP-IDF Log │ │ Standard C │ │ POSIX Time │ │ │
│ │ │ System │ │ Library │ │ Functions │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • ESP_LOGI() │ │ • printf() │ │ • gettimeofday() │ │ │
│ │ │ • ESP_LOGE() │ │ • snprintf() │ │ • localtime() │ │ │
│ │ │ • ESP_LOGD() │ │ • vsnprintf() │ │ • struct timeval │ │ │
│ │ │ • ESP_LOGW() │ │ • memset() │ │ │ │ │
│ │ │ • ESP_LOGV() │ │ • strlen() │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Data Flow Component Diagram
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Data Flow Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Application │ │ ASF Logger │ │ ESP-IDF Log │ │
│ │ Code │ │ Macros │ │ System │ │
│ │ │ │ │ │ │ │
│ │ • Log calls │───►│ • ASF_LOGI() │───►│ • ESP_LOGI() │ │
│ │ • Error msgs │ │ • ASF_LOGE() │ │ • ESP_LOGE() │ │
│ │ • Debug info │ │ • ASF_LOGD() │ │ • Level filtering │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Message ID │ │ Message │ │ Console/UART │ │
│ │ Management │ │ Formatter │ │ Output │ │
│ │ │ │ │ │ │ │
│ │ • ID validation │───►│ • Timestamp │───►│ • Serial output │ │
│ │ • Range check │ │ • Color codes │ │ • JTAG debug │ │
│ │ • Uniqueness │ │ • Format string │ │ • Custom handlers │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Configuration │ │ Level │ │
│ │ Manager │ │ Filter │ │
│ │ │ │ │ │
│ │ • Runtime cfg │───►│ • Min level │ │
│ │ • Feature flags │ │ • Early return │ │
│ │ • Buffer size │ │ • Performance │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Deployment Diagrams
### ESP32 System Deployment
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ ESP32 Device │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Flash Memory │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Application │ │ ASF Logger │ │ ESP-IDF │ │ │
│ │ │ Code │ │ Component │ │ Framework │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • Main app │ │ • logger.cpp │ │ • Log system │ │ │
│ │ │ • GPIO wrapper │ │ • logger.hpp │ │ • UART drivers │ │ │
│ │ │ • UART wrapper │ │ • Macros │ │ • System libs │ │ │
│ │ │ • Other modules │ │ • Config │ │ • FreeRTOS │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RAM Memory │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Stack │ │ Static Data │ │ Heap │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • Log buffers │ │ • Logger config │ │ • Dynamic alloc │ │ │
│ │ │ • Function vars │ │ • Color strings │ │ • Task stacks │ │ │
│ │ │ • Call stack │ │ • Constants │ │ • Buffers │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Hardware Interfaces │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ UART │ │ JTAG │ │ GPIO │ │ │
│ │ │ Controller │ │ Interface │ │ Pins │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • Serial output │ │ • Debug output │ │ • Status LEDs │ │ │
│ │ │ • Console I/O │ │ • Trace data │ │ • Debug signals │ │ │
│ │ │ • Log routing │ │ • Real-time │ │ • External conn │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ External Connections │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ Serial │ │ Debug Probe │ │ Network/WiFi │ │
│ │ Console │ │ │ │ │ │
│ │ │ │ • JTAG adapter │ │ • Remote logging │ │
│ │ • Terminal app │ │ • OpenOCD │ │ • Log aggregation │ │
│ │ • Log viewer │ │ • GDB debug │ │ • Cloud logging │ │
│ │ • Real-time │ │ • Trace viewer │ │ • Monitoring systems │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Activity Diagrams
### Logging Process Activity
```
┌─────────────────────────────────────────────────────────┐
│ Logging Activity │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ [Start] │
│ Application Log Call │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Macro Expansion │
│ │
│ ASF_LOGI(TAG, ID, format, args) │
│ ↓ │
│ asf::logger::logInfo(TAG, ID, format, args) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Level Check │
│ │
│ Is INFO >= minLevel? │
└─────────────────────────────────────────────────────────┘
┌─────────┴─────────┐
│ │
No Yes
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────────────┐
│ [End] │ │ Format Message │
│ Early Return │ │ │
│ (No Output) │ │ • Get timestamp │
└─────────────────────┘ │ • Add color codes │
│ • Add message ID │
│ • Format with args │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ Call ESP-IDF │
│ │
│ ESP_LOGI("ASF", formatted_msg) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ Output Routing │
│ │
│ • Check ESP-IDF log level │
│ • Route to console/UART │
│ • Apply ESP-IDF formatting │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [End] │
│ Message Output │
└─────────────────────────────────┘
```
### Configuration Change Activity
```
┌─────────────────────────────────────────────────────────┐
│ Configuration Change Activity │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ [Start] │
│ Configuration Change Request │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Validate Parameters │
│ │
│ • Check log level range │
│ • Validate boolean flags │
│ • Check buffer size limits │
└─────────────────────────────────────────────────────────┘
┌─────────┴─────────┐
│ │
Valid Invalid
│ │
▼ ▼
┌─────────────────────────────────┐ ┌─────────────────────┐
│ Update Configuration │ │ [End] │
│ │ │ Return Error │
│ • Modify static config │ │ │
│ • Store new values │ └─────────────────────┘
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ Update ESP-IDF Settings │
│ │
│ esp_log_level_set("*", level) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ Apply Changes │
│ │
│ • New settings active │
│ • Subsequent logs affected │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [End] │
│ Configuration Updated │
└─────────────────────────────────┘
```
This comprehensive visual documentation provides detailed UML diagrams and architectural views of the ASF Logger system, showing how all components interact and the flow of data through the system. These diagrams can be used for understanding the system architecture, debugging issues, and planning future enhancements.

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.

View File

@@ -0,0 +1,630 @@
# ASF Logger - Usage Guide
## Table of Contents
1. [Quick Start](#quick-start)
2. [Basic Usage](#basic-usage)
3. [Advanced Configuration](#advanced-configuration)
4. [Message ID Management](#message-id-management)
5. [Integration Examples](#integration-examples)
6. [Best Practices](#best-practices)
7. [Troubleshooting](#troubleshooting)
8. [Migration Guide](#migration-guide)
## Quick Start
### 1. Include the Logger
```cpp
#include "logger.hpp"
```
### 2. Initialize in main()
```cpp
void app_main() {
// Initialize with default settings
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
asf::logger::initialize(config);
// Your application code here
}
```
### 3. Start Logging
```cpp
static const char* TAG = "MAIN";
ASF_LOGI(TAG, 1001, "Application started");
ASF_LOGW(TAG, 1002, "Warning: Low memory detected");
ASF_LOGE(TAG, 1003, "Error: Failed to initialize sensor");
```
### 4. Expected Output
```
2025-01-21T10:30:45.123Z : MAIN[INFO] : 1001 : Application started
2025-01-21T10:30:45.124Z : MAIN[WARNING] : 1002 : Warning: Low memory detected
2025-01-21T10:30:45.125Z : MAIN[ERROR] : 1003 : Error: Failed to initialize sensor
```
## Basic Usage
### Available Log Levels
```cpp
// Verbose - Most detailed information
ASF_LOGV(TAG, 1001, "Detailed debug information: %d", value);
// Debug - Debug information
ASF_LOGD(TAG, 1002, "Debug: Processing item %d", itemId);
// Info - General information
ASF_LOGI(TAG, 1003, "System initialized successfully");
// Warning - Warning conditions
ASF_LOGW(TAG, 1004, "Warning: Configuration missing, using default");
// Error - Error conditions
ASF_LOGE(TAG, 1005, "Error: Failed to connect to server");
```
### Long Form Macros
```cpp
// Alternative syntax (same functionality)
ASF_LOG_VERBOSE(TAG, 1001, "Verbose message");
ASF_LOG_DEBUG(TAG, 1002, "Debug message");
ASF_LOG_INFO(TAG, 1003, "Info message");
ASF_LOG_WARNING(TAG, 1004, "Warning message");
ASF_LOG_ERROR(TAG, 1005, "Error message");
```
### Direct Function Calls
```cpp
// Direct function calls (less common usage)
asf::logger::logInfo(TAG, 1001, "Direct function call");
asf::logger::logError(TAG, 1002, "Error via function call");
```
## Advanced Configuration
### Custom Configuration
```cpp
void setupCustomLogger() {
asf::logger::LoggerConfig config = {};
// Set minimum log level
config.minLevel = asf::logger::LogLevel::DEBUG;
// Configure output format
config.enableTimestamp = true; // Show timestamps
config.enableColor = false; // Disable colors (for file output)
config.enableId = true; // Show message IDs
// Set buffer size
config.maxMessageLength = 512; // Longer messages
// Apply configuration
asf::logger::initialize(config);
}
```
### Runtime Configuration Changes
```cpp
void configureLoggerAtRuntime() {
// Change log level during runtime
asf::logger::setLogLevel(asf::logger::LogLevel::WARNING);
// Toggle features
asf::logger::enableTimestamp(false); // Disable timestamps
asf::logger::enableColor(true); // Enable colors
asf::logger::enableId(false); // Disable message IDs
// Check current settings
asf::logger::LogLevel currentLevel = asf::logger::getLogLevel();
ASF_LOGI("CONFIG", 2001, "Current log level: %s",
asf::logger::logLevelToString(currentLevel));
}
```
### Production vs Development Settings
```cpp
void setupProductionLogger() {
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
// Production settings
config.minLevel = asf::logger::LogLevel::WARNING; // Only warnings and errors
config.enableColor = false; // No colors for log files
config.maxMessageLength = 128; // Smaller buffer
asf::logger::initialize(config);
}
void setupDevelopmentLogger() {
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
// Development settings
config.minLevel = asf::logger::LogLevel::VERBOSE; // All messages
config.enableColor = true; // Colorful output
config.maxMessageLength = 512; // Larger buffer
asf::logger::initialize(config);
}
```
## Message ID Management
### ID Allocation Strategy
```cpp
// Create a header file for your module's log IDs
// my_module_log_ids.hpp
#pragma once
namespace MyModuleLogIds {
// Info messages (1001-1199)
static const uint32_t MODULE_INIT = 1001;
static const uint32_t CONFIG_LOADED = 1002;
static const uint32_t TASK_STARTED = 1003;
static const uint32_t CONNECTION_ESTABLISHED = 1004;
// Debug messages (1201-1399)
static const uint32_t DEBUG_STATE_CHANGE = 1201;
static const uint32_t DEBUG_DATA_RECEIVED = 1202;
static const uint32_t DEBUG_PROCESSING = 1203;
// Warning messages (1401-1599)
static const uint32_t CONFIG_MISSING = 1401;
static const uint32_t MEMORY_LOW = 1402;
static const uint32_t TIMEOUT_WARNING = 1403;
// Error messages (1601-1799)
static const uint32_t INIT_FAILED = 1601;
static const uint32_t CONNECTION_FAILED = 1602;
static const uint32_t CRITICAL_ERROR = 1603;
}
```
### Using Message IDs
```cpp
#include "my_module_log_ids.hpp"
static const char* TAG = "MY_MODULE";
void myModuleInit() {
ASF_LOGI(TAG, MyModuleLogIds::MODULE_INIT, "Initializing module");
if (!loadConfiguration()) {
ASF_LOGW(TAG, MyModuleLogIds::CONFIG_MISSING,
"Configuration file missing, using defaults");
} else {
ASF_LOGI(TAG, MyModuleLogIds::CONFIG_LOADED, "Configuration loaded successfully");
}
if (!initializeHardware()) {
ASF_LOGE(TAG, MyModuleLogIds::INIT_FAILED, "Hardware initialization failed");
return;
}
ASF_LOGI(TAG, MyModuleLogIds::TASK_STARTED, "Module initialization complete");
}
```
### Global ID Registry
```cpp
// global_log_ids.hpp
#pragma once
namespace GlobalLogIds {
// System-wide ID ranges
namespace System {
static const uint32_t BOOT_START = 1;
static const uint32_t BOOT_COMPLETE = 2;
static const uint32_t SHUTDOWN_START = 3;
}
namespace GPIO {
static const uint32_t BASE = 2000;
static const uint32_t INIT_SUCCESS = BASE + 1;
static const uint32_t CONFIG_ERROR = BASE + 601;
}
namespace UART {
static const uint32_t BASE = 3000;
static const uint32_t PORT_OPENED = BASE + 1;
static const uint32_t TRANSMISSION_ERROR = BASE + 601;
}
namespace WiFi {
static const uint32_t BASE = 4000;
static const uint32_t CONNECTED = BASE + 1;
static const uint32_t CONNECTION_FAILED = BASE + 601;
}
}
```
## Integration Examples
### GPIO Wrapper Integration
```cpp
// gpio_wrapper.cpp
#include "logger.hpp"
#include "global_log_ids.hpp"
static const char* TAG = "GPIO_WRAPPER";
class Gpio {
public:
bool configure(uint32_t pin, GpioMode mode) {
ASF_LOGI(TAG, GlobalLogIds::GPIO::INIT_SUCCESS,
"Configuring GPIO pin %lu as %s", pin, getModeString(mode));
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GlobalLogIds::GPIO::CONFIG_ERROR,
"Invalid GPIO pin number: %lu", pin);
return false;
}
// ESP-IDF configuration code...
esp_err_t ret = gpio_config(&config);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GlobalLogIds::GPIO::CONFIG_ERROR,
"GPIO configuration failed: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, GlobalLogIds::GPIO::INIT_SUCCESS,
"GPIO pin %lu configured successfully", pin);
return true;
}
bool setLevel(uint32_t pin, uint32_t level) {
ASF_LOGD(TAG, GlobalLogIds::GPIO::BASE + 201,
"Setting GPIO pin %lu to level %lu", pin, level);
// Implementation...
return true;
}
};
```
### UART Wrapper Integration
```cpp
// uart_wrapper.cpp
#include "logger.hpp"
#include "global_log_ids.hpp"
static const char* TAG = "UART_WRAPPER";
class Uart {
public:
bool initialize(UartPort port, const UartConfig& config) {
ASF_LOGI(TAG, GlobalLogIds::UART::PORT_OPENED,
"Initializing UART port %d with baudrate %d",
static_cast<int>(port), static_cast<int>(config.baudrate));
// Configuration and error handling with logging...
return true;
}
int32_t transmit(UartPort port, const uint8_t* data, size_t length) {
ASF_LOGD(TAG, GlobalLogIds::UART::BASE + 201,
"Transmitting %zu bytes on UART port %d",
length, static_cast<int>(port));
// Transmission logic with error logging...
return length;
}
};
```
### Application-Level Integration
```cpp
// main.cpp
#include "logger.hpp"
#include "global_log_ids.hpp"
static const char* TAG = "MAIN";
void app_main() {
// Initialize logger first
setupLogger();
ASF_LOGI(TAG, GlobalLogIds::System::BOOT_START, "System boot started");
// Initialize components
if (!initializeGPIO()) {
ASF_LOGE(TAG, 1001, "GPIO initialization failed");
return;
}
if (!initializeUART()) {
ASF_LOGE(TAG, 1002, "UART initialization failed");
return;
}
if (!initializeWiFi()) {
ASF_LOGE(TAG, 1003, "WiFi initialization failed");
return;
}
ASF_LOGI(TAG, GlobalLogIds::System::BOOT_COMPLETE, "System boot completed successfully");
// Main application loop
while (true) {
runApplicationTasks();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void setupLogger() {
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
#ifdef DEBUG_BUILD
config.minLevel = asf::logger::LogLevel::DEBUG;
config.enableColor = true;
#else
config.minLevel = asf::logger::LogLevel::INFO;
config.enableColor = false;
#endif
asf::logger::initialize(config);
}
```
## Best Practices
### 1. Consistent Tagging
```cpp
// Good: Use consistent, descriptive tags
static const char* TAG = "SENSOR_MGR";
static const char* TAG = "WIFI_CTRL";
static const char* TAG = "DATA_PROC";
// Avoid: Inconsistent or unclear tags
static const char* TAG = "S1";
static const char* TAG = "Module";
static const char* TAG = "temp";
```
### 2. Meaningful Message IDs
```cpp
// Good: Structured ID allocation
namespace SensorLogIds {
static const uint32_t SENSOR_INIT_SUCCESS = 5001;
static const uint32_t SENSOR_READ_COMPLETE = 5002;
static const uint32_t SENSOR_CALIBRATION_ERROR = 5601;
}
// Avoid: Random or unclear IDs
ASF_LOGI(TAG, 42, "Something happened");
ASF_LOGE(TAG, 999999, "Error occurred");
```
### 3. Appropriate Log Levels
```cpp
// Good: Use appropriate levels
ASF_LOGV(TAG, 1001, "Entering function processData()"); // Verbose
ASF_LOGD(TAG, 1002, "Processing %d items", itemCount); // Debug
ASF_LOGI(TAG, 1003, "Data processing completed successfully"); // Info
ASF_LOGW(TAG, 1004, "Queue is 80%% full, consider optimization"); // Warning
ASF_LOGE(TAG, 1005, "Critical: Database connection lost"); // Error
// Avoid: Wrong levels
ASF_LOGE(TAG, 1001, "Function started"); // Error level for normal operation
ASF_LOGI(TAG, 1002, "CRITICAL FAILURE"); // Info level for critical error
```
### 4. Performance-Conscious Logging
```cpp
// Good: Check level before expensive operations
if (asf::logger::getLogLevel() <= asf::logger::LogLevel::DEBUG) {
char debugBuffer[256];
formatComplexDebugInfo(debugBuffer, sizeof(debugBuffer));
ASF_LOGD(TAG, 1001, "Debug info: %s", debugBuffer);
}
// Good: Use appropriate data types
ASF_LOGI(TAG, 1002, "Processing item %lu of %lu", currentItem, totalItems);
// Avoid: Expensive operations in log calls
ASF_LOGD(TAG, 1003, "Debug: %s", expensiveStringOperation().c_str());
```
### 5. Error Context
```cpp
// Good: Provide context with errors
esp_err_t ret = gpio_config(&config);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2001, "GPIO configuration failed for pin %lu: %s",
pin, esp_err_to_name(ret));
return false;
}
// Good: Include relevant state information
ASF_LOGW(TAG, 2002, "Retry attempt %d/%d failed, retrying in %d ms",
currentAttempt, maxAttempts, retryDelay);
```
## Troubleshooting
### Common Issues
#### 1. No Log Output
**Problem**: Logger initialized but no messages appear.
**Solutions**:
```cpp
// Check log level
asf::logger::LogLevel currentLevel = asf::logger::getLogLevel();
ASF_LOGI("DEBUG", 9001, "Current log level: %s",
asf::logger::logLevelToString(currentLevel));
// Ensure level is appropriate
asf::logger::setLogLevel(asf::logger::LogLevel::VERBOSE);
// Test with error level (always visible)
ASF_LOGE("TEST", 9002, "Test error message");
```
#### 2. Truncated Messages
**Problem**: Long messages are cut off.
**Solutions**:
```cpp
// Increase buffer size
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.maxMessageLength = 512; // Increase from default 256
asf::logger::initialize(config);
// Or split long messages
ASF_LOGI(TAG, 1001, "Long message part 1: %s", part1);
ASF_LOGI(TAG, 1002, "Long message part 2: %s", part2);
```
#### 3. Performance Issues
**Problem**: Logging is slowing down the application.
**Solutions**:
```cpp
// Increase minimum log level for production
asf::logger::setLogLevel(asf::logger::LogLevel::WARNING);
// Use conditional logging for debug
#ifdef DEBUG_BUILD
ASF_LOGD(TAG, 1001, "Debug information: %d", value);
#endif
// Check level before expensive formatting
if (asf::logger::getLogLevel() <= asf::logger::LogLevel::DEBUG) {
ASF_LOGD(TAG, 1002, "Expensive debug: %s", formatExpensiveData());
}
```
#### 4. Memory Issues
**Problem**: Stack overflow or memory corruption.
**Solutions**:
```cpp
// Reduce buffer size
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.maxMessageLength = 128; // Reduce buffer size
asf::logger::initialize(config);
// Avoid very long format strings
// Bad:
ASF_LOGI(TAG, 1001, "Very long format string with many parameters %d %d %d %d %d %d %d %d",
a, b, c, d, e, f, g, h);
// Good:
ASF_LOGI(TAG, 1001, "Parameters: a=%d, b=%d", a, b);
ASF_LOGI(TAG, 1002, "More parameters: c=%d, d=%d", c, d);
```
### Debug Mode
```cpp
void enableDebugMode() {
// Enable all logging
asf::logger::setLogLevel(asf::logger::LogLevel::VERBOSE);
// Enable all features
asf::logger::enableTimestamp(true);
asf::logger::enableColor(true);
asf::logger::enableId(true);
ASF_LOGI("DEBUG", 9999, "Debug mode enabled - all logging active");
}
```
## Migration Guide
### From ESP-IDF Logging
#### Step 1: Replace Headers
```cpp
// Remove ESP-IDF log includes (if standalone)
// #include "esp_log.h"
// Add ASF logger
#include "logger.hpp"
```
#### Step 2: Initialize Logger
```cpp
void app_main() {
// Add logger initialization
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
asf::logger::initialize(config);
// Existing code...
}
```
#### Step 3: Replace Log Calls
```cpp
// Before (ESP-IDF)
static const char* TAG = "MY_MODULE";
ESP_LOGI(TAG, "Module initialized");
ESP_LOGW(TAG, "Warning: %s", warningMsg);
ESP_LOGE(TAG, "Error code: %d", errorCode);
// After (ASF Logger)
static const char* TAG = "MY_MODULE";
ASF_LOGI(TAG, 1001, "Module initialized");
ASF_LOGW(TAG, 1002, "Warning: %s", warningMsg);
ASF_LOGE(TAG, 1003, "Error code: %d", errorCode);
```
#### Step 4: Add Message IDs
```cpp
// Create ID definitions
namespace MyModuleIds {
static const uint32_t INIT_SUCCESS = 1001;
static const uint32_t WARNING_OCCURRED = 1002;
static const uint32_t ERROR_CODE = 1003;
}
// Use in logging
ASF_LOGI(TAG, MyModuleIds::INIT_SUCCESS, "Module initialized");
ASF_LOGW(TAG, MyModuleIds::WARNING_OCCURRED, "Warning: %s", warningMsg);
ASF_LOGE(TAG, MyModuleIds::ERROR_CODE, "Error code: %d", errorCode);
```
### Gradual Migration Strategy
```cpp
// Phase 1: Dual logging (for testing)
#define DUAL_LOG_INFO(tag, id, format, ...) do { \
ESP_LOGI(tag, format, ##__VA_ARGS__); \
ASF_LOGI(tag, id, format, ##__VA_ARGS__); \
} while(0)
// Phase 2: Switch to ASF only
#define DUAL_LOG_INFO(tag, id, format, ...) \
ASF_LOGI(tag, id, format, ##__VA_ARGS__)
// Phase 3: Remove dual logging, use ASF directly
// ASF_LOGI(tag, id, format, ...)
```
This comprehensive usage guide provides practical examples and best practices for integrating and using the ASF Logger in your ESP-IDF projects. The structured approach to message IDs and consistent logging patterns will help maintain clean, traceable logs throughout your application.

View File

@@ -0,0 +1,366 @@
/**
* @file logger.cpp
* @brief ASF Logger - Implementation of ESP-IDF logging wrapper
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "logger.hpp"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/time.h>
namespace asf {
namespace logger {
// Static configuration
static LoggerConfig s_config = {
.minLevel = LogLevel::INFO,
.enableTimestamp = true,
.enableColor = true,
.enableId = true,
.maxMessageLength = 256,
.filePath = nullptr
};
// Color codes for different log levels
static const char* COLOR_RESET = "\033[0m";
static const char* COLOR_VERBOSE = "\033[37m"; // White
static const char* COLOR_DEBUG = "\033[36m"; // Cyan
static const char* COLOR_INFO = "\033[32m"; // Green
static const char* COLOR_WARNING = "\033[33m"; // Yellow
static const char* COLOR_ERROR = "\033[31m"; // Red
void initialize(const LoggerConfig& config)
{
s_config = config;
// Set ESP-IDF log level based on our minimum level
esp_log_level_t espLevel = ESP_LOG_NONE;
switch (config.minLevel) {
case LogLevel::VERBOSE:
espLevel = ESP_LOG_VERBOSE;
break;
case LogLevel::DEBUG:
espLevel = ESP_LOG_DEBUG;
break;
case LogLevel::INFO:
espLevel = ESP_LOG_INFO;
break;
case LogLevel::WARNING:
espLevel = ESP_LOG_WARN;
break;
case LogLevel::ERROR:
espLevel = ESP_LOG_ERROR;
break;
case LogLevel::NONE:
espLevel = ESP_LOG_NONE;
break;
}
esp_log_level_set("*", espLevel);
}
void setLogLevel(LogLevel level)
{
s_config.minLevel = level;
// Update ESP-IDF log level
LoggerConfig tempConfig = s_config;
tempConfig.minLevel = level;
initialize(tempConfig);
}
LogLevel getLogLevel()
{
return s_config.minLevel;
}
void enableTimestamp(bool enable)
{
s_config.enableTimestamp = enable;
}
void enableColor(bool enable)
{
s_config.enableColor = enable;
}
void enableId(bool enable)
{
s_config.enableId = enable;
}
void enableFileLogging(const char* filePath)
{
s_config.filePath = filePath;
}
const char* getIsoTimestamp(char* buffer, size_t bufferSize)
{
if (buffer == nullptr || bufferSize < 24) {
return "";
}
// Get current time with microseconds
struct timeval tv;
gettimeofday(&tv, nullptr);
struct tm* timeinfo = localtime(&tv.tv_sec);
// Format: YYYY-MM-DDTHH:MM:SS.sssZ
snprintf(buffer, bufferSize, "%04d-%02d-%02dT%02d:%02d:%02d.%03ldZ",
timeinfo->tm_year + 1900,
timeinfo->tm_mon + 1,
timeinfo->tm_mday,
timeinfo->tm_hour,
timeinfo->tm_min,
timeinfo->tm_sec,
tv.tv_usec / 1000);
return buffer;
}
void log(const char* tag, uint32_t id, LogLevel level, Criticality criticality, const char* format, ...)
{
// Check if we should log this level
if (level < s_config.minLevel) {
return;
}
if (tag == nullptr || format == nullptr) {
return;
}
// Prepare message buffer
char messageBuffer[s_config.maxMessageLength];
// Format the user message
va_list args;
va_start(args, format);
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
// Prepare final output buffer
char outputBuffer[s_config.maxMessageLength + 128]; // Extra space for timestamp, tag, etc.
char timestampBuffer[32];
// Build the formatted log message
const char* colorStart = s_config.enableColor ? logLevelToColor(level) : "";
const char* colorEnd = s_config.enableColor ? COLOR_RESET : "";
const char* timestamp = s_config.enableTimestamp ? getIsoTimestamp(timestampBuffer, sizeof(timestampBuffer)) : "";
const char* levelStr = logLevelToString(level);
const char* critStr = criticalityToString(criticality);
if (s_config.enableTimestamp && s_config.enableId) {
snprintf(outputBuffer, sizeof(outputBuffer), "%s%s : %s[%s][%s] : %lu : %s%s",
colorStart, timestamp, tag, levelStr, critStr, id, messageBuffer, colorEnd);
} else if (s_config.enableTimestamp && !s_config.enableId) {
snprintf(outputBuffer, sizeof(outputBuffer), "%s%s : %s[%s][%s] : %s%s",
colorStart, timestamp, tag, levelStr, critStr, messageBuffer, colorEnd);
} else if (!s_config.enableTimestamp && s_config.enableId) {
snprintf(outputBuffer, sizeof(outputBuffer), "%s%s[%s][%s] : %lu : %s%s",
colorStart, tag, levelStr, critStr, id, messageBuffer, colorEnd);
} else {
snprintf(outputBuffer, sizeof(outputBuffer), "%s%s[%s][%s] : %s%s",
colorStart, tag, levelStr, critStr, messageBuffer, colorEnd);
}
// Output using ESP-IDF logging system
switch (level) {
case LogLevel::VERBOSE:
ESP_LOGV("ASF", "%s", outputBuffer);
break;
case LogLevel::DEBUG:
ESP_LOGD("ASF", "%s", outputBuffer);
break;
case LogLevel::INFO:
ESP_LOGI("ASF", "%s", outputBuffer);
break;
case LogLevel::WARNING:
ESP_LOGW("ASF", "%s", outputBuffer);
break;
case LogLevel::ERROR:
ESP_LOGE("ASF", "%s", outputBuffer);
break;
case LogLevel::NONE:
break;
}
// Write to file if enabled
if (s_config.filePath != nullptr) {
FILE* f = fopen(s_config.filePath, "a");
if (f != nullptr) {
// Re-format without color codes for file output
char fileBuffer[s_config.maxMessageLength + 128];
if (s_config.enableTimestamp && s_config.enableId) {
snprintf(fileBuffer, sizeof(fileBuffer), "%s : %s[%s][%s] : %lu : %s\n",
timestamp, tag, levelStr, critStr, id, messageBuffer);
} else if (s_config.enableTimestamp && !s_config.enableId) {
snprintf(fileBuffer, sizeof(fileBuffer), "%s : %s[%s][%s] : %s\n",
timestamp, tag, levelStr, critStr, messageBuffer);
} else if (!s_config.enableTimestamp && s_config.enableId) {
snprintf(fileBuffer, sizeof(fileBuffer), "%s[%s][%s] : %lu : %s\n",
tag, levelStr, critStr, id, messageBuffer);
} else {
snprintf(fileBuffer, sizeof(fileBuffer), "%s[%s][%s] : %s\n",
tag, levelStr, critStr, messageBuffer);
}
fprintf(f, "%s", fileBuffer);
fclose(f);
}
}
}
void logVerbose(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
if (LogLevel::VERBOSE < s_config.minLevel) {
return;
}
va_list args;
va_start(args, format);
char messageBuffer[s_config.maxMessageLength];
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
log(tag, id, LogLevel::VERBOSE, criticality, "%s", messageBuffer);
}
void logDebug(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
if (LogLevel::DEBUG < s_config.minLevel) {
return;
}
va_list args;
va_start(args, format);
char messageBuffer[s_config.maxMessageLength];
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
log(tag, id, LogLevel::DEBUG, criticality, "%s", messageBuffer);
}
void logInfo(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
if (LogLevel::INFO < s_config.minLevel) {
return;
}
va_list args;
va_start(args, format);
char messageBuffer[s_config.maxMessageLength];
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
log(tag, id, LogLevel::INFO, criticality, "%s", messageBuffer);
}
void logWarning(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
if (LogLevel::WARNING < s_config.minLevel) {
return;
}
va_list args;
va_start(args, format);
char messageBuffer[s_config.maxMessageLength];
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
log(tag, id, LogLevel::WARNING, criticality, "%s", messageBuffer);
}
void logError(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
if (LogLevel::ERROR < s_config.minLevel) {
return;
}
va_list args;
va_start(args, format);
char messageBuffer[s_config.maxMessageLength];
vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
va_end(args);
log(tag, id, LogLevel::ERROR, criticality, "%s", messageBuffer);
}
LoggerConfig getDefaultConfig()
{
LoggerConfig config = {};
config.minLevel = LogLevel::INFO;
config.enableTimestamp = true;
config.enableColor = true;
config.enableId = true;
config.maxMessageLength = 256;
config.filePath = nullptr;
return config;
}
const char* logLevelToString(LogLevel level)
{
switch (level) {
case LogLevel::VERBOSE:
return "VERBOSE";
case LogLevel::DEBUG:
return "DEBUG";
case LogLevel::INFO:
return "INFO";
case LogLevel::WARNING:
return "WARNING";
case LogLevel::ERROR:
return "ERROR";
case LogLevel::NONE:
return "NONE";
default:
return "UNKNOWN";
}
}
const char* logLevelToColor(LogLevel level)
{
switch (level) {
case LogLevel::VERBOSE:
return COLOR_VERBOSE;
case LogLevel::DEBUG:
return COLOR_DEBUG;
case LogLevel::INFO:
return COLOR_INFO;
case LogLevel::WARNING:
return COLOR_WARNING;
case LogLevel::ERROR:
return COLOR_ERROR;
case LogLevel::NONE:
return COLOR_RESET;
default:
return COLOR_RESET;
}
}
const char* criticalityToString(Criticality criticality)
{
switch (criticality) {
case Criticality::LOW:
return "LOW";
case Criticality::MEDIUM:
return "MEDIUM";
case Criticality::HIGH:
return "HIGH";
case Criticality::VERY_HIGH:
return "VERY_HIGH";
default:
return "UNKNOWN";
}
}
} // namespace logger
} // namespace asf

View File

@@ -0,0 +1,226 @@
/**
* @file logger.hpp
* @brief ASF Logger - Wrapper for ESP-IDF logging functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*
* @details
* Abstract logger class for the application and device driver layers
* - Does not depend directly on the low-level logging mechanism provided by platform
* - Supports logging levels (info, warn, debug, error, verbose)
* - Formats messages with ISO timestamp, TAG, level, and unique ID
* - Low overhead design for minimal performance impact
* - Easily included in all modules without compile/linking errors
* - Efficient in compilation time and flash space
*
* Design: Namespace with free functions for simplicity and zero overhead
*/
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <cstdint>
#include <cstdarg>
#include "esp_log.h"
#include "esp_timer.h"
/**
* @brief Logger namespace containing all logging functionality
*/
namespace asf {
namespace logger {
/**
* @brief Log level enumeration
*/
enum class LogLevel : uint8_t
{
VERBOSE = 0, ///< Verbose level (most detailed)
DEBUG = 1, ///< Debug level
INFO = 2, ///< Information level
WARNING = 3, ///< Warning level
ERROR = 4, ///< Error level
NONE = 5 ///< No logging
};
/**
* @brief Log criticality enumeration
*/
enum class Criticality : uint8_t
{
LOW, ///< Low criticality
MEDIUM, ///< Medium criticality
HIGH, ///< High criticality
VERY_HIGH ///< Very high criticality
};
/**
* @brief Logger configuration structure
*/
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
const char* filePath; ///< Path to log file (nullptr to disable file logging)
};
/**
* @brief Initialize the logger with configuration
* @param config Logger configuration
*/
void initialize(const LoggerConfig& config);
/**
* @brief Set minimum log level
* @param level Minimum log level to display
*/
void setLogLevel(LogLevel level);
/**
* @brief Get current minimum log level
* @return Current minimum log level
*/
LogLevel getLogLevel();
/**
* @brief Enable or disable timestamp in log output
* @param enable True to enable, false to disable
*/
void enableTimestamp(bool enable);
/**
* @brief Enable or disable color in log output
* @param enable True to enable, false to disable
*/
void enableColor(bool enable);
/**
* @brief Enable or disable ID in log output
* @param enable True to enable, false to disable
*/
void enableId(bool enable);
/**
* @brief Enable file logging
* @param filePath Path to the log file (e.g., "/ESP/log.txt")
*/
void enableFileLogging(const char* filePath);
/**
* @brief Get ISO 8601 formatted timestamp
* @param buffer Buffer to store timestamp
* @param bufferSize Size of buffer
* @return Pointer to buffer
*/
const char* getIsoTimestamp(char* buffer, size_t bufferSize);
/**
* @brief Main logging function
* @param tag Log tag (module name)
* @param id Unique message ID
* @param level Log level
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void log(const char* tag, uint32_t id, LogLevel level, Criticality criticality, const char* format, ...);
/**
* @brief Log verbose message
* @param tag Log tag (module name)
* @param id Unique message ID
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void logVerbose(const char* tag, uint32_t id, Criticality criticality, const char* format, ...);
/**
* @brief Log debug message
* @param tag Log tag (module name)
* @param id Unique message ID
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void logDebug(const char* tag, uint32_t id, Criticality criticality, const char* format, ...);
/**
* @brief Log info message
* @param tag Log tag (module name)
* @param id Unique message ID
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void logInfo(const char* tag, uint32_t id, Criticality criticality, const char* format, ...);
/**
* @brief Log warning message
* @param tag Log tag (module name)
* @param id Unique message ID
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void logWarning(const char* tag, uint32_t id, Criticality criticality, const char* format, ...);
/**
* @brief Log error message
* @param tag Log tag (module name)
* @param id Unique message ID
* @param criticality Message criticality
* @param format Printf-style format string
* @param ... Variable arguments
*/
void logError(const char* tag, uint32_t id, Criticality criticality, const char* format, ...);
/**
* @brief Get default logger configuration
* @return Default logger configuration
*/
LoggerConfig getDefaultConfig();
/**
* @brief Convert log level to string
* @param level Log level
* @return String representation of log level
*/
const char* logLevelToString(LogLevel level);
/**
* @brief Convert log level to color code
* @param level Log level
* @return ANSI color code string
*/
const char* logLevelToColor(LogLevel level);
/**
* @brief Convert criticality to string
* @param criticality Message criticality
* @return String representation of criticality
*/
const char* criticalityToString(Criticality criticality);
} // namespace logger
} // namespace asf
// Convenience macros for easier usage
#define ASF_LOG_VERBOSE(tag, id, crit, format, ...) asf::logger::logVerbose(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOG_DEBUG(tag, id, crit, format, ...) asf::logger::logDebug(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOG_INFO(tag, id, crit, format, ...) asf::logger::logInfo(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOG_WARNING(tag, id, crit, format, ...) asf::logger::logWarning(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOG_ERROR(tag, id, crit, format, ...) asf::logger::logError(tag, id, crit, format, ##__VA_ARGS__)
// Short form macros
#define ASF_LOGV(tag, id, crit, format, ...) ASF_LOG_VERBOSE(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOGD(tag, id, crit, format, ...) ASF_LOG_DEBUG(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOGI(tag, id, crit, format, ...) ASF_LOG_INFO(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOGW(tag, id, crit, format, ...) ASF_LOG_WARNING(tag, id, crit, format, ##__VA_ARGS__)
#define ASF_LOGE(tag, id, crit, format, ...) ASF_LOG_ERROR(tag, id, crit, format, ##__VA_ARGS__)
#endif // LOGGER_HPP

View File

@@ -0,0 +1,355 @@
/**
* @file gpio_wrapper_example.cpp
* @brief Example of integrating ASF Logger with GPIO wrapper
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "logger.hpp"
#include "gpio.hpp"
// Module-specific log tag and IDs
static const char* TAG = "GPIO_WRAPPER";
namespace GpioLogIds {
// Info messages (2001-2199)
static const uint32_t WRAPPER_INIT = 2001;
static const uint32_t WRAPPER_DESTROY = 2002;
static const uint32_t PIN_CONFIGURED = 2003;
static const uint32_t ISR_INSTALLED = 2004;
static const uint32_t INTERRUPT_ATTACHED = 2005;
static const uint32_t INTERRUPT_DETACHED = 2006;
// Debug messages (2201-2399)
static const uint32_t PIN_LEVEL_SET = 2201;
static const uint32_t PIN_LEVEL_READ = 2202;
static const uint32_t PIN_TOGGLED = 2203;
// Warning messages (2401-2599)
static const uint32_t ISR_ALREADY_INSTALLED = 2401;
static const uint32_t PORT_NOT_INITIALIZED = 2402;
// Error messages (2601-2799)
static const uint32_t INVALID_PIN = 2601;
static const uint32_t CONFIG_FAILED = 2602;
static const uint32_t SET_LEVEL_FAILED = 2603;
static const uint32_t ISR_INSTALL_FAILED = 2604;
static const uint32_t INTERRUPT_ATTACH_FAILED = 2605;
static const uint32_t INTERRUPT_DETACH_FAILED = 2606;
}
/**
* @brief Example GPIO wrapper with ASF Logger integration
*/
class GpioWithLogger
{
public:
GpioWithLogger() : m_isrInstalled_(false)
{
ASF_LOGI(TAG, GpioLogIds::WRAPPER_INIT, "GPIO wrapper initialized");
}
~GpioWithLogger()
{
if (m_isrInstalled_) {
uninstallIsr();
}
ASF_LOGI(TAG, GpioLogIds::WRAPPER_DESTROY, "GPIO wrapper destroyed");
}
bool configure(uint32_t pin, GpioMode mode)
{
ASF_LOGI(TAG, GpioLogIds::PIN_CONFIGURED, "Configuring GPIO pin %lu", pin);
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GpioLogIds::INVALID_PIN, "Invalid GPIO pin: %lu", pin);
return false;
}
gpio_config_t config = {};
config.pin_bit_mask = (1ULL << pin);
config.mode = convertMode(mode);
config.intr_type = GPIO_INTR_DISABLE;
// Configure pull-up/down based on mode
switch (mode) {
case GpioMode::INPUT_PULLUP:
config.pull_up_en = GPIO_PULLUP_ENABLE;
config.pull_down_en = GPIO_PULLDOWN_DISABLE;
ASF_LOGD(TAG, GpioLogIds::PIN_CONFIGURED, "Pin %lu configured with pull-up", pin);
break;
case GpioMode::INPUT_PULLDOWN:
config.pull_up_en = GPIO_PULLUP_DISABLE;
config.pull_down_en = GPIO_PULLDOWN_ENABLE;
ASF_LOGD(TAG, GpioLogIds::PIN_CONFIGURED, "Pin %lu configured with pull-down", pin);
break;
default:
config.pull_up_en = GPIO_PULLUP_DISABLE;
config.pull_down_en = GPIO_PULLDOWN_DISABLE;
ASF_LOGD(TAG, GpioLogIds::PIN_CONFIGURED, "Pin %lu configured without pull resistors", pin);
break;
}
esp_err_t ret = gpio_config(&config);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::CONFIG_FAILED, "Failed to configure GPIO pin %lu: %s",
pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, GpioLogIds::PIN_CONFIGURED, "GPIO pin %lu configured successfully", pin);
return true;
}
bool setLevel(uint32_t pin, uint32_t level)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GpioLogIds::INVALID_PIN, "Invalid GPIO pin: %lu", pin);
return false;
}
esp_err_t ret = gpio_set_level(static_cast<gpio_num_t>(pin), level);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::SET_LEVEL_FAILED, "Failed to set GPIO pin %lu level: %s",
pin, esp_err_to_name(ret));
return false;
}
ASF_LOGD(TAG, GpioLogIds::PIN_LEVEL_SET, "GPIO pin %lu set to level %lu", pin, level);
return true;
}
int32_t getLevel(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GpioLogIds::INVALID_PIN, "Invalid GPIO pin: %lu", pin);
return -1;
}
int level = gpio_get_level(static_cast<gpio_num_t>(pin));
ASF_LOGD(TAG, GpioLogIds::PIN_LEVEL_READ, "GPIO pin %lu level read: %d", pin, level);
return level;
}
bool toggleLevel(uint32_t pin)
{
int32_t currentLevel = getLevel(pin);
if (currentLevel < 0) {
return false;
}
bool result = setLevel(pin, currentLevel == 0 ? 1 : 0);
if (result) {
ASF_LOGD(TAG, GpioLogIds::PIN_TOGGLED, "GPIO pin %lu toggled from %ld to %d",
pin, currentLevel, currentLevel == 0 ? 1 : 0);
}
return result;
}
bool installIsr(int flags = 0)
{
if (m_isrInstalled_) {
ASF_LOGW(TAG, GpioLogIds::ISR_ALREADY_INSTALLED, "GPIO ISR already installed");
return true;
}
esp_err_t ret = gpio_install_isr_service(flags);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::ISR_INSTALL_FAILED, "Failed to install GPIO ISR service: %s",
esp_err_to_name(ret));
return false;
}
m_isrInstalled_ = true;
ASF_LOGI(TAG, GpioLogIds::ISR_INSTALLED, "GPIO ISR service installed successfully");
return true;
}
bool uninstallIsr()
{
if (!m_isrInstalled_) {
ASF_LOGW(TAG, GpioLogIds::PORT_NOT_INITIALIZED, "GPIO ISR not installed");
return true;
}
gpio_uninstall_isr_service();
m_isrInstalled_ = false;
ASF_LOGI(TAG, GpioLogIds::ISR_INSTALLED, "GPIO ISR service uninstalled");
return true;
}
bool attachInterrupt(uint32_t pin, GpioIntType intType, GpioCallback callback, void* arg)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GpioLogIds::INVALID_PIN, "Invalid GPIO pin: %lu", pin);
return false;
}
if (!m_isrInstalled_) {
ASF_LOGW(TAG, GpioLogIds::PORT_NOT_INITIALIZED, "GPIO ISR not installed, installing now");
if (!installIsr()) {
return false;
}
}
// Set interrupt type
esp_err_t ret = gpio_set_intr_type(static_cast<gpio_num_t>(pin), convertIntType(intType));
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::INTERRUPT_ATTACH_FAILED,
"Failed to set interrupt type for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
// Add ISR handler
ret = gpio_isr_handler_add(static_cast<gpio_num_t>(pin),
reinterpret_cast<gpio_isr_t>(callback), arg);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::INTERRUPT_ATTACH_FAILED,
"Failed to add ISR handler for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, GpioLogIds::INTERRUPT_ATTACHED, "Interrupt attached to GPIO pin %lu", pin);
return true;
}
bool detachInterrupt(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, GpioLogIds::INVALID_PIN, "Invalid GPIO pin: %lu", pin);
return false;
}
// Disable interrupt
esp_err_t ret = gpio_set_intr_type(static_cast<gpio_num_t>(pin), GPIO_INTR_DISABLE);
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::INTERRUPT_DETACH_FAILED,
"Failed to disable interrupt for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
// Remove ISR handler
ret = gpio_isr_handler_remove(static_cast<gpio_num_t>(pin));
if (ret != ESP_OK) {
ASF_LOGE(TAG, GpioLogIds::INTERRUPT_DETACH_FAILED,
"Failed to remove ISR handler for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, GpioLogIds::INTERRUPT_DETACHED, "Interrupt detached from GPIO pin %lu", pin);
return true;
}
static bool isValidPin(uint32_t pin)
{
return GPIO_IS_VALID_GPIO(pin);
}
private:
bool m_isrInstalled_;
gpio_mode_t convertMode(GpioMode mode)
{
switch (mode) {
case GpioMode::INPUT:
case GpioMode::INPUT_PULLUP:
case GpioMode::INPUT_PULLDOWN:
return GPIO_MODE_INPUT;
case GpioMode::OUTPUT:
return GPIO_MODE_OUTPUT;
case GpioMode::OUTPUT_OD:
return GPIO_MODE_OUTPUT_OD;
default:
return GPIO_MODE_INPUT;
}
}
gpio_int_type_t convertIntType(GpioIntType intType)
{
switch (intType) {
case GpioIntType::DISABLE:
return GPIO_INTR_DISABLE;
case GpioIntType::RISING_EDGE:
return GPIO_INTR_POSEDGE;
case GpioIntType::FALLING_EDGE:
return GPIO_INTR_NEGEDGE;
case GpioIntType::ANY_EDGE:
return GPIO_INTR_ANYEDGE;
case GpioIntType::LOW_LEVEL:
return GPIO_INTR_LOW_LEVEL;
case GpioIntType::HIGH_LEVEL:
return GPIO_INTR_HIGH_LEVEL;
default:
return GPIO_INTR_DISABLE;
}
}
};
/**
* @brief Example usage of GPIO wrapper with ASF Logger
*/
void exampleUsage()
{
// Initialize ASF Logger
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.minLevel = asf::logger::LogLevel::DEBUG; // Enable debug messages
asf::logger::initialize(config);
ASF_LOGI("MAIN", 1001, "Starting GPIO wrapper example");
// Create GPIO instance
GpioWithLogger gpio;
// Configure GPIO pin as output
if (gpio.configure(2, GpioMode::OUTPUT)) {
ASF_LOGI("MAIN", 1002, "GPIO pin 2 configured as output");
// Set pin high
gpio.setLevel(2, 1);
// Toggle pin
gpio.toggleLevel(2);
} else {
ASF_LOGE("MAIN", 1003, "Failed to configure GPIO pin 2");
}
// Configure GPIO pin as input with pull-up
if (gpio.configure(4, GpioMode::INPUT_PULLUP)) {
ASF_LOGI("MAIN", 1004, "GPIO pin 4 configured as input with pull-up");
// Read pin level
int32_t level = gpio.getLevel(4);
ASF_LOGI("MAIN", 1005, "GPIO pin 4 level: %ld", level);
}
ASF_LOGI("MAIN", 1006, "GPIO wrapper example completed");
}
/**
* @brief Interrupt handler example
*/
void IRAM_ATTR gpioInterruptHandler(uint32_t pin, void* arg)
{
// Note: In interrupt context, logging should be minimal
// Consider using a queue to defer logging to a task
ASF_LOGI("ISR", 9001, "Interrupt on pin %lu", pin);
}
/**
* @brief Example with interrupt handling
*/
void interruptExample()
{
ASF_LOGI("MAIN", 1007, "Starting interrupt example");
GpioWithLogger gpio;
// Configure pin as input
gpio.configure(5, GpioMode::INPUT);
// Attach interrupt
gpio.attachInterrupt(5, GpioIntType::FALLING_EDGE, gpioInterruptHandler, nullptr);
ASF_LOGI("MAIN", 1008, "Interrupt example setup complete");
}

View File

@@ -0,0 +1,5 @@
idf_component_register(
SRCS "test_logger.cpp"
INCLUDE_DIRS "."
REQUIRES unity logger
)

View File

@@ -0,0 +1,292 @@
/**
* @file test_logger.cpp
* @brief Unit tests for ASF Logger
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "logger.hpp"
#include <cstring>
#include <cstdio>
// Test constants
static const char* TEST_TAG = "TEST_LOGGER";
static const uint32_t TEST_ID = 12345;
/**
* @brief Test logger initialization
*/
void test_logger_initialization()
{
// Test default configuration
asf::logger::LoggerConfig defaultConfig = asf::logger::getDefaultConfig();
TEST_ASSERT_EQUAL(asf::logger::LogLevel::INFO, defaultConfig.minLevel);
TEST_ASSERT_TRUE(defaultConfig.enableTimestamp);
TEST_ASSERT_TRUE(defaultConfig.enableColor);
TEST_ASSERT_TRUE(defaultConfig.enableId);
TEST_ASSERT_EQUAL(256, defaultConfig.maxMessageLength);
// Test custom configuration
asf::logger::LoggerConfig customConfig = {};
customConfig.minLevel = asf::logger::LogLevel::DEBUG;
customConfig.enableTimestamp = false;
customConfig.enableColor = false;
customConfig.enableId = false;
customConfig.maxMessageLength = 128;
asf::logger::initialize(customConfig);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::DEBUG, asf::logger::getLogLevel());
}
/**
* @brief Test log level functionality
*/
void test_log_levels()
{
// Test setting different log levels
asf::logger::setLogLevel(asf::logger::LogLevel::VERBOSE);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::VERBOSE, asf::logger::getLogLevel());
asf::logger::setLogLevel(asf::logger::LogLevel::DEBUG);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::DEBUG, asf::logger::getLogLevel());
asf::logger::setLogLevel(asf::logger::LogLevel::INFO);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::INFO, asf::logger::getLogLevel());
asf::logger::setLogLevel(asf::logger::LogLevel::WARNING);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::WARNING, asf::logger::getLogLevel());
asf::logger::setLogLevel(asf::logger::LogLevel::ERROR);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::ERROR, asf::logger::getLogLevel());
asf::logger::setLogLevel(asf::logger::LogLevel::NONE);
TEST_ASSERT_EQUAL(asf::logger::LogLevel::NONE, asf::logger::getLogLevel());
}
/**
* @brief Test log level to string conversion
*/
void test_log_level_to_string()
{
TEST_ASSERT_EQUAL_STRING("VERBOSE", asf::logger::logLevelToString(asf::logger::LogLevel::VERBOSE));
TEST_ASSERT_EQUAL_STRING("DEBUG", asf::logger::logLevelToString(asf::logger::LogLevel::DEBUG));
TEST_ASSERT_EQUAL_STRING("INFO", asf::logger::logLevelToString(asf::logger::LogLevel::INFO));
TEST_ASSERT_EQUAL_STRING("WARNING", asf::logger::logLevelToString(asf::logger::LogLevel::WARNING));
TEST_ASSERT_EQUAL_STRING("ERROR", asf::logger::logLevelToString(asf::logger::LogLevel::ERROR));
TEST_ASSERT_EQUAL_STRING("NONE", asf::logger::logLevelToString(asf::logger::LogLevel::NONE));
}
/**
* @brief Test log level to color conversion
*/
void test_log_level_to_color()
{
// Test that color codes are returned (non-empty strings)
const char* verboseColor = asf::logger::logLevelToColor(asf::logger::LogLevel::VERBOSE);
const char* debugColor = asf::logger::logLevelToColor(asf::logger::LogLevel::DEBUG);
const char* infoColor = asf::logger::logLevelToColor(asf::logger::LogLevel::INFO);
const char* warningColor = asf::logger::logLevelToColor(asf::logger::LogLevel::WARNING);
const char* errorColor = asf::logger::logLevelToColor(asf::logger::LogLevel::ERROR);
TEST_ASSERT_NOT_NULL(verboseColor);
TEST_ASSERT_NOT_NULL(debugColor);
TEST_ASSERT_NOT_NULL(infoColor);
TEST_ASSERT_NOT_NULL(warningColor);
TEST_ASSERT_NOT_NULL(errorColor);
// Test that different levels have different colors
TEST_ASSERT_NOT_EQUAL_STRING(verboseColor, debugColor);
TEST_ASSERT_NOT_EQUAL_STRING(debugColor, infoColor);
TEST_ASSERT_NOT_EQUAL_STRING(infoColor, warningColor);
TEST_ASSERT_NOT_EQUAL_STRING(warningColor, errorColor);
}
/**
* @brief Test ISO timestamp generation
*/
void test_iso_timestamp()
{
char timestampBuffer[32];
const char* timestamp = asf::logger::getIsoTimestamp(timestampBuffer, sizeof(timestampBuffer));
TEST_ASSERT_NOT_NULL(timestamp);
TEST_ASSERT_GREATER_THAN(0, strlen(timestamp));
// Check basic format (should contain 'T' and 'Z')
TEST_ASSERT_NOT_NULL(strchr(timestamp, 'T'));
TEST_ASSERT_NOT_NULL(strchr(timestamp, 'Z'));
// Test with insufficient buffer
char smallBuffer[10];
const char* smallTimestamp = asf::logger::getIsoTimestamp(smallBuffer, sizeof(smallBuffer));
TEST_ASSERT_EQUAL_STRING("", smallTimestamp);
// Test with null buffer
const char* nullTimestamp = asf::logger::getIsoTimestamp(nullptr, 32);
TEST_ASSERT_EQUAL_STRING("", nullTimestamp);
}
/**
* @brief Test configuration toggles
*/
void test_configuration_toggles()
{
// Test timestamp toggle
asf::logger::enableTimestamp(true);
// No direct way to test this without capturing output, but ensure no crash
asf::logger::enableTimestamp(false);
// No direct way to test this without capturing output, but ensure no crash
// Test color toggle
asf::logger::enableColor(true);
// No direct way to test this without capturing output, but ensure no crash
asf::logger::enableColor(false);
// No direct way to test this without capturing output, but ensure no crash
// Test ID toggle
asf::logger::enableId(true);
// No direct way to test this without capturing output, but ensure no crash
asf::logger::enableId(false);
// No direct way to test this without capturing output, but ensure no crash
}
/**
* @brief Test basic logging functions
*/
void test_basic_logging()
{
// Initialize logger with verbose level to test all functions
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.minLevel = asf::logger::LogLevel::VERBOSE;
asf::logger::initialize(config);
// Test all logging functions (should not crash)
asf::logger::logVerbose(TEST_TAG, TEST_ID, "Verbose message: %d", 1);
asf::logger::logDebug(TEST_TAG, TEST_ID, "Debug message: %d", 2);
asf::logger::logInfo(TEST_TAG, TEST_ID, "Info message: %d", 3);
asf::logger::logWarning(TEST_TAG, TEST_ID, "Warning message: %d", 4);
asf::logger::logError(TEST_TAG, TEST_ID, "Error message: %d", 5);
// Test main log function
asf::logger::log(TEST_TAG, TEST_ID, asf::logger::LogLevel::INFO, "Main log function: %s", "test");
}
/**
* @brief Test logging macros
*/
void test_logging_macros()
{
// Initialize logger with verbose level
asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
config.minLevel = asf::logger::LogLevel::VERBOSE;
asf::logger::initialize(config);
// Test all macros (should not crash)
ASF_LOG_VERBOSE(TEST_TAG, TEST_ID, "Verbose macro: %d", 1);
ASF_LOG_DEBUG(TEST_TAG, TEST_ID, "Debug macro: %d", 2);
ASF_LOG_INFO(TEST_TAG, TEST_ID, "Info macro: %d", 3);
ASF_LOG_WARNING(TEST_TAG, TEST_ID, "Warning macro: %d", 4);
ASF_LOG_ERROR(TEST_TAG, TEST_ID, "Error macro: %d", 5);
// Test short form macros
ASF_LOGV(TEST_TAG, TEST_ID, "Verbose short macro: %d", 1);
ASF_LOGD(TEST_TAG, TEST_ID, "Debug short macro: %d", 2);
ASF_LOGI(TEST_TAG, TEST_ID, "Info short macro: %d", 3);
ASF_LOGW(TEST_TAG, TEST_ID, "Warning short macro: %d", 4);
ASF_LOGE(TEST_TAG, TEST_ID, "Error short macro: %d", 5);
}
/**
* @brief Test log level filtering
*/
void test_log_level_filtering()
{
// Set log level to WARNING
asf::logger::setLogLevel(asf::logger::LogLevel::WARNING);
// These should be filtered out (no crash expected)
asf::logger::logVerbose(TEST_TAG, TEST_ID, "This should be filtered");
asf::logger::logDebug(TEST_TAG, TEST_ID, "This should be filtered");
asf::logger::logInfo(TEST_TAG, TEST_ID, "This should be filtered");
// These should pass through (no crash expected)
asf::logger::logWarning(TEST_TAG, TEST_ID, "This should pass");
asf::logger::logError(TEST_TAG, TEST_ID, "This should pass");
}
/**
* @brief Test null parameter handling
*/
void test_null_parameters()
{
// Test with null tag (should not crash)
asf::logger::logInfo(nullptr, TEST_ID, "Test message");
// Test with null format (should not crash)
asf::logger::logInfo(TEST_TAG, TEST_ID, nullptr);
// Test main log function with null parameters
asf::logger::log(nullptr, TEST_ID, asf::logger::LogLevel::INFO, "Test message");
asf::logger::log(TEST_TAG, TEST_ID, asf::logger::LogLevel::INFO, nullptr);
}
/**
* @brief Test long messages
*/
void test_long_messages()
{
// Create a long message
char longMessage[512];
memset(longMessage, 'A', sizeof(longMessage) - 1);
longMessage[sizeof(longMessage) - 1] = '\0';
// Should handle long messages gracefully (truncate if necessary)
asf::logger::logInfo(TEST_TAG, TEST_ID, "Long message: %s", longMessage);
}
/**
* @brief Test performance (basic timing)
*/
void test_performance()
{
// Set to ERROR level to minimize output
asf::logger::setLogLevel(asf::logger::LogLevel::ERROR);
// Perform many log calls that should be filtered out
for (int i = 0; i < 1000; i++) {
asf::logger::logInfo(TEST_TAG, i, "Performance test message %d", i);
}
// This test mainly ensures no crashes during high-frequency logging
TEST_ASSERT_TRUE(true); // If we reach here, performance test passed
}
/**
* @brief Run all logger tests
*/
extern "C" void app_main()
{
UNITY_BEGIN();
RUN_TEST(test_logger_initialization);
RUN_TEST(test_log_levels);
RUN_TEST(test_log_level_to_string);
RUN_TEST(test_log_level_to_color);
RUN_TEST(test_iso_timestamp);
RUN_TEST(test_configuration_toggles);
RUN_TEST(test_basic_logging);
RUN_TEST(test_logging_macros);
RUN_TEST(test_log_level_filtering);
RUN_TEST(test_null_parameters);
RUN_TEST(test_long_messages);
RUN_TEST(test_performance);
UNITY_END();
}