|
| 1 | +--- |
| 2 | +title: "ESP-IDF tutorial series: Logging" |
| 3 | +date: "2025-09-26" |
| 4 | +showAuthor: false |
| 5 | +# Add a summary |
| 6 | +summary: "This article shows how ESP-IDF’s logging system uses tags and verbosity levels to produce structured, configurable output, helping you keep code clean and debug more effectively." |
| 7 | +# Create your author entry |
| 8 | +# - Create your page at `content/authors/<author-name>/_index.md` |
| 9 | +# - Add your personal data at `data/authors/<author-name>.json` |
| 10 | +# - Add your author name(s) below |
| 11 | +authors: |
| 12 | + - "francesco-bez" # same as in the file paths above |
| 13 | +# Add tags |
| 14 | +tags: ["ESP32C3", "ESP-IDF", "errors"] |
| 15 | +--- |
| 16 | + |
| 17 | +## Introduction |
| 18 | + |
| 19 | +The most straightforward way to see what your C program is doing is to drop in a few `printf` statements. It’s simple, immediate, and every developer knows how to use it. But as soon as your application grows beyond a toy example, `printf` starts to show its limits: output becomes messy, important details get buried, and managing those statements turns into a chore. Using a logging library, on the other hand, gives you structured, configurable, and context-rich feedback without scattering prints all over your code. |
| 20 | + |
| 21 | +It lets you control what gets recorded, where it goes, and how it looks, all while keeping your source clean. Additionally, logging can be easily disabled in production builds via a configuration option, so you don’t have to worry about performance or leaking sensitive information. In the sections that follow, we’ll refer to the logging library simply as “logging,” and we’ll explore why it is better than `printf` for debugging, as well as how adopting it can make your codebase easier to maintain and scale. |
| 22 | + |
| 23 | + |
| 24 | +## Logging library |
| 25 | + |
| 26 | +The ESP-IDF provides its logging functionality through the `log` component, included via `esp_log.h`. You’ve probably already seen this logging in action, as it’s used extensively throughout the ESP-IDF libraries. |
| 27 | + |
| 28 | +ESP-IDF offers two logging implementations: __Log V1__ and __Log V2__, selectable through `CONFIG_LOG_VERSION`. Log V1 is the simpler, default option optimized for early and DRAM logging but with higher flash usage and limited flexibility, while Log V2 reduces flash usage, adds powerful customization features, and centralizes formatting at the cost of slightly higher stack usage. In this article, we will focus on Log V1, while Log V2 will be covered in a future article. |
| 29 | + |
| 30 | + |
| 31 | +Let’s examine the output from the `hello_world` example. When you monitor your device while running `hello_world`, you’ll see something like: |
| 32 | +<!-- |
| 33 | +```bash |
| 34 | +[...] |
| 35 | +I (223) spi_flash: flash io: dio |
| 36 | +W (226) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. |
| 37 | +I (238) sleep_gpio: Configure to isolate all GPIO pins in sleep state |
| 38 | +I (245) sleep_gpio: Enable automatic switching of GPIO sleep configuration |
| 39 | +I (251) main_task: Started on CPU0 |
| 40 | +I (251) main_task: Calling app_main() |
| 41 | +[...] |
| 42 | +``` --> |
| 43 | + |
| 44 | +{{< figure |
| 45 | +default=true |
| 46 | +src="img/hello_world.webp" |
| 47 | +caption="Fig.1 - Hello World output" |
| 48 | + |
| 49 | +>}} |
| 50 | +
|
| 51 | + |
| 52 | +Lines starting with `I` are shown in green, while those starting with `W` appear in orange. These are examples of log messages: `I` denotes an informational message, and `W` denotes a warning. Although both are essentially printed messages with some extra formatting, they represent conceptually different types of messages. Their visibility can be controlled through a configuration parameter called *verbosity*. |
| 53 | + |
| 54 | +### Logging core elements |
| 55 | + |
| 56 | +To grasp how logging works in ESP-IDF, we will examine its three core elements: |
| 57 | + |
| 58 | +* Tags |
| 59 | +* Logging macros |
| 60 | +* Verbosity levels |
| 61 | + |
| 62 | +<!-- |
| 63 | +In the ESP-IDF logging system, verbosity levels such as ERROR, WARNING, INFO, DEBUG, and VERBOSE let you control the amount and type of information your application produces. By adjusting the log level, you can focus on critical errors in production while enabling detailed debug information during development or troubleshooting. This selective filtering keeps logs readable, improves performance, and helps you quickly pinpoint issues without wading through irrelevant messages. --> |
| 64 | + |
| 65 | +#### Tags |
| 66 | + |
| 67 | +A tag is simply a string used to group related log messages, typically by file or component. Tags are passed to the logging macros, as demonstrated in the next section. A common convention is to define a `TAG` constant as a `static const char *` pointer. In ESP-IDF, tags are usually declared at the top of the file like this: |
| 68 | + |
| 69 | +```c |
| 70 | +static const char *TAG = "TAG_NAME"; |
| 71 | +``` |
| 72 | + |
| 73 | +{{< alert icon="circle-info" cardColor="#b3e0f2" iconColor="#04a5e5">}} |
| 74 | +The `static` keyword limits the symbol’s visibility to the current file, while `const` ensures the string is stored in read-only memory instead of consuming RAM. |
| 75 | +{{< /alert >}} |
| 76 | + |
| 77 | +#### Logging macros |
| 78 | + |
| 79 | +ESP-IDF offers a powerful logging system through the `ESP_LOGx` family of macros that are used for reporting errors, warnings, and other runtime information. These macros are task-safe, lightweight, and can be grouped per tag. |
| 80 | + |
| 81 | +The available macros are: |
| 82 | + |
| 83 | +* `ESP_LOGE` – Error |
| 84 | +* `ESP_LOGW` – Warning |
| 85 | +* `ESP_LOGI` – Info |
| 86 | +* `ESP_LOGD` – Debug |
| 87 | +* `ESP_LOGV` – Verbose |
| 88 | + |
| 89 | +Here is a sample showing how to use one of these macros in your code: |
| 90 | + |
| 91 | +```c |
| 92 | +ESP_LOGE(TAG, "Failed to initialize sensor: %s", esp_err_to_name(ret)); |
| 93 | +``` |
| 94 | +
|
| 95 | +Each logging macro takes two main arguments: |
| 96 | +
|
| 97 | +* __Tag__ - the label under which the log will be grouped. |
| 98 | +* __Message__ – a `printf`-style format string followed by variadic arguments. |
| 99 | +
|
| 100 | +#### Verbosity |
| 101 | +
|
| 102 | +The difference between the logging macros is not just visual, it’s also tied to the _verbosity level_. |
| 103 | +Verbosity levels determine which messages are actually printed to the serial console. |
| 104 | +
|
| 105 | +The reason why it’s possible to change the verbosity is that reducing it in ESP-IDF (e.g., via `CONFIG_LOG_DEFAULT_LEVEL`) provides several advantages: |
| 106 | +
|
| 107 | +* **Smaller binary size:** Fewer log strings and logging calls reduce the compiled code size. |
| 108 | +* **Lower CPU load:** With fewer logs generated, the CPU spends less time formatting and outputting messages, improving runtime performance. |
| 109 | +* **Reduced memory usage:** Less memory is needed for log strings and temporary buffers, which helps applications with tight memory limits. |
| 110 | +
|
| 111 | +These optimizations make logging more efficient without losing control over critical messages. |
| 112 | +
|
| 113 | +
|
| 114 | +The `esp_log` component supports the following levels, from lowest to highest: |
| 115 | +
|
| 116 | +* __No output__ |
| 117 | +* __Error__ |
| 118 | +* __Warning__ |
| 119 | +* __Info__ |
| 120 | +* __Debug__ |
| 121 | +* __Verbose__ |
| 122 | +
|
| 123 | +This order matters: for example, `ESP_LOGE` messages will appear even at the lowest verbosity setting that allows output, while `ESP_LOGV` messages are only shown when verbosity is set to the highest level. |
| 124 | +
|
| 125 | +You can change the default verbosity level in `menuconfig`: |
| 126 | +
|
| 127 | + * `> ESP-IDF: SDK Configuration Editor (menuconfig)`<br> |
| 128 | + → `Component Config Log Level` → `Default Log Verbosity` |
| 129 | +
|
| 130 | +
|
| 131 | +Fig.2 illustrates how different verbosity settings affect which log messages are shown. The default verbosity level is _info_. |
| 132 | +
|
| 133 | +{{< figure |
| 134 | +default=true |
| 135 | +src="img/logging_diagram.webp" |
| 136 | +height=500 |
| 137 | +caption="Fig.2 - Logging diagram" |
| 138 | + >}} |
| 139 | +
|
| 140 | +### Filtering Logs |
| 141 | +
|
| 142 | +ESP-IDF gives you fine-grained control over log output, allowing you to filter messages by verbosity level and tag. You can do this both __at runtime in your application__ and __from the host using the IDF monitor__. |
| 143 | +
|
| 144 | +#### Filtering at Runtime |
| 145 | +
|
| 146 | +You can also adjust log verbosity directly within your application using the `esp_log_level_set()` function: |
| 147 | +
|
| 148 | +```c |
| 149 | +esp_log_level_set("TAG", ESP_LOG_WARN); // Only warnings and errors for "TAG" |
| 150 | +esp_log_level_set("TAG", ESP_LOG_NONE); // Disable all logs for "TAG" |
| 151 | +``` |
| 152 | + |
| 153 | +This makes it possible to dynamically change log levels for specific modules or components while your program is running. |
| 154 | + |
| 155 | + |
| 156 | +#### Filtering in the IDF Monitor |
| 157 | + |
| 158 | +When using the IDF monitor, you can control which logs are displayed by filtering them by tag and verbosity level. This can be done with the `--print-filter` option or by setting the `ESP_IDF_MONITOR_PRINT_FILTER` environment variable. All logs are still transferred to the host over the serial port, but only those that match the filter are shown. |
| 159 | + |
| 160 | +A filter is defined as a series of `<tag>:<log_level>` pairs, where `<log_level>` can be one of: |
| 161 | + |
| 162 | +* `N` (None) |
| 163 | +* `E` (Error) |
| 164 | +* `W` (Warning) |
| 165 | +* `I` (Info) |
| 166 | +* `D` (Debug) |
| 167 | +* `V` (Verbose) |
| 168 | +* `*` (all) |
| 169 | + |
| 170 | +For example you can write |
| 171 | + |
| 172 | +```sh |
| 173 | +idf.py monitor --print-filter="tag1:W" |
| 174 | +``` |
| 175 | + |
| 176 | +Shows only warnings and errors for `tag1`. |
| 177 | + |
| 178 | +```sh |
| 179 | +idf.py monitor --print-filter="tag1:I tag2:W" |
| 180 | +``` |
| 181 | + |
| 182 | +Shows `tag1` logs at Info level or less, and `tag2` logs at Warning level or less. |
| 183 | + |
| 184 | +If no log level is specified, the default is __verbose__. You can also define global filters. For instance, `*:E` displays only errors across all tags. |
| 185 | + |
| 186 | +{{< alert iconColor="#df8e1d" cardColor="#edcea3">}} |
| 187 | +Tag names must not include spaces, asterisks `*`, or colons `:`. |
| 188 | +More details: [ESP-IDF Monitor Output Filtering](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-monitor.html#output-filtering). |
| 189 | +{{< /alert >}} |
| 190 | + |
| 191 | + |
| 192 | +## Putting it all together |
| 193 | + |
| 194 | +Let’s try out the logging system in practice. |
| 195 | +We’ll start from a bare-bones example available on [this GitHub repository](https://github.com/FBEZ-docs-and-templates/devrel-tutorials-code/tree/main/tutorial-basic-project). |
| 196 | + |
| 197 | +The `main.c` file for this example looks like this: |
| 198 | +```c |
| 199 | +#include <stdio.h> |
| 200 | +#include "sdkconfig.h" |
| 201 | + |
| 202 | + |
| 203 | +void app_main(void) |
| 204 | +{ |
| 205 | + printf("Hello tutorial!\n"); |
| 206 | +} |
| 207 | +``` |
| 208 | +
|
| 209 | +In this section we will |
| 210 | +1. Assign a tag to the logs (i.e. `TAG`) |
| 211 | +2. Log a message for each level |
| 212 | +3. Change verbosity level and check the output |
| 213 | +
|
| 214 | +### Assign a tag to the logs |
| 215 | +
|
| 216 | +When using a logging macro, the first argument is a _tag_ that groups related log messages. |
| 217 | +You can pass a string literal directly, but it’s usually better to define a constant and reuse it throughout the same component or file. |
| 218 | +
|
| 219 | +
|
| 220 | +In our example, we’ll use: |
| 221 | +
|
| 222 | +```c |
| 223 | +static const char *TAG = "APP_MAIN"; |
| 224 | +``` |
| 225 | + |
| 226 | +### Log a message for each level |
| 227 | + |
| 228 | +Before using the log macros, include the `esp_log.h` header: |
| 229 | + |
| 230 | +```c |
| 231 | +#include "esp_log.h" |
| 232 | +``` |
| 233 | + |
| 234 | +Next, replace the `app_main` function with the following: |
| 235 | + |
| 236 | +```c |
| 237 | +void app_main(void) |
| 238 | +{ |
| 239 | + printf("\n\n\n*** STARTING LOGS ***\n\n"); |
| 240 | + ESP_LOGE(TAG, "Log ERROR!"); |
| 241 | + ESP_LOGW(TAG, "Log WARNING!"); |
| 242 | + ESP_LOGI(TAG, "Log INFO!"); |
| 243 | + ESP_LOGD(TAG, "Log DEBUG!"); |
| 244 | + ESP_LOGV(TAG, "Log VERBOSE!"); |
| 245 | + printf("\n\n\n*** ENDING LOGS ***\n\n\n"); |
| 246 | +} |
| 247 | +``` |
| 248 | +
|
| 249 | +{{< alert icon="circle-info" cardColor="#b3e0f2" iconColor="#04a5e5">}} |
| 250 | +The `printf` statements are used simply to separate the logs we created from the default ESP-IDF logs. |
| 251 | +{{< /alert >}} |
| 252 | +
|
| 253 | +Now, build, flash, and monitor the code: |
| 254 | +
|
| 255 | +* `> ESP-IDF: Build, Flash and Start a Monitor on Your Device` |
| 256 | +
|
| 257 | +The relevant output is shown in Fig. 3. |
| 258 | +
|
| 259 | +<!--  --> |
| 260 | +
|
| 261 | +{{< figure |
| 262 | +default=true |
| 263 | +src="img/first_output.webp" |
| 264 | +caption="Fig.3 - Logging output - Info level" |
| 265 | +
|
| 266 | +>}} |
| 267 | +
|
| 268 | +You can see that the error log appears in red, the warning log in yellow, and the info log in green. |
| 269 | +The debug and verbose logs are missing because the current verbosity level does not allow them to be displayed. |
| 270 | +
|
| 271 | +### Change verbosity level and check the output |
| 272 | +
|
| 273 | +Let’s inspect the current verbosity level in `menuconfig`: |
| 274 | +
|
| 275 | +* `> ESP-IDF: SDK Configuration Editor (menuconfig)`<br> |
| 276 | + → `Component Config` → `Log Level` → `Default Log Verbosity` |
| 277 | +
|
| 278 | +The dropdown menu (see Fig. 4) lists all available levels. By default, it is set to `info`. |
| 279 | +
|
| 280 | +{{< figure |
| 281 | +default=true |
| 282 | +src="img/menuconfig.webp" |
| 283 | +height=600 |
| 284 | +caption="Fig.4 - Choosing verbosity level" |
| 285 | +
|
| 286 | +>}} |
| 287 | +
|
| 288 | +Now, let’s see what happens when we increase the verbosity: |
| 289 | +
|
| 290 | +* Change the dropdown value to `verbose` |
| 291 | +* Save the configuration |
| 292 | +* Build, flash, and start the monitor:<br> |
| 293 | +
|
| 294 | + * `> ESP-IDF: Build, Flash and Start a Monitor on Your Device` |
| 295 | +
|
| 296 | +As expected, all log levels are now displayed, as shown in Fig. 5. |
| 297 | +
|
| 298 | +{{< figure |
| 299 | +default=true |
| 300 | +src="img/second_output.webp" |
| 301 | +height=600 |
| 302 | +caption="Fig.5 - Logging output - Verbose level" |
| 303 | +
|
| 304 | +>}} |
| 305 | +
|
| 306 | +Note that the `ESP_LOGD` and `ESP_LOGV` messages appear in white, similar to `printf`. |
| 307 | +
|
| 308 | +{{< alert icon="lightbulb" iconColor="#179299" cardColor="#9cccce">}} |
| 309 | +For production, logs are usually disabled. You can do this by selecting `No output` in the dropdown menu. |
| 310 | +{{< /alert >}} |
| 311 | +
|
| 312 | +### Change verbosity at runtime |
| 313 | +
|
| 314 | +You can also adjust the logging verbosity while your program is running. |
| 315 | +Update your `app_main` function as follows: |
| 316 | +
|
| 317 | +```c |
| 318 | +void app_main(void) |
| 319 | +{ |
| 320 | + printf("\n\n\n*** STARTING LOGS ***\n\n"); |
| 321 | + ESP_LOGE(TAG, "Log ERROR!"); |
| 322 | + ESP_LOGW(TAG, "Log WARNING!"); |
| 323 | + ESP_LOGI(TAG, "Log INFO!"); |
| 324 | + ESP_LOGD(TAG, "Log DEBUG!"); |
| 325 | + ESP_LOGV(TAG, "Log VERBOSE!"); |
| 326 | +
|
| 327 | + esp_log_level_set("main", ESP_LOG_WARN); |
| 328 | +
|
| 329 | + ESP_LOGE(TAG, "Log ERROR after set level"); |
| 330 | + ESP_LOGW(TAG, "Log WARNING set level"); |
| 331 | + ESP_LOGI(TAG, "Log INFO set level"); |
| 332 | + ESP_LOGD(TAG, "Log DEBUG!"); |
| 333 | + ESP_LOGV(TAG, "Log VERBOSE!"); |
| 334 | + printf("\n\n\n*** ENDING LOGS ***\n\n\n"); |
| 335 | +} |
| 336 | +``` |
| 337 | + |
| 338 | +In this example, we first print all log levels, then change the verbosity level of the `main` component to `WARN`. After that, only warnings and errors will appear in the output. |
| 339 | + |
| 340 | +* Build, flash, and monitor your device: |
| 341 | + |
| 342 | + * `> ESP-IDF: Build, Flash and Start a Monitor on Your Device` |
| 343 | + |
| 344 | +Your output should now look similar to Fig.5. |
| 345 | + |
| 346 | +{{< figure |
| 347 | +default=true |
| 348 | +src="img/runtime_change.webp" |
| 349 | +height=500 |
| 350 | +caption="Fig.5 - Runtime filtering" |
| 351 | + >}} |
| 352 | + |
| 353 | +As you can see, the configuration log level is set to verbose, so in the first block all messages—including verbose—are printed. After calling `esp_log_level_set`, the log level for the _main_ component is reduced to warning, so only warnings and errors are displayed from that point on. |
| 354 | + |
| 355 | + |
| 356 | +### Filtering with esp-idf monitor |
| 357 | + |
| 358 | +As a last step, let's use the `idf.py monitor` to filter only the logs with tag "main" and just the ones with verbosity level less than info. |
| 359 | + |
| 360 | +* `> ESP-IDF: Open ESP-IDF Terminal` |
| 361 | +* In the terminal type: `idf.py monitor --print-filter="main:I"` |
| 362 | + |
| 363 | +The output should look like Fig. 6. |
| 364 | + |
| 365 | +{{< figure |
| 366 | +default=true |
| 367 | +src="img/runtime_change.webp" |
| 368 | +height=500 |
| 369 | +caption="Fig.6 - idf.py monitor filtering" |
| 370 | + >}} |
| 371 | + |
| 372 | +In this case, even though the verbosity level is set to __verbose__, the monitor filters out all messages with the main tag below _info_. |
| 373 | + |
| 374 | +In this example, this process is equivalent to setting the verbosity level to info in `menuconfig`. However, when working with multiple components, you can set a different displayed verbosity level for each one individually. |
| 375 | + |
| 376 | +## Conclusion |
| 377 | + |
| 378 | +In this article, we explored the ESP-IDF logging system, showed how to define a module tag, used the `ESP_LOGx` macros for different severity levels, and adjusted verbosity to control which messages to display. |
| 379 | + |
| 380 | +We also demonstrated how to change log levels at runtime and filter messages in the IDF monitor, giving developers fine-grained control over log visibility and improving both development efficiency and application performance. |
| 381 | +Unlike `printf`, logging provides structured, context-rich output that can be filtered by level, grouped by module, and disabled in production, making debugging and monitoring much easier. |
| 382 | + |
| 383 | +### Further Reading |
| 384 | + |
| 385 | +* For a deeper dive into the **logging library**, see the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/log.html). |
| 386 | +* To explore all host-side filtering options, refer to the [IDF Monitor documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-monitor.html#output-filtering). |
0 commit comments