# 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.