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