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