Files
ASF_01_sys_sw_arch/software design/components/utils/logger/ARCHITECTURE.md
2026-01-19 16:19:41 +01:00

18 KiB

ASF Logger - Architecture and Design Document

Table of Contents

  1. Overview
  2. Architecture Design
  3. UML Diagrams
  4. Design Patterns
  5. Component Structure
  6. API Design
  7. Performance Analysis
  8. Integration Guide
  9. Testing Strategy
  10. 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

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

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

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

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)
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:

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:

// 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:

// 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

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

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

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
#include "logger.hpp"
  1. Initialize Logger
void app_main() {
    asf::logger::LoggerConfig config = asf::logger::getDefaultConfig();
    config.minLevel = asf::logger::LogLevel::DEBUG;
    asf::logger::initialize(config);
}
  1. Define Module Constants
// 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;
}
  1. Replace Existing Logging
// Before
ESP_LOGI(TAG, "Module initialized");

// After
ASF_LOGI(TAG, MyModuleLogIds::INIT_SUCCESS, "Module initialized");

Integration Patterns

Pattern 1: Module-Specific Wrapper

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

#define MODULE_LOGI(id, format, ...) \
    ASF_LOGI("MODULE_NAME", id, format, ##__VA_ARGS__)

Pattern 3: Template Wrapper

template<const char* ModuleTag>
class TypedLogger {
public:
    static void info(uint32_t id, const char* format, ...) {
        // Implementation
    }
};

Testing Strategy

Test Categories

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

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

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.