Mastering ESP-IDF Logging: A Complete Guide to Debugging with ESP_LOG

Introduction

Embedded development often feels like flying blind—especially when things go wrong and there's no GUI or operating system to help diagnose the issue. In these situations, logs become your eyes. If you're building firmware for ESP32, ESP32-S3, or other Espressif chips using the ESP-IDF framework, the esp_log module is your most powerful ally.

The ESP-IDF logging system is not just about printing messages—it's a fully-featured, configurable framework that helps you monitor the behavior of your firmware, identify and isolate bugs, and keep track of system performance. Whether you're new to ESP32 development or a seasoned engineer, mastering the logging system will supercharge your debugging workflow.

This guide dives deep into how to use ESP-IDF’s logging module for effective debugging, offering examples, best practices, configuration tips, and more.


What is esp_log and Why Use It?

The esp_log module is a logging utility provided by ESP-IDF. It outputs messages to the UART console (by default), and supports various severity levels (from errors to verbose messages).

Key Features:

  • Granular log levels: ERROR, WARNING, INFO, DEBUG, VERBOSE
  • Per-module log configuration
  • Runtime control of log levels
  • Output redirection and formatting options

This logging capability is especially useful for:

  • Debugging initialization sequences
  • Tracking sensor values or state changes
  • Diagnosing memory leaks or task crashes
  • Monitoring hardware communication (I2C, SPI, UART)
  • Verifying real-time behavior in multitasking apps

Setting Up the Logging Module

1. Include the Logging Header

To use logging, include the following in your .c or .cpp file:

#include "esp_log.h"

This gives you access to all the logging macros and helper functions.


2. Define a Log Tag

A tag helps identify which component or module the log message is coming from. It can be the filename, a feature name, or a custom identifier.

static const char *TAG = "SensorModule";

This tag will be used in all log macros so you can filter or identify them in logs.


3. Use Logging Macros

ESP-IDF provides several macros to log at different levels:

Macro Purpose Sample Output
ESP_LOGE Error messages E (1234) SensorModule: Sensor read failed
ESP_LOGW Warning messages W (1250) SensorModule: High temperature
ESP_LOGI Informational messages I (1300) SensorModule: Sensor initialized
ESP_LOGD Debug messages D (1320) SensorModule: Raw data = 123
ESP_LOGV Verbose (very detailed) logs V (1350) SensorModule: Entered state 2

Example usage:

ESP_LOGI(TAG, "Sensor initialized successfully");
ESP_LOGW(TAG, "High temperature detected: %d", temp);
ESP_LOGE(TAG, "Sensor read failed with error code: %d", err_code);

These macros work like printf() and support format specifiers.


Configuring Log Levels

Logging verbosity can be controlled both globally and per-module. This is crucial because debug and verbose logs can significantly slow down your system or flood the serial monitor.

1. Global Log Level via menuconfig

Run this in your project directory:

idf.py menuconfig

Navigate to:

Component config → Log output → Default log verbosity

Choose a level:

  • None: disables logging
  • Error: logs only critical issues
  • Warning: includes non-fatal issues
  • Info: logs operational messages
  • Debug: includes low-level details
  • Verbose: logs everything (useful during deep debugging)

2. Set Log Level at Runtime

You can dynamically change log levels during runtime:

esp_log_level_set("*", ESP_LOG_VERBOSE);               // Enable all logs
esp_log_level_set("SensorModule", ESP_LOG_DEBUG);      // Only for this tag

This is particularly useful if you're debugging only one subsystem and want to avoid unnecessary noise from others.


Real-World Debugging Example

Here’s a full example showing how logging can be used to debug a temperature sensor:

#include "esp_log.h"
#include "driver/adc.h"

static const char *TAG = "TempSensor";

void read_temperature() {
    ESP_LOGI(TAG, "Starting temperature read");

    int raw = adc1_get_raw(ADC1_CHANNEL_6);
    if (raw < 0) {
        ESP_LOGE(TAG, "Failed to read ADC channel");
        return;
    }

    float voltage = (raw / 4095.0) * 3.3;
    float temperature = (voltage - 0.5) * 100;

    ESP_LOGD(TAG, "Raw ADC: %d, Voltage: %.2fV", raw, voltage);

    if (temperature > 60) {
        ESP_LOGW(TAG, "High temperature detected: %.2f°C", temperature);
    }

    ESP_LOGI(TAG, "Temperature: %.2f°C", temperature);
}

Sample Output on Serial Monitor:

I (1234) TempSensor: Starting temperature read
D (1240) TempSensor: Raw ADC: 2048, Voltage: 1.65V
I (1250) TempSensor: Temperature: 28.75°C

If an error occurs:

E (1234) TempSensor: Failed to read ADC channel

Advanced Logging Tips

Redirect Logs to UART1 or External Display

By default, logs go to UART0. You can change the output UART using esp_log_set_vprintf() to redirect logs (for example, to UART1 or even to an LCD, SD card, or network).

Filter Logs with idf.py monitor

Use filter options in the monitor:

idf.py monitor | grep SensorModule

Or filter by log level:

idf.py monitor | grep "E ("

Reduce Noise with Custom Log Levels in Production

Before releasing your firmware, reduce logs to Error or Warning to avoid performance overhead and leaking internal system behavior.


Best Practices

  • Use meaningful, consistent tags per module (e.g., BLEManager, WiFiConn, SensorReader).
  • Avoid placing logs inside time-critical code such as ISRs or tight loops.
  • Use ESP_LOGE() before assert() to capture debug info before halting.
  • Use esp_err_to_name(err) or esp_err_to_name_r() for readable error messages.
  • Limit log verbosity in production builds for performance and security.

Conclusion

The ESP-IDF logging system is an indispensable tool for every ESP32 developer. With minimal setup and powerful flexibility, it can help you trace bugs, monitor application behavior, and ensure your firmware runs as expected.

By understanding the different logging levels, how to configure them, and where to place logs effectively, you'll be well-equipped to debug even the most complex embedded systems.

So the next time your ESP32 app isn’t behaving as expected, remember: logs don’t lie—if you're listening.