Merge branch 'feature/logv2_2' into 'master'

feat(log): Log v2

Closes IDF-245, IDFGH-3855, IDF-2956, IDF-7883, and IDFGH-13066

See merge request espressif/esp-idf!31128
This commit is contained in:
Konstantin Kondrashov
2025-01-09 13:55:06 +08:00
44 changed files with 2477 additions and 410 deletions

View File

@@ -1,5 +1,26 @@
menu "Log"
choice BOOTLOADER_LOG_VERSION
prompt "Log version"
help
Select the log version to be used by the ESP log component.
The app log version (CONFIG_LOG_VERSION) controls the version used in the bootloader,
preventing the selection of different versions.
For description of V1 and V2 see CONFIG_LOG_VERSION.
config BOOTLOADER_LOG_VERSION_1
bool "V1" if LOG_VERSION_1
config BOOTLOADER_LOG_VERSION_2
bool "V2" if LOG_VERSION_2
endchoice
config BOOTLOADER_LOG_VERSION
int
default 1 if BOOTLOADER_LOG_VERSION_1
default 2 if BOOTLOADER_LOG_VERSION_2
help
This configuration sets the log version number based on the chosen log version.
choice BOOTLOADER_LOG_LEVEL
bool "Bootloader log verbosity"
default BOOTLOADER_LOG_LEVEL_INFO

View File

@@ -3,11 +3,23 @@ menu "Format"
config BOOTLOADER_LOG_COLORS
bool "Color"
default n
select BOOTLOADER_LOG_COLORS_SUPPORT if BOOTLOADER_LOG_VERSION_2
help
Use ANSI terminal colors in log output
Enable ANSI terminal color codes.
Enable ANSI terminal color codes. Logs (info, errors, warnings) will contain color codes.
In order to view these, your terminal program must support ANSI color codes.
config BOOTLOADER_LOG_COLORS_SUPPORT
bool "Allow enabling color output at run time"
depends on BOOTLOADER_LOG_VERSION_2
default n
help
Enables support for color codes in the esp_log() function. If CONFIG_LOG_COLORS is enabled, this option
is always active. If CONFIG_LOG_COLORS is disabled, this option allows you to still handle color codes
in specific files by defining ESP_LOG_COLOR_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase RAM/FLASH usage due to additional color handling
functionality. It provides flexibility to manage color output even when CONFIG_LOG_COLORS is turned off.
choice BOOTLOADER_LOG_TIMESTAMP_SOURCE
prompt "Timestamp"
default BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS
@@ -28,11 +40,25 @@ menu "Format"
config BOOTLOADER_LOG_TIMESTAMP_SOURCE_NONE
bool "None"
depends on NO_SYMBOL # hide it now, turn it on final MR
depends on BOOTLOADER_LOG_VERSION_2
config BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS
bool "Milliseconds Since Boot"
select BOOTLOADER_LOG_TIMESTAMP_SUPPORT if BOOTLOADER_LOG_VERSION_2
endchoice # BOOTLOADER_LOG_TIMESTAMP_SOURCE
config BOOTLOADER_LOG_TIMESTAMP_SUPPORT
bool "Allow enabling timestamp output at run time"
depends on BOOTLOADER_LOG_VERSION_2
default y
help
Enables support for timestamp in the esp_log() function.
If CONFIG_LOG_TIMESTAMP_SOURCE_NONE, this option allows you to still handle timestamp
in specific files by defining ESP_LOG_TIMESTAMP_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase RAM/FLASH usage due to additional timestamp handling
functionality. It provides flexibility to manage timestamp output even when
CONFIG_LOG_TIMESTAMP_SOURCE_NONE.
endmenu

View File

@@ -12,7 +12,9 @@
#include "esp_err.h"
#include "esp_check.h"
#include "esp_system.h"
#include "esp_private/log_util.h"
#include "esp_log.h"
#include "esp_private/cache_utils.h"
#include "spi_flash_mmap.h"
#include "esp_flash_internal.h"
#if CONFIG_NEWLIB_ENABLED
@@ -107,6 +109,8 @@ ESP_SYSTEM_INIT_FN(init_flash, CORE, BIT(0), 130)
#if CONFIG_SPI_FLASH_BROWNOUT_RESET
spi_flash_needs_reset_check();
#endif // CONFIG_SPI_FLASH_BROWNOUT_RESET
// The log library will call the registered callback function to check if the cache is disabled.
esp_log_util_set_cache_enabled_cb(spi_flash_cache_enabled);
return ESP_OK;
}
#endif // !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP

View File

@@ -13,7 +13,13 @@ endif()
set(srcs "src/${system_target}/log_timestamp.c"
"src/log_timestamp_common.c"
"src/${system_target}/log_lock.c")
"src/${system_target}/log_lock.c"
"src/buffer/log_buffers.c"
"src/${system_target}/util.c"
"src/util.c"
"src/log_print.c"
"src/log.c")
set(priv_requires "")
if(NOT non_os_build)
@@ -21,10 +27,6 @@ if(NOT non_os_build)
list(APPEND srcs "src/os/log_write.c")
# Buffer APIs call ESP_LOG_LEVEL -> esp_log_write, which can not used in bootloader.
list(APPEND srcs "src/buffer/log_buffers.c"
"src/util.c")
list(APPEND srcs "src/log_level/log_level.c"
"src/log_level/tag_log_level/tag_log_level.c")

View File

@@ -1,5 +1,51 @@
menu "Log"
choice LOG_VERSION
prompt "Log version"
default LOG_VERSION_1
help
Select the log version to be used by the ESP log component.
- "V1": This version integrates log formatting into the format string provided by the user.
Logs are processed and formatted during compile time, leading to a larger binary file.
Example: ESP_LOGI("boot", "chip revision: v%d.%d", major, minor);
Output: I (56) boot: chip revision: v3.0
Note: Log strings are stored in Flash with added formatting characters.
Format string on flash: "[0;32mI (%lu) %s: chip revision: v%d.%d [0m"
- "V2": This version centralizes log formatting within the esp_log() function.
User-supplied format strings are stored without added formatting, reducing binary size.
Example: ESP_LOGI("boot", "chip revision: v%d.%d", major, minor);
Output: I (56) boot: chip revision: v3.0
Note: This version supports runtime configuration of formatting and is more flexible,
logging from constrained environments (ex.: ISR, Startup, Cache disabled).
It may consumes a bit more stack and affect performance.
Format string on flash: "chip revision: v%d.%d"
Use V1 for minimal stack usage and simpler implementation.
Use V2 for smaller binary sizes, more flexible log formatting, and advanced features like disabling
colors or timestamps.
config LOG_VERSION_1
bool "V1"
help
Select this option to use Log V1. Recommended for projects with strict stack constraints
or that prioritize performance over flexibility.
config LOG_VERSION_2
bool "V2"
help
Select this option to use Log V2. Recommended for projects that require smaller binaries,
runtime log formatting configuration, or advanced logging features.
endchoice
config LOG_VERSION
int
default 1 if LOG_VERSION_1
default 2 if LOG_VERSION_2
help
This configuration sets the log version number based on the chosen log version.
orsource "./Kconfig.level"
orsource "./Kconfig.format"

View File

@@ -3,10 +3,23 @@ menu "Format"
config LOG_COLORS
bool "Color"
default n
select LOG_COLORS_SUPPORT if LOG_VERSION_2
help
Enable ANSI terminal color codes.
Enable ANSI terminal color codes. Logs (info, errors, warnings) will contain color codes.
In order to view these, your terminal program must support ANSI color codes.
config LOG_COLORS_SUPPORT
bool "Allow enabling color output at run time"
depends on LOG_VERSION_2
default n
help
Enables support for color codes in the esp_log() function. If CONFIG_LOG_COLORS is enabled, this option
is always active. If CONFIG_LOG_COLORS is disabled, this option allows you to still handle color codes
in specific files by defining ESP_LOG_COLOR_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase IRAM usage due to additional color handling
functionality. It provides flexibility to manage color output even when CONFIG_LOG_COLORS is turned off.
choice LOG_TIMESTAMP_SOURCE
prompt "Timestamp"
default LOG_TIMESTAMP_SOURCE_RTOS
@@ -33,24 +46,49 @@ menu "Format"
- "System time (YY-MM-DD HH:MM:SS.sss)" it is the same as the above,
but also prints the date as well.
- "Unix time in milliseconds" is the same as the two above,
but in Unix time format and in milliseconds.
e.g. (1718795571035).
- NOTE: Currently this will not get used in logging from binary blobs
(i.e WiFi & Bluetooth libraries), these will always print
milliseconds since boot.
config LOG_TIMESTAMP_SOURCE_NONE
bool "None"
depends on NO_SYMBOL # hide it now, turn it on final MR
depends on LOG_VERSION_2
config LOG_TIMESTAMP_SOURCE_RTOS
bool "Milliseconds Since Boot"
select LOG_TIMESTAMP_SUPPORT if LOG_VERSION_2
config LOG_TIMESTAMP_SOURCE_SYSTEM
bool "System Time (HH:MM:SS.sss)"
select LOG_TIMESTAMP_SUPPORT if LOG_VERSION_2
config LOG_TIMESTAMP_SOURCE_SYSTEM_FULL
bool "System Time (YY-MM-DD HH:MM:SS.sss)"
depends on NO_SYMBOL # hide it now, turn it on final MR
select LOG_TIMESTAMP_SUPPORT if LOG_VERSION_2
depends on LOG_VERSION_2
config LOG_TIMESTAMP_SOURCE_UNIX
bool "Unix time in milliseconds"
select LOG_TIMESTAMP_SUPPORT if LOG_VERSION_2
depends on LOG_VERSION_2
endchoice # LOG_TIMESTAMP_SOURCE
config LOG_TIMESTAMP_SUPPORT
bool "Allow enabling timestamp output at run time"
depends on LOG_VERSION_2
default y
help
Enables support for timestamp in the esp_log() function.
If CONFIG_LOG_TIMESTAMP_SOURCE_NONE, this option allows you to still handle timestamp
in specific files by defining ESP_LOG_TIMESTAMP_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase IRAM usage due to additional timestamp handling
functionality. It provides flexibility to manage timestamp output even when
CONFIG_LOG_TIMESTAMP_SOURCE_NONE.
endmenu

View File

@@ -19,16 +19,25 @@
using namespace std;
#define EARLY_TIMESTAMP "[0-9]*"
#if CONFIG_LOG_TIMESTAMP_SOURCE_RTOS
#define TIMESTAMP "[0-9]*"
#define TIMESTAMP_FORMAT "\\([0-9]*\\) "
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM
#define TIMESTAMP "[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}"
#define TIMESTAMP_FORMAT "\\([0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}\\) "
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL
#define TIMESTAMP "[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}"
#define TIMESTAMP_FORMAT "\\([0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}\\) "
#else
#define TIMESTAMP ""
#define TIMESTAMP_FORMAT ""
#define EARLY_TIMESTAMP ""
#define EARLY_TIMESTAMP_FORMAT ""
#endif
#ifndef EARLY_TIMESTAMP
#define EARLY_TIMESTAMP "[0-9]*"
#define EARLY_TIMESTAMP_FORMAT "\\([0-9]*\\) "
#endif
static const char *TEST_TAG = "test";
@@ -150,7 +159,7 @@ PutcFixture *PutcFixture::instance = nullptr;
TEST_CASE("verbose log level")
{
PrintFixture fix(ESP_LOG_VERBOSE);
const std::regex test_print("V \\(" TIMESTAMP "\\) test: verbose", std::regex::ECMAScript);
const std::regex test_print("V " TIMESTAMP_FORMAT "test: verbose", std::regex::ECMAScript);
ESP_LOGV(TEST_TAG, "verbose");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -159,7 +168,7 @@ TEST_CASE("verbose log level")
TEST_CASE("debug log level")
{
PrintFixture fix(ESP_LOG_DEBUG);
const std::regex test_print("D \\(" TIMESTAMP "\\) test: debug", std::regex::ECMAScript);
const std::regex test_print("D " TIMESTAMP_FORMAT "test: debug", std::regex::ECMAScript);
ESP_LOGD(TEST_TAG, "debug");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -168,7 +177,7 @@ TEST_CASE("debug log level")
TEST_CASE("info log level")
{
PrintFixture fix(ESP_LOG_INFO);
const std::regex test_print("I \\(" TIMESTAMP "\\) test: info", std::regex::ECMAScript);
const std::regex test_print("I " TIMESTAMP_FORMAT "test: info", std::regex::ECMAScript);
ESP_LOGI(TEST_TAG, "info");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -177,7 +186,7 @@ TEST_CASE("info log level")
TEST_CASE("warn log level")
{
PrintFixture fix(ESP_LOG_WARN);
const std::regex test_print("W \\(" TIMESTAMP "\\) test: warn", std::regex::ECMAScript);
const std::regex test_print("W " TIMESTAMP_FORMAT "test: warn", std::regex::ECMAScript);
ESP_LOGW(TEST_TAG, "warn");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -186,7 +195,7 @@ TEST_CASE("warn log level")
TEST_CASE("error log level")
{
PrintFixture fix(ESP_LOG_ERROR);
const std::regex test_print("E \\(" TIMESTAMP "\\) test: error", std::regex::ECMAScript);
const std::regex test_print("E " TIMESTAMP_FORMAT "test: error", std::regex::ECMAScript);
ESP_LOGE(TEST_TAG, "error");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -196,7 +205,7 @@ TEST_CASE("error log level")
TEST_CASE("changing log level")
{
PrintFixture fix(ESP_LOG_INFO);
const std::regex test_print("I \\(" TIMESTAMP "\\) test: must indeed be printed", std::regex::ECMAScript);
const std::regex test_print("I " TIMESTAMP_FORMAT "test: must indeed be printed", std::regex::ECMAScript);
ESP_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -223,7 +232,7 @@ TEST_CASE("log buffer")
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
};
ESP_LOG_BUFFER_HEX(TEST_TAG, buffer, sizeof(buffer));
const std::regex buffer_regex("I \\(" TIMESTAMP "\\) test: 01 02 03 04 05 06 07 08 11 12 13 14 15 16 17 18", std::regex::ECMAScript);
const std::regex buffer_regex("I " TIMESTAMP_FORMAT "test: 01 02 03 04 05 06 07 08 11 12 13 14 15 16 17 18", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex));
}
@@ -234,7 +243,7 @@ TEST_CASE("log bytes > 127")
0xff, 0x80,
};
ESP_LOG_BUFFER_HEX(TEST_TAG, buffer, sizeof(buffer));
const std::regex buffer_regex("I \\(" TIMESTAMP "\\) test: ff 80", std::regex::ECMAScript);
const std::regex buffer_regex("I " TIMESTAMP_FORMAT "test: ff 80", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex));
}
@@ -242,11 +251,11 @@ TEST_CASE("log buffer char")
{
PrintFixture fix(ESP_LOG_INFO);
const char g[] = "The way to get started is to quit talking and begin doing. - Walt Disney";
const std::regex buffer_regex("I \\(" TIMESTAMP "\\) test: The way to get s.*\n\
.*I \\(" TIMESTAMP "\\) test: tarted is to qui.*\n\
.*I \\(" TIMESTAMP "\\) test: t talking and be.*\n\
.*I \\(" TIMESTAMP "\\) test: gin doing. - Wal.*\n\
.*I \\(" TIMESTAMP "\\) test: t Disney", std::regex::ECMAScript);
const std::regex buffer_regex("I " TIMESTAMP_FORMAT "test: The way to get s.*\n\
.*I " TIMESTAMP_FORMAT "test: tarted is to qui.*\n\
.*I " TIMESTAMP_FORMAT "test: t talking and be.*\n\
.*I " TIMESTAMP_FORMAT "test: gin doing. - Wal.*\n\
.*I " TIMESTAMP_FORMAT "test: t Disney", std::regex::ECMAScript);
ESP_LOG_BUFFER_CHAR(TEST_TAG, g, sizeof(g));
CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex) == true);
}
@@ -259,7 +268,7 @@ TEST_CASE("log buffer dump")
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8
};
ESP_LOG_BUFFER_HEXDUMP(TEST_TAG, buffer, sizeof(buffer), ESP_LOG_INFO);
const std::regex buffer_regex("I \\(" TIMESTAMP "\\) test: 0x[0-9a-f]+\\s+"
const std::regex buffer_regex("I " TIMESTAMP_FORMAT "test: 0x[0-9a-f]+\\s+"
"00 00 00 00 05 06 07 08 ff fe fd fc fb fa f9 f8 "
"\\s+|[\\.]{16}|", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex));
@@ -277,7 +286,7 @@ TEST_CASE("rom printf")
TEST_CASE("early verbose log level")
{
PutcFixture fix;
const std::regex test_print("V \\(" EARLY_TIMESTAMP "\\) test: verbose", std::regex::ECMAScript);
const std::regex test_print("V " EARLY_TIMESTAMP_FORMAT "test: verbose", std::regex::ECMAScript);
ESP_EARLY_LOGV(TEST_TAG, "verbose");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -286,7 +295,7 @@ TEST_CASE("early verbose log level")
TEST_CASE("early debug log level")
{
PutcFixture fix;
const std::regex test_print("D \\(" EARLY_TIMESTAMP "\\) test: debug", std::regex::ECMAScript);
const std::regex test_print("D " EARLY_TIMESTAMP_FORMAT "test: debug", std::regex::ECMAScript);
ESP_EARLY_LOGD(TEST_TAG, "debug");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -295,7 +304,7 @@ TEST_CASE("early debug log level")
TEST_CASE("early info log level")
{
PutcFixture fix;
const std::regex test_print("I \\(" EARLY_TIMESTAMP "\\) test: info", std::regex::ECMAScript);
const std::regex test_print("I " EARLY_TIMESTAMP_FORMAT "test: info", std::regex::ECMAScript);
ESP_EARLY_LOGI(TEST_TAG, "info");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -304,7 +313,7 @@ TEST_CASE("early info log level")
TEST_CASE("early warn log level")
{
PutcFixture fix;
const std::regex test_print("W \\(" EARLY_TIMESTAMP "\\) test: warn", std::regex::ECMAScript);
const std::regex test_print("W " EARLY_TIMESTAMP_FORMAT "test: warn", std::regex::ECMAScript);
ESP_EARLY_LOGW(TEST_TAG, "warn");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -313,7 +322,7 @@ TEST_CASE("early warn log level")
TEST_CASE("early error log level")
{
PutcFixture fix;
const std::regex test_print("E \\(" EARLY_TIMESTAMP "\\) test: error", std::regex::ECMAScript);
const std::regex test_print("E " EARLY_TIMESTAMP_FORMAT "test: error", std::regex::ECMAScript);
ESP_EARLY_LOGE(TEST_TAG, "error");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -323,7 +332,7 @@ TEST_CASE("early error log level")
TEST_CASE("changing early log level")
{
PutcFixture fix(ESP_LOG_INFO);
const std::regex test_print("I \\(" EARLY_TIMESTAMP "\\) test: must indeed be printed", std::regex::ECMAScript);
const std::regex test_print("I " EARLY_TIMESTAMP_FORMAT "test: must indeed be printed", std::regex::ECMAScript);
ESP_EARLY_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
@@ -393,15 +402,119 @@ TEST_CASE("esp_log_util_cvt")
TEST_CASE("esp_log_timestamp_str")
{
char buffer[64];
bool critical = true;
uint64_t timestamp_ms = esp_log_timestamp64(critical);
esp_log_timestamp_str(critical, timestamp_ms, buffer);
bool constrained_env = true;
uint64_t timestamp_ms = esp_log_timestamp64(constrained_env);
esp_log_timestamp_str(constrained_env, timestamp_ms, buffer);
const std::regex test_print(EARLY_TIMESTAMP, std::regex::ECMAScript);
CHECK(regex_search(string(buffer), test_print) == true);
critical = false;
timestamp_ms = esp_log_timestamp64(critical);
esp_log_timestamp_str(critical, timestamp_ms, buffer);
constrained_env = false;
timestamp_ms = esp_log_timestamp64(constrained_env);
esp_log_timestamp_str(constrained_env, timestamp_ms, buffer);
const std::regex test_print2(TIMESTAMP, std::regex::ECMAScript);
CHECK(regex_search(string(buffer), test_print2) == true);
}
#if ESP_LOG_VERSION == 2
TEST_CASE("esp_log with formatting")
{
PrintFixture fix(ESP_LOG_INFO);
esp_log_config_t config = {
.opts = {
.log_level = ESP_LOG_NONE,
.constrained_env = false,
.require_formatting = true,
.dis_color = ESP_LOG_COLOR_DISABLED,
.dis_timestamp = ESP_LOG_TIMESTAMP_DISABLED,
.reserved = 0,
}
};
for (int i = 0; i <= 10; i++) {
if (5 <= i && i < 7) {
config.opts.log_level = ESP_LOG_INFO;
} else if (7 <= i && i < 9) {
config.opts.log_level = ESP_LOG_WARN;
} else if (9 <= i) {
config.opts.log_level = ESP_LOG_ERROR;
};
esp_log(config, TEST_TAG, "Temp = %dC", i);
if (5 <= i && i < 7) {
const std::regex test_print("I " TIMESTAMP_FORMAT "test: Temp = [0-9]+C", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
} else if (7 <= i && i < 9) {
const std::regex test_print("W " TIMESTAMP_FORMAT "test: Temp = [0-9]+C", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
} else if (9 <= i) {
const std::regex test_print("E " TIMESTAMP_FORMAT "test: Temp = [0-9]+C", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
} else {
const std::regex test_print("", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
fix.reset_buffer();
}
}
TEST_CASE("esp_log without formatting")
{
PrintFixture fix(ESP_LOG_INFO);
esp_log_config_t config = {
.opts = {
.log_level = ESP_LOG_NONE,
.constrained_env = false,
.require_formatting = false, // print just text
.dis_color = ESP_LOG_COLOR_DISABLED,
.dis_timestamp = ESP_LOG_TIMESTAMP_DISABLED,
.reserved = 0,
}
};
for (int i = 0; i <= 10; i++) {
if (5 <= i && i < 7) {
config.opts.log_level = ESP_LOG_INFO;
} else if (7 <= i && i < 9) {
config.opts.log_level = ESP_LOG_WARN;
} else if (9 <= i) {
config.opts.log_level = ESP_LOG_ERROR;
};
esp_log(config, TEST_TAG, "Temp = %dC\n", i);
if (i >= 5) {
const std::regex test_print("Temp = [0-9]+C", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
} else {
const std::regex test_print("", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
fix.reset_buffer();
}
}
TEST_CASE("esp_log TAG can be NULL")
{
PrintFixture fix(ESP_LOG_INFO);
esp_log_config_t config = {
.opts = {
.log_level = ESP_LOG_ERROR,
.constrained_env = false,
.require_formatting = true,
.dis_color = ESP_LOG_COLOR_DISABLED,
.dis_timestamp = ESP_LOG_TIMESTAMP_DISABLED,
.reserved = 0,
}
};
esp_log(config, NULL, "Temp = %dC", 120);
const std::regex test_print("E " TIMESTAMP_FORMAT "Temp = 120C", std::regex::ECMAScript);
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
fix.reset_buffer();
}
#endif // ESP_LOG_VERSION == 2

View File

@@ -8,7 +8,14 @@ from pytest_embedded import Dut
@pytest.mark.host_test
@pytest.mark.parametrize('config', [
'default',
'system_timestamp'
'v1_color',
'v2_color',
'v2_no_color_no_support',
'v2_no_timestamp',
'v2_no_timestamp_no_support',
'v2_rtos_timestamp',
'v2_system_full_timestamp',
'v2_system_timestamp',
'tag_level_linked_list',
'tag_level_linked_list_and_array_cache',
'tag_level_none',

View File

@@ -0,0 +1,2 @@
CONFIG_LOG_VERSION_1=y
CONFIG_LOG_COLORS=y

View File

@@ -0,0 +1,2 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_COLORS=y

View File

@@ -0,0 +1,3 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_COLORS=n
CONFIG_LOG_COLORS_SUPPORT=n

View File

@@ -0,0 +1,2 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_TIMESTAMP_SOURCE_NONE=y

View File

@@ -0,0 +1,3 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_TIMESTAMP_SOURCE_NONE=n
CONFIG_LOG_TIMESTAMP_SUPPORT=n

View File

@@ -0,0 +1,2 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y

View File

@@ -0,0 +1,2 @@
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL=y

View File

@@ -11,6 +11,7 @@
#include <inttypes.h>
#include "sdkconfig.h"
#include "esp_rom_sys.h"
#include "esp_log_config.h"
#include "esp_log_level.h"
#include "esp_log_color.h"
#include "esp_log_buffer.h"
@@ -22,197 +23,256 @@
extern "C" {
#endif
/**
* @brief Logs a formatted message using the provided log message configs and a variable argument list.
*
* @param config Configuration and level of the log message.
* @param tag The tag string used to indicate the component from which to log.
* It is also used to check whether logging is enabled for that tag (depends on CONFIG_LOG_TAG_LEVEL_IMPL).
* If NULL then the tag is not printed.
* @param format The format string for the log message.
* @param ... Optional arguments to be formatted according to the format string.
*/
void esp_log(esp_log_config_t config, const char* tag, const char* format, ...);
/**
* @brief Logs a formatted message using the provided log message configs and a variable argument list.
*
* @param config Configuration and level of the log message.
* @param tag The tag string used to indicate the component from which to log.
* It is also used to check whether logging is enabled for that tag (depends on CONFIG_LOG_TAG_LEVEL_IMPL).
* If NULL then the tag is not printed.
* @param format The format string for the log message.
* @param args List of arguments.
*/
void esp_log_va(esp_log_config_t config, const char *tag, const char *format, va_list args);
/// macro to output logs in startup code, before heap allocator and syscalls have been initialized.
/// Log at ``ESP_LOG_ERROR`` level. @see ``printf``,``ESP_LOGE``,``ESP_DRAM_LOGE``
/**
* In the future, we want to become compatible with clang.
* Hence, we provide two versions of the following macros which are using variadic arguments.
* The first one is using the GNU extension \#\#__VA_ARGS__. The second one is using the C++20 feature __VA_OPT__(,).
* The first one is using the GNU extension ``##__VA_ARGS__``. The second one is using the C++20 feature ``__VA_OPT__(,)``.
* This allows users to compile their code with standard C++20 enabled instead of the GNU extension.
* Below C++20, we haven't found any good alternative to using \#\#__VA_ARGS__.
* Below C++20, we haven't found any good alternative to using ``##__VA_ARGS__``.
*/
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_EARLY_LOGE( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_ERROR, E __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_WARN`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGW( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_WARN, W __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_INFO`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGI( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_INFO, I __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_DEBUG`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGD( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_DEBUG, D __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_VERBOSE`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGV( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_VERBOSE, V __VA_OPT__(,) __VA_ARGS__)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_EARLY_LOGE( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_ERROR, E, ##__VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_WARN`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGW( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_WARN, W, ##__VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_INFO`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGI( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_INFO, I, ##__VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_DEBUG`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGD( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_DEBUG, D, ##__VA_ARGS__)
/// macro to output logs in startup code at ``ESP_LOG_VERBOSE`` level. @see ``ESP_EARLY_LOGE``,``ESP_LOGE``, ``printf``
#define ESP_EARLY_LOGV( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_VERBOSE, V, ##__VA_ARGS__)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
/** @cond */
#define ESP_EARLY_LOGE(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_ERROR, E __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_EARLY_LOGW(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_WARN, W __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_EARLY_LOGI(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_INFO, I __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_EARLY_LOGD(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_DEBUG, D __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_EARLY_LOGV(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_VERBOSE, V __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOG_EARLY_IMPL(tag, format, log_level, log_tag_letter, ...) do { \
if (_ESP_LOG_EARLY_ENABLED(log_level)) { \
esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag, ##__VA_ARGS__); \
}} while(0)
#if !NON_OS_BUILD
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_LOGE( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, format __VA_OPT__(,) __VA_ARGS__)
#define ESP_LOGW( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, format __VA_OPT__(,) __VA_ARGS__)
#define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format __VA_OPT__(,) __VA_ARGS__)
#define ESP_LOGD( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, tag, format __VA_OPT__(,) __VA_ARGS__)
#define ESP_LOGV( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, tag, format __VA_OPT__(,) __VA_ARGS__)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_LOGE( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#define ESP_LOGW( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, format, ##__VA_ARGS__)
#define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__)
#define ESP_LOGD( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__)
#define ESP_LOGV( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, tag, format, ##__VA_ARGS__)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#if ESP_LOG_VERSION == 1 && NON_OS_BUILD
#define ESP_LOGE(tag, format, ...) do { ESP_EARLY_LOGE(tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGW(tag, format, ...) do { ESP_EARLY_LOGW(tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGI(tag, format, ...) do { ESP_EARLY_LOGI(tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGD(tag, format, ...) do { ESP_EARLY_LOGD(tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGV(tag, format, ...) do { ESP_EARLY_LOGV(tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#else
#define ESP_LOGE(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGW(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGI(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGD(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_LOGV(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, tag, format __VA_OPT__(,) __VA_ARGS__); } while(0)
#endif
#define ESP_DRAM_LOGE(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_ERROR, E __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_DRAM_LOGW(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_WARN, W __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_DRAM_LOGI(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_INFO, I __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_DRAM_LOGD(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_DEBUG, D __VA_OPT__(,) __VA_ARGS__); } while(0)
#define ESP_DRAM_LOGV(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_VERBOSE, V __VA_OPT__(,) __VA_ARGS__); } while(0)
/** @endcond */
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
/**
* @brief Early log macros to output logs in startup code, before heap allocator and syscalls have been initialized.
* The log level can be changed per-tag using ``esp_log_level_set(TAG, level)``.
*/
/// macro to output logs in startup code at ``ESP_LOG_ERROR`` level.
#define ESP_EARLY_LOGE(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_ERROR, E, ##__VA_ARGS__); } while(0)
/// macro to output logs in startup code at ``ESP_LOG_WARN`` level.
#define ESP_EARLY_LOGW(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_WARN, W, ##__VA_ARGS__); } while(0)
/// macro to output logs in startup code at ``ESP_LOG_INFO`` level.
#define ESP_EARLY_LOGI(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_INFO, I, ##__VA_ARGS__); } while(0)
/// macro to output logs in startup code at ``ESP_LOG_DEBUG`` level.
#define ESP_EARLY_LOGD(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_DEBUG, D, ##__VA_ARGS__); } while(0)
/// macro to output logs in startup code at ``ESP_LOG_VERBOSE`` level.
#define ESP_EARLY_LOGV(tag, format, ...) do { ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_VERBOSE, V, ##__VA_ARGS__); } while(0)
/**
* Macro to output logs at ESP_LOG_ERROR level.
*
* @note This macro cannot be used when interrupts are disabled or inside an ISR. @see ``ESP_DRAM_LOGE``.
*
* @param tag tag of the log, which can be used to change the log level by ``esp_log_level_set`` at runtime.
*
* @see ``printf``
* @brief Normal logging macros to output logs.
* The log level can be changed per-tag using ``esp_log_level_set(TAG, level)``.
*/
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_LOGE( tag, format, ... ) ESP_EARLY_LOGE(tag, format __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs at ``ESP_LOG_WARN`` level. @see ``ESP_LOGE``
#define ESP_LOGW( tag, format, ... ) ESP_EARLY_LOGW(tag, format __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs at ``ESP_LOG_INFO`` level. @see ``ESP_LOGE``
#define ESP_LOGI( tag, format, ... ) ESP_EARLY_LOGI(tag, format __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs at ``ESP_LOG_DEBUG`` level. @see ``ESP_LOGE``
#define ESP_LOGD( tag, format, ... ) ESP_EARLY_LOGD(tag, format __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs at ``ESP_LOG_VERBOSE`` level. @see ``ESP_LOGE``
#define ESP_LOGV( tag, format, ... ) ESP_EARLY_LOGV(tag, format __VA_OPT__(,) __VA_ARGS__)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_LOGE( tag, format, ... ) ESP_EARLY_LOGE(tag, format, ##__VA_ARGS__)
/// macro to output logs at ``ESP_LOG_WARN`` level. @see ``ESP_LOGE``
#define ESP_LOGW( tag, format, ... ) ESP_EARLY_LOGW(tag, format, ##__VA_ARGS__)
/// macro to output logs at ``ESP_LOG_INFO`` level. @see ``ESP_LOGE``
#define ESP_LOGI( tag, format, ... ) ESP_EARLY_LOGI(tag, format, ##__VA_ARGS__)
/// macro to output logs at ``ESP_LOG_DEBUG`` level. @see ``ESP_LOGE``
#define ESP_LOGD( tag, format, ... ) ESP_EARLY_LOGD(tag, format, ##__VA_ARGS__)
/// macro to output logs at ``ESP_LOG_VERBOSE`` level. @see ``ESP_LOGE``
#define ESP_LOGV( tag, format, ... ) ESP_EARLY_LOGV(tag, format, ##__VA_ARGS__)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#endif // !NON_OS_BUILD
#if ESP_LOG_VERSION == 1 && NON_OS_BUILD
/// macro to output logs at ``ESP_LOG_ERROR`` level.
#define ESP_LOGE(tag, format, ...) do { ESP_EARLY_LOGE(tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_WARN`` level.
#define ESP_LOGW(tag, format, ...) do { ESP_EARLY_LOGW(tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_INFO`` level.
#define ESP_LOGI(tag, format, ...) do { ESP_EARLY_LOGI(tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_DEBUG`` level.
#define ESP_LOGD(tag, format, ...) do { ESP_EARLY_LOGD(tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_VERBOSE`` level.
#define ESP_LOGV(tag, format, ...) do { ESP_EARLY_LOGV(tag, format, ##__VA_ARGS__); } while(0)
#else
/// macro to output logs at ``ESP_LOG_ERROR`` level.
#define ESP_LOGE(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_WARN`` level.
#define ESP_LOGW(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_INFO`` level.
#define ESP_LOGI(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_DEBUG`` level.
#define ESP_LOGD(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__); } while(0)
/// macro to output logs at ``ESP_LOG_VERBOSE`` level.
#define ESP_LOGV(tag, format, ...) do { ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, tag, format, ##__VA_ARGS__); } while(0)
#endif
/** runtime macro to output logs at a specified level.
/**
* @brief Macros to output logs when the cache is disabled.
* Unlike normal logging macros, it's possible to use this macro when interrupts are disabled or inside an ISR.
* Placing log strings in DRAM reduces available DRAM, so only use when absolutely essential.
*
* Usage: `ESP_DRAM_LOGE(DRAM_STR("my_tag"), "format", ...), or `ESP_DRAM_LOGE(TAG, "format", ...)`,
* where TAG is a char* that points to a str in the DRAM.
*/
/// macro to output logs when the cache is disabled at ``ESP_LOG_ERROR`` level.
#define ESP_DRAM_LOGE(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_ERROR, E, ##__VA_ARGS__); } while(0)
/// macro to output logs when the cache is disabled at ``ESP_LOG_WARN`` level.
#define ESP_DRAM_LOGW(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_WARN, W, ##__VA_ARGS__); } while(0)
/// macro to output logs when the cache is disabled at ``ESP_LOG_INFO`` level.
#define ESP_DRAM_LOGI(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_INFO, I, ##__VA_ARGS__); } while(0)
/// macro to output logs when the cache is disabled at ``ESP_LOG_DEBUG`` level.
#define ESP_DRAM_LOGD(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_DEBUG, D, ##__VA_ARGS__); } while(0)
/// macro to output logs when the cache is disabled at ``ESP_LOG_VERBOSE`` level.
#define ESP_DRAM_LOGV(tag, format, ...) do { ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_VERBOSE, V, ##__VA_ARGS__); } while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
/// runtime macro to output logs at a specified configs. Also check the level with ``LOG_LOCAL_LEVEL``.
#if ESP_LOG_VERSION == 2
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_LOG_LEVEL_LOCAL(configs, tag, format, ...) do { if (ESP_LOG_ENABLED(configs)) { ESP_LOG_LEVEL(configs, tag, format __VA_OPT__(,) __VA_ARGS__); } } while(0)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_LOG_LEVEL_LOCAL(configs, tag, format, ...) do { if (ESP_LOG_ENABLED(configs)) { ESP_LOG_LEVEL(configs, tag, format, ##__VA_ARGS__); } } while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#else // ESP_LOG_VERSION == 1
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_LOG_LEVEL_LOCAL(configs, tag, format, ...) do { if (_ESP_LOG_ENABLED(configs)) { ESP_LOG_LEVEL(configs, tag, format __VA_OPT__(,) __VA_ARGS__); } } while(0)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_LOG_LEVEL_LOCAL(configs, tag, format, ...) do { if (_ESP_LOG_ENABLED(configs)) { ESP_LOG_LEVEL(configs, tag, format, ##__VA_ARGS__); } } while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#endif // ESP_LOG_VERSION == 1
/** runtime macro to output logs at a specified level and with ESP_LOG_CONFIGS_DEFAULT.
*
* @param configs it includes level and other log configurations.
* @param tag tag of the log, which can be used to change the log level by ``esp_log_level_set`` at runtime.
* @param level level of the output log.
* @param format format of the output log. See ``printf``
* @param ... variables to be replaced into the log. See ``printf``
*
* @see ``printf``
*/
#if ESP_LOG_VERSION == 2
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT), tag, format __VA_OPT__(,) __VA_ARGS__); \
} while(0)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT), tag, format, ##__VA_ARGS__); \
} while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#else // ESP_LOG_VERSION == 1
#if defined(__cplusplus) && (__cplusplus > 201703L)
#if CONFIG_LOG_TIMESTAMP_SOURCE_RTOS
#define ESP_LOG_LEVEL(level, tag, format, ...) do { \
if (level==ESP_LOG_ERROR ) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_WARN ) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_DEBUG ) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_VERBOSE ) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else { esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_WARN), tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_DEBUG), tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_VERBOSE), tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_INFO), tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
} while(0)
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM
#define ESP_LOG_LEVEL(level, tag, format, ...) do { \
if (level==ESP_LOG_ERROR ) { esp_log_write(ESP_LOG_ERROR, tag, LOG_SYSTEM_TIME_FORMAT(E, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_WARN ) { esp_log_write(ESP_LOG_WARN, tag, LOG_SYSTEM_TIME_FORMAT(W, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_DEBUG ) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_SYSTEM_TIME_FORMAT(D, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (level==ESP_LOG_VERBOSE ) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_SYSTEM_TIME_FORMAT(V, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else { esp_log_write(ESP_LOG_INFO, tag, LOG_SYSTEM_TIME_FORMAT(I, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), tag, LOG_SYSTEM_TIME_FORMAT(E, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_WARN), tag, LOG_SYSTEM_TIME_FORMAT(W, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_DEBUG), tag, LOG_SYSTEM_TIME_FORMAT(D, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_VERBOSE), tag, LOG_SYSTEM_TIME_FORMAT(V, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
else { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_INFO), tag, LOG_SYSTEM_TIME_FORMAT(I, format), esp_log_system_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
} while(0)
#elif NON_OS_BUILD
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { ESP_EARLY_LOGE(tag, format __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { ESP_EARLY_LOGW(tag, format __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { ESP_EARLY_LOGD(tag, format __VA_OPT__(,) __VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { ESP_EARLY_LOGV(tag, format __VA_OPT__(,) __VA_ARGS__); } \
else { ESP_EARLY_LOGI(tag, format __VA_OPT__(,) __VA_ARGS__); } \
} while(0)
#endif //CONFIG_LOG_TIMESTAMP_SOURCE_xxx
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#if CONFIG_LOG_TIMESTAMP_SOURCE_RTOS
#define ESP_LOG_LEVEL(level, tag, format, ...) do { \
if (level==ESP_LOG_ERROR ) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_WARN ) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_DEBUG ) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_VERBOSE ) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else { esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_WARN), tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_DEBUG), tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_VERBOSE), tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
else { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_INFO), tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
} while(0)
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM
#define ESP_LOG_LEVEL(level, tag, format, ...) do { \
if (level==ESP_LOG_ERROR ) { esp_log_write(ESP_LOG_ERROR, tag, LOG_SYSTEM_TIME_FORMAT(E, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_WARN ) { esp_log_write(ESP_LOG_WARN, tag, LOG_SYSTEM_TIME_FORMAT(W, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_DEBUG ) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_SYSTEM_TIME_FORMAT(D, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (level==ESP_LOG_VERBOSE ) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_SYSTEM_TIME_FORMAT(V, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else { esp_log_write(ESP_LOG_INFO, tag, LOG_SYSTEM_TIME_FORMAT(I, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), tag, LOG_SYSTEM_TIME_FORMAT(E, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_WARN), tag, LOG_SYSTEM_TIME_FORMAT(W, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_DEBUG), tag, LOG_SYSTEM_TIME_FORMAT(D, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_VERBOSE), tag, LOG_SYSTEM_TIME_FORMAT(V, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
else { esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_INFO), tag, LOG_SYSTEM_TIME_FORMAT(I, format), esp_log_system_timestamp(), tag, ##__VA_ARGS__); } \
} while(0)
#elif NON_OS_BUILD
#define ESP_LOG_LEVEL(configs, tag, format, ...) do { \
if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_ERROR) { ESP_EARLY_LOGE(tag, format, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_WARN) { ESP_EARLY_LOGW(tag, format, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_DEBUG) { ESP_EARLY_LOGD(tag, format, ##__VA_ARGS__); } \
else if (ESP_LOG_GET_LEVEL(configs)==ESP_LOG_VERBOSE) { ESP_EARLY_LOGV(tag, format, ##__VA_ARGS__); } \
else { ESP_EARLY_LOGI(tag, format, ##__VA_ARGS__); } \
} while(0)
#endif //CONFIG_LOG_TIMESTAMP_SOURCE_xxx
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
/** runtime macro to output logs at a specified level. Also check the level with ``LOG_LOCAL_LEVEL``.
* If ``CONFIG_LOG_MASTER_LEVEL`` set, also check first against ``esp_log_get_level_master()``.
*
* @see ``printf``, ``ESP_LOG_LEVEL``
*/
#define ESP_LOG_LEVEL_LOCAL(level, tag, format, ...) do { \
if (_ESP_LOG_ENABLED(level)) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \
} while(0)
/**
* @brief Macro to output logs when the cache is disabled. Log at ``ESP_LOG_ERROR`` level.
*
* @note Unlike normal logging macros, it's possible to use this macro when interrupts are
* disabled or inside an ISR.
*
* Similar to @see ``ESP_EARLY_LOGE``, the log level cannot be changed per-tag, however
* esp_log_level_set("*", level) will set the default level which controls these log lines also.
*
* Usage: `ESP_DRAM_LOGE(DRAM_STR("my_tag"), "format", or `ESP_DRAM_LOGE(TAG, "format", ...)`,
* where TAG is a char* that points to a str in the DRAM.
*
* @note Placing log strings in DRAM reduces available DRAM, so only use when absolutely essential.
*
* @see ``esp_rom_printf``,``ESP_LOGE``
*/
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_DRAM_LOGE( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_ERROR, E __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_WARN`` level. @see ``ESP_DRAM_LOGW``,``ESP_LOGW``, ``esp_rom_printf``
#define ESP_DRAM_LOGW( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_WARN, W __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_INFO`` level. @see ``ESP_DRAM_LOGI``,``ESP_LOGI``, ``esp_rom_printf``
#define ESP_DRAM_LOGI( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_INFO, I __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_DEBUG`` level. @see ``ESP_DRAM_LOGD``,``ESP_LOGD``, ``esp_rom_printf``
#define ESP_DRAM_LOGD( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_DEBUG, D __VA_OPT__(,) __VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_VERBOSE`` level. @see ``ESP_DRAM_LOGV``,``ESP_LOGV``, ``esp_rom_printf``
#define ESP_DRAM_LOGV( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_VERBOSE, V __VA_OPT__(,) __VA_ARGS__)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_DRAM_LOGE( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_ERROR, E, ##__VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_WARN`` level. @see ``ESP_DRAM_LOGW``,``ESP_LOGW``, ``esp_rom_printf``
#define ESP_DRAM_LOGW( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_WARN, W, ##__VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_INFO`` level. @see ``ESP_DRAM_LOGI``,``ESP_LOGI``, ``esp_rom_printf``
#define ESP_DRAM_LOGI( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_INFO, I, ##__VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_DEBUG`` level. @see ``ESP_DRAM_LOGD``,``ESP_LOGD``, ``esp_rom_printf``
#define ESP_DRAM_LOGD( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_DEBUG, D, ##__VA_ARGS__)
/// macro to output logs when the cache is disabled at ``ESP_LOG_VERBOSE`` level. @see ``ESP_DRAM_LOGV``,``ESP_LOGV``, ``esp_rom_printf``
#define ESP_DRAM_LOGV( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_VERBOSE, V, ##__VA_ARGS__)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#endif // ESP_LOG_VERSION == 1
/** @cond */
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_DRAM_LOG_IMPL(tag, format, log_level, log_tag_letter, ...) do { \
if (_ESP_LOG_EARLY_ENABLED(log_level)) { \
esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag __VA_OPT__(,) __VA_ARGS__); \
}} while(0)
#define ESP_LOG_EARLY_IMPL(tag, format, configs, log_tag_letter, ...) do { \
if (ESP_LOG_VERSION == 1) { \
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
} else { \
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV), tag, format __VA_OPT__(,) __VA_ARGS__); } \
} } while(0)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_DRAM_LOG_IMPL(tag, format, log_level, log_tag_letter, ...) do { \
if (_ESP_LOG_EARLY_ENABLED(log_level)) { \
esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag, ##__VA_ARGS__); \
}} while(0)
#define ESP_LOG_EARLY_IMPL(tag, format, configs, log_tag_letter, ...) do { \
if (ESP_LOG_VERSION == 1) { \
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
} else { \
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV), tag, format, ##__VA_ARGS__); } \
} } while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define ESP_DRAM_LOG_IMPL(tag, format, configs, log_tag_letter, ...) do { \
if (ESP_LOG_VERSION == 1) { \
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag __VA_OPT__(,) __VA_ARGS__); } \
} else { \
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV | ESP_LOG_CONFIG_DIS_COLOR | ESP_LOG_CONFIG_DIS_TIMESTAMP), tag, DRAM_STR(format) __VA_OPT__(,) __VA_ARGS__); } \
} } while(0)
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
#define ESP_DRAM_LOG_IMPL(tag, format, configs, log_tag_letter, ...) do { \
if (ESP_LOG_VERSION == 1) { \
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag, ##__VA_ARGS__); } \
} else { \
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT(configs | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV | ESP_LOG_CONFIG_DIS_COLOR | ESP_LOG_CONFIG_DIS_TIMESTAMP), tag, DRAM_STR(format), ##__VA_ARGS__); } \
} } while(0)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
/** @endcond */

View File

@@ -13,8 +13,6 @@
extern "C" {
#endif
#if !NON_OS_BUILD || __DOXYGEN__
/**
* @brief Logs a buffer of hexadecimal bytes at the specified log level.
*
@@ -173,8 +171,6 @@ static inline void esp_log_buffer_char(const char *tag, const void *buffer, uint
}
/** @endcond */
#endif // !NON_OS_BUILD || __DOXYGEN__
#ifdef __cplusplus
}
#endif

View File

@@ -6,6 +6,7 @@
#pragma once
#include "esp_log_config.h"
#include "sdkconfig.h"
#ifdef __cplusplus
@@ -13,6 +14,13 @@ extern "C" {
#endif
/** @cond */
// Determines whether esp_log() includes code to handle color codes.
#if (!BOOTLOADER_BUILD && CONFIG_LOG_COLORS_SUPPORT) || (BOOTLOADER_BUILD && CONFIG_BOOTLOADER_LOG_COLORS_SUPPORT)
#define ESP_LOG_SUPPORT_COLOR (1)
#else
#define ESP_LOG_SUPPORT_COLOR (0)
#endif
// ANSI Color Codes:
// Macros for defining foreground colors (text).
#define LOG_ANSI_COLOR_BLACK "30"
@@ -77,7 +85,7 @@ extern "C" {
* printf(LOG_ANSI_COLOR_FORMAT(LOG_ANSI_COLOR_STYLE_BOLD, LOG_ANSI_COLOR_WHITE, LOG_ANSI_COLOR_BG_BLUE) "%s" LOG_ANSI_COLOR_RESET "\n", text_str);
*/
#if (!BOOTLOADER_BUILD && CONFIG_LOG_COLORS) || (BOOTLOADER_BUILD && CONFIG_BOOTLOADER_LOG_COLORS)
#if !ESP_LOG_COLOR_DISABLED
#define LOG_COLOR_BLACK LOG_ANSI_COLOR_BLACK
#define LOG_COLOR_RED LOG_ANSI_COLOR_RED
#define LOG_COLOR_GREEN LOG_ANSI_COLOR_GREEN

View File

@@ -0,0 +1,156 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include "esp_log_level.h"
#include "esp_assert.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @cond */
/// Log version macros.
#define ESP_LOG_V2 (1)
#if BOOTLOADER_BUILD
#define ESP_LOG_VERSION (CONFIG_BOOTLOADER_LOG_VERSION)
#else
#define ESP_LOG_VERSION (CONFIG_LOG_VERSION)
#endif
/**
* This define controls the log configuration options, particularly when the built project operates in constrained environments.
*
* For the bootloader build:
* It is always set to "1" because certain log features are unnecessary, reducing the binary size.
*
* For the application build:
* If this define is set in the user namespace, all logs in the specific file will use a simplified path for logging.
*
* This define determines whether the "constrained_env" flag will be enabled in ESP_LOG_CONFIGS_DEFAULT, indicating that logging is capable of functioning
* in constrained environments such as the bootloader, ISR, startup code, early log, or when the cache is disabled. In these cases,
* `esp_log()` utilizes a simplified implementation to output logs.
*/
#if BOOTLOADER_BUILD
#define ESP_LOG_CONSTRAINED_ENV (1)
#else // !BOOTLOADER_BUILD
#ifndef ESP_LOG_CONSTRAINED_ENV
#define ESP_LOG_CONSTRAINED_ENV (0)
#endif
#endif // !BOOTLOADER_BUILD
/**
* This define helps control log configuration options, specifically whether color output will be appended to log messages.
* If this define is not set in the user namespace, its value will be determined by Kconfig options.
* If the define is set in the user namespace, it will override the default configurations (ESP_LOG_CONFIGS_DEFAULT) for the user's specific file.
* This define controls the "dis_color" flag, which is included in ESP_LOG_CONFIGS_DEFAULT
*/
#ifndef ESP_LOG_COLOR_DISABLED
#if (!BOOTLOADER_BUILD && CONFIG_LOG_COLORS) || (BOOTLOADER_BUILD && CONFIG_BOOTLOADER_LOG_COLORS)
#define ESP_LOG_COLOR_DISABLED (0)
#else
#define ESP_LOG_COLOR_DISABLED (1)
#endif
#endif
/**
* This define helps control log configuration options, specifically whether timestamp will be appended to log message.
* If this define is not set in the user namespace, its value will be determined by Kconfig options.
* If the define is set in the user namespace, it will override the default configurations (ESP_LOG_CONFIGS_DEFAULT) for the user's specific file.
* This define controls the "dis_timestamp" flag, which is included in ESP_LOG_CONFIGS_DEFAULT
*/
#ifndef ESP_LOG_TIMESTAMP_DISABLED
#if (!BOOTLOADER_BUILD && CONFIG_LOG_TIMESTAMP_SOURCE_NONE) || (BOOTLOADER_BUILD && CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_NONE)
#define ESP_LOG_TIMESTAMP_DISABLED (1)
#else
#define ESP_LOG_TIMESTAMP_DISABLED (0)
#endif
#endif
/**
* This define helps control log configuration options, specifically whether formatting (ex.: color, timestamp) will be appended to log message.
* If the define is set in the user namespace, it will override the default configurations (ESP_LOG_CONFIGS_DEFAULT) for the user's specific file.
* This define controls the "require_formatting" flag, which is included in ESP_LOG_CONFIGS_DEFAULT
*/
#ifndef ESP_LOG_FORMATTING_DISABLED
#define ESP_LOG_FORMATTING_DISABLED (0)
#endif
/** @endcond */
/**
* @brief Logging configuration structure for ESP log.
*/
typedef struct {
union {
struct {
esp_log_level_t log_level: ESP_LOG_LEVEL_LEN; /*!< Log level */
uint32_t constrained_env: 1; /*!< Flag indicating if logging is from a constrained environment (e.g., bootloader, ISR, startup code, early log, or when the cache is disabled). In such cases, esp_rom_vprintf is used instead of the vprintf. */
uint32_t require_formatting: 1; /*!< Flag specifying whether the log message needs additional formatting. If set, esp_log() will add formatting elements like color, timestamp, and tag to the log message. */
uint32_t dis_color: 1; /*!< Flag to disable color in log output. If set, log messages will not include color codes. */
uint32_t dis_timestamp: 1; /*!< Flag to disable timestamps in log output. If set, log messages will not include timestamps. */
uint32_t reserved: 25; /*!< Reserved for future use. Should be initialized to 0. */
} opts;
uint32_t data; /*!< Raw data representing all options in a 32-bit word. */
};
} esp_log_config_t;
/** @cond */
#define ESP_LOG_OFFSET_CONSTRAINED_ENV (ESP_LOG_LEVEL_LEN) /*!< Offset for constrained_env field from esp_log_config_t */
#define ESP_LOG_OFFSET_REQUIRE_FORMATTING (4) /*!< Offset for require_formatting field from esp_log_config_t */
#define ESP_LOG_OFFSET_DIS_COLOR_OFFSET (5) /*!< Offset for dis_color field from esp_log_config_t */
#define ESP_LOG_OFFSET_DIS_TIMESTAMP (6) /*!< Offset for dis_timestamp field from esp_log_config_t */
ESP_STATIC_ASSERT(ESP_LOG_OFFSET_CONSTRAINED_ENV == ESP_LOG_LEVEL_LEN, "The log level should not overlap the following fields in esp_log_config_t");
/** @endcond */
#define ESP_LOG_CONFIG_LEVEL_MASK ((1 << ESP_LOG_LEVEL_LEN) - 1) /*!< Mask for level field in esp_log_config_t */
#define ESP_LOG_CONFIG_CONSTRAINED_ENV (1 << ESP_LOG_OFFSET_CONSTRAINED_ENV) /*!< Value for constrained_env field in esp_log_config_t */
#define ESP_LOG_CONFIG_REQUIRE_FORMATTING (1 << ESP_LOG_OFFSET_REQUIRE_FORMATTING) /*!< Value for require_formatting field in esp_log_config_t */
#define ESP_LOG_CONFIG_DIS_COLOR (1 << ESP_LOG_OFFSET_DIS_COLOR_OFFSET) /*!< Value for dis_color field in esp_log_config_t */
#define ESP_LOG_CONFIG_DIS_TIMESTAMP (1 << ESP_LOG_OFFSET_DIS_TIMESTAMP) /*!< Value for dis_timestamp field in esp_log_config_t */
/**
* @brief Macro for setting log configurations according to selected Kconfig options.
*
* @note The `require_formatting` flag is always set for logv2.
*/
#ifndef ESP_LOG_CONFIGS_DEFAULT
#define ESP_LOG_CONFIGS_DEFAULT ( \
((ESP_LOG_CONSTRAINED_ENV) ? (ESP_LOG_CONFIG_CONSTRAINED_ENV) : 0) \
| ((ESP_LOG_FORMATTING_DISABLED) ? (0) : (ESP_LOG_CONFIG_REQUIRE_FORMATTING)) \
| ((ESP_LOG_COLOR_DISABLED) ? (ESP_LOG_CONFIG_DIS_COLOR) : 0) \
| ((ESP_LOG_TIMESTAMP_DISABLED) ? (ESP_LOG_CONFIG_DIS_TIMESTAMP) : 0))
#endif
/**
* @brief Macro to initialize an `esp_log_config_t` structure.
*
* This macro directly initializes an `esp_log_config_t` structure by setting the raw `data` field
* with the provided `configs` parameter. It is useful when you have already defined the configuration
* settings and wish to apply them directly.
*
* @param configs The raw configuration data to initialize the `esp_log_config_t` structure.
*
* @return An initialized `esp_log_config_t` structure containing the specified configuration data.
*/
#if defined(__cplusplus)
#define ESP_LOG_CONFIG_INIT(configs) (__extension__({ \
esp_log_config_t __esp_log_config; \
__esp_log_config.data = (configs); \
__esp_log_config;}))
#else
#define ESP_LOG_CONFIG_INIT(configs) ((esp_log_config_t){.data = (configs)})
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -29,11 +29,25 @@ typedef enum {
#define ESP_LOG_LEVEL_LEN (3) /*!< Number of bits used to represent the log level */
#define ESP_LOG_LEVEL_MASK ((1 << ESP_LOG_LEVEL_LEN) - 1) /*!< Mask for log level */
/// Returns level from config
#define ESP_LOG_GET_LEVEL(config) ((config) & ESP_LOG_LEVEL_MASK)
/** @cond */
ESP_STATIC_ASSERT(ESP_LOG_MAX <= ESP_LOG_LEVEL_MASK, "Log level items of esp_log_level_t must fit ESP_LOG_LEVEL_MASK");
// LOG_LOCAL_LEVEL controls what log levels are included in the binary.
/**
* @brief Controls the log levels included in the binary.
*
* This define determines the maximum log level that will be compiled into the binary for the current build configuration.
* It adjusts the verbosity of logging by specifying which log levels are included or excluded.
*
* If this define is not explicitly set in the user namespace, its value is derived from Kconfig options:
* - `CONFIG_BOOTLOADER_LOG_LEVEL` for bootloader builds.
* - `CONFIG_SECURE_TEE_LOG_LEVEL` for Secure TEE builds.
* - `CONFIG_LOG_MAXIMUM_LEVEL` for all other builds.
*
* If explicitly defined in the user's code, it overrides these default values, allowing fine-grained control over log verbosity.
*/
#ifndef LOG_LOCAL_LEVEL
#if BOOTLOADER_BUILD
#define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL
@@ -43,6 +57,7 @@ ESP_STATIC_ASSERT(ESP_LOG_MAX <= ESP_LOG_LEVEL_MASK, "Log level items of esp_log
#define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL
#endif
#endif // LOG_LOCAL_LEVEL
/** @endcond */
/**
* @brief Check if a specific log level is enabled at compile-time.
@@ -52,27 +67,28 @@ ESP_STATIC_ASSERT(ESP_LOG_MAX <= ESP_LOG_LEVEL_MASK, "Log level items of esp_log
* determine if logging for the specified level should be included in the binary,
* helping to exclude logs that are not configured.
*
* @param level log level.
* @param configs it includes log configs and level.
* @return true if the specified log level is enabled, false otherwise.
*/
#define ESP_LOG_ENABLED(level) (LOG_LOCAL_LEVEL >= (level))
#define ESP_LOG_ENABLED(configs) (LOG_LOCAL_LEVEL >= ESP_LOG_GET_LEVEL(configs))
/** @cond */
#if NON_OS_BUILD
#define _ESP_LOG_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level))
#define _ESP_LOG_EARLY_ENABLED(log_level) _ESP_LOG_ENABLED(log_level)
#define _ESP_LOG_ENABLED(log_level) ESP_LOG_ENABLED(log_level)
#define _ESP_LOG_EARLY_ENABLED(log_level) ESP_LOG_ENABLED(log_level)
#else // !NON_OS_BUILD
#if CONFIG_LOG_MASTER_LEVEL
#define _ESP_LOG_ENABLED(log_level) (esp_log_get_level_master() >= (log_level) && LOG_LOCAL_LEVEL >= (log_level))
#define _ESP_LOG_ENABLED(log_level) (esp_log_get_level_master() >= ESP_LOG_GET_LEVEL(log_level) && ESP_LOG_ENABLED(log_level))
#else // !CONFIG_LOG_MASTER_LEVEL
#define _ESP_LOG_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level))
#define _ESP_LOG_ENABLED(log_level) ESP_LOG_ENABLED(log_level)
#endif // !CONFIG_LOG_MASTER_LEVEL
/* For early log, there is no log tag filtering. So we want to log only if both the LOG_LOCAL_LEVEL and the
currently configured min log level are higher than the log level */
#define _ESP_LOG_EARLY_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level) && esp_log_get_default_level() >= (log_level))
#define _ESP_LOG_EARLY_ENABLED(log_level) (ESP_LOG_ENABLED(log_level) && esp_log_get_default_level() >= ESP_LOG_GET_LEVEL(log_level))
#endif // !NON_OS_BUILD
@@ -91,7 +107,11 @@ currently configured min log level are higher than the log level */
__attribute__((always_inline))
static inline esp_log_level_t esp_log_get_default_level(void)
{
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
#if BOOTLOADER_BUILD
return (esp_log_level_t) CONFIG_BOOTLOADER_LOG_LEVEL;
#elif ESP_TEE_BUILD
return (esp_log_level_t) CONFIG_SECURE_TEE_LOG_LEVEL;
#elif CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
extern esp_log_level_t esp_log_default_level;
return esp_log_default_level;
#else
@@ -104,14 +124,13 @@ static inline esp_log_level_t esp_log_get_default_level(void)
/**
* @brief Master log level.
*
* Optional master log level to check against for ESP_LOGx macros before calling
* esp_log_write. Allows one to set a higher CONFIG_LOG_MAXIMUM_LEVEL but not
* Allows one to set a higher CONFIG_LOG_MAXIMUM_LEVEL but not
* impose a performance hit during normal operation (only when instructed). An
* application may set esp_log_set_level_master(level) to globally enforce a
* maximum log level. ESP_LOGx macros above this level will be skipped immediately,
* rather than calling esp_log or esp_log_write and doing a cache hit.
* maximum log level. ESP_LOG macros above this level will be skipped,
* rather than doing a tag lookup.
*
* @note The tradeoff is increased application size.
* The Master log level is not applicable for the bootloader.
*
* @param level Master log level
*/
@@ -119,6 +138,7 @@ void esp_log_set_level_master(esp_log_level_t level);
/**
* @brief Returns master log level.
* The Master log level is not applicable for the bootloader.
* @return Master log level
*/
esp_log_level_t esp_log_get_level_master(void);

View File

@@ -7,11 +7,22 @@
#pragma once
#include <stdint.h>
#include "esp_log_config.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @cond */
// Determines whether esp_log() includes code to handle timestamp.
#if (!BOOTLOADER_BUILD && CONFIG_LOG_TIMESTAMP_SUPPORT) || (BOOTLOADER_BUILD && CONFIG_BOOTLOADER_LOG_TIMESTAMP_SUPPORT)
#define ESP_LOG_SUPPORT_TIMESTAMP (1)
#else
#define ESP_LOG_SUPPORT_TIMESTAMP (0)
#endif
/** @endcond */
/**
* @brief Function which returns timestamp to be used in log output
*

View File

@@ -12,6 +12,16 @@
extern "C" {
#endif
/**
* @brief Checks if a log message with the given log level and tag should be logged.
*
* @param level The log level of the message.
* @param tag The tag associated with the message.
* @return true if the log message can be logged,
* false otherwise.
*/
bool esp_log_is_tag_loggable(esp_log_level_t level, const char *tag);
/**
* @brief Set the default log level.
*

View File

@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#include "esp_log_config.h"
#include "esp_log_write.h"
#include "esp_rom_sys.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Outputs a formatted log message.
*
* @note It internally calls a vprintf function that was set by esp_log_set_vprintf().
* @see esp_log_set_vprintf
*
* @param config The config log
* @param format The format string for the log message.
* @param args The variable argument list containing the values to be formatted.
*/
__attribute__((always_inline)) static inline void esp_log_vprintf(esp_log_config_t config, const char *format, va_list args)
{
#if BOOTLOADER_BUILD
esp_rom_vprintf(format, args);
#else // APP
extern vprintf_like_t esp_log_vprint_func;
#if ESP_LOG_VERSION == 2
vprintf_like_t vprint_func[2] = {
esp_log_vprint_func,
esp_rom_vprintf,
};
vprint_func[config.opts.constrained_env](format, args);
#else // ESP_LOG_VERSION == 1
esp_log_vprint_func(format, args);
#endif // ESP_LOG_VERSION == 1
#endif // APP
}
/**
* @brief Outputs a formatted log message.
*
* @note It internally calls esp_log_vprintf().
* @see esp_log_vprintf
*
* @param config The config log
* @param format The format string for the log message.
* @param ... Optional arguments to be formatted according to the format string.
*/
void esp_log_printf(esp_log_config_t config, const char *format, ...) __attribute__((format(printf, 2, 3)));
#ifdef __cplusplus
}
#endif

View File

@@ -18,18 +18,18 @@ extern "C" {
*
* This function retrieves the timestamp in milliseconds for logging purposes.
*
* @param[in] critical Flag indicating whether the timestamp is needed for a critical log.
* @param[in] constrained_env Flag indicating whether the timestamp is needed for a log from a constrained environment.
* If this flag is true, it means the function is called in one of the following states:
* - early stage, when the Freertos scheduler is not running,
* - ISR,
* - CACHE is disabled.
*
* If the critical flag is set then uint32 timestamp is returned due to cpu ticks being used for this case.
* If the constrained_env flag is set then uint32 timestamp is returned due to cpu ticks being used for this case.
* For some applications this uint32 timestamp may overflow after 4294967295/1000/86400 = 49 days of operation.
*
* @return The uint64 timestamp in milliseconds.
*/
uint64_t esp_log_timestamp64(bool critical);
uint64_t esp_log_timestamp64(bool constrained_env);
/**
* @brief Convert the uint64 timestamp to a string representation.
@@ -40,8 +40,8 @@ uint64_t esp_log_timestamp64(bool critical);
* - Date and time,
* - Time.
*
* @param[in] critical Flag indicating whether the timestamp is critical. If this flag is true,
* it means the function is called in one of the following states:
* @param[in] constrained_env Flag indicating whether the timestamp is needed for a log from a constrained environment.
* If this flag is true, it means the function is called in one of the following states:
* - early stage, when the Freertos scheduler is not running,
* - ISR,
* - CACHE is disabled.
@@ -50,7 +50,7 @@ uint64_t esp_log_timestamp64(bool critical);
*
* @return Pointer to the buffer containing the string representation of the timestamp.
*/
char* esp_log_timestamp_str(bool critical, uint64_t timestamp_ms, char* buffer);
char* esp_log_timestamp_str(bool constrained_env, uint64_t timestamp_ms, char* buffer);
#ifdef __cplusplus
}

View File

@@ -6,6 +6,8 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -64,6 +66,45 @@ int esp_log_util_cvt_hex(unsigned long long val, int pad, char *buf);
*/
int esp_log_util_cvt_dec(unsigned long long val, int pad, char *buf);
/**
* @typedef esp_log_cache_enabled_t
* @brief Callback function type for checking the state of the SPI flash cache.
*
* This function pointer is used to determine whether the SPI flash cache is enabled
* during logging operations.
*
* @return
* - true if the SPI flash cache is enabled.
* - false if the SPI flash cache is disabled.
*/
typedef bool (*esp_log_cache_enabled_t)(void);
/**
* @brief Sets the callback function to check the SPI flash cache state.
*
* This function allows setting a custom callback to check whether the SPI flash
* cache is enabled. If a callback is provided, it will be used during logging
* operations to ensure that logging does not interfere with cache-disabled scenarios.
*
* @note This function must be called during system startup to initialize it.
*
* @param[in] func Pointer to the callback function of type `esp_log_cache_enabled_t`.
* Pass `NULL` to disable the cache check.
*/
void esp_log_util_set_cache_enabled_cb(esp_log_cache_enabled_t func);
/**
* @brief Check if the current context is constrained.
*
* This function checks if logging in the current context is doing from:
* - ISR context.
* - Disabled SPI flash cache.
* - Task scheduler not running.
*
* @return true if the context is constrained, false otherwise.
*/
bool esp_log_util_is_constrained(void);
#ifdef __cplusplus
}
#endif

View File

@@ -7,3 +7,9 @@ entries:
log_timestamp:esp_log_timestamp (noflash)
log_timestamp:esp_log_early_timestamp (noflash)
log_lock (noflash)
util (noflash)
log_timestamp_common (noflash)
log_print (noflash)
log (noflash)
if LOG_MASTER_LEVEL = y:
log_level: esp_log_get_level_master (noflash)

View File

@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
bool esp_log_util_is_constrained(void)
{
return false;
}

123
components/log/src/log.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include "esp_log_config.h"
#include "esp_log_level.h"
#include "esp_log_color.h"
#include "esp_log_timestamp.h"
#include "esp_private/log_level.h"
#include "esp_private/log_timestamp.h"
#include "esp_private/log_lock.h"
#include "esp_private/log_util.h"
#include "esp_private/log_print.h"
#include "sdkconfig.h"
static __attribute__((unused)) const char s_lvl_name[ESP_LOG_MAX] = {
'\0', // NONE
'E', // ERROR
'W', // WARNING
'I', // INFO
'D', // DEBUG
'V', // VERBOSE
};
static __attribute__((unused)) const char s_lvl_color[ESP_LOG_MAX][8] = {
"\0", // NONE
LOG_ANSI_COLOR_REGULAR(LOG_ANSI_COLOR_RED)"\0", // ERROR
LOG_ANSI_COLOR_REGULAR(LOG_ANSI_COLOR_YELLOW)"\0", // WARNING
LOG_ANSI_COLOR_REGULAR(LOG_ANSI_COLOR_GREEN)"\0", // INFO
"\0", // DEBUG
"\0", // VERBOSE
};
static __attribute__((unused)) bool is_level_loggable(esp_log_config_t config)
{
#if BOOTLOADER_BUILD
return (config.opts.log_level != ESP_LOG_NONE) && (esp_log_get_default_level() >= config.opts.log_level);
#else
return (config.opts.log_level != ESP_LOG_NONE)
#if CONFIG_LOG_MASTER_LEVEL
&& (esp_log_get_level_master() >= config.opts.log_level)
#endif // CONFIG_LOG_MASTER_LEVEL
&& ((config.opts.constrained_env) ? (esp_log_get_default_level() >= config.opts.log_level) : true);
#endif
}
void __attribute__((optimize("-O3"))) esp_log_va(esp_log_config_t config, const char *tag, const char *format, va_list args)
{
#if ESP_LOG_VERSION == 1
if (config.opts.log_level != ESP_LOG_NONE && esp_log_is_tag_loggable(config.opts.log_level, tag)) {
extern vprintf_like_t esp_log_vprint_func;
esp_log_vprint_func(format, args);
}
#else // ESP_LOG_VERSION == 2
if (is_level_loggable(config)) {
__attribute__((unused)) uint64_t timestamp = 0;
config.opts.dis_timestamp |= !ESP_LOG_SUPPORT_TIMESTAMP;
config.opts.constrained_env = ESP_LOG_CONSTRAINED_ENV || config.opts.constrained_env || esp_log_util_is_constrained();
if (!config.opts.dis_timestamp && ((!ESP_LOG_CONSTRAINED_ENV && config.opts.require_formatting) || ESP_LOG_CONSTRAINED_ENV)) {
timestamp = esp_log_timestamp64(config.opts.constrained_env);
}
#if !ESP_LOG_CONSTRAINED_ENV
if (!config.opts.constrained_env && tag != NULL && !esp_log_is_tag_loggable(config.opts.log_level, tag)) {
return;
}
#endif
// formatting log
if (config.opts.require_formatting) { // 1. print "<color_start><level_name> <(time)> <tag>: "
#if !ESP_LOG_CONSTRAINED_ENV
if (!config.opts.constrained_env) {
// flockfile&funlockfile are used here to prevent other threads
// from writing to the same stream simultaneously using printf-like functions.
// Below is formatting log, there are multiple calls to vprintf to log a single message.
flockfile(stdout);
}
#endif
config.opts.dis_color = !ESP_LOG_SUPPORT_COLOR || config.opts.dis_color || (s_lvl_color[config.opts.log_level][0] == '\0');
char timestamp_buffer[32] = { 0 };
if (!config.opts.dis_timestamp) {
esp_log_timestamp_str(config.opts.constrained_env, timestamp, timestamp_buffer);
}
esp_log_printf(config, "%s%c %s%s%s%s%s",
(!config.opts.dis_color) ? s_lvl_color[config.opts.log_level] : "",
s_lvl_name[config.opts.log_level],
(!config.opts.dis_timestamp) ? "(" : "",
timestamp_buffer,
(!config.opts.dis_timestamp) ? ") " : "",
(tag) ? tag : "",
(tag) ? ": " : "");
}
esp_log_vprintf(config, format, args); // 2. print user message
if (config.opts.require_formatting) { // 3. print "<color_end><\n>"
esp_log_printf(config, "%s", (config.opts.dis_color) ? "\n" : LOG_RESET_COLOR"\n");
#if !ESP_LOG_CONSTRAINED_ENV
if (!config.opts.constrained_env) {
funlockfile(stdout);
}
#endif
}
}
#endif // ESP_LOG_VERSION == 2
}
void __attribute__((optimize("-O3"))) esp_log(esp_log_config_t config, const char* tag, const char* format, ...)
{
#if ESP_LOG_VERSION == 1
if (1) {
#else // ESP_LOG_VERSION == 2
if (is_level_loggable(config)) {
#endif
va_list args;
va_start(args, format);
esp_log_va(config, tag, format, args);
va_end(args);
}
}

View File

@@ -6,7 +6,7 @@
#include <stdbool.h>
#include <stddef.h>
#include "esp_log_level.h"
#include "esp_private/log_level.h"
#include "esp_attr.h"
#include "sdkconfig.h"
@@ -38,3 +38,18 @@ void esp_log_set_level_master(esp_log_level_t level)
s_master_log_level = level;
}
#endif // CONFIG_LOG_MASTER_LEVEL
bool esp_log_is_tag_loggable(esp_log_level_t level, const char *tag)
{
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
(void)tag;
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
return (esp_log_default_level >= level);
#else
(void)level;
return true;
#endif
#else // !CONFIG_LOG_TAG_LEVEL_IMPL_NONE
return esp_log_level_get_timeout(tag) >= level && level > ESP_LOG_NONE;
#endif // !CONFIG_LOG_TAG_LEVEL_IMPL_NONE
}

View File

@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdio.h>
#include "esp_private/log_print.h"
#include "esp_log_config.h"
#include "sdkconfig.h"
void esp_log_printf(esp_log_config_t config, const char *format, ...)
{
va_list args;
va_start(args, format);
esp_log_vprintf(config, format, args);
va_end(args);
}

View File

@@ -7,6 +7,7 @@
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include "esp_log_config.h"
#include "esp_log_timestamp.h"
#include "esp_private/log_util.h"
#include "esp_private/log_timestamp.h"
@@ -62,14 +63,14 @@ char *esp_log_system_timestamp(void)
}
#endif // !NON_OS_BUILD
uint64_t esp_log_timestamp64(bool critical)
uint64_t esp_log_timestamp64(bool constrained_env)
{
uint64_t timestamp_ms;
#if CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_NONE || CONFIG_LOG_TIMESTAMP_SOURCE_NONE
(void) critical;
#if ESP_LOG_TIMESTAMP_DISABLED
(void) constrained_env;
timestamp_ms = 0;
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM || CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL
if (critical) {
#elif !BOOTLOADER_BUILD && (CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM || CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL || CONFIG_LOG_TIMESTAMP_SOURCE_UNIX)
if (constrained_env) {
timestamp_ms = esp_log_early_timestamp();
} else {
#if CONFIG_IDF_TARGET_LINUX
@@ -84,20 +85,20 @@ uint64_t esp_log_timestamp64(bool critical)
#endif
}
#else
(void) critical;
(void) constrained_env;
timestamp_ms = esp_log_timestamp();
#endif
return timestamp_ms;
}
char* esp_log_timestamp_str(bool critical, uint64_t timestamp_ms, char* buffer)
char* esp_log_timestamp_str(bool constrained_env, uint64_t timestamp_ms, char* buffer)
{
char* out_buffer = buffer;
#if CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_NONE || CONFIG_LOG_TIMESTAMP_SOURCE_NONE
(void)critical;
#if ESP_LOG_TIMESTAMP_DISABLED
(void)constrained_env;
*buffer = '\0';
#elif CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM || CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL
if (critical) {
#elif !BOOTLOADER_BUILD && (CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM || CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM_FULL)
if (constrained_env) {
esp_log_util_cvt_dec(timestamp_ms, 0, buffer);
} else {
struct tm timeinfo;
@@ -122,7 +123,7 @@ char* esp_log_timestamp_str(bool critical, uint64_t timestamp_ms, char* buffer)
esp_log_util_cvt_dec(msec, 3, buffer); // (ms)
}
#else
(void)critical;
(void)constrained_env;
esp_log_util_cvt_dec(timestamp_ms, 0, buffer);
#endif
return out_buffer;

View File

@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
bool esp_log_util_is_constrained(void)
{
return true;
}

View File

@@ -10,18 +10,19 @@
#include <stdio.h>
#include "esp_log_write.h"
#include "esp_private/log_lock.h"
#include "esp_private/log_level.h"
#include "sdkconfig.h"
#include "esp_log_level.h"
#include "esp_log_config.h"
#include "esp_log.h"
static vprintf_like_t s_log_print_func = &vprintf;
vprintf_like_t esp_log_vprint_func = &vprintf;
vprintf_like_t esp_log_set_vprintf(vprintf_like_t func)
{
esp_log_impl_lock();
vprintf_like_t orig_func = s_log_print_func;
s_log_print_func = func;
esp_log_impl_unlock();
return orig_func;
/* This builtin, as described by Intel, is not a traditional
* test-and-set operation, but rather an atomic exchange operation. It
* writes value into *ptr, and returns the previous contents of *ptr.
*/
return __atomic_exchange_n(&esp_log_vprint_func, func, __ATOMIC_SEQ_CST);
}
void esp_log_writev(esp_log_level_t level,
@@ -29,10 +30,7 @@ void esp_log_writev(esp_log_level_t level,
const char *format,
va_list args)
{
esp_log_level_t level_for_tag = esp_log_level_get_timeout(tag);
if (ESP_LOG_NONE != level_for_tag && level <= level_for_tag) {
(*s_log_print_func)(format, args);
}
esp_log_va(ESP_LOG_CONFIG_INIT(level), tag, format, args);
}
void esp_log_write(esp_log_level_t level,
@@ -41,6 +39,6 @@ void esp_log_write(esp_log_level_t level,
{
va_list list;
va_start(list, format);
esp_log_writev(level, tag, format, list);
esp_log_va(ESP_LOG_CONFIG_INIT(level), tag, format, list);
va_end(list);
}

View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_private/log_util.h"
#include "esp_compiler.h"
#include "sdkconfig.h"
static esp_log_cache_enabled_t esp_log_cache_enabled = NULL;
void esp_log_util_set_cache_enabled_cb(esp_log_cache_enabled_t func)
{
esp_log_cache_enabled = func;
}
bool esp_log_util_is_constrained(void)
{
return (xPortInIsrContext()
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
|| ((esp_log_cache_enabled) ? !esp_log_cache_enabled() : false)
#endif
|| unlikely(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING));
}

View File

@@ -48,7 +48,12 @@ TEST_CASE("test master logging level performance", "[log]")
#ifdef CONFIG_LOG_MASTER_LEVEL
esp_log_set_level_master(ESP_LOG_NONE);
TEST_ASSERT_INT_WITHIN(100, 150, calc_time_of_logging(ITERATIONS));
#if ESP_LOG_VERSION == 1
const int typical_value = 150;
#else // ESP_LOG_VERSION == 2
const int typical_value = 250;
#endif // ESP_LOG_VERSION == 2
TEST_ASSERT_INT_WITHIN(100, typical_value, calc_time_of_logging(ITERATIONS));
#else
esp_log_level_set("*", ESP_LOG_NONE);
TEST_ASSERT_INT_WITHIN(DELTA_US, EXPECTED_US, calc_time_of_logging(ITERATIONS) / ITERATIONS);

View File

@@ -55,8 +55,7 @@ void putc_to_buffer(char c)
static int print_to_buffer(const char *format, va_list args)
{
int ret = vsnprintf(s_print_buffer, BUFFER_SIZE, format, args);
printf(s_print_buffer);
int ret = vsnprintf(&s_print_buffer[s_counter], BUFFER_SIZE, format, args);
s_counter += ret;
assert(s_counter < BUFFER_SIZE);
return ret;

View File

@@ -0,0 +1,360 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include "sys/param.h"
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_rom_uart.h"
#include "sdkconfig.h"
typedef struct {
SemaphoreHandle_t done;
int stack_usage;
int (*func)(void);
} perf_log_test_t;
static const char * TAG = "test";
const int max_loops = 5;
static void print_via_vprintf(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
static void print_via_esp_rom_vprintf(const char *format, ...)
{
va_list args;
va_start(args, format);
esp_rom_vprintf(format, args);
va_end(args);
}
static int test_stack_usage_esp_rom_printf(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
esp_rom_printf("test msg with value %d\n", 1);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_esp_rom_vprintf(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
print_via_esp_rom_vprintf("test msg with value %d\n", 2);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_early_log(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
ESP_EARLY_LOGI(TAG, "test msg with value %d", 3);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_dram_log(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
ESP_DRAM_LOGI(TAG, "test msg with value %d", 4);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_printf(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
printf("test msg with value %d\n", 5);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_vprintf(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
print_via_vprintf("test msg with value %d\n", 6);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static int test_stack_usage_esp_log(void)
{
UBaseType_t start, end;
start = uxTaskGetStackHighWaterMark(NULL);
ESP_LOGI(TAG, "test msg with value %d", 7);
end = uxTaskGetStackHighWaterMark(NULL);
return start - end;
}
static void log_stack_usage_task(void* arg)
{
perf_log_test_t *a = (perf_log_test_t *) arg;
const uint32_t mask = esp_cpu_intr_get_enabled_mask();
esp_cpu_intr_disable(0xFFFFFFFF);
a->stack_usage = a->func();
esp_cpu_intr_enable(mask);
TEST_ASSERT(xSemaphoreGive(a->done));
vTaskDelete(NULL);
}
TEST_CASE("Stack usage for log APIs", "[log]")
{
int priority = uxTaskPriorityGet(NULL) + 1;
perf_log_test_t args = {
.done = xSemaphoreCreateBinary(),
.stack_usage = 0,
.func = NULL,
};
typedef struct {
const char *name;
int (*func)(void);
int stack_usages;
} log_test_case_t;
log_test_case_t test_cases[] = {
{"esp_rom_printf", test_stack_usage_esp_rom_printf, 0},
{"esp_rom_vprintf", test_stack_usage_esp_rom_vprintf, 0},
{"early_log", test_stack_usage_early_log, 0},
{"dram_log", test_stack_usage_dram_log, 0},
{"printf", test_stack_usage_printf, 0},
{"vprintf", test_stack_usage_vprintf, 0},
{"esp_log", test_stack_usage_esp_log, 0},
};
for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
args.func = test_cases[i].func;
TEST_ASSERT(xTaskCreatePinnedToCore(log_stack_usage_task, test_cases[i].name, 4095, &args, priority, NULL, 0));
TEST_ASSERT(xSemaphoreTake(args.done, 100 / portTICK_PERIOD_MS));
test_cases[i].stack_usages = args.stack_usage;
}
printf("\nStack usage for %s chip:\n", CONFIG_IDF_TARGET);
for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
printf("%-20s %d bytes\n", test_cases[i].name, test_cases[i].stack_usages);
}
vSemaphoreDelete(args.done);
}
static void mock_putc(char c)
{
}
static int mock_vprintf(const char *format, va_list args)
{
return 0;
}
static int measuring_execution_time_esp_rom_printf(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
esp_rom_printf("\r");
} else {
esp_rom_printf("test msg %d\n", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
return diff;
}
static int measuring_execution_time_esp_rom_vprintf(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
print_via_esp_rom_vprintf("\r");
} else {
print_via_esp_rom_vprintf("test msg %d\n", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
return diff;
}
static int measuring_execution_time_early_log(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
if (without_output) {
esp_rom_install_channel_putc(1, mock_putc);
}
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
ESP_EARLY_LOGI(TAG, "");
} else {
ESP_EARLY_LOGI(TAG, "test msg %d", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
if (without_output) {
esp_rom_install_channel_putc(1, esp_rom_output_putc);
}
return diff;
}
static int measuring_execution_time_dram_log(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
if (without_output) {
esp_rom_install_channel_putc(1, mock_putc);
}
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
ESP_DRAM_LOGI(TAG, "");
} else {
ESP_DRAM_LOGI(TAG, "test msg %d", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
if (without_output) {
esp_rom_install_channel_putc(1, esp_rom_output_putc);
}
return diff;
}
static int measuring_execution_time_printf(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
printf("\r");
} else {
printf("test msg %d\n", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
return diff;
}
static int measuring_execution_time_vprintf(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
print_via_vprintf("\r");
} else {
print_via_vprintf("test msg %d\n", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
return diff;
}
static int measuring_execution_time_esp_log_macro(bool without_output)
{
int diff;
int64_t start, end;
diff = INT32_MAX;
vprintf_like_t original_vprintf;
if (without_output) {
original_vprintf = esp_log_set_vprintf(mock_vprintf);
}
for (int i = 0; i < max_loops; i++) {
start = esp_timer_get_time();
if (without_output) {
ESP_LOGI(TAG, "\r");
} else {
ESP_LOGI(TAG, "test msg %d", i);
}
end = esp_timer_get_time();
diff = MIN(diff, end - start);
}
if (without_output) {
esp_log_set_vprintf(original_vprintf);
}
return diff;
}
TEST_CASE("Performance measurement for log APIs", "[log]")
{
#if ESP_LOG_VERSION == 2
const int count_prints = 3;
#else
const int count_prints = 1;
#endif
const uint32_t mask = esp_cpu_intr_get_enabled_mask();
esp_cpu_intr_disable(0xFFFFFFFF);
int m_esp_rom_printf = measuring_execution_time_esp_rom_printf(true);
int m_esp_rom_printf_output = measuring_execution_time_esp_rom_printf(false);
int m_esp_rom_vprintf = measuring_execution_time_esp_rom_vprintf(true);
int m_esp_rom_vprintf_output = measuring_execution_time_esp_rom_vprintf(false);
int m_early_log = measuring_execution_time_early_log(true);
int m_early_log_output = measuring_execution_time_early_log(false);
int m_dram_log_printf = measuring_execution_time_dram_log(true);
int m_dram_log_printf_output = measuring_execution_time_dram_log(false);
int m_printf = measuring_execution_time_printf(true);
int m_printf_output = measuring_execution_time_printf(false);
int m_vprintf = measuring_execution_time_vprintf(true);
int m_vprintf_output = measuring_execution_time_vprintf(false);
int m_esp_log_macro = measuring_execution_time_esp_log_macro(true);
int m_esp_log_macro_output = measuring_execution_time_esp_log_macro(false);
esp_cpu_intr_enable(mask);
esp_rom_printf("\nPerformance measurements for %s chip:\n", CONFIG_IDF_TARGET);
esp_rom_printf("Function w/o output (us) | w/ output (us)\n");
esp_rom_printf("esp_rom_printf %d %d\n", m_esp_rom_printf, m_esp_rom_printf_output);
esp_rom_printf("esp_rom_vprintf %d %d\n", m_esp_rom_vprintf, m_esp_rom_vprintf_output);
esp_rom_printf("ESP_EARLY_LOGI %d + %d %d\n", m_early_log, count_prints * m_esp_rom_printf, m_early_log_output);
esp_rom_printf("ESP_DRAM_LOGI %d + %d %d\n", m_dram_log_printf, count_prints * m_esp_rom_vprintf, m_dram_log_printf_output);
esp_rom_printf("printf %d %d\n", m_printf, m_printf_output);
esp_rom_printf("vprintf %d %d\n", m_vprintf, m_vprintf_output);
esp_rom_printf("ESP_LOGI %d + %d %d\n", m_esp_log_macro, count_prints * m_vprintf, m_esp_log_macro_output);
}

View File

@@ -6,137 +6,548 @@ Logging library
Overview
--------
The logging library provides three ways for setting log verbosity:
ESP-IDF provides a flexible logging system with two configurable versions, **Log V1** and **Log V2**, selectable via :ref:`CONFIG_LOG_VERSION`. This document outlines their features, configurations, usage guidelines, and performance comparisons.
- **At compile time**: in menuconfig, set the verbosity level using the option :ref:`CONFIG_LOG_DEFAULT_LEVEL`.
- Optionally, also in menuconfig, set the maximum verbosity level using the option :ref:`CONFIG_LOG_MAXIMUM_LEVEL`. By default, this is the same as the default level, but it can be set higher in order to compile more optional logs into the firmware.
- **At runtime**: all logs for verbosity levels lower than :ref:`CONFIG_LOG_DEFAULT_LEVEL` are enabled by default. The function :cpp:func:`esp_log_level_set` can be used to set a logging level on a per-module basis. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. Note that the ability to change the log level at runtime depends on :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`.
- **At runtime**: if :ref:`CONFIG_LOG_MASTER_LEVEL` is enabled then a ``Master logging level`` can be set using :cpp:func:`esp_log_set_level_master`. This option adds an additional logging level check for all compiled logs. Note that this will increase application size. This feature is useful if you want to compile a lot of logs that are selectable at runtime, but also want to avoid the performance hit from looking up the tags and their log level when you don't want log output.
- **Log V1** (default): The original implementation designed for simplicity. It is optimized for early and DRAM logging but has higher flash usage and lacks flexibility.
- **Log V2**: The enhanced implementation improves flexibility, reduces flash usage, and centralizes log formatting but requires a bit more stack.
There are the following verbosity levels:
**Log V2** is backward-compatible with **Log V1**, meaning projects written using **Log V1** can switch to **Log V2** without modification. However, projects utilizing Log V2-specific features cannot revert to **Log V1** due to compatibility constraints.
- Error (lowest)
- Warning
- Info
- Debug
- Verbose (highest)
Features of **Log V1**
^^^^^^^^^^^^^^^^^^^^^^
.. note::
- 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:
The function :cpp:func:`esp_log_level_set` cannot set logging levels higher than specified by :ref:`CONFIG_LOG_MAXIMUM_LEVEL`. To increase log level for a specific file above this maximum at compile time, use the macro `LOG_LOCAL_LEVEL` (see the details below).
- Larger binary size due to redundant formatting items.
- Inflexible due to the lack of support for custom log formatting.
- Build errors point to the wrong argument number in macros.
Features of **Log V2**
^^^^^^^^^^^^^^^^^^^^^^
How to Use Logging Library
--------------------------
- Centralized formatting via a single function, :cpp:func:`esp_log`.
- Reduces binary size by only storing the user-defined format string.
- The timestamp is captured only when it is required for output and the log level permits logging.
- Allows customization for log output:
In each C file that uses logging functionality, define the TAG variable as shown below:
- Disable/enable color, timestamps, or tags globally, per file or per message log.
- Output logs without formatting (useful for binary logging).
- Apply different log settings for bootloader and app.
- The format argument can be dynamic and set as a variable, allowing greater flexibility in constructing log messages.
- Unified handler for logs across bootloader, ISR, startup code, and constrained environments.
- Drawbacks:
- Consumes more stack and memory.
- The log handler is slightly slower than **Log V1**, but the difference is negligible compared to the time spent transferring the data, e.g. over UART.
Log Levels
----------
Log levels are configured separately for application and bootloader. This separation allows developers to apply different logging settings via Kconfig options for each. For example, concise logs can be enabled for the bootloader while detailed debugging is enabled for the application. Use the bootloader-specific Kconfig options to configure log levels for the bootloader independently of the main application.
There are six verbosity levels:
- **Verbose** - Highly detailed and frequent debugging messages, often including internal states, that may overwhelm the output. (highest)
- **Debug** - Detailed diagnostic messages intended to aid in debugging (e.g., variable values, pointer addresses).
- **Info** - General information messages that describe the normal operation of the system.
- **Warning** - Events that could potentially cause issues but have been handled or mitigated.
- **Error** - Critical errors indicating that the software cannot recover without intervention.
- **None** - No log output. Used to completely disable logging. (lowest)
Log Level Settings
------------------
Log level settings control which logs are included in the binary and their visibility at runtime. There are two types of log level settings:
- **Log level**: Specifies which log levels are displayed at runtime. The bootloader's **log level** is configured via :ref:`CONFIG_BOOTLOADER_LOG_LEVEL`, while the application's **log level** is set via :ref:`CONFIG_LOG_DEFAULT_LEVEL`. The current log level can be retrieved using the function ``esp_log_get_default_level``.
- **Maximum log level**: Determines which log levels are included in the binary. Logs above this level are discarded at compile time and excluded from the final image. It can be set higher than the **log level**, allowing additional logs to be included in the binary and enabling them for debugging later if needed via :cpp:func:`esp_log_level_set`. The :ref:`CONFIG_LOG_MAXIMUM_LEVEL` option enables this feature for the application, but the bootloader does not support it. For the bootloader, the **maximum log level** is always the same as the **log level**.
Example for the application: if the **log level** is set to **Warning** and the **maximum log level** is set to **Debug**, the binary will include log messages of levels **Error**, **Warning**, **Info**, and **Debug**. However, at runtime, only log messages of levels **Error** and **Warning** will be outputted unless the log level is explicitly changed using :cpp:func:`esp_log_level_set`. The log level can be adjusted, increased or decreased, depending on the user's needs.
``Maximum Log Level`` Setting
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``LOG_LOCAL_LEVEL`` definition allows you to override the **maximum log level** for a specific source file or component without modifying the Kconfig options. It effectively sets the **maximum log level** locally, enabling or excluding specific logs in the binary.
This approach is especially useful when you need more detailed logs for specific areas of the code without globally increasing the **maximum log level**, thereby avoiding unnecessary impacts on the binary size.
- Change the **maximum log level** for a source file (do not add it in header files, as it may not work due to the single-inclusion approach used by header files). Define ``LOG_LOCAL_LEVEL`` with one of the values from :cpp:type:`esp_log_level_t` before including ``esp_log.h``. This allows you to control which log messages are included in the binary for that specific source file.
.. code-block:: c
// in a my_file.c file
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
- Change the **maximum log level** for an entire component by defining ``LOG_LOCAL_LEVEL`` in the component's `CMakeLists.txt`. This ensures that the specified log level is applied across all source files within the component, controlling which log messages are included in the binary:
.. code-block:: cmake
# in a component's CMakeLists.txt file
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE")
Runtime **Log Level** Setting
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Only the application supports changing the log level at runtime. The bootloader does not support this feature.
By default, all log levels up to the **log level** are enabled at the startup. The function :cpp:func:`esp_log_level_set` can be used to set the **log level** globally or on a per-module basis. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. This functionality depends on :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`, 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 ``ERROR`` for all components (global setting):
.. code-block:: c
static const char* TAG = "MyModule";
esp_log_level_set("*", ESP_LOG_ERROR);
Then use one of logging macros to produce output, e.g:
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 Wi-Fi component (module-specific setting):
.. 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);
Several macros are available for different verbosity levels:
Use Logging Library
---------------------
* ``ESP_LOGE`` - Error (lowest)
* ``ESP_LOGW`` - Warning
* ``ESP_LOGI`` - Info
* ``ESP_LOGD`` - Debug
* ``ESP_LOGV`` - Verbose (highest)
Additionally, there are ``ESP_EARLY_LOGx`` versions for each of these macros, e.g. :c:macro:`ESP_EARLY_LOGE`. These versions have to be used explicitly in the early startup code only, before heap allocator and syscalls have been initialized. Normal ``ESP_LOGx`` macros can also be used while compiling the bootloader, but they will fall back to the same implementation as ``ESP_EARLY_LOGx`` macros.
There are also ``ESP_DRAM_LOGx`` versions for each of these macros, e.g. :c:macro:`ESP_DRAM_LOGE`. These versions are used in some places where logging may occur with interrupts disabled or with flash cache inaccessible. Use of this macros should be as sparse as possible, as logging in these types of code should be avoided for performance reasons.
.. note::
Inside critical sections interrupts are disabled so it's only possible to use ``ESP_DRAM_LOGx`` (preferred) or ``ESP_EARLY_LOGx``. Even though it's possible to log in these situations, it's better if your program can be structured not to require it.
To override default verbosity level at file or component scope, define the ``LOG_LOCAL_LEVEL`` macro.
At file scope, define it before including ``esp_log.h``, e.g.:
In each C file that uses the logging functionality, define the ``TAG`` variable.
.. 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
At component scope, define it in the component CMakeLists:
.. code-block:: bash
.. code-block:: cmake
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE")
Dynamic Log Level Control
-------------------------
To configure logging output per module at runtime, add calls to the function :cpp:func:`esp_log_level_set` as follows:
.. code-block:: c
esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level
esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack
esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client
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::
The "DRAM" and "EARLY" log macro variants documented above do not support per module setting of log verbosity. These macros will always log at the "default" verbosity level, which can only be changed at runtime by calling ``esp_log_level("*", level)``.
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.
Even when logs are disabled by using a tag name, they will still require a processing time of around 10.9 microseconds per entry.
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.
The log component provides several options to better adjust the system to your needs, reducing memory usage and speeding up operations. The :ref:`CONFIG_LOG_TAG_LEVEL_IMPL` option sets the method of tag level checks:
- 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". This option disables the ability to set the log level per tag. The ability to change the log level at runtime depends on :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`. If disabled, changing the log level at runtime using :cpp:func:`esp_log_level_set` is not possible. This implementation is suitable for highly constrained environments.
- "Linked list" (no cache). This option enables the ability to set the log level per tag. This approach searches the linked list of all tags for the log level, which may be slower for a large number of tags but may have lower memory requirements than the cache approach.
- (Default) "Cache + Linked List". This option enables the ability to set the log level per tag. 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.
There are three groups of macros available:
When the :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` option is enabled, log levels to be changed at runtime via :cpp:func:`esp_log_level_set`. Dynamic log levels increase flexibility but also incurs additional code size.
If your application does not require dynamic log level changes and you do not need to control logs per module using tags, consider disabling :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`. It reduces IRAM usage by approximately 260 bytes, DRAM usage by approximately 264 bytes, and flash usage by approximately 1 KB compared to the default option. It is not only streamlines logs for memory efficiency but also contributes to speeding up log operations in your application about 10 times.
- **ESP_LOGx**: Standard logging macros suitable for most use cases during normal operation. Use these in your application code for logging in non-constrained environments, avoiding use in ISRs, early startup, or when the flash cache is disabled. A key characteristic of these macros is that they use the `vprintf` function from the Newlib library for formatting and outputting logs.
.. note::
- **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.
The "Linked list" and "Cache + Linked List" options will automatically enable :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`.
- **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.
Master Logging Level
^^^^^^^^^^^^^^^^^^^^
.. 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.
To enable the Master logging level feature, the :ref:`CONFIG_LOG_MASTER_LEVEL` option must be enabled. It adds an additional level check for ``ESP_LOGx`` macros before calling :cpp:func:`esp_log_write`. This allows to set a higher :ref:`CONFIG_LOG_MAXIMUM_LEVEL`, but not inflict a performance hit during normal operation (only when directed). An application may set the master logging level (:cpp:func:`esp_log_set_level_master`) globally to enforce a maximum log level. ``ESP_LOGx`` macros above this level will be skipped immediately, rather than calling :cpp:func:`esp_log_write` and doing a tag lookup. It is recommended to only use this in an top-level application and not in shared components as this would override the global log level for any user using the component. By default, at startup, the Master logging level is :ref:`CONFIG_LOG_DEFAULT_LEVEL`.
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.
Note that this feature increases application size because the additional check is added into all ``ESP_LOGx`` macros.
Log Format
----------
The snippet below shows how it works. Setting the Master logging level to ``ESP_LOG_NONE`` disables all logging globally. :cpp:func:`esp_log_level_set` does not currently affect logging. But after the Master logging level is released, the logs will be printed as set by :cpp:func:`esp_log_level_set`.
- **Log V1**: Only supports disabling color formatting globally. Other formatting options, such as timestamp and tag, are always enabled.
- **Log V2**:
- Allows complete customization of formatting, including the ability to disable color, tag, and timestamp formatting globally, per file, per module, or even for individual log messages.
- Provides finer control over log output, making it more adaptable to specific use cases and environments.
.. 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 a INFO message
esp_log_level_set("lib_name", ESP_LOG_WARN); // enables WARN logs from lib_name
// #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); // disables all logs globally. esp_log_level_set has no effect at the moment
.. code-block:: none
ESP_LOGW("lib_name", "Message for print"); // no print, Master logging level blocks it
esp_log_level_set("lib_name", ESP_LOG_INFO); // enable INFO logs from lib_name
ESP_LOGI("lib_name", "Message for print"); // no print, Master logging level blocks it
I (56) boot: chip revision: v3.0
esp_log_set_level_master(ESP_LOG_INFO); // enables all INFO logs globally
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", "Message for print"); // prints a INFO message
The logging system supports the following formatting options, applicable for both the application and bootloader:
- **Color**: Adds color codes to enhance log visibility globally. Controlled by :ref:`CONFIG_LOG_COLORS`, which is disabled by default because the ESP-IDF monitor tool (`idf.py monitor`) can detect the log level by its **level name** and apply the standard IDF color scheme.
- For **Log V2**, the :ref:`CONFIG_LOG_COLORS_SUPPORT` option enables runtime support for adding color output to specific logs, files, or components, even if global color is disabled. To enable color for a specific context use ``ESP_LOG_COLOR_DISABLED``.
- **Level Name**: A single letter (I, W, E, D, V) indicating log verbosity, displayed at the start of each message. Useful for identifying log levels, especially when color is disabled, as utilized by the ESP-IDF monitor tool.
- **Timestamp**: Adds a timestamp to log messages globally. Controlled by :ref:`CONFIG_LOG_TIMESTAMP_SOURCE`.
- **None**: No timestamp. Useful for log analysis or debugging where timing is not critical. Saves processing power and memory. Available only for **Log V2**.
- **Milliseconds since boot** `(18532)` (default): Derived from the RTOS tick count multiplied by the tick period.
- **System time (HH:MM:SS.sss)** `(14:31:18.532)`: Displays time in hours, minutes, seconds, and milliseconds.
- **System time (YY-MM-DD HH:MM:SS.sss)** `(2023-08-15 14:31:18.532)`: Similar to the above, but also includes the date.
- **Unix time in milliseconds** `(1692099078532)`: Displays Unix time in milliseconds.
- For **Log V2**, the :ref:`CONFIG_LOG_TIMESTAMP_SUPPORT` option enables runtime support for adding timestamp output to specific logs, files, or components, even if global timestamp is disabled. To enable the **Milliseconds since boot** timestamp for a specific context, use ``ESP_LOG_TIMESTAMP_DISABLED``.
- **Tag**: Displays a user-defined identifier for the source module.
- For **Log V2**, the tag can be passed to the macros as ``NULL``, in which case it will not be printed, and per-component log level check will not work.
- **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 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**:
- Define as ``1`` to force the log handler :cpp:func:`esp_log` to use a safe printf function suitable for the specified scope.
- **ESP_LOG_FORMATTING_DISABLED**:
- Default: ``0`` (enables all formatting items such as color, timestamps, tags, and end line).
- Define as ``1`` to disable all formatting items for the specified scope.
- **ESP_LOG_COLOR_DISABLED**: Requires :ref:`CONFIG_LOG_COLORS_SUPPORT` to be enabled.
- If global color (:ref:`CONFIG_LOG_COLORS`) is disabled, define as ``0`` to enable color output for the specified scope.
- If global color (:ref:`CONFIG_LOG_COLORS`) is enabled, define as ``1`` to disable color output for the specified scope.
- **ESP_LOG_TIMESTAMP_DISABLED**: Requires :ref:`CONFIG_LOG_TIMESTAMP_SUPPORT` to be enabled.
- If global timestamping (:ref:`CONFIG_LOG_TIMESTAMP_SOURCE`) is disabled, define as ``0`` to enable tick timestamp output for the specified scope.
- If global timestamping (:ref:`CONFIG_LOG_TIMESTAMP_SOURCE`) is enabled, define as ``1`` to disable tick timestamp output for the specified scope.
Per-Log Formatting
^^^^^^^^^^^^^^^^^^
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:
.. 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);
Log Level Control
-----------------
Only the application supports changing the log level at runtime. The bootloader does not support this feature.
The logging library allows adjusting log output per module (tag) at runtime using the function :cpp:func:`esp_log_level_set`. This feature applies only to non-constrained environments (**ESP_LOGx** macros). Constrained environments (e.g., **ESP_EARLY_LOGx** or **ESP_DRAM_LOGx**) do not support dynamic log levels due to the absence of locks and lightweight requirements in their log handlers.
.. 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);
There are three settings that control the ability to change the log level at runtime globally or per module (tag):
- **Dynamic Log Level Control** (:ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`, enabled by default): Enables runtime log level changes via :cpp:func:`esp_log_level_set`. This feature increases flexibility but adds memory and performance overhead. If binary size is a concern and dynamic log level changes are unnecessary, consider disabling this option, especially when :ref:`CONFIG_LOG_TAG_LEVEL_IMPL` is set to **None**, to minimize program size.
If your application does not require dynamic log level adjustments, disabling this option can improve efficiency by:
- Reducing memory consumption:
- **IRAM**: about 260 bytes
- **DRAM**: about 264 bytes
- **Flash**: about 1 KB
- Boosting log operation performance by up to 10 times.
- **Tag-Level Checks** (:ref:`CONFIG_LOG_TAG_LEVEL_IMPL`, default **Cache + Linked List**): Determines how per-tag log level checks are performed, affecting memory usage and lookup speed:
- **None**: Disables per-tag log level checks entirely, reducing overhead but removing runtime flexibility.
- **Linked List**: Enables per-tag log level settings using a linked list-only implementation (no cache). This method searches through all tags in the linked list to determine the log level, which may result in slower lookups for a large number of tags but consumes less memory compared to the **Cache** approach. The linked list approach performs full string comparisons of log tags to identify the appropriate log level. Unlike **Cache**, it does not rely on tag pointer comparisons, making it suitable for dynamic tag definitions. Select this option if you prioritize memory savings, need to enable or disable logs for specific modules, or want to use tags defined as variables. Selecting this option automatically enables **Dynamic Log Level Control**. The linked list entries are allocated on the heap during the execution of ``ESP_LOGx`` macros when a new tag is encountered.
- **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`.
- **Array**: A simple implementation without reordering, suitable for low-memory applications that prioritize simplicity.
- **Binary Min-Heap** (default): An optimized implementation for fast lookups with automatic reordering. Ideal for high-performance applications with sufficient memory. The **Cache Size** (:ref:`CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE`) defines the capacity, which defaults to 31 entries.
A larger cache size enhances lookup performance for frequently accessed log tags but increases memory consumption. In contrast, a smaller cache size conserves memory but may result in more frequent evictions of less commonly used log tags.
- **Master Log Level** (:ref:`CONFIG_LOG_MASTER_LEVEL`, disabled by default): It is an optional setting designed for specific debugging scenarios. It enables a global "master" log level check that occurs before timestamps and tag cache lookups. This is useful for compiling numerous logs that can be selectively enabled or disabled at runtime while minimizing performance impact when log output is unnecessary.
Common use cases include temporarily disabling logs during time-critical or CPU-intensive operations and re-enabling them later.
.. note:: For **Log V1**, this feature may significantly increase program size based on the number of compiled logs. For **Log V2**, the impact is minimal as the check is integrated within the log handler.
If enabled, the master log level defaults to :ref:`CONFIG_LOG_DEFAULT_LEVEL` and can be adjusted at runtime using :cpp:func:`esp_log_set_level_master`. This global check takes precedence over ``esp_log_get_default_level``.
The snippet below shows how it works. Setting the **Master log level** to ``ESP_LOG_NONE`` disables all logging globally. :cpp:func:`esp_log_level_set` does not currently affect logging. However, after the **Master log level** is adjusted to a higher level, logs will be printed as configured by :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::
Even when logs are disabled by tag, processing still takes approximately 10.9 microseconds. To reduce this overhead, consider using the **Master Log Level** or disabling **Tag-Level Checks** functionality.
Logging of Buffers
------------------
The logging system provides macros for logging buffer data. These macros can be used in both bootloader and application, and they are independent of the log version. Available macros:
- :c:macro:`ESP_LOG_BUFFER_HEX` and :c:macro:`ESP_LOG_BUFFER_HEX_LEVEL`: Logs a buffer of hexadecimal bytes. The data is split into lines with 16 bytes per line. :c:macro:`ESP_LOG_BUFFER_HEX` is only for the ``Info`` log level.
.. 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` 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
#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:`ESP_LOG_BUFFER_HEXDUMP`: Dumps a buffer in a formatted hex dump style, displaying both the memory address and corresponding ASCII values. This is especially useful for debugging raw memory content.
.. 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|
The number of lines in the output depends on the size of the buffer.
Performance and Measurements
----------------------------
When logging is used in a task, the task stack must be configured with at least 2 KB of space to ensure sufficient memory for logging operations.
The following measurements were performed using tests inside the log component with default settings (the maximum and default log levels were set to INFO, color support was disabled, without master log and timestamps were enabled) across different chips:
- Performance measurements for log APIs
- Stack usage for log APIs
``esp_rom_printf`` and ``esp_rom_vprintf`` produce similar results. Similarly, ``vprintf`` and ``printf`` yield comparable outcomes. Hence, only one of each pair is included in the tables below.
.. list-table:: Stack Usage (bytes)
:header-rows: 1
* - Function
- 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
The stack usage differences between **Log V1** and **Log V2** are negligible.
.. list-table:: Performance (without output in microseconds)
:header-rows: 1
* - Function
- 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
If logging to UART is measured, the performance numbers for **Log V1** and **Log V2** are nearly identical. The slight differences in processing overhead introduced by **Log V2** become negligible compared to the time it takes to send logs over UART. Thus, in most practical use cases, the performance impact of switching to **Log V2** will be unnoticeable.
**Memory Usage (bytes)**
The following measurements were performed using the ``esp_timer`` example with default settings for ESP32: the maximum and default log levels were set to INFO, color support was disabled, and timestamps were enabled. After enabling the **Log V2** option, the example was rebuilt, and the memory usage differences were compared using the command:
.. code-block:: bash
idf.py size --diff ~/esp/logv2/build_v1
.. list-table::
:header-rows: 1
* - Version
- IRAM
- DRAM
- Flash Code
- Flash Data
- App binary size
* - Log V2
- +1772
- 36
- 956
- 1172
- 181104 (384)
.. list-table::
:header-rows: 1
:align: center
* - Version
- Bootloader binary size
* - Log V2
- 26272 (+160)
Enabling **Log V2** increases IRAM usage while reducing the overall application binary size, Flash code, and data usage.
Logging to Host via JTAG
^^^^^^^^^^^^^^^^^^^^^^^^
------------------------
By default, the logging library uses the vprintf-like function to write formatted output to the dedicated UART. By calling a simple API, all log output may be routed to JTAG instead, making logging several times faster. For details, please refer to Section :ref:`app_trace-logging-to-host`.
Thread Safety
^^^^^^^^^^^^^
-------------
The log string is first written into a memory buffer and then sent to the UART for printing. Log calls are thread-safe, i.e., logs of different threads do not conflict with each other.
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 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.
Application Example
-------------------

View File

@@ -1,4 +1,4 @@
Migration from 5.5 to 5.5
Migration from 5.4 to 5.5
-------------------------
:link_to_translation:`zh_CN:[中文]`

View File

@@ -4,6 +4,33 @@ System
:link_to_translation:`en:[English]`
Time
-----------------------
----
* ``{IDF_TARGET_NAME}/rtc.h`` is deprecated, include the replacement ``esp_rtc_time.h`` instead.
Log
---
**Log V2** is introduced in this ESP-IDF version as an enhanced and optional logging implementation. It is fully compatible with **Log V1**, allowing projects to continue using **Log V1** without changes. Developers can enable **Log V2** via the Kconfig option :ref:`CONFIG_LOG_VERSION`. In future ESP-IDF versions, **Log V2** may become the default.
**Key Points:**
- Centralized log handling, dynamic formatting, and greater flexibility.
- Slightly higher stack and IRAM usage, with a reduced binary size (refer to measurements in the log document).
- **Log V2**-specific features (e.g., dynamic formatting, detecting the execution context) are not backward-compatible with **Log V1**.
If you use the ``esp_log_write`` (or ``esp_log_writev``) function, replace it with ``esp_log`` (or ``esp_log_va``) to reduce one nested call.
The log handler (``esp_log``) supports internal formatting, so there is no need to construct formatting manually. The code below can be simplified by using ``ESP_LOG_LEVEL_LOCAL``.
.. code-block:: c
#if ESP_LOG_VERSION == 2
ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, OT_PLAT_LOG_TAG, format, args);
#else
if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) {
esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), OT_PLAT_LOG_TAG, LOG_COLOR_E "E(%lu) %s:", esp_log_timestamp(), OT_PLAT_LOG_TAG);
esp_log_va(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), OT_PLAT_LOG_TAG, format, args);
esp_log(ESP_LOG_CONFIG_INIT(ESP_LOG_ERROR), OT_PLAT_LOG_TAG, LOG_RESET_COLOR "\n");
}
#endif

View File

@@ -6,137 +6,548 @@
概述
--------
日志库提供了三种设置日志级别的方式:
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 占用较高,缺乏灵活性
- **Log 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 选项为每个模块设置不同的日志级别,从而实现配置的独立性。例如,可以为引导加载程序启用简洁的日志,而为应用程序启用详细的调试日志。使用引导加载程序专用的 Kconfig 选项,可以为引导加载程序独立配置日志级别,不会影响主应用程序。
日志库共有六个详细程度级别:
- **Verbose** - 输出高度详细且频繁的调试信息,通常包括内部状态,可能会使输出过于繁杂。(最高级别)
- **Debug** - 输出详细的诊断信息(例如变量值、指针地址等),适用于调试。
- **Info** - 输出描述系统正常运行的一般信息。
- **Warning** - 输出可能引发问题,但已被处理或减轻影响的事件。
- **Error** - 仅输出严重错误,这些错误如果不进行干预处理,软件无法自行恢复。
- **None** - 无日志输出,即完全禁用日志。(最低级别)
日志级别设置
------------------
通过日志级别设置,可以选择将哪些日志包含在二进制文件中,并决定这些日志在运行时的可见性。日志级别设置包括以下两种:
- **日志级别**:指定在运行时显示哪些级别的日志。引导加载程序的 **日志级别** 通过 :ref:`CONFIG_BOOTLOADER_LOG_LEVEL` 配置,而应用程序的 **日志级别** 通过 :ref:`CONFIG_LOG_DEFAULT_LEVEL` 设置。通过函数 ``esp_log_get_default_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 // 可选:增加包含在二进制文件中的日志级别(仅适用于本文件)
#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++); // 如果需要,使用 DRAM_STR 宏添加 DRAM
在组件中,该宏应在组件的 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) /* 仅用于 Log v2 */
// #define ESP_LOG_TIMESTAMP_DISABLED (1) /* 仅用于 Log v2 */
#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 时钟 tick 计数乘以 tick 周期得出。
- **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, // 设置 log level
.constrained_env = false, // 指定是否为受限环境
.require_formatting = true, // 启用格式处理
.dis_color = ESP_LOG_COLOR_DISABLED, // 使用全局颜色设置
.dis_timestamp = ESP_LOG_TIMESTAMP_DISABLED, // 使用全局时间戳设置
.reserved = 0, // 保留后续使用
}
};
// ...
if (temperature > 55) {
configs.opts.log_level = ESP_LOG_WARN;
}
//与 ESP_LOGx 宏相似,但可以采用自定义配置
// 如果 configs 变量为常量,编译器在编译过程中会排除低于 maximum log level 的日志
//如果 configs 不是常量则不适用
ESP_LOG_LEVEL_LOCAL(configs, TAG, "Temp = %dC", temperature);
// // 注意:以下调用绕过了编译时日志级别检查
// 这些日志无法从二进制文件中丢弃
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
// 将所有组件的日志级别设置为ERROR全局设置
esp_log_level_set("*", ESP_LOG_ERROR);
// 将 Wi-Fi 组件的日志级别设置为 WARNING特定模块设置
esp_log_level_set("wifi", ESP_LOG_WARN);
// 将 DHCP 客户端的日志级别设置为 INFO模块相关设置
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**:仅使用链表实现按标签设置日志级别(不使用缓存)。这种方法会遍历链表中的所有标签来确定日志级别,因此当标签数量较大时,会导致查找速度变慢,但与 **Cache** 方式相比,能节省更多内存空间。链表方法对日志标签进行完整的字符串比较,从而识别日志级别。与 **Cache** 方法不同,链表方法不依赖于标签指针比较,因此更适用于动态的标签定义。如需优先考虑节省内存、对特定模块启用或禁用日志,或希望使用定义为变量的标签,请选择此方法。选择此方法会自动启用 **Dynamic Log Level Control** (动态日志级别控制)功能。运行 ``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** 影响很小,因为检查已集成到了日志处理程序中。
如果启用此功能master 日志级别默认为 :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 日志级别在启动时为 CONFIG_LOG_DEFAULT_LEVEL, 且等于 ESP_LOG_INFO
ESP_LOGI("lib_name", "Message for print"); // 打印 INFO 消息
esp_log_level_set("lib_name", ESP_LOG_WARN); // 为 lib_name 启用 WARN 级别日志
// 全局禁用所有日志esp_log_level_set 目前没有作用
esp_log_set_level_master(ESP_LOG_NONE);
ESP_LOGW("lib_name", "Message for print"); // master 日志级别阻止了打印
esp_log_level_set("lib_name", ESP_LOG_INFO); // 开启 lib_name 的 INFO 日志
ESP_LOGI("lib_name", "Message for print"); // master 日志级别阻止了打印
// 全局启用所有 INFO 日志
esp_log_set_level_master(ESP_LOG_INFO);
ESP_LOGI("lib_name", "Message for print"); // 打印 INFO 信息
.. 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禁用颜色支持未启用 master 日志级别,启用时间戳),在不同芯片上进行了如下两组测试:
- 日志 API 性能测试
- 日志 API 堆栈用量测试
``esp_rom_printf````esp_rom_vprintf`` 的结果相似,同样,``vprintf````printf`` 也得出相似结果。因此,下表仅展示每对相似测试中的一个结果。
.. list-table:: **堆栈使用情况(单位:字节)**
:header-rows: 1
* - 功能
- 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** 之间的堆栈使用量差异可以忽略不计。
.. list-table:: 性能(不包括输出,单位:微秒)
:header-rows: 1
* - 功能
- 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
.. list-table::
:header-rows: 1
* - 日志系统版本
- IRAM
- DRAM
- flash 代码
- flash 数据
- App 二进制大小
* - Log V2
- +1772
- 36
- 956
- 1172
- 181104 (384)
.. list-table::
:header-rows: 1
:align: center
* - 日志系统版本
- 引导加载程序二进制大小
* - 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 打印,从而确保不同任务之间的线程安全。除非需要确保可靠的日志输出,否则应避免在受限环境中记录日志。
应用示例
-------------------

View File

@@ -1,5 +1,5 @@
Migration from 5.5 to 5.5
-------------------------
从 5.4 迁移到 5.5
-----------------
:link_to_translation:`en:[English]`

View File

@@ -7,3 +7,8 @@
-----------------------
* ``{IDF_TARGET_NAME}/rtc.h`` 已弃用,应使用 ``esp_rtc_time.h`` 替代。
Log
---
.. include:: ../../../../en/migration-guides/release-5.x/5.5/system.rst