docs: Update translation for log.rst

This commit is contained in:
renpeiying
2024-12-26 16:56:37 +08:00
committed by BOT
parent b37218ce1a
commit 6c1152b9ac
2 changed files with 460 additions and 91 deletions

View File

@@ -16,7 +16,7 @@ ESP-IDF provides a flexible logging system with two configurable versions, **Log
Features of **Log V1**
^^^^^^^^^^^^^^^^^^^^^^
- Formatting is included in the "format" argument and compiled into Flash.
- Formatting is included in the ``format`` argument and compiled into Flash.
- Fast early and DRAM logging compared to ESP_LOG.
- Simple implementation but has limitations:
@@ -107,7 +107,7 @@ Example: Set the log level to ``ERROR`` for all components (global setting):
Adjusting log output per module (tag) depends on :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`, which is enabled by default. If this feature is not required, you can disable it to reduce code size and improve performance:
Example: Set the log level to ``WARNING`` only for the WiFi component (module-specific setting).
Example: Set the log level to ``WARNING`` only for the Wi-Fi component (module-specific setting).
.. code-block:: c
@@ -136,7 +136,7 @@ In each C file that uses the logging functionality, define the ``TAG`` variable.
.. note::
The ``TAG`` variable points to a string literal stored in Flash memory. If the same ``TAG`` string is used multiple times within a single build unit (translation unit), the compiler and linker typically optimize it to a single copy in Flash through a process called **string pooling**. However, if the same ``TAG`` string is used across different components or translation units, each component or unit will have its own copy in Flash unless global linker optimizations are applied.
The ``TAG`` variable points to a string literal stored in flash memory. If the same ``TAG`` string is used multiple times within a single build unit (translation unit), the compiler and linker typically optimize it to a single copy in flash through a process called **string pooling**. However, if the same ``TAG`` string is used across different components or translation units, each component or unit will have its own copy in flash unless global linker optimizations are applied.
The logging library provides a wide range of macros to accommodate various use cases, from general-purpose logging to early startup and constrained environments. Choosing the right macro and structuring your program accordingly can help optimize performance and ensure reliable operation. However, it is recommended to structure your program to avoid logging in constrained environments whenever possible.
@@ -152,7 +152,10 @@ There are three groups of macros available:
- **ESP_EARLY_LOGx**: Designed for use in constrained environments during early startup, before the heap allocator or syscalls are initialized. These macros are commonly used in critical startup code or in critical sections where interrupts are disabled. A key characteristic of these macros is that they use the ROM `printf` function, always output timestamps in microseconds, and do not support per-module log verbosity settings.
- **ESP_DRAM_LOGx**: Designed for use in constrained environments where logging occurs with interrupts disabled or when the flash cache is inaccessible. These macros should be used sparingly, as they can impact performance. They are suitable for critical sections or interrupt routines where other logging macros may not work reliably. A key characteristic of these macros is that they use the ROM `printf` function, do not output timestamps, allocate the format argument in DRAM to ensure accessibility when the cache is disabled, and do not support per-module log verbosity settings. Note: Use the **DRAM_STR("my_tag")** macro to allocate the tag in DRAM. This is necessary to ensure access to the tag when the flash cache is disabled.
- **ESP_DRAM_LOGx**: Designed for use in constrained environments where logging occurs with interrupts disabled or when the flash cache is inaccessible. These macros should be used sparingly, as they can impact performance. They are suitable for critical sections or interrupt routines where other logging macros may not work reliably. A key characteristic of these macros is that they use the ROM `printf` function, do not output timestamps, allocate the format argument in DRAM to ensure accessibility when the cache is disabled, and do not support per-module log verbosity settings.
.. Note::
Use the **DRAM_STR("my_tag")** macro to allocate the tag in DRAM. This is necessary to ensure access to the tag when the flash cache is disabled.
The difference between **Log V1** and **Log V2** is that in **Log V2**, all logs from these macros are routed through a single handler. This handler can automatically detect constrained environments (e.g., early startup, disabled interrupts, or flash cache inaccessible) and dynamically selects the appropriate printing function, ensuring efficient logging across various runtime contexts.
@@ -210,7 +213,7 @@ The logging system supports the following formatting options, applicable for bot
- **End Line**: Adds a newline character at the end of the log messages.
The following options are applicable only for **Log V2** and are used alongside the provided log macros. These defines can be set in the same manner as ``LOG_LOCAL_LEVEL``. Their scope depends on where they are defined (e.g., file, component, or globally):
The following options are applicable only for **Log V2** and are used alongside the provided log macros. These definitions can be set in the same manner as ``LOG_LOCAL_LEVEL``. Their scope depends on where they are defined (e.g., file, component, or globally):
- **ESP_LOG_CONSTRAINED_ENV**:
@@ -234,7 +237,7 @@ The following options are applicable only for **Log V2** and are used alongside
Per-Log Formatting
^^^^^^^^^^^^^^^^^^
The above defines work seamlessly with the provided log macros. However, if you require more flexibility or the ability to change settings at runtime, such as adjusting the log level based on a value (for example, temperature), this can be done using alternative macros. Note that in this case, the logs cannot be discarded from the binary, as they bypass compile-time log level checks.
The above definition works seamlessly with the provided log macros. However, if you require more flexibility or the ability to change settings at runtime, such as adjusting the log level based on a value (for example, temperature), this can be done using alternative macros. Note that in this case, the logs cannot be discarded from the binary, as they bypass compile-time log level checks.
The example below demonstrates how to adjust formatting for individual log messages:
@@ -305,11 +308,11 @@ There are three settings that control the ability to change the log level at run
.. note::
Keep in mind that the linked list per-tag log level check implementation has the following flaw: the linked list entries are allocated on the task stack during the execution of ``ESP_LOGx`` macros when a new tag is encountered. Deleting the task that created these entries may lead to invalid list entries and potential crashes during traversal.
Keep in mind that the linked list per-tag log level check implementation has the following flaw: The linked list entries are allocated on the task stack during the execution of ``ESP_LOGx`` macros when a new tag is encountered. Deleting the task that created these entries may lead to invalid list entries and potential crashes during traversal.
- **Cache + Linked List** (Default): It is a hybrid mode that combines caching with a **linked list** for log tag level checks. This hybrid approach offers a balance between speed and memory usage. The cache stores recently accessed log tags and their corresponding log levels, providing faster lookups for frequently used tags. The cache approach compares the tag pointers, which is faster than performing full string comparisons. For less frequently used tags, the **linked list** is utilized to search for the log level. This option may not work properly when dynamic tag definitions are used, as it relies on tag pointer comparisons in the cache, which are not suitable for dynamically defined tags. This hybrid approach improves the efficiency of log level retrieval by leveraging the speed of caching for common tags and the memory efficiency of a linked list for less frequently used tags. Selecting this option automatically enables **Dynamic Log Level Control**.
There are some cache configurations to balance memory usage and lookup performance. These settings determine how log tag levels are stored and accessed, :ref:`CONFIG_LOG_TAG_LEVEL_CACHE_IMPL`:
There are some cache configurations to balance memory usage and lookup performance. These settings determine how log tag levels are stored and accessed: :ref:`CONFIG_LOG_TAG_LEVEL_CACHE_IMPL`.
- **Array**: A simple implementation without reordering, suitable for low-memory applications that prioritize simplicity.
@@ -372,7 +375,7 @@ The logging system provides macros for logging buffer data. These macros can be
I (954) MyModule: 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73
I (964) MyModule: 74 61 72 74 65 64 20 69 73 20 61 6e 64 20 66
- :c:macro:`ESP_LOG_BUFFER_CHAR` and :c:macro:`ESP_LOG_BUFFER_CHAR_LEVEL`: Logs a buffer of printable characters. Each line contains up to 16 characters :c:macro:`ESP_LOG_BUFFER_CHAR` is only for the ``Info`` log level.
- :c:macro:`ESP_LOG_BUFFER_CHAR` and :c:macro:`ESP_LOG_BUFFER_CHAR_LEVEL`: Logs a buffer of printable characters. Each line contains up to 16 characters. :c:macro:`ESP_LOG_BUFFER_CHAR` is only for the ``Info`` log level.
.. code-block:: c
@@ -497,7 +500,7 @@ Thread Safety
Logging from constrained environments (or for **ESP_EARLY_LOGx** and **ESP_DRAM_LOGx**) does not use locking mechanisms, which can lead to rare cases of log corruption if other tasks are logging in parallel. To minimize such risks, it is recommended to use general-purpose macros whenever possible.
General-purpose macros (**ESP_LOGx**) ensure thread safety by acquiring locks during log output. In **Log V2**, additional protection is provided by ``flockfile`` during multiple ``vprintf`` calls for formatings.
General-purpose macros (**ESP_LOGx**) ensure thread safety by acquiring locks during log output. In **Log V2**, additional protection is provided by ``flockfile`` during multiple ``vprintf`` calls for formatting.
Logs are first written to a memory buffer before being sent to the UART, ensuring thread-safe operations across different tasks. Avoid logging from constrained environments unless necessary to maintain reliable log output.

View File

@@ -6,137 +6,503 @@
概述
--------
日志库提供了三种设置日志级别的方式:
ESP-IDF 提供了一套灵活的日志系统,包括两个可配置版本:**Log V1****Log V2**,可通过 :ref:`CONFIG_LOG_VERSION` 参数进行选择。本文档概述了这两个日志系统版本的特性、配置及使用方法,并比较了二者的性能表现。
- **编译时**:在 menuconfig 中,使用选项 :ref:`CONFIG_LOG_DEFAULT_LEVEL` 来设置日志级别
- 另外,还可以选择在 menuconfig 中使用选项 :ref:`CONFIG_LOG_MAXIMUM_LEVEL` 设置最高日志级别。这个选项默认被配置为默认级别,但这个选项也可以被配置为更高级别,将更多的可选日志编译到固件中
- **运行时**:默认启用所有级别低于 :ref:`CONFIG_LOG_DEFAULT_LEVEL` 的日志。:cpp:func:`esp_log_level_set` 函数可以为各个模块分别设置不同的日志级别,可通过人类可读的 ASCII 零终止字符串标签来识别不同的模块。注意,在运行时是否可以更改日志级别由 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` 决定。
- **运行时**:启用 :ref:`CONFIG_LOG_MASTER_LEVEL` 时,可以使用 :cpp:func:`esp_log_set_level_master` 函数设置 ``主日志级别`` (Master logging level)。该选项会为所有已编译的日志添加额外的日志级别检查。注意,使用此选项会增加应用程序大小。如果希望在运行时编译大量可选日志,同时避免在不需要日志输出时查找标签及其级别带来的性能损耗,此功能会非常有用。
- **Log V1**(默认):原始实现方式,具备简洁性,针对早期日志和 DRAM 日志进行了优化,但 flash 占用较高,缺乏灵活性
- **日志 V2**:增强实现方式,更加灵活,降低了 flash 占用,并集中处理日志格式,但需要更多的堆栈
以下是不同的日志级别:
**Log V2** 向后兼容 **Log V1**,这意味着使用 **Log V1** 编写的项目可以直接切换到 **Log V2**,无需额外修改。但是,使用 **Log V2** 特定功能的项目无法恢复到 **Log V1**
- 错误Error最低级别
- 警告 (Warning)
- 普通 (Info)
- 调试 (Debug)
- 冗余Verbose最高级别
**Log V1** 的特性
^^^^^^^^^^^^^^^^^^^^^^
.. note::
- 日志格式信息由 ``format`` 参数定义,在编译时嵌入了 flash。
- 相比 ESP_LOG能更快记录早期日志和 DRAM 日志。
- 实现简单,但具有局限性:
注意,函数 :cpp:func:`esp_log_level_set` 无法将日志级别设置为高于 :ref:`CONFIG_LOG_MAXIMUM_LEVEL` 指定的级别。如需在编译时将特定文件的日志级别提高到此最高级别以上,请使用 `LOG_LOCAL_LEVEL` 宏(详细信息见下文)。
- 由于包含冗余的格式化信息,二进制文件体积较大
- 不支持自定义日志格式,缺乏灵活性。
- 编译错误所指向的宏中的参数位置不准确。
**Log V2** 的特性
^^^^^^^^^^^^^^^^^^^^^^
如何使用日志库
-----------------------
- 通过单个函数 :cpp:func:`esp_log` 集中处理格式。
- 仅存储用户定义的格式字符串,从而减小二进制文件大小。
- 仅在输出需要且日志级别允许记录时,才获取时间戳。
- 允许自定义日志输出:
在使用日志功能的所有 C 文件中,将 TAG 变量定义如下:
- 为某个层级(全局、文件或消息)禁用或启用颜色、时间戳或标签。
- 输出不经过格式化处理的原始日志(适用于二进制日志)。
- 为引导加载程序和应用程序采用不同的日志设置。
- 格式参数可以动态设置为变量,构建日志消息更灵活。
- 在引导加载程序、ISR、启动代码和受限环境中日志处理机制保持统一。
- 缺点:
- 消耗更多的栈和内存。
- 日志处理速度比 **Log V1** 稍慢,但在 UART 传输过程中,其差异可忽略不计。
日志级别
----------
对于应用程序和引导加载程序,日志级别需要分别配置。这一分离配置的特点使开发者能够通过 Kconfig 选项为每个模块应用不同的日志设置。例如可以为引导加载程序启用简洁的日志而为应用程序启用详细的调试日志Debug 模式)。使用引导加载程序专用的 Kconfig 选项,可以为引导加载程序独立配置日志级别,不会影响主应用程序。
日志库有六个详细程度级别:
- (最高)
- **Verbose** - 输出高度详细且频繁的调试信息,通常包括内部状态,可能会使输出过于繁杂。
- **Debug** - 输出详细的诊断信息(例如变量值、指针地址等),适用于调试。
- **Info** - 输出描述系统正常运行的一般信息。
- **Warning** - 输出可能引发问题,但已被处理或减轻影响的事件。
- **Error** - 仅输出严重错误,这些错误如果不进行干预处理,软件无法自行恢复。
- **None** - 无日志输出,即完全禁用日志。
- (最低)
日志级别设置
------------------
日志级别设置控制将哪些日志包含在二进制文件中,以及这些日志在运行时的可见性。日志级别设置有两种:
- **日志级别Log level**:指定运行时显示哪些级别的日志。引导加载程序的 **日志级别** 通过 :ref:`CONFIG_BOOTLOADER_LOG_LEVEL` 配置,而应用程序的 **日志级别** 通过 :ref:`CONFIG_LOG_DEFAULT_LEVEL` 设置。通过函数 ``esp_log_get_default_level`` 能够获取当前日志级别。
- **最大日志级别Maximum log level**:决定将哪些日志级别包含在二进制文件中。高于此级别的日志会在编译时丢弃,不包含在最终镜像中。**最大日志级别** 可以将其设置得高于 **日志级别**,从而在二进制文件中包含额外的日志,在必要时便可通过 :cpp:func:`esp_log_level_set` 启用这些日志以帮助调试。使用 :ref:`CONFIG_LOG_MAXIMUM_LEVEL` 选项可以为应用程序启用此功能,但引导加载程序不支持此功能。对于引导加载程序,**最大日志级别** 始终与 **日志级别** 相同。
为应用程序设置日志级别示例:如果将 **日志级别** 设置为 **Warning****最大日志级别** 设置为 **Debug**,则二进制文件会包含 **Error****Warning****Info****Debug** 级别的日志消息。然而,在运行时仅输出 **Error****Warning** 级别的日志,除非通过 :cpp:func:`esp_log_level_set` 显式更改日志级别。根据具体需求,日志级别可以提高或降低。
**最大日志级别** 设置
^^^^^^^^^^^^^^^^^^^^^^^^^
根据 ``LOG_LOCAL_LEVEL`` 的定义可知,可以使用此参数覆盖特定源文件或组件的 **最大日志级别**,而无需修改 Kconfig 选项。此参数能在本地设置 **最大日志级别**,启用或排除二进制文件中的特定日志。
通过此方法,能够有效为代码的特定部分提供更详细的日志,而无需全局提高 **最大日志级别**,避免了对二进制文件大小产生不必要的影响。
- 更改某个源文件的 **最大日志级别**(不要在头文件中添加该定义,因为头文件采用单次包含的机制,可能无法正常工作):在包含 ``esp_log.h`` 之前,使用 :cpp:type:`esp_log_level_t` 中的一个值来定义 ``LOG_LOCAL_LEVEL``,控制将哪些日志消息包含在该源文件的二进制文件中。
.. code-block:: c
// 在某个 my_file.c 文件中
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
- 更改整个组件的 **最大日志级别**:在组件的 `CMakeLists.txt` 文件中定义 ``LOG_LOCAL_LEVEL``。指定的日志级别适用于组件内的所有源文件,控制将哪些日志消息包含在二进制文件中:
.. code-block:: cmake
# 在组件的 CMakeLists.txt 文件中
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE")
运行时 **日志级别** 设置
^^^^^^^^^^^^^^^^^^^^^^^^^
仅应用程序支持在运行时更改日志级别,启动引导加载程序不支持此功能。
默认情况下,系统启动时会启用 **日志级别** 以下的所有日志级别。可以使用函数 :cpp:func:`esp_log_level_set` 全局或按模块设置 **日志级别**。模块可通过标签识别,这些标签是人类可读以零结尾的 ASCII 字符串。此功能依赖于 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`,此选项默认启用。如无需此功能,可以将其禁用,以减少代码量并提升性能。
例如,将所有组件的日志级别设置为 ``ERROR``(全局设置):
.. code-block:: c
static const char* TAG = "MyModule";
esp_log_level_set("*", ESP_LOG_ERROR);
然后使用一个日志宏进行输出,例如:
根据模块(标签)调整日志输出的功能依赖于 :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`,该选项默认启用。如不需要此功能,可以将其禁用,以减少代码量并提升性能。
例如,仅将 Wi-Fi 组件的日志级别设置为 ``WARNING``(模块特定设置):
.. code-block:: c
ESP_LOGW(TAG, "Baud rate error %.1f%%. Requested: %d baud, actual: %d baud", error * 100, baud_req, baud_real);
esp_log_level_set("wifi", ESP_LOG_WARN);
可使用下列宏来定义不同的日志级别:
使用日志库
---------------
* ``ESP_LOGE`` - 错误(最低级别)
* ``ESP_LOGW`` - 警告
* ``ESP_LOGI`` - 普通
* ``ESP_LOGD`` - 调试
* ``ESP_LOGV`` - 冗余(最高级别)
此外,上述宏还有对应的 ``ESP_EARLY_LOGx`` 版本,如 :c:macro:`ESP_EARLY_LOGE`。这些版本的宏必须在堆分配器和系统调用初始化之前,在早期启动代码中显式使用。通常情况下,编译引导加载程序时也可以使用普通的 ``ESP_LOGx`` 宏,但其最终实现与 ``ESP_EARLY_LOGx`` 宏相同。
上述宏还有对应的 ``ESP_DRAM_LOGx`` 版本,如 :c:macro:`ESP_DRAM_LOGE`。在禁用中断或无法访问 flash cache 的情况下需要输出日志时,可以使用这些版本的宏。但是,应尽量避免使用这些宏版本,因为在上述情况下输出日志可能会影响性能。
.. note::
在关键部分中断被禁用,因此只能使用 ``ESP_DRAM_LOGx`` (首选)或 ``ESP_EARLY_LOGx`` 宏。尽管这样可以输出日志,但最好可以调整程序使其不用输出日志。
如需在文件或组件范围内覆盖默认的日志级别,请定义 ``LOG_LOCAL_LEVEL`` 宏。
在文件中,该宏应在包含 ``esp_log.h`` 文件前进行定义,例如:
在每个使用日志记录功能的 C 文件中定义 ``TAG`` 变量。
.. code-block:: c
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
// #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE // Optional: Increase log level that will be included in binary (only for this file)
#include "esp_log.h"
static const char* TAG = "MyModule";
// ...
ESP_LOGI(TAG, "Baud rate error %.1f%%. Requested: %d baud, actual: %d baud", error * 100, baud_req, baud_real);
ESP_EARLY_LOGW(TAG, "Early log message %d", i++);
ESP_DRAM_LOGE(DRAM_STR("TAG_IN_DRAM"), "DRAM log message %d", i++); // Use DRAM_STR macro to put in DRAM if needed
在组件中,该宏应在组件的 CMakeList 中进行定义:
.. code-block:: bash
.. code-block:: cmake
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE")
动态控制日志级别
----------------
如需在运行时按模块配置日志输出,请按如下方式调用 :cpp:func:`esp_log_level_set` 函数:
.. code-block:: c
esp_log_level_set("*", ESP_LOG_ERROR); // 将所有组件的日志级别设置为错误 (ERROR) 级别
esp_log_level_set("wifi", ESP_LOG_WARN); // 启用来自 WiFi 堆栈的警告 (WARN) 日志
esp_log_level_set("dhcpc", ESP_LOG_INFO); // 启用来自 DHCP 客户端的普通 (INFO) 日志
I (112500) MyModule: Baud rate error 1.5%. Requested: 115200 baud, actual: 116928 baud
W (112500) MyModule: Early log message 1
E TAG_IN_DRAM: DRAM log message 2
.. note::
上文介绍的 "DRAM" 和 "EARLY" 日志宏变型不支持按照模块设置日志级别。这些宏始终以“默认”级别记录日志,且只能在运行时调用 ``esp_log_level("*", level)`` 对日志级别进行更改
``TAG`` 变量指向存储在 flash 中的一个字符串字面量。如果在单个构建单元(翻译单元)中多次使用相同的 ``TAG`` 字符串,编译器和链接器通常会通过 **字符串池化** 过程将其优化为 flash 中的单个副本。然而,如果不同的组件或翻译单元使用了相同的 ``TAG`` 字符串,每个组件或单元在 flash 中都会存储一个副本,除非应用了全局链接器优化
即使已通过标签名称禁用日志输出,每个条目仍需约 10.9 微秒的处理时间
日志库提供了多种宏以适应不同的使用场景,包括通用日志记录、早期启动日志记录和受限环境日志等。选择合适的宏并据此构建相应的程序结构,有助于优化性能,确保可靠运行。但是,建议在设计程序结构时尽量避免在受限环境中进行日志记录
日志组件提供多种选项,可以更好地调整系统以满足需求,从而减少内存使用并提高操作速度。:ref:`CONFIG_LOG_TAG_LEVEL_IMPL` 可配置检查标签级别:
- Verbose: :c:macro:`ESP_LOGV`, :c:macro:`ESP_EARLY_LOGV`, :c:macro:`ESP_DRAM_LOGV`.
- Debug: :c:macro:`ESP_LOGD`, :c:macro:`ESP_EARLY_LOGD`, :c:macro:`ESP_DRAM_LOGD`.
- Info: :c:macro:`ESP_LOGI`, :c:macro:`ESP_EARLY_LOGI`, :c:macro:`ESP_DRAM_LOGI`.
- Warning: :c:macro:`ESP_LOGW`, :c:macro:`ESP_EARLY_LOGW`, :c:macro:`ESP_DRAM_LOGW`.
- Error: :c:macro:`ESP_LOGE`, :c:macro:`ESP_EARLY_LOGE`, :c:macro:`ESP_DRAM_LOGE`.
- ``None``:选择此选项,则会禁用为每个标签设置日志级别的功能。在运行时是否可以更改日志级别取决于 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`。如果禁用,则无法在运行时使用 :cpp:func:`esp_log_level_set` 更改日志级别。该选项适用于高度受限的环境。
- ``Linked list (no cache)``:选择此选项,则会启用为每个标签设置日志级别的功能。此方法在链表中搜索所有标签的日志级别。如果标签数量比较多,这种方法可能会比较慢,但内存要求可能低于下面的 cache 方式。
- ``Cache + Linked List`` 默认选项选择此选项则会启用为每个标签设置日志级别的功能。这种混合方法在速度和内存使用之间实现了平衡。cache 中存储最近访问的日志标签及其相应的日志级别,从而更快地查找常用标签。
这些宏可分为以下三组:
启用 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` 选项后,则可在运行时通过 :cpp:func:`esp_log_level_set` 更改日志级别。动态更改日志级别提高了灵活性,但也会产生额外的代码开销。
如果应用程序不需要动态更改日志级别,并且不需要使用标签来控制每个模块的日志,建议禁用 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`。与默认选项相比,这可以节约大概 260 字节的 IRAM、264 字节的 DRAM、以及 1 KB 的 flash。这不仅可以简化日志提高内存效率还可以将应用程序中的日志操作速度提高约 10 倍。
- **ESP_LOGx**: 标准日志宏,适用于正常运行期间的大多数用例。在非受限环境下,可在应用程序代码中使用这些宏来记录日志,但不要在中断服务例程 (ISR)、早期启动阶段或 flash 缓存被禁用时使用。这些宏的一个重要特点是,它们使用 Newlib 库的 `vprintf` 函数进行格式处理和日志输出
.. note::
- **ESP_EARLY_LOGx**: 专为早期启动阶段的受限环境设计,在堆分配器或系统调用尚未初始化时使用。这些宏通常用于关键的启动代码或中断被禁用的关键区域。这些宏的一个重要特点是,它们使用 ROM 的 `printf` 函数,以微秒为单位输出时间戳,并且不支持按模块设置日志详细级别。
``Linked list````Cache + Linked List`` 选项将自动启用 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`
- **ESP_DRAM_LOGx**: 专为受限环境设计,在中断被禁用或 flash 缓存不可访问时记录日志。这些宏可能会影响性能,应谨慎使用。这些宏适用于其他日志宏可能无法可靠运行的关键区域或中断例程。这些宏的特点是,它们使用 ROM 的 `printf` 函数,不输出时间戳,将格式参数分配在 DRAM 中以确保缓存禁用时的可访问性,并且不支持按模块设置日志详细级别
主日志级别
^^^^^^^^^^^^^^^^^^^^
.. Note::
使用 **DRAM_STR("my_tag")** 宏在 DRAM 中分配标签。这能够确保在 flash 缓存被禁用时仍能访问标签。
要启用主日志级别功能,须启用 :ref:`CONFIG_LOG_MASTER_LEVEL` 选项。该功能在调用 :cpp:func:`esp_log_write` 之前为 ``ESP_LOGx`` 宏添加了额外的级别检查。这样就可以设置更高的 :ref:`CONFIG_LOG_MAXIMUM_LEVEL`,并且不会在正常操作期间对性能造成影响(仅在有指示时)。应用程序可以全局设置主日志级别(:cpp:func:`esp_log_set_level_master`)以强制执行最高日志级别。高于此级别的 ``ESP_LOGx`` 宏将直接跳过,不会调用 :cpp:func:`esp_log_write` 并进行标签查找。建议只在顶层应用程序中使用此功能,不要在共享组件中使用,因为这将覆盖所有使用该组件的用户的全局日志级别。默认情况下,启动时主日志级别是 :ref:`CONFIG_LOG_DEFAULT_LEVEL`
**Log V1****Log V2** 的区别在于,在 **Log V2** 中,所有来自这些宏的日志都发送到同一个处理程序进行处理。该处理程序可以自动检测受限环境(例如,早期启动、禁用中断或 flash 缓存不可访问的情景),并动态选择适当的打印函数,确保在不同的运行环境中实现高效的日志记录
注意,由于此功能为所有 ``ESP_LOGx`` 宏添加了额外的检查,会导致应用程序的大小增加。
日志格式
----------
以下代码片段展示了主日志级别的运行方式。将主日志级别设置为 ``ESP_LOG_NONE`` 将在全局范围内禁用所有日志记录。:cpp:func:`esp_log_level_set` 目前不会影响日志记录。但在主日志级别释放后,日志将按照 :cpp:func:`esp_log_level_set` 中的设置打印输出。
- **Log V1**:仅支持全局禁用颜色格式。其他格式选项(如时间戳和标签)始终启用
- **Log V2**
- 允许完全自定义日志格式,包括全局、按文件、按模块,甚至为单个日志消息禁用颜色、标签和时间戳格式。
- 更精细的日志输出控制,更适用于特定的用例和环境。
.. code-block:: c
// 在启动时,主日志级别为 CONFIG_LOG_DEFAULT_LEVEL并等于ESP_LOG_INFO
ESP_LOGI("lib_name", "用于打印的消息"); // 打印普通 (INFO) 级别消息
esp_log_level_set("lib_name", ESP_LOG_WARN); // 启用 lib_name 的警告 (WARN) 日志
// #define ESP_LOG_COLOR_DISABLED (1) /* For Log v2 only */
// #define ESP_LOG_TIMESTAMP_DISABLED (1) /* For Log v2 only */
#include "esp_log.h"
static const char* TAG = "boot";
// ...
ESP_LOGI(TAG, "chip revision: v%d.%d", major, minor);
esp_log_set_level_master(ESP_LOG_NONE); // 全局禁用所有日志。esp_log_level_set 目前没有生效
.. code-block:: none
ESP_LOGW("lib_name", "用于打印的消息"); // 主日志级别阻止了打印
esp_log_level_set("lib_name", ESP_LOG_INFO); // 启用 lib_name 的 INFO 日志
ESP_LOGI("lib_name", "用于打印的消息"); // 主日志级别阻止了打印
I (56) boot: chip revision: v3.0
esp_log_set_level_master(ESP_LOG_INFO); // 全局启用所有 INFO 日志
level name |end of line
| |
[0;32mI (56) boot: chip revision: v3.0[0m
|_____| |___||____||_________________||_|
|start | |tag | |end color
|color | |user string
|timestamp
ESP_LOGI("lib_name", "用于打印的消息"); // 打印一条 INFO 消息
日志系统支持以下格式化选项,并且同时适用于应用程序和引导加载程序:
- **Color**:增加颜色代码,全局增强日志的可见性。由 :ref:`CONFIG_LOG_COLORS` 控制,默认情况下禁用,因为 ESP-IDF 监视工具 `idf.py monitor` 可以通过 **级别名称** 检测日志级别并应用标准的 IDF 颜色方案。
- 对于 **Log V2**,选项 :ref:`CONFIG_LOG_COLORS_SUPPORT` 支持在运行时为特定日志、文件或组件添加颜色输出,即使全局颜色已禁用。要为特定上下文启用颜色,请使用 ``ESP_LOG_COLOR_DISABLED``
- **Level Name**表示日志详细级别的单个字母I, W, E, D, V显示在每条日志消息的开头用于识别日志级别。这在禁用颜色时非常有用例如在禁用颜色时 ESP-IDF 监视工具就会使用该信息。
- **Timestamp**:为日志消息全局添加时间戳。由 :ref:`CONFIG_LOG_TIMESTAMP_SOURCE` 控制。
- **None**:不显示时间戳。在日志分析或调试中,当时间不关键时非常有用,还能够节省处理性能和内存。仅适用于 **Log V2**
- **Milliseconds since boot** `(18532)`(默认):通过 RTOS 时钟滴答计数乘以滴答周期得出。
- **System time (HH:MM:SS.sss)** `14:31:18.532`:以小时、分钟、秒和毫秒显示时间。
- **System time (YY-MM-DD HH:MM:SS.sss)** `(2023-08-15 14:31:18.532)`:同上,还包括日期。
- **Unix time in milliseconds** `(1692099078532)`:以毫秒显示 Unix 时间。
- 对于 **Log V2**,选项 :ref:`CONFIG_LOG_TIMESTAMP_SUPPORT` 支持在运行时为特定日志、文件或组件添加时间戳输出,即使全局时间戳已禁用。要为特定上下文启用 **Milliseconds since boot** 时间戳,请使用 ``ESP_LOG_TIMESTAMP_DISABLED``
- **Tag**:显示用户定义的源模块标识符。
- 对于 **Log V2**,可以将 tag 设置为 ``NULL`` 传递给宏在这种情况下tag 不会被打印,且无法按组件进行日志级别检查。
- **End Line**:在日志消息的末尾添加换行符。
以下选项仅适用于 **Log V2**,并与提供的日志宏一起使用。这些定义可以用和 ``LOG_LOCAL_LEVEL`` 相同的方式设置。它们的作用范围取决于定义的位置(例如文件、组件或全局):
- **ESP_LOG_CONSTRAINED_ENV**
- 定义为 ``1``时,强制日志处理程序 :cpp:func:`esp_log` 使用适合指定作用域的安全 printf 函数。
- **ESP_LOG_FORMATTING_DISABLED**:
- 默认为 ``0``(启用所有格式化项,如颜色、时间戳、标记和末尾换行)。
- 定义为 ``1`` 时,为指定范围禁用所有的格式化项。
- **ESP_LOG_COLOR_DISABLED** 要求 :ref:`CONFIG_LOG_COLORS_SUPPORT` 启用。
- 如果全局颜色 (:ref:`CONFIG_LOG_COLORS`) 已禁用,则定义为 ``0``,以启用指定范围的颜色输出。
- 如果启用了全局颜色(:ref:`CONFIG_LOG_COLORS`),则定义为 ``1``,表示禁用指定范围的颜色输出。
- **ESP_LOG_TIMESTAMP_DISABLED** 要求启用 :ref:`CONFIG_LOG_TIMESTAMP_SUPPORT`
- 如果已禁用全局时间戳(:ref:`CONFIG_LOG_TIMESTAMP_SOURCE`),则定义为 ``0``,以启用指定范围的时间戳输出。
- 如果全局时间戳(:ref:`CONFIG_LOG_TIMESTAMP_SOURCE`)已启用,则定义为 ``1``,表示禁用指定范围的时间戳输出。
设置每条日志的输出格式
^^^^^^^^^^^^^^^^^^^^^^^^^^^
上述定义与提供的日志宏可以无缝配合使用。如果需要更高的灵活性,或需要在运行时调整设置,例如根据某个值(例如温度)调整日志级别,可以使用其他的宏来实现。需要注意的是,在这种情况下,日志不能从二进制文件中丢弃,因为它们绕过了编译时的日志级别检查。
下面的示例演示了如何调整单独的日志消息的格式:
.. code-block:: c
#include "esp_log.h"
esp_log_config_t configs = {
.opts = {
.log_level = ESP_LOG_INFO, // Set log level
.constrained_env = false, // Specify constrained environment
.require_formatting = true, // Enable formatting
.dis_color = ESP_LOG_COLOR_DISABLED, // Use global color setting
.dis_timestamp = ESP_LOG_TIMESTAMP_DISABLED, // Use global timestamp setting
.reserved = 0, // Reserved for future use
}
};
// ...
if (temperature > 55) {
configs.opts.log_level = ESP_LOG_WARN;
}
// Similar to ESP_LOGx macros but allows applying custom configurations
// If the configs var is constant, the compiler can exclude the log during compilation
// if it is below the maximum log level, otherwise not.
ESP_LOG_LEVEL_LOCAL(configs, TAG, "Temp = %dC", temperature);
// Note: The following calls bypass compile-time log level checks,
// they cannot be discarded from the binary
esp_log(configs, TAG, "Temp = %dC", temperature);
ESP_LOG_LEVEL(configs, TAG, "Temp = %dC", temperature);
日志级别控制
-----------------
只有应用程序支持在运行时更改日志级别。引导加载程序不支持此功能。
日志库允许在运行时使用函数 :cpp:func:`esp_log_level_set` 调整每个模块(标签)的日志输出。此功能仅适用于非受限环境(**ESP_LOGx** 宏)。受限环境(如 **ESP_EARLY_LOGx****ESP_DRAM_LOGx**)不支持动态日志级别,因为它们的日志处理程序中没有锁和轻量级要求。
.. code-block:: c
// Set log level to ERROR for all components (global setting)
esp_log_level_set("*", ESP_LOG_ERROR);
// Set log level to WARNING for the WiFi component (module-specific setting)
esp_log_level_set("wifi", ESP_LOG_WARN);
// Set log level to INFO for the DHCP client (module-specific setting)
esp_log_level_set("dhcpc", ESP_LOG_INFO);
有三种设置可控制在运行时更改全局或每个模块(标签)的日志级别:
- **Dynamic Log Level Control**:ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`,默认已启用):动态日志级别控制。启用后,可以通过 :cpp:func:`esp_log_level_set` 函数在运行时更改日志级别。该功能提高了灵活性,但也增加了内存和性能开销。如需考虑二进制文件的大小,并且无需在运行时动态更改日志级别,建议禁用此选项,特别是在 :ref:`CONFIG_LOG_TAG_LEVEL_IMPL` 设置为 **None** 时,以尽量减小程序大小。
如果你的应用程序不需要动态调整日志级别,禁用此选项可以提高效率:
- 降低内存消耗:
- **IRAM**: ~260 bytes
- **DRAM**: ~264 bytes
- **Flash**: ~1 KB
- 提高日志操作性能,最多提高 10 倍。
- **Tag-Level Checks**:ref:`CONFIG_LOG_TAG_LEVEL_IMPL`,默认值为 **Cache + Linked List**):标签级别检查,决定了如何检查每个标签的日志级别,影响内存使用和查找速度:
- **None**:完全禁用按标签进行日志级别检查,减少了开销,但失去了运行时的灵活性。
- **Linked List**:仅使用链表实现每个标签的日志级别设置(不使用缓存)。这种方法会遍历链表中的所有标签来确定日志级别,因此,当标签数量较大时,会导致查找速度变慢,但与 **缓存** 方式相比,能节省更多内存空间。链表方法通过对日志标签进行完整的字符串比较来确定适当的日志级别。与 **缓存** 方法不同,链表方式不依赖于标签指针比较,因此更适合动态定义的标签。如需优先考虑节省内存、启用或禁用特定模块的日志,或希望使用定义为变量的标签,请选择此方法。选择此方法会自动启用 **Dynamic Log Level Control** 功能。
.. note::
注意,链表方式实现的标签日志级别检查存在以下不足:运行 ``ESP_LOGx`` 宏遇到新标签时,会在任务栈中分配链表条目。如果删除了创建这些条目的任务,链表中可能包含无效项,并且在遍历时可能会导致崩溃。
- **Cache + Linked List**(默认):缓存 + 链表,采用缓存与链表结合的方式,进行日志标签级别的检查,在内存占用和速度直接提供了平衡。缓存中会存储最近访问的日志标签及其对应的日志级别,为常用标签实现了更快的查找速度。这是因为缓存方式会比较标签指针,与执行完整字符串相比速度更快。对不常用标签,通过链表进行日志级别查找。但是,使用动态标签定义时,此选项可能无法正常工作,因为它依赖缓存中的标签指针比较,不适用于动态定义的标签。此混合方法利用了常用标签的缓存速度优势和不常用标签的链表存储效率,提升了日志级别查找的总体效率。选择此选项会自动启用 **Dynamic Log Level Control**
有一些缓存配置可以平衡内存使用和查找性能。这些配置决定了日志标签级别的存储和访问方式,详见 :ref:`CONFIG_LOG_TAG_LEVEL_CACHE_IMPL`
- **Array**:数组方式,实现简单,不进行重新排序,适合注重简洁性的低内存应用。
- **Binary Min-Heap**(默认)最小二叉堆,优化的实现方式,支持快速查找并自动重新排序,适用于具有充足内存的高性能应用。**缓存大小** (:ref:`CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE`) 定义了其容量,默认包含 31 个条目。
缓存容量越大,查找常用日志标签的性能越高,但内存消耗也会增加。相反,缓存容量越小越节省内存,但可能会淘汰更多使用较少的标签。
- **Master Log Level**:ref:`CONFIG_LOG_MASTER_LEVEL`,默认禁用):这是一个可选设置,专为特定调试场景设计。此设置启用后,会在生成时间戳和标签缓存查找之前,启用全局 master 日志级别检查。这一选项适用于编译大量日志的情况,可以在运行时有选择地启用或禁用日志,同时在不需要日志输出时,尽量减少对性能的影响。
例如,通常可以在在时间紧迫或 CPU 密集型操作期间临时禁用日志,并在之后重新启用日志。
.. note:: 对于 **Log V1**,此功能可能会基于已编译日志的数量而显著增加程序大小。对于 **Log V2** 影响很小,因为检查已集成到了日志处理程序中。
如果启用此功能,主日志级别默认为 :ref:`CONFIG_LOG_DEFAULT_LEVEL`,并可在运行时通过 :cpp:func:`esp_log_set_level_master` 进行调整。此全局检查优先于 ``esp_log_get_default_level``
以下代码片段演示了此功能的原理。将 **Master Log Level** 设置为 ``ESP_LOG_NONE`` 会在全局范围内禁用所有日志。此时,:cpp:func:`esp_log_level_set` 不会影响日志输出。但是,当 **Master Log Level** 调整为更高级别后,日志会按照 :cpp:func:`esp_log_level_set` 的配置打印出来:
.. code-block:: c
// Master logging level is CONFIG_LOG_DEFAULT_LEVEL at start-up and = ESP_LOG_INFO
ESP_LOGI("lib_name", "Message for print"); // Prints an INFO message
esp_log_level_set("lib_name", ESP_LOG_WARN); // Enables WARN logs for lib_name
// Disables all logs globally. esp_log_level_set has no effect at the moment
esp_log_set_level_master(ESP_LOG_NONE);
ESP_LOGW("lib_name", "Message for print"); // No print, Master logging level blocks it
esp_log_level_set("lib_name", ESP_LOG_INFO); // Enables INFO logs for lib_name
ESP_LOGI("lib_name", "Message for print"); // No print, Master logging level blocks it
// Enables all INFO logs globally
esp_log_set_level_master(ESP_LOG_INFO);
ESP_LOGI("lib_name", "Message for print"); // Prints an INFO message
.. note::
即使按标签禁用日志,处理时间仍需约 10.9 微秒。要减少这一开销,可考虑使用 **Master Log Level** 或禁用 **Tag-Level Checks** 功能。
缓冲区日志
----------
日志系统提供用于记录缓冲区数据的宏。这些宏可在引导加载程序和应用程序中使用,且不限制日志版本。可用的宏包括:
- :c:macro:`ESP_LOG_BUFFER_HEX`:c:macro:`ESP_LOG_BUFFER_HEX_LEVEL`:记录十六进制字节缓冲区。数据按每行 16 个字节分割。:c:macro:`ESP_LOG_BUFFER_HEX` 仅适用于 ``Info`` 日志级别。
.. code-block:: c
#include "esp_log_buffer.h"
uint8_t buffer[] = {
0x54, 0x68, 0x65, 0x20, 0x77, 0x61, 0x79, 0x20,
0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x73,
0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, 0x69,
0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66
};
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, sizeof(buffer), ESP_LOG_DEBUG);
.. code-block:: none
I (954) MyModule: 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73
I (964) MyModule: 74 61 72 74 65 64 20 69 73 20 61 6e 64 20 66
- :c:macro:`ESP_LOG_BUFFER_CHAR`:c:macro:`ESP_LOG_BUFFER_CHAR_LEVEL`:记录可打印字符的缓冲区。每行最多包含 16 个字符。:c:macro:`ESP_LOG_BUFFER_CHAR` 仅适用于 ``Info`` 日志级别。
.. code-block:: c
#include "esp_log_buffer.h"
char buffer[] = "The quick brown fox jumps over the lazy dog.";
ESP_LOG_BUFFER_CHAR_LEVEL(TAG, buffer, sizeof(buffer), ESP_LOG_WARN);
.. code-block:: none
I (980) MyModule: The quick brown
I (985) MyModule: fox jumps over
I (990) MyModule: the lazy dog.
- :c:macro:`EP_LOG_BUFFER_HEXDUMP`:以格式化的十六进制转储方式转储缓冲区,同时显示内存地址和相应的 ASCII 值。适用于调试原始内存内容。
.. code-block:: c
#include "esp_log_buffer.h"
uint8_t buffer[] = {
0x54, 0x68, 0x65, 0x20, 0x77, 0x61, 0x79, 0x20,
0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x73,
0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, 0x69
};
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, sizeof(buffer), ESP_LOG_INFO);
.. code-block:: none
I (1013) MyModule: 0x3ffb5bc0 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73 |The way to get s|
I (1024) MyModule: 0x3ffb5bd0 74 61 72 74 65 64 20 69 73 20 74 6f 20 71 75 69 |tarted is to qui|
输出的行数取决于缓冲区大小。
性能测量
----------
在任务中使用日志时,任务栈必须配置至少 2 KB 的空间,以确保日志操作内存充足。
使用日志组件中的测试工具,基于默认设置(最大和默认日志级别设置为 INFO禁用颜色支持未启用主日志级别启用时间戳在不同芯片上进行了如下两组测量
- 日志 API 性能测量
- 日志 API 堆栈用量
``esp_rom_printf````esp_rom_vprintf`` 的结果相似同样vprintf 和 printf 也得出相似结果。因此,下表仅包括了每对相似测试中的一个。
**堆栈用(字节)**
+-------------------+-------+---------+---------+
| 功能 | ESP32 | ESP32C2 | ESP32C3 |
+-------------------+-------+---------+---------+
| esp_rom_printf | 128 | 192 | 192 |
+-------------------+-------+---------+---------+
| ESP_EARLY_LOGI V1 | 128 | 192 | 192 |
+-------------------+-------+---------+---------+
| ESP_EARLY_LOGI V2 | 336 | 324 | 324 |
+-------------------+-------+---------+---------+
| ESP_DRAM_LOGI V1 | 128 | 192 | 192 |
+-------------------+-------+---------+---------+
| ESP_DRAM_LOGI V2 | 336 | 324 | 324 |
+-------------------+-------+---------+---------+
| vprintf | 1168 | 384 | 1344 |
+-------------------+-------+---------+---------+
| ESP_LOGI V1 | 1184 | 384 | 1344 |
+-------------------+-------+---------+---------+
| ESP_LOGI V2 | 1152 | 592 | 1504 |
+-------------------+-------+---------+---------+
**Log V1****Log V2** 之间的堆栈使用量差异可以忽略不计。
**性能(无微秒输出)**
+-------------------+-------+---------+---------+
| 功能 | ESP32 | ESP32C2 | ESP32C3 |
+===================+=======+=========+=========+
| esp_rom_printf | 1 | 2 | 1 |
+-------------------+-------+---------+---------+
| ESP_EARLY_LOGI V1 | 15 | 24 | 14 |
+-------------------+-------+---------+---------+
| ESP_EARLY_LOGI V2 | 28 | 36 | 25 |
+-------------------+-------+---------+---------+
| ESP_DRAM_LOGI V1 | 6 | 9 | 5 |
+-------------------+-------+---------+---------+
| ESP_DRAM_LOGI V2 | 19 | 22 | 14 |
+-------------------+-------+---------+---------+
| vprintf | 15 | 9 | 7 |
+-------------------+-------+---------+---------+
| ESP_LOGI V1 | 27 | 16 | 12 |
+-------------------+-------+---------+---------+
| ESP_LOGI V2 | 77 | 54 | 40 |
+-------------------+-------+---------+---------+
关于通过 UART 输出日志的性能,**Log V1****Log V2** 的几乎完全相同。与通过 UART 发送日志所需的时间相比,**Log V2** 在处理开销方面带来的微小差异可以忽略不计。因此,在大多数实际用例中,切换到 **Log V2** 对性能的影响可以忽略。
**内存占用(字节)**
以下测量使用了 ``esp_timer`` 示例和 ESP32 的默认设置,最大和默认日志级别设为 INFO禁用颜色支持启用时间戳。启用 **Log V2** 后重新构建了示例,然后使用以下命令比较内存占用的差异:
.. code-block:: bash
idf.py size --diff ~/esp/logv2/build_v1
+------------------------+---------------+--------------+---------------+------------+-----------------+
| 日志系统版本 | IRAM | DRAM | flash 代码 | flash 数据 | App 二进制大小 |
+========================+===============+==============+===============+============+=================+
| Log V2 | +1772 | -36 | -956 | -1172 | 181104 (-384) |
+------------------------+---------------+--------------+---------------+------------+-----------------+
+------------------------+--------------------------+
| 日志系统版本 | 引导加载程序二进制大小 |
+========================+==========================+
| Log V2 | 26272 (+160) |
+------------------------+-------------------------——+
启用 **Log V2** 会增加 IRAM 的使用量同时减少整个应用程序的二进制文件大小、flash 代码和数据量。
通过 JTAG 将日志记录到主机
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
------------------------------
默认情况下,日志库使用类似 vprintf 的函数将格式化输出写入专用 UART。通过调用一个简单的 API即可将所有日志通过 JTAG 输出,将日志输出速度提高数倍。如需了解详情请参阅 :ref:`app_trace-logging-to-host`
默认情况下,日志库使用类似 vprintf 的函数将格式化输出写入专用 UART。通过调用一个简单的 API所有日志输出都可以路由到 JTAG从而使日志记录速度提高数倍。详情请参阅章节 :ref:`app_trace-logging-to-host`
线程安全
^^^^^^^^^^^^^
-------------
日志字符串首先被写入内存 buffer然后发送到 UART 打印。日志调用是线程安全的,即不同线程的日志不会互相冲突
在受限环境(或 **ESP_EARLY_LOGx****ESP_DRAM_LOGx**)记录日志时,不使用锁机制。因此,如果其他任务并行记录日志,可能会导致日志损坏的罕见情况。为降低此类风险,建议尽可能使用通用宏
通用宏 (**ESP_LOGx**) 通过在日志输出过程中获取锁来确保线程安全。在 **Log V2** 中,``flockfile`` 在多个 ``vprintf`` 调用进行格式化处理时提供了额外保护。
日志首先写入内存 buffer然后发送到 UART 打印,从而确保不同任务之间的线程安全。除非需要确保可靠的日志输出,否则应避免在受限环境中记录日志。
应用示例
-------------------