feat(log): Refactoring tag level check APIs

This commit is contained in:
Konstantin Kondrashov 2024-02-28 20:33:12 +02:00
parent f375125e28
commit c3b0418a4e
34 changed files with 1339 additions and 568 deletions

View File

@ -6,7 +6,7 @@ set(priv_requires "")
if(BOOTLOADER_BUILD)
list(APPEND srcs "log_noos.c")
else()
list(APPEND srcs "log.c")
list(APPEND srcs "src/os/log_write.c")
if(${target} STREQUAL "linux")
list(APPEND srcs "log_linux.c")
@ -18,10 +18,23 @@ else()
# 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")
if(CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST OR CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST)
list(APPEND srcs "src/log_level/tag_log_level/linked_list/log_linked_list.c")
endif()
if(CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY)
list(APPEND srcs "src/log_level/tag_log_level/cache/log_array.c")
elseif(CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP)
list(APPEND srcs "src/log_level/tag_log_level/cache/log_binary_heap.c")
endif()
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "include/esp_private"
LDFRAGMENTS linker.lf
PRIV_REQUIRES ${priv_requires})
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "include/esp_private"
LDFRAGMENTS linker.lf
PRIV_REQUIRES ${priv_requires})

View File

@ -1,137 +1,7 @@
menu "Log output"
menu "Log"
choice LOG_DEFAULT_LEVEL
bool "Default log verbosity"
default LOG_DEFAULT_LEVEL_INFO
help
Specify how much output to see in logs by default.
You can set lower verbosity level at runtime using
esp_log_level_set function.
orsource "./Kconfig.level"
By default, this setting limits which log statements
are compiled into the program. For example, selecting
"Warning" would mean that changing log level to "Debug"
at runtime will not be possible. To allow increasing log
level above the default at runtime, see the next option.
config LOG_DEFAULT_LEVEL_NONE
bool "No output"
config LOG_DEFAULT_LEVEL_ERROR
bool "Error"
config LOG_DEFAULT_LEVEL_WARN
bool "Warning"
config LOG_DEFAULT_LEVEL_INFO
bool "Info"
config LOG_DEFAULT_LEVEL_DEBUG
bool "Debug"
config LOG_DEFAULT_LEVEL_VERBOSE
bool "Verbose"
endchoice
config LOG_DEFAULT_LEVEL
int
default 0 if LOG_DEFAULT_LEVEL_NONE
default 1 if LOG_DEFAULT_LEVEL_ERROR
default 2 if LOG_DEFAULT_LEVEL_WARN
default 3 if LOG_DEFAULT_LEVEL_INFO
default 4 if LOG_DEFAULT_LEVEL_DEBUG
default 5 if LOG_DEFAULT_LEVEL_VERBOSE
choice LOG_MAXIMUM_LEVEL
bool "Maximum log verbosity"
default LOG_MAXIMUM_EQUALS_DEFAULT
help
This config option sets the highest log verbosity that it's possible to select
at runtime by calling esp_log_level_set(). This level may be higher than
the default verbosity level which is set when the app starts up.
This can be used enable debugging output only at a critical point, for a particular
tag, or to minimize startup time but then enable more logs once the firmware has
loaded.
Note that increasing the maximum available log level will increase the firmware
binary size.
This option only applies to logging from the app, the bootloader log level is
fixed at compile time to the separate "Bootloader log verbosity" setting.
config LOG_MAXIMUM_EQUALS_DEFAULT
bool "Same as default"
config LOG_MAXIMUM_LEVEL_ERROR
bool "Error"
depends on LOG_DEFAULT_LEVEL < 1
config LOG_MAXIMUM_LEVEL_WARN
bool "Warning"
depends on LOG_DEFAULT_LEVEL < 2
config LOG_MAXIMUM_LEVEL_INFO
bool "Info"
depends on LOG_DEFAULT_LEVEL < 3
config LOG_MAXIMUM_LEVEL_DEBUG
bool "Debug"
depends on LOG_DEFAULT_LEVEL < 4
config LOG_MAXIMUM_LEVEL_VERBOSE
bool "Verbose"
depends on LOG_DEFAULT_LEVEL < 5
endchoice
config LOG_MAXIMUM_LEVEL
int
default LOG_DEFAULT_LEVEL if LOG_MAXIMUM_EQUALS_DEFAULT
default 0 if LOG_MAXIMUM_LEVEL_NONE
default 1 if LOG_MAXIMUM_LEVEL_ERROR
default 2 if LOG_MAXIMUM_LEVEL_WARN
default 3 if LOG_MAXIMUM_LEVEL_INFO
default 4 if LOG_MAXIMUM_LEVEL_DEBUG
default 5 if LOG_MAXIMUM_LEVEL_VERBOSE
config LOG_MASTER_LEVEL
bool "Enable global master log level"
default "n"
help
Enables an additional global "master" log level check that occurs
before a log tag cache lookup. This is useful if you want to
compile in a lot of logs that are selectable at runtime, but avoid the
performance hit during periods where you don't want log output. Examples
include remote log forwarding, or disabling logs during a time-critical
or CPU-intensive section and re-enabling them later. Results in
larger program size depending on number of logs compiled in.
If enabled, defaults to LOG_DEFAULT_LEVEL and can be set using
esp_log_set_level_master().
This check takes precedence over ESP_LOG_LEVEL_LOCAL.
config LOG_COLORS
bool "Use ANSI terminal colors in log output"
default "y"
help
Enable ANSI terminal color codes in bootloader output.
In order to view these, your terminal program must support ANSI color codes.
choice LOG_TIMESTAMP_SOURCE
prompt "Log Timestamps"
default LOG_TIMESTAMP_SOURCE_RTOS
help
Choose what sort of timestamp is displayed in the log output:
- Milliseconds since boot is calulated from the RTOS tick count multiplied
by the tick period. This time will reset after a software reboot.
e.g. (90000)
- System time is taken from POSIX time functions which use the chip's
RTC and high resoultion timers to maintain an accurate time. The system time is
initialized to 0 on startup, it can be set with an SNTP sync, or with
POSIX time functions. This time will not reset after a software reboot.
e.g. (00:01:30.000)
- 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_RTOS
bool "Milliseconds Since Boot"
config LOG_TIMESTAMP_SOURCE_SYSTEM
bool "System Time"
endchoice
orsource "./Kconfig.format"
endmenu

View File

@ -0,0 +1,36 @@
menu "Format"
config LOG_COLORS
bool "Color"
default y
help
Enable ANSI terminal color codes.
In order to view these, your terminal program must support ANSI color codes.
choice LOG_TIMESTAMP_SOURCE
prompt "Timestamp"
default LOG_TIMESTAMP_SOURCE_RTOS
help
Choose what sort of timestamp is displayed in the log output:
- "Milliseconds since boot" is calculated from the RTOS tick count multiplied
by the tick period. This time will reset after a software reboot.
e.g. (90000)
- "System time" is taken from POSIX time functions which use the chip's
RTC and high resolution timers to maintain an accurate time. The system time is
initialized to 0 on startup, it can be set with an SNTP sync, or with
POSIX time functions. This time will not reset after a software reboot.
e.g. (00:01:30.000)
- 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_RTOS
bool "Milliseconds Since Boot"
config LOG_TIMESTAMP_SOURCE_SYSTEM
bool "System Time"
endchoice # LOG_TIMESTAMP_SOURCE
endmenu

View File

@ -0,0 +1,90 @@
menu "Log Level"
choice LOG_DEFAULT_LEVEL
bool "Default log verbosity"
default LOG_DEFAULT_LEVEL_INFO
help
Specify how much output to see in logs by default.
You can set lower verbosity level at runtime using
esp_log_level_set() function if LOG_DYNAMIC_LEVEL_CONTROL
is enabled.
By default, this setting limits which log statements
are compiled into the program. For example, selecting
"Warning" would mean that changing log level to "Debug"
at runtime will not be possible. To allow increasing log
level above the default at runtime, see the next option.
config LOG_DEFAULT_LEVEL_NONE
bool "No output"
config LOG_DEFAULT_LEVEL_ERROR
bool "Error"
config LOG_DEFAULT_LEVEL_WARN
bool "Warning"
config LOG_DEFAULT_LEVEL_INFO
bool "Info"
config LOG_DEFAULT_LEVEL_DEBUG
bool "Debug"
config LOG_DEFAULT_LEVEL_VERBOSE
bool "Verbose"
endchoice
config LOG_DEFAULT_LEVEL
int
default 0 if LOG_DEFAULT_LEVEL_NONE
default 1 if LOG_DEFAULT_LEVEL_ERROR
default 2 if LOG_DEFAULT_LEVEL_WARN
default 3 if LOG_DEFAULT_LEVEL_INFO
default 4 if LOG_DEFAULT_LEVEL_DEBUG
default 5 if LOG_DEFAULT_LEVEL_VERBOSE
choice LOG_MAXIMUM_LEVEL
bool "Maximum log verbosity"
default LOG_MAXIMUM_EQUALS_DEFAULT
help
This config option sets the highest log verbosity that it's possible to select
at runtime by calling esp_log_level_set(). This level may be higher than
the default verbosity level which is set when the app starts up.
This can be used enable debugging output only at a critical point, for a particular
tag, or to minimize startup time but then enable more logs once the firmware has
loaded.
Note that increasing the maximum available log level will increase the firmware
binary size.
This option only applies to logging from the app, the bootloader log level is
fixed at compile time to the separate "Bootloader log verbosity" setting.
config LOG_MAXIMUM_EQUALS_DEFAULT
bool "Same as default"
config LOG_MAXIMUM_LEVEL_ERROR
bool "Error"
depends on LOG_DEFAULT_LEVEL < 1
config LOG_MAXIMUM_LEVEL_WARN
bool "Warning"
depends on LOG_DEFAULT_LEVEL < 2
config LOG_MAXIMUM_LEVEL_INFO
bool "Info"
depends on LOG_DEFAULT_LEVEL < 3
config LOG_MAXIMUM_LEVEL_DEBUG
bool "Debug"
depends on LOG_DEFAULT_LEVEL < 4
config LOG_MAXIMUM_LEVEL_VERBOSE
bool "Verbose"
depends on LOG_DEFAULT_LEVEL < 5
endchoice
config LOG_MAXIMUM_LEVEL
int
default LOG_DEFAULT_LEVEL if LOG_MAXIMUM_EQUALS_DEFAULT
default 0 if LOG_MAXIMUM_LEVEL_NONE
default 1 if LOG_MAXIMUM_LEVEL_ERROR
default 2 if LOG_MAXIMUM_LEVEL_WARN
default 3 if LOG_MAXIMUM_LEVEL_INFO
default 4 if LOG_MAXIMUM_LEVEL_DEBUG
default 5 if LOG_MAXIMUM_LEVEL_VERBOSE
orsource "./Kconfig.level_settings"
endmenu

View File

@ -0,0 +1,115 @@
menu "Level Settings"
config LOG_MASTER_LEVEL
bool "Enable global master log level"
default "n"
help
Enables an additional global "master" log level check that occurs before a log tag cache
lookup. This is useful if you want to compile in a lot of logs that are selectable at
runtime, but avoid the performance hit during periods where you don't want log output.
Examples include remote log forwarding, or disabling logs during a time-critical or
CPU-intensive section and re-enabling them later. Results in larger program size
depending on number of logs compiled in.
If enabled, defaults to LOG_DEFAULT_LEVEL and can be set using
esp_log_set_level_master(). This check takes precedence over ESP_LOG_LEVEL_LOCAL.
config LOG_DYNAMIC_LEVEL_CONTROL
bool "Enable dynamic log level changes at runtime"
default y
help
Enabling this option allows dynamic changes to the log level at runtime
(using esp_log_level_set()), providing the ability to increase or decrease
the log level during program execution.
If disabled, the log level remains static once set at compile-time and calling
esp_log_level_set() will have no effect.
If binary size is a critical consideration and dynamic log level changes are not needed,
consider disabling this option when LOG_TAG_LEVEL_IMPL_NONE=y to minimize program size.
choice LOG_TAG_LEVEL_IMPL
bool "Method of tag level checks"
default LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST
help
Choose the per-tag log level implementation for the log library. This functionality is used
to enable/disable logs for a particular tag at run time. Applicable only for
application logs (i.e., not bootloader logs).
config LOG_TAG_LEVEL_IMPL_NONE
bool "None"
help
This option disables the ability to set the log level per tag.
The ability to change the log level at runtime depends on LOG_DYNAMIC_LEVEL_CONTROL.
If LOG_DYNAMIC_LEVEL_CONTROL is disabled, then changing the log level at runtime
using `esp_log_level_set()` is not possible.
This implementation is suitable for highly constrained environments.
config LOG_TAG_LEVEL_IMPL_LINKED_LIST
bool "Linked List"
select LOG_DYNAMIC_LEVEL_CONTROL
help
Select this option to use the linked list-only implementation (no cache) for log level retrieval.
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.
The linked list approach compares the whole strings of log tags for finding the log level.
config LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST
bool "Cache + Linked List"
select LOG_DYNAMIC_LEVEL_CONTROL
help
Select this option to use a hybrid mode: cache in combination with the 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 comparing the whole strings.
For less frequently used tags, the linked list is used to search for the log level, which may be
slower for a large number of tags but has lower memory requirements compared to a full cache.
This hybrid approach aims to improve the efficiency of log level retrieval by combining the benefits
of both cache and linked list implementations.
endchoice # LOG_TAG_LEVEL_IMPL
choice LOG_TAG_LEVEL_CACHE_IMPL
bool "Cache implementation"
default LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP
depends on LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST
help
The cache stores recently accessed log tags (address of tag) and their corresponding log levels,
providing faster lookups for frequently used tags. Cache size can be configured using the
LOG_TAG_LEVEL_IMPL_CACHE_SIZE option. The cache approach compares the tag pointers, which is
faster than comparing the whole strings.
config LOG_TAG_LEVEL_CACHE_ARRAY
bool "Array"
help
This option enables the use of a simple array-based cache implementation for storing and
retrieving log tag levels. There is no additional code that reorders the cache for fast lookups.
Suitable for projects where memory usage optimization is crucial and the simplicity of implementation
is preferred.
config LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP
bool "Binary Min-Heap"
help
This option enables the use of a binary min-heap-based cache implementation for efficient
storage and retrieval of log tag levels. It does automatically optimizing cache for fast lookups.
Suitable for projects where speed of lookup is critical and memory usage can accommodate
the overhead of maintaining a binary min-heap structure.
endchoice # LOG_TAG_LEVEL_CACHE_IMPL
config LOG_TAG_LEVEL_IMPL_CACHE_SIZE
int "Log Tag Cache Size"
default 31
depends on LOG_TAG_LEVEL_CACHE_ARRAY || LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP
help
This option sets the size of the cache used for log tag entries. The cache stores recently accessed
log tags and their corresponding log levels, which helps improve the efficiency of log level retrieval.
The value must be a power of 2 minus 1 (e.g., 1, 3, 7, 15, 31, 63, 127, 255, ...)
to ensure proper cache behavior. For LOG_TAG_LEVEL_CACHE_ARRAY option the value can be any,
without restrictions.
Note: A larger cache size can improve lookup performance for frequently used log tags but may consume
more memory. Conversely, a smaller cache size reduces memory usage but may lead to more frequent cache
evictions for less frequently used log tags.
endmenu

View File

@ -177,6 +177,7 @@ TEST_CASE("error log level")
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
TEST_CASE("changing log level")
{
PrintFixture fix(ESP_LOG_INFO);
@ -197,6 +198,7 @@ TEST_CASE("changing log level")
ESP_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
#endif // CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
TEST_CASE("log buffer")
{
@ -302,6 +304,7 @@ TEST_CASE("early error log level")
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
TEST_CASE("changing early log level")
{
PutcFixture fix(ESP_LOG_INFO);
@ -322,6 +325,7 @@ TEST_CASE("changing early log level")
ESP_EARLY_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
#endif // CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
TEST_CASE("esp_log_util_cvt")
{

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
@ -6,5 +6,11 @@ from pytest_embedded import Dut
@pytest.mark.linux
@pytest.mark.host_test
@pytest.mark.parametrize('config', [
'default',
'tag_level_linked_list',
'tag_level_linked_list_and_array_cache',
'tag_level_none',
], indirect=True)
def test_log_linux(dut: Dut) -> None:
dut.expect_exact('All tests passed', timeout=5)

View File

@ -0,0 +1 @@
# This is left intentionally blank. It inherits all configurations from sdkconfg.defaults

View File

@ -0,0 +1 @@
CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST=y

View File

@ -0,0 +1,2 @@
CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y
CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY=y

View File

@ -0,0 +1,2 @@
CONFIG_LOG_TAG_LEVEL_IMPL_NONE=y
CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y

View File

@ -4,12 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ESP_LOG_H__
#define __ESP_LOG_H__
#pragma once
#include <stdint.h>
#include <stdarg.h>
#include <inttypes.h>
#include "esp_log_level.h"
#include "sdkconfig.h"
#include "esp_rom_sys.h"
#include "esp_log_level.h"
@ -21,70 +21,6 @@ extern "C" {
typedef int (*vprintf_like_t)(const char *, va_list);
/**
* @brief Default log level
*
* This is used by the definition of ESP_EARLY_LOGx macros. It is not
* recommended to set this directly, call esp_log_level_set("*", level)
* instead.
*/
extern esp_log_level_t esp_log_default_level;
#if defined(CONFIG_LOG_MASTER_LEVEL) || __DOXYGEN__
/**
* @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
* 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_write and doing a cache hit.
*
* The tradeoff is increased application size.
*
* @param level Master log level
*/
void esp_log_set_level_master(esp_log_level_t level);
/**
* @brief Returns master log level.
* @return Master log level
*/
esp_log_level_t esp_log_get_level_master(void);
#endif //CONFIG_LOG_MASTER_LEVEL
/**
* @brief Set log level for given tag
*
* If logging for given component has already been enabled, changes previous setting.
*
* @note Note that this function can not raise log level above the level set using
* CONFIG_LOG_MAXIMUM_LEVEL setting in menuconfig.
* To raise log level above the default one for a given file, define
* LOG_LOCAL_LEVEL to one of the ESP_LOG_* values, before including
* esp_log.h in this file.
*
* @param tag Tag of the log entries to enable. Must be a non-NULL zero terminated string.
* Value "*" resets log level for all tags to the given value.
*
* @param level Selects log level to enable. Only logs at this and lower verbosity
* levels will be shown.
*/
void esp_log_level_set(const char* tag, esp_log_level_t level);
/**
* @brief Get log level for a given tag, can be used to avoid expensive log statements
*
* @param tag Tag of the log to query current level. Must be a non-NULL zero terminated
* string.
*
* @return The current log level for the given tag
*/
esp_log_level_t esp_log_level_get(const char* tag);
/**
* @brief Set function used to output log entries
*
@ -223,14 +159,6 @@ void esp_log_writev(esp_log_level_t level, const char* tag, const char* format,
#define ESP_EARLY_LOGV( tag, format, ... ) ESP_LOG_EARLY_IMPL(tag, format, ESP_LOG_VERBOSE, V, ##__VA_ARGS__)
#endif // !(defined(__cplusplus) && (__cplusplus > 201703L))
#ifdef BOOTLOADER_BUILD
#define _ESP_LOG_EARLY_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level))
#else
/* 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_default_level >= (log_level))
#endif
#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__); \
@ -336,15 +264,9 @@ void esp_log_writev(esp_log_level_t level, const char* tag, const char* format,
*
* @see ``printf``, ``ESP_LOG_LEVEL``
*/
#ifdef CONFIG_LOG_MASTER_LEVEL
#define ESP_LOG_LEVEL_LOCAL(level, tag, format, ...) do { \
if ( (esp_log_get_level_master() >= level) && (LOG_LOCAL_LEVEL >= level) ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \
if (_ESP_LOG_ENABLED(level)) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \
} while(0)
#else
#define ESP_LOG_LEVEL_LOCAL(level, tag, format, ...) do { \
if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \
} while(0)
#endif //CONFIG_LOG_MASTER_LEVEL
/**
* @brief Macro to output logs when the cache is disabled. Log at ``ESP_LOG_ERROR`` level.
@ -403,5 +325,3 @@ void esp_log_writev(esp_log_level_t level, const char* tag, const char* format,
#ifdef __cplusplus
}
#endif
#endif /* __ESP_LOG_H__ */

View File

@ -6,6 +6,7 @@
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#ifdef __cplusplus
@ -27,16 +28,117 @@ typedef enum {
/** @cond */
// LOG_LOCAL_LEVEL controls what log levels are included in the binary.
#ifndef LOG_LOCAL_LEVEL
#ifndef BOOTLOADER_BUILD
#define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL
#else
#if BOOTLOADER_BUILD
#define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL
#endif
#endif
#else // !BOOTLOADER_BUILD
#define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL
#endif // !BOOTLOADER_BUILD
#endif // LOG_LOCAL_LEVEL
#if BOOTLOADER_BUILD
#define _ESP_LOG_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level))
#define _ESP_LOG_EARLY_ENABLED(log_level) _ESP_LOG_ENABLED(log_level)
#else // !BOOTLOADER_BUILD
#if CONFIG_LOG_MASTER_LEVEL
#define _ESP_LOG_ENABLED(log_level) (esp_log_get_level_master() >= (log_level) && LOG_LOCAL_LEVEL >= (log_level))
#else // !CONFIG_LOG_MASTER_LEVEL
#define _ESP_LOG_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (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))
#endif // !BOOTLOADER_BUILD
/** @endcond */
/**
* @brief Get the default log level.
*
* This function returns the default log level.
* The default log level is used by the definition of ESP_LOGx macros and
* can be overridden for specific tags using `esp_log_level_set("*", level)`.
* If CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=n, changing the default log level is not possible.
*
* @return The default log level.
*/
__attribute__((always_inline))
static inline esp_log_level_t esp_log_get_default_level(void)
{
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
extern esp_log_level_t esp_log_default_level;
return esp_log_default_level;
#else
return (esp_log_level_t) CONFIG_LOG_DEFAULT_LEVEL;
#endif
}
#if defined(CONFIG_LOG_MASTER_LEVEL) || __DOXYGEN__
/**
* @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
* 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.
*
* @note The tradeoff is increased application size.
*
* @param level Master log level
*/
void esp_log_set_level_master(esp_log_level_t level);
/**
* @brief Returns master log level.
* @return Master log level
*/
esp_log_level_t esp_log_get_level_master(void);
#endif // CONFIG_LOG_MASTER_LEVEL
/**
* @brief Set log level for given tag
*
* If logging for given component has already been enabled, changes previous setting.
*
* @note Note that this function can not raise log level above the level set using
* CONFIG_LOG_MAXIMUM_LEVEL setting in menuconfig.
*
* To raise log level above the default one for a given file, define
* LOG_LOCAL_LEVEL to one of the ESP_LOG_* values, before including esp_log.h in this file.
*
* If CONFIG_LOG_DYNAMIC_LEVEL_CONTROL is not selected the static (no-op) implementation of log level is used.
* Changing the log level is not possible, esp_log_level_set does not work.
*
* @param tag Tag of the log entries to enable. Must be a non-NULL zero terminated string.
* Value "*" resets log level for all tags to the given value.
* If the tag is NULL then a silent return happens.
* @param level Selects log level to enable.
* Only logs at this and lower verbosity levels will be shown.
*/
void esp_log_level_set(const char* tag, esp_log_level_t level);
/**
* @brief Get log level for a given tag, can be used to avoid expensive log statements
*
* If CONFIG_LOG_DYNAMIC_LEVEL_CONTROL is not selected the static (no-op) implementation of log level is used.
* Changing the log level is not possible, esp_log_level_set does not work. This function returns the default log level.
*
* @param tag Tag of the log to query current level. Must be a zero terminated string.
* If tag is NULL then the default log level is returned (see esp_log_get_default_level()).
* @return The current log level for the given tag.
*/
esp_log_level_t esp_log_level_get(const char* tag);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "../esp_log_level.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set the default log level.
*
* This function sets the default log level used by the ESP-IDF log library.
* The default log level is used by the definition of ESP_LOGx macros and
* can be overridden for specific tags using `esp_log_level_set("*", level)`.
* This is for internal use only.
*
* If CONFIG_LOG_DYNAMIC_LEVEL_CONTROL is not set, this function does not change the default log level.
*
* @param level The new default log level to set.
*/
void esp_log_set_default_level(esp_log_level_t level);
/**
* @brief Get log level for a given tag, can be used to avoid expensive log statements
*
* The function takes the lock before checking the tag level,
* if the lock wait time exceeds 10 ms (default), then ESP_LOG_NONE is returned.
*
* @param tag Tag of the log to query current level. Must be a zero terminated string.
* If tag is NULL then the default log level is returned (see esp_log_get_default_level()).
* @return The current log level for the given tag.
*/
esp_log_level_t esp_log_level_get_timeout(const char *tag);
#ifdef __cplusplus
}
#endif

View File

@ -1,7 +1,7 @@
[mapping:log]
archive: liblog.a
entries:
log:esp_log_write (noflash)
log_write:esp_log_write (noflash)
log_freertos:esp_log_timestamp (noflash)
log_freertos:esp_log_early_timestamp (noflash)
log_freertos:esp_log_impl_lock (noflash)

View File

@ -1,333 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Log library implementation notes.
*
* Log library stores all tags provided to esp_log_level_set as a linked
* list. See uncached_tag_entry_t structure.
*
* To avoid looking up log level for given tag each time message is
* printed, this library caches pointers to tags. Because the suggested
* way of creating tags uses one 'TAG' constant per file, this caching
* should be effective. Cache is a binary min-heap of cached_tag_entry_t
* items, ordering is done on 'generation' member. In this context,
* generation is an integer which is incremented each time an operation
* with cache is performed. When cache is full, new item is inserted in
* place of an oldest item (that is, with smallest 'generation' value).
* After that, bubble-down operation is performed to fix ordering in the
* min-heap.
*
*/
#include <stdbool.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "esp_log.h"
#include "esp_log_private.h"
#ifndef NDEBUG
// Enable built-in checks in queue.h in debug builds
#define INVARIANTS
// Enable consistency checks and cache statistics in this file.
#define LOG_BUILTIN_CHECKS
#endif
#include "sys/queue.h"
// Number of tags to be cached. Must be 2**n - 1, n >= 2.
#define TAG_CACHE_SIZE 31
#define MAX_GENERATION ((1 << 29) - 1)
typedef struct {
const char *tag;
uint32_t level : 3;
uint32_t generation : 29; // this size should be the same in MAX_GENERATION
} cached_tag_entry_t;
typedef struct uncached_tag_entry_ {
SLIST_ENTRY(uncached_tag_entry_) entries;
uint8_t level; // esp_log_level_t as uint8_t
char tag[0]; // beginning of a zero-terminated string
} uncached_tag_entry_t;
#ifdef CONFIG_LOG_MASTER_LEVEL
esp_log_level_t g_master_log_level = CONFIG_LOG_DEFAULT_LEVEL;
#endif
esp_log_level_t esp_log_default_level = CONFIG_LOG_DEFAULT_LEVEL;
static SLIST_HEAD(log_tags_head, uncached_tag_entry_) s_log_tags = SLIST_HEAD_INITIALIZER(s_log_tags);
static cached_tag_entry_t s_log_cache[TAG_CACHE_SIZE];
static uint32_t s_log_cache_max_generation = 0;
static uint32_t s_log_cache_entry_count = 0;
static vprintf_like_t s_log_print_func = &vprintf;
#ifdef LOG_BUILTIN_CHECKS
static uint32_t s_log_cache_misses = 0;
#endif
static inline bool get_cached_log_level(const char *tag, esp_log_level_t *level);
static inline bool get_uncached_log_level(const char *tag, esp_log_level_t *level);
static inline void add_to_cache(const char *tag, esp_log_level_t level);
static void heap_bubble_down(int index);
static inline void heap_swap(int i, int j);
static inline bool should_output(esp_log_level_t level_for_message, esp_log_level_t level_for_tag);
static inline void clear_log_level_list(void);
static void fix_cache_generation_overflow(void);
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;
}
#ifdef CONFIG_LOG_MASTER_LEVEL
esp_log_level_t esp_log_get_level_master(void)
{
return g_master_log_level;
}
void esp_log_set_level_master(esp_log_level_t level)
{
g_master_log_level = level;
}
#endif // CONFIG_LOG_MASTER_LEVEL
void esp_log_level_set(const char *tag, esp_log_level_t level)
{
esp_log_impl_lock();
// for wildcard tag, remove all linked list items and clear the cache
if (strcmp(tag, "*") == 0) {
esp_log_default_level = level;
clear_log_level_list();
esp_log_impl_unlock();
return;
}
// search for existing tag
uncached_tag_entry_t *it = NULL;
SLIST_FOREACH(it, &s_log_tags, entries) {
if (strcmp(it->tag, tag) == 0) {
// one tag in the linked list matched, update the level
it->level = level;
// quit with it != NULL
break;
}
}
// no existing tag, append new one
if (it == NULL) {
// allocate new linked list entry and append it to the head of the list
size_t tag_len = strlen(tag) + 1;
size_t entry_size = offsetof(uncached_tag_entry_t, tag) + tag_len;
uncached_tag_entry_t *new_entry = (uncached_tag_entry_t *) malloc(entry_size);
if (!new_entry) {
esp_log_impl_unlock();
return;
}
new_entry->level = (uint8_t) level;
memcpy(new_entry->tag, tag, tag_len); // we know the size and strncpy would trigger a compiler warning here
SLIST_INSERT_HEAD(&s_log_tags, new_entry, entries);
}
// search in the cache and update the entry it if exists
for (uint32_t i = 0; i < s_log_cache_entry_count; ++i) {
#ifdef LOG_BUILTIN_CHECKS
assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
#endif
if (strcmp(s_log_cache[i].tag, tag) == 0) {
s_log_cache[i].level = level;
break;
}
}
esp_log_impl_unlock();
}
/* Common code for getting the log level from cache, esp_log_impl_lock()
should be called before calling this function. The function unlocks,
as indicated in the name.
*/
static esp_log_level_t s_log_level_get_and_unlock(const char *tag)
{
esp_log_level_t level_for_tag;
// Look for the tag in cache first, then in the linked list of all tags
if (!get_cached_log_level(tag, &level_for_tag)) {
if (!get_uncached_log_level(tag, &level_for_tag)) {
level_for_tag = esp_log_default_level;
}
add_to_cache(tag, level_for_tag);
#ifdef LOG_BUILTIN_CHECKS
++s_log_cache_misses;
#endif
}
esp_log_impl_unlock();
return level_for_tag;
}
esp_log_level_t esp_log_level_get(const char *tag)
{
esp_log_impl_lock();
return s_log_level_get_and_unlock(tag);
}
void clear_log_level_list(void)
{
uncached_tag_entry_t *it;
while ((it = SLIST_FIRST(&s_log_tags)) != NULL) {
SLIST_REMOVE_HEAD(&s_log_tags, entries);
free(it);
}
s_log_cache_entry_count = 0;
s_log_cache_max_generation = 0;
#ifdef LOG_BUILTIN_CHECKS
s_log_cache_misses = 0;
#endif
}
void esp_log_writev(esp_log_level_t level,
const char *tag,
const char *format,
va_list args)
{
if (!esp_log_impl_lock_timeout()) {
return;
}
esp_log_level_t level_for_tag = s_log_level_get_and_unlock(tag);
if (!should_output(level, level_for_tag)) {
return;
}
(*s_log_print_func)(format, args);
}
void esp_log_write(esp_log_level_t level,
const char *tag,
const char *format, ...)
{
va_list list;
va_start(list, format);
esp_log_writev(level, tag, format, list);
va_end(list);
}
static inline bool get_cached_log_level(const char *tag, esp_log_level_t *level)
{
// Look for `tag` in cache
uint32_t i;
for (i = 0; i < s_log_cache_entry_count; ++i) {
#ifdef LOG_BUILTIN_CHECKS
assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
#endif
if (s_log_cache[i].tag == tag) {
break;
}
}
if (i == s_log_cache_entry_count) { // Not found in cache
return false;
}
// Return level from cache
*level = (esp_log_level_t) s_log_cache[i].level;
// If cache has been filled, start taking ordering into account
// (other options are: dynamically resize cache, add "dummy" entries
// to the cache; this option was chosen because code is much simpler,
// and the unfair behavior of cache will show it self at most once, when
// it has just been filled)
if (s_log_cache_entry_count == TAG_CACHE_SIZE) {
// Update item generation
s_log_cache[i].generation = s_log_cache_max_generation++;
// Restore heap ordering
heap_bubble_down(i);
// Check for generation count wrap and fix if necessary
if (s_log_cache_max_generation == MAX_GENERATION) {
fix_cache_generation_overflow();
}
}
return true;
}
static inline void add_to_cache(const char *tag, esp_log_level_t level)
{
uint32_t generation = s_log_cache_max_generation++;
// First consider the case when cache is not filled yet.
// In this case, just add new entry at the end.
// This happens to satisfy binary min-heap ordering.
if (s_log_cache_entry_count < TAG_CACHE_SIZE) {
s_log_cache[s_log_cache_entry_count] = (cached_tag_entry_t) {
.generation = generation,
.level = level,
.tag = tag
};
++s_log_cache_entry_count;
} else {
// Cache is full, so we replace the oldest entry (which is at index 0
// because this is a min-heap) with the new one, and do bubble-down
// operation to restore min-heap ordering.
s_log_cache[0] = (cached_tag_entry_t) {
.tag = tag,
.level = level,
.generation = generation
};
heap_bubble_down(0);
}
// Check for generation count wrap and fix if necessary
if (s_log_cache_max_generation == MAX_GENERATION) {
fix_cache_generation_overflow();
}
}
static void fix_cache_generation_overflow(void)
{
// Fix generation count wrap
for (uint32_t i = 0; i < s_log_cache_entry_count; ++i) {
s_log_cache[i].generation = i;
}
s_log_cache_max_generation = s_log_cache_entry_count;
}
static inline bool get_uncached_log_level(const char *tag, esp_log_level_t *level)
{
// Walk the linked list of all tags and see if given tag is present in the list.
// This is slow because tags are compared as strings.
uncached_tag_entry_t *it;
SLIST_FOREACH(it, &s_log_tags, entries) {
if (strcmp(tag, it->tag) == 0) {
*level = it->level;
return true;
}
}
return false;
}
static inline bool should_output(esp_log_level_t level_for_message, esp_log_level_t level_for_tag)
{
return level_for_message <= level_for_tag;
}
static void heap_bubble_down(int index)
{
while (index < TAG_CACHE_SIZE / 2) {
int left_index = index * 2 + 1;
int right_index = left_index + 1;
int next = (s_log_cache[left_index].generation < s_log_cache[right_index].generation) ? left_index : right_index;
heap_swap(index, next);
index = next;
}
}
static inline void heap_swap(int i, int j)
{
cached_tag_entry_t tmp = s_log_cache[i];
s_log_cache[i] = s_log_cache[j];
s_log_cache[j] = tmp;
}

View File

@ -13,7 +13,7 @@
#include "esp_cpu.h" // for esp_cpu_get_cycle_count()
#include "esp_compiler.h"
#include "esp_log.h"
#include "esp_log_private.h"
#include "esp_private/log_lock.h"
// Maximum time to wait for the mutex in a logging statement.
//

View File

@ -8,7 +8,7 @@
#include <time.h>
#include <assert.h>
#include <stdint.h>
#include "esp_log_private.h"
#include "esp_private/log_lock.h"
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

View File

@ -5,7 +5,7 @@
*/
#include <assert.h>
#include "esp_log_private.h"
#include "esp_private/log_lock.h"
#include "esp_rom_sys.h"
#include "esp_cpu.h"

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stddef.h>
#include "esp_log_level.h"
#include "esp_attr.h"
#include "sdkconfig.h"
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
esp_log_level_t esp_log_default_level = CONFIG_LOG_DEFAULT_LEVEL;
#endif
#ifdef CONFIG_LOG_MASTER_LEVEL
static esp_log_level_t s_master_log_level = CONFIG_LOG_DEFAULT_LEVEL;
#endif
void esp_log_set_default_level(esp_log_level_t level)
{
#if CONFIG_LOG_DYNAMIC_LEVEL_CONTROL
esp_log_default_level = level;
#else
(void)level;
#endif
}
#ifdef CONFIG_LOG_MASTER_LEVEL
esp_log_level_t esp_log_get_level_master(void)
{
return s_master_log_level;
}
void esp_log_set_level_master(esp_log_level_t level)
{
s_master_log_level = level;
}
#endif // CONFIG_LOG_MASTER_LEVEL

View File

@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file implements a simple array cache approach for storing and retrieving log
* tag levels in the ESP-IDF log library. It provides functions to set and get
* log levels for specific tags using a cache data structure. The cache stores
* recently accessed log tags and their corresponding log levels, providing
* faster lookups for frequently used tags.
*
* The cache-based approach implemented in this file is intended to be used in
* conjunction with the linked list approach for log tag level checks. The two
* approaches complement each other to provide a more efficient and flexible log
* level management system.
*
* The cache size is configurable using the TAG_CACHE_SIZE macro, which determines
* the maximum number of log tags that can be stored in the cache.
* When the cache is full, the oldest entry is replaced with the new one.
*
* The esp_log_cache_set_level function allows users to update the log level
* for a specific tag in the cache. If the tag is not found in the cache, it
* does not add a new entry.
*
* The esp_log_cache_get_level function retrieves the log level for a given tag
* from the cache. If the tag is not found in the cache, it returns a cache miss
* indication. At the top of layer esp_log_cache_add should be called to add the
* tag into the cache.
*
* The cache-based approach aims to improve the efficiency of log level
* retrieval by providing a balance between speed and memory usage. However, the
* cache has a fixed size, and when it becomes full, the cache may evict less
* frequently used tags, resulting in a cache miss.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "esp_log_level.h"
#include "esp_private/log_level.h"
#include "esp_compiler.h"
#include "esp_assert.h"
#include "sdkconfig.h"
#define TAG_CACHE_SIZE (CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE)
#define IS_CACHE_FULL (s_cache_entry_count >= TAG_CACHE_SIZE)
#define ENTRY_COUNT() (IS_CACHE_FULL ? TAG_CACHE_SIZE : s_cache_entry_count)
typedef struct {
const char *tag;
uint32_t level;
} cached_tag_entry_t;
static cached_tag_entry_t s_cache[TAG_CACHE_SIZE];
static uint32_t s_cache_entry_count = 0;
void esp_log_cache_set_level(const char *tag, esp_log_level_t level)
{
// search in the cache and update the entry it if exists
uint32_t entry_count = ENTRY_COUNT();
for (uint32_t i = 0; i < entry_count; ++i) {
if (strcmp(s_cache[i].tag, tag) == 0) {
s_cache[i].level = level;
return;
}
}
}
bool esp_log_cache_get_level(const char *tag, esp_log_level_t *level)
{
// Look for tag in cache
uint32_t entry_count = ENTRY_COUNT();
for (uint32_t i = 0; i < entry_count; ++i) {
if (s_cache[i].tag == tag) {
// Return level from cache
*level = (esp_log_level_t) s_cache[i].level;
return true;
}
}
// Not found in cache
return false;
}
void esp_log_cache_clean(void)
{
s_cache_entry_count = 0;
}
void esp_log_cache_add(const char *tag, esp_log_level_t level)
{
s_cache[s_cache_entry_count++ % TAG_CACHE_SIZE] = (cached_tag_entry_t) {
.level = level,
.tag = tag
};
if (s_cache_entry_count == UINT32_MAX) {
s_cache_entry_count = TAG_CACHE_SIZE;
}
}

View File

@ -0,0 +1,195 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This file implements a cache-based approach for storing and retrieving log
* tag levels in the ESP-IDF log library. It provides functions to set and get
* log levels for specific tags using a cache data structure. The cache stores
* recently accessed log tags and their corresponding log levels, providing
* faster lookups for frequently used tags.
*
* The cache-based approach implemented in this file is intended to be used in
* conjunction with the linked list approach for log tag level checks. The two
* approaches complement each other to provide a more efficient and flexible log
* level management system.
*
* The cache size is configurable using the TAG_CACHE_SIZE macro, which determines
* the maximum number of log tags that can be stored in the cache. When the
* cache is full, a binary min-heap ordering is used to replace the oldest entry
* with a new one.
*
* The esp_log_cache_set_level function allows users to update the log level
* for a specific tag in the cache. If the tag is not found in the cache, it
* does not add a new entry.
*
* The esp_log_cache_get_level function retrieves the log level for a given tag
* from the cache. If the tag is not found in the cache, it returns a cache miss
* indication. At the top of layer esp_log_cache_add should be called to add the
* tag into the cache.
*
* The cache-based approach aims to improve the efficiency of log level
* retrieval by providing a balance between speed and memory usage. However, the
* cache has a fixed size, and when it becomes full, the cache may evict less
* frequently used tags, resulting in a cache miss.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "esp_log_level.h"
#include "esp_private/log_level.h"
#include "esp_compiler.h"
#include "esp_assert.h"
#include "sdkconfig.h"
#ifndef NDEBUG
// Enable consistency checks and cache statistics in this file.
#define LOG_BUILTIN_CHECKS
#endif
ESP_STATIC_ASSERT(((CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE & (CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE + 1)) == 0), "Number of tags to be cached must be 2**n - 1, n >= 2. [1, 3, 7, 15, 31, 63, 127, 255, ...]");
#define TAG_CACHE_SIZE (CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE)
#define MAX_GENERATION ((1 << 29) - 1)
typedef struct {
const char *tag;
uint32_t level : 3;
uint32_t generation : 29; // this size should be the same in MAX_GENERATION
} cached_tag_entry_t;
static cached_tag_entry_t s_log_cache[TAG_CACHE_SIZE];
static uint32_t s_log_cache_max_generation = 0;
static uint32_t s_log_cache_entry_count = 0;
#ifdef LOG_BUILTIN_CHECKS
static uint32_t s_log_cache_misses = 0;
#endif
static void heap_bubble_down(int index);
static inline void heap_swap(int i, int j);
static void fix_cache_generation_overflow(void);
void esp_log_cache_set_level(const char *tag, esp_log_level_t level)
{
// search in the cache and update the entry it if exists
for (uint32_t i = 0; i < s_log_cache_entry_count; ++i) {
#ifdef LOG_BUILTIN_CHECKS
assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
#endif
if (strcmp(s_log_cache[i].tag, tag) == 0) {
s_log_cache[i].level = level;
return;
}
}
}
bool esp_log_cache_get_level(const char *tag, esp_log_level_t *level)
{
// Look for tag in cache
uint32_t i;
for (i = 0; i < s_log_cache_entry_count; ++i) {
#ifdef LOG_BUILTIN_CHECKS
assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
#endif
if (s_log_cache[i].tag == tag) {
break;
}
}
if (i == s_log_cache_entry_count) { // Not found in cache
#ifdef LOG_BUILTIN_CHECKS
++s_log_cache_misses;
#endif
return false;
}
// Return level from cache
*level = (esp_log_level_t) s_log_cache[i].level;
// If cache has been filled, start taking ordering into account
// (other options are: dynamically resize cache, add "dummy" entries
// to the cache; this option was chosen because code is much simpler,
// and the unfair behavior of cache will show it self at most once, when
// it has just been filled)
if (s_log_cache_entry_count == TAG_CACHE_SIZE) {
// Update item generation
s_log_cache[i].generation = s_log_cache_max_generation++;
// Restore heap ordering
heap_bubble_down(i);
// Check for generation count wrap and fix if necessary
if (s_log_cache_max_generation == MAX_GENERATION) {
fix_cache_generation_overflow();
}
}
return true;
}
void esp_log_cache_clean(void)
{
s_log_cache_entry_count = 0;
s_log_cache_max_generation = 0;
#ifdef LOG_BUILTIN_CHECKS
s_log_cache_misses = 0;
#endif
}
void esp_log_cache_add(const char *tag, esp_log_level_t level)
{
uint32_t generation = s_log_cache_max_generation++;
// First consider the case when cache is not filled yet.
// In this case, just add new entry at the end.
// This happens to satisfy binary min-heap ordering.
if (s_log_cache_entry_count < TAG_CACHE_SIZE) {
s_log_cache[s_log_cache_entry_count] = (cached_tag_entry_t) {
.generation = generation,
.level = level,
.tag = tag
};
++s_log_cache_entry_count;
} else {
// Cache is full, so we replace the oldest entry (which is at index 0
// because this is a min-heap) with the new one, and do bubble-down
// operation to restore min-heap ordering.
s_log_cache[0] = (cached_tag_entry_t) {
.tag = tag,
.level = level,
.generation = generation
};
heap_bubble_down(0);
}
// Check for generation count wrap and fix if necessary
if (s_log_cache_max_generation == MAX_GENERATION) {
fix_cache_generation_overflow();
}
}
static void fix_cache_generation_overflow(void)
{
// Fix generation count wrap
for (uint32_t i = 0; i < s_log_cache_entry_count; ++i) {
s_log_cache[i].generation = i;
}
s_log_cache_max_generation = s_log_cache_entry_count;
}
static void heap_bubble_down(int index)
{
while (index < TAG_CACHE_SIZE / 2) {
int left_index = index * 2 + 1;
int right_index = left_index + 1;
int next = (s_log_cache[left_index].generation < s_log_cache[right_index].generation) ? left_index : right_index;
heap_swap(index, next);
index = next;
}
}
static inline void heap_swap(int i, int j)
{
cached_tag_entry_t tmp = s_log_cache[i];
s_log_cache[i] = s_log_cache[j];
s_log_cache[j] = tmp;
}

View File

@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "esp_log_level.h"
/**
* @brief Set the log level for a specific log tag in the cache.
*
* This function searches for the specified log tag in the cache and updates its
* log level with the provided level. If the tag is found in the cache, its log
* level will be updated to the new value. If the tag is not present in the
* cache, this function does nothing.
*
* @param tag The log tag for which to set the log level in the cache.
* @param level The log level to be set for the specified log tag.
*/
void esp_log_cache_set_level(const char *tag, esp_log_level_t level);
/**
* @brief Get the log level for a specific log tag from the cache.
*
* This function retrieves the log level for the specified log tag from the
* cache. If the tag is found in the cache, its corresponding log level will be
* returned via the `level` pointer argument. If the tag is not present in the
* cache, this function returns `false` to indicate a cache miss, and the
* `level` value remains unchanged.
*
* @param tag The log tag for which to retrieve the log level from the cache.
* @param level Pointer to a variable where the retrieved log level will be
* stored.
* @return `true` if the log level was successfully retrieved from the cache,
* `false` if the tag was not found in the cache.
*/
bool esp_log_cache_get_level(const char *tag, esp_log_level_t *level);
/**
* @brief Clear the log level cache.
*
* This function clears the log level cache, removing all entries from the cache
* and resetting its state. After calling this function, the cache will be empty
* and ready to store new log tag entries.
*/
void esp_log_cache_clean(void);
/**
* @brief Add a log tag entry to the cache.
*
* This function adds a new log tag entry to the cache with the provided log
* level. If the cache is not full, the new entry will be appended to the end of
* the cache. If the cache is already full, the oldest entry in the cache will
* be replaced with the new entry, and the cache will be rearranged to maintain
* binary min-heap ordering.
*
* @param tag The log tag to be added to the cache.
* @param level The log level to be associated with the log tag in the cache.
*/
void esp_log_cache_add(const char *tag, esp_log_level_t level);

View File

@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file implements a linked list-based approach for storing and retrieving
* log tag levels in the ESP-IDF log library. It provides functions to set and
* get log levels for specific tags using a linked list data structure. The
* linked list stores log tags and their corresponding log levels as individual
* nodes.
*
* The esp_log_linked_list_set_level() function allows users to set the log level
* for a specific tag, and if the tag does not exist in the linked list, it adds
* a new entry for that tag. The esp_log_linked_list_get_level() function
* retrieves the log level for a given tag from the linked list. Please note that
* this approach searches through the entire tag string to find a matching tag
* during log level retrieval.
*
* The esp_log_linked_list_clean() function enables users to clear the entire linked
* list, freeing the memory occupied by the list entries.
*
* The linked list approach provides a simple and memory-efficient method for
* managing log levels, making it suitable for use in environments with limited
* resources. However, due to its linear search nature, log level retrieval from
* the linked list may become slower as the number of tags increases.
*/
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef NDEBUG
// Enable built-in checks in queue.h in debug builds
#define INVARIANTS
#endif
#include "sys/queue.h"
#include "log_linked_list.h"
#include "esp_err.h"
#include "sdkconfig.h"
typedef struct uncached_tag_entry_ {
SLIST_ENTRY(uncached_tag_entry_) entries;
uint8_t level; // esp_log_level_t as uint8_t
char tag[0]; // beginning of a zero-terminated string
} uncached_tag_entry_t;
static SLIST_HEAD(log_tags_head, uncached_tag_entry_) s_log_tags = SLIST_HEAD_INITIALIZER(s_log_tags);
static inline esp_err_t add_to_list(const char *tag, esp_log_level_t level);
static inline bool set_log_level(const char *tag, esp_log_level_t level);
bool esp_log_linked_list_set_level(const char *tag, esp_log_level_t level)
{
if (!set_log_level(tag, level)) {
// no existing tag, append new one
return add_to_list(tag, level) == ESP_OK;
}
return true;
}
// Getting the log level from linked list
// if tag is not found then returns false.
bool esp_log_linked_list_get_level(const char *tag, esp_log_level_t *level)
{
// Walk the linked list of all tags and see if given tag is present in the list.
// This is slow because tags are compared as strings.
uncached_tag_entry_t *it;
SLIST_FOREACH(it, &s_log_tags, entries) {
if (strcmp(tag, it->tag) == 0) {
*level = it->level;
return true;
}
}
return false;
}
void esp_log_linked_list_clean(void)
{
uncached_tag_entry_t *it;
while ((it = SLIST_FIRST(&s_log_tags)) != NULL) {
SLIST_REMOVE_HEAD(&s_log_tags, entries);
free(it);
}
}
static inline esp_err_t add_to_list(const char *tag, esp_log_level_t level)
{
// allocate new linked list entry and append it to the head of the list
size_t tag_len = strlen(tag) + 1;
size_t entry_size = offsetof(uncached_tag_entry_t, tag) + tag_len;
uncached_tag_entry_t *new_entry = (uncached_tag_entry_t *) malloc(entry_size);
if (!new_entry) {
return ESP_ERR_NO_MEM;
}
new_entry->level = (uint8_t) level;
memcpy(new_entry->tag, tag, tag_len);
SLIST_INSERT_HEAD(&s_log_tags, new_entry, entries);
return ESP_OK;
}
static inline bool set_log_level(const char *tag, esp_log_level_t level)
{
// search for existing tag
uncached_tag_entry_t *it = NULL;
SLIST_FOREACH(it, &s_log_tags, entries) {
if (strcmp(it->tag, tag) == 0) {
// one tag in the linked list matched, update the level
it->level = level;
return true;
}
}
return false;
}

View File

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "esp_log_level.h"
/**
* @brief Set the log level for a specific log tag using the linked list
* approach.
*
* If the tag already exists in the linked list, its log level will be updated
* with the new value. If the tag is not found in the linked list, a new entry
* will be appended to the linked list with the provided log level.
*
* @param tag The log tag for which to set the log level.
* @param level The log level to be set for the specified log tag.
* @return true If the log level was successfully set or updated,
* false Otherwise.
*/
bool esp_log_linked_list_set_level(const char *tag, esp_log_level_t level);
/**
* @brief Get the log level for a specific log tag using the linked list
* approach.
*
* If the tag is found in the linked list, its corresponding log level will be
* returned.
*
* @param tag The log tag for which to retrieve the log level.
* @param level Pointer to a variable where the retrieved log level will be
* stored.
* @return true if the log level was successfully retrieved from the linked list,
* false if the tag was not found in the linked list.
*/
bool esp_log_linked_list_get_level(const char *tag, esp_log_level_t *level);
/**
* @brief Clears the linked list of all tags and frees the allocated memory for
* each tag entry.
*
* This function clears the linked list of all tags that have been set using
* esp_log_level_set(). It also frees the memory allocated for each tag entry in
* the linked list.
*/
void esp_log_linked_list_clean(void);

View File

@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "esp_log_level.h"
#include "esp_private/log_lock.h"
#include "esp_private/log_level.h"
#include "sdkconfig.h"
#if CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST || CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST
#include "linked_list/log_linked_list.h"
#endif
#if CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY || CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP
#define CACHE_ENABLED 1
#include "cache/log_cache.h"
#else
#define CACHE_ENABLED 0
#endif
#if !CONFIG_LOG_TAG_LEVEL_IMPL_NONE
static inline void log_level_set(const char *tag, esp_log_level_t level);
static esp_log_level_t log_level_get(const char *tag, bool timeout);
static inline void log_level_set(const char *tag, esp_log_level_t level)
{
if (tag == NULL) {
// expected non-NULL zero terminated string.
return;
}
esp_log_impl_lock();
// for wildcard tag, remove all linked list items and clear the cache
if (strcmp(tag, "*") == 0) {
esp_log_set_default_level(level);
esp_log_linked_list_clean();
#if CACHE_ENABLED
esp_log_cache_clean();
#endif
} else {
__attribute__((unused)) bool success = esp_log_linked_list_set_level(tag, level);
#if CACHE_ENABLED
if (success) {
esp_log_cache_set_level(tag, level);
}
#endif
}
esp_log_impl_unlock();
}
static esp_log_level_t log_level_get(const char *tag, bool timeout)
{
esp_log_level_t level_for_tag = esp_log_get_default_level();
if (tag == NULL) {
return level_for_tag;
}
if (timeout) {
if (esp_log_impl_lock_timeout() == false) {
return ESP_LOG_NONE;
}
} else {
esp_log_impl_lock();
}
bool search_in_linked_list = !CACHE_ENABLED;
#if CACHE_ENABLED
bool cache_miss = !esp_log_cache_get_level(tag, &level_for_tag);
if (cache_miss) {
search_in_linked_list = true;
esp_log_cache_add(tag, level_for_tag);
}
#endif // CACHE_ENABLED
if (search_in_linked_list) {
esp_log_linked_list_get_level(tag, &level_for_tag);
}
esp_log_impl_unlock();
return level_for_tag;
}
#endif // !CONFIG_LOG_TAG_LEVEL_IMPL_NONE
void esp_log_level_set(const char *tag, esp_log_level_t level)
{
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
(void)tag;
esp_log_set_default_level(level);
#else
log_level_set(tag, level);
#endif
}
esp_log_level_t esp_log_level_get_timeout(const char *tag)
{
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
(void)tag;
return esp_log_get_default_level();
#else
return log_level_get(tag, true);
#endif
}
esp_log_level_t esp_log_level_get(const char *tag)
{
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
(void)tag;
return esp_log_get_default_level();
#else
return log_level_get(tag, false);
#endif
}

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "esp_log.h"
#include "esp_private/log_lock.h"
#include "esp_private/log_level.h"
#include "sdkconfig.h"
static vprintf_like_t s_log_print_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;
}
void esp_log_writev(esp_log_level_t level,
const char *tag,
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);
}
}
void esp_log_write(esp_log_level_t level,
const char *tag,
const char *format, ...)
{
va_list list;
va_start(list, format);
esp_log_writev(level, tag, format, list);
va_end(list);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,14 @@
#include "esp_timer.h"
#include "sdkconfig.h"
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
#define EXPECTED_US 3
#define DELTA_US 3
#else
#define EXPECTED_US 11
#define DELTA_US 5
#endif
static const char * TAG = "log_test";
static int calc_time_of_logging(int iterations)
@ -43,14 +51,14 @@ TEST_CASE("test master logging level performance", "[log]")
TEST_ASSERT_INT_WITHIN(100, 150, calc_time_of_logging(ITERATIONS));
#else
esp_log_level_set("*", ESP_LOG_NONE);
TEST_ASSERT_INT_WITHIN(5, 11, calc_time_of_logging(ITERATIONS) / ITERATIONS);
TEST_ASSERT_INT_WITHIN(DELTA_US, EXPECTED_US, calc_time_of_logging(ITERATIONS) / ITERATIONS);
#endif
esp_log_level_set("*", ESP_LOG_NONE);
#ifdef CONFIG_LOG_MASTER_LEVEL
esp_log_set_level_master(ESP_LOG_DEBUG);
#endif
TEST_ASSERT_INT_WITHIN(5, 11, calc_time_of_logging(ITERATIONS) / ITERATIONS);
TEST_ASSERT_INT_WITHIN(DELTA_US, EXPECTED_US, calc_time_of_logging(ITERATIONS) / ITERATIONS);
esp_log_level_set("*", ESP_LOG_INFO);
ESP_LOGI(TAG, "End");

View File

@ -0,0 +1,145 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "unity.h"
#include "esp_rom_uart.h"
#include "sdkconfig.h"
/*
* The debug logs in this file will be compiled into a binary file,
* even though CONFIG_LOG_DEFAULT_LEVEL and CONFIG_LOG_MAXIMUM_LEVEL
* are at INFO level.
*/
#define LOG_LOCAL_LEVEL (ESP_LOG_DEBUG)
#include "esp_log.h"
static const char * TAG1 = "ESP_LOG";
static const char * TAG2 = "ESP_EARLY_LOG";
#define BUFFER_SIZE (256)
static unsigned s_counter = 0;
static char s_print_buffer[BUFFER_SIZE];
static void reset_buffer(void)
{
s_counter = 0;
s_print_buffer[0] = 0;
}
static unsigned get_counter(void)
{
return s_counter;
}
static char* get_buffer(void)
{
return s_print_buffer;
}
void putc_to_buffer(char c)
{
s_print_buffer[s_counter++] = c;
if (c == '\n') {
s_print_buffer[s_counter] = 0;
printf("%s", s_print_buffer);
}
}
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);
s_counter += ret;
assert(s_counter < BUFFER_SIZE);
return ret;
}
TEST_CASE("LOG_LOCAL_LEVEL can be re-defined", "[log]")
{
TEST_ASSERT_EQUAL(CONFIG_LOG_DEFAULT_LEVEL, ESP_LOG_INFO);
TEST_ASSERT_EQUAL(CONFIG_LOG_MAXIMUM_LEVEL, ESP_LOG_INFO);
#if CONFIG_LOG_MASTER_LEVEL
// ignore the master log feature in this test
esp_log_set_level_master(ESP_LOG_VERBOSE);
#endif
/*
* LOG_LOCAL_LEVEL == ESP_LOG_DEBUG. It defines which log levels
* are included in binary from a particular file.
* They are not enabled by default to print. Use esp_log_level_set()
* to raise the log level.
*/
// for ESP_LOGx
vprintf_like_t old_vprintf = esp_log_set_vprintf(print_to_buffer);
reset_buffer();
ESP_LOGV(TAG1, "No VERBOSE log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
ESP_LOGD(TAG1, "There is a debug log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
esp_log_level_set("*", ESP_LOG_DEBUG);
ESP_LOGD(TAG1, "There is a debug log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is a debug log"));
reset_buffer();
ESP_LOGI(TAG1, "There is an info log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is an info log"));
esp_log_level_set("*", ESP_LOG_ERROR);
reset_buffer();
ESP_LOGI(TAG1, "There is an info log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
ESP_LOGE(TAG1, "There is an error log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is an error log"));
esp_log_set_vprintf(old_vprintf);
esp_log_level_set("*", ESP_LOG_INFO);
// for ESP_EARLY_LOGx
esp_rom_install_channel_putc(1, putc_to_buffer);
reset_buffer();
ESP_EARLY_LOGV(TAG2, "No VERBOSE log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
ESP_EARLY_LOGD(TAG1, "There is a debug log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
esp_log_level_set("*", ESP_LOG_DEBUG);
ESP_EARLY_LOGD(TAG1, "There is a debug log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is a debug log"));
reset_buffer();
ESP_EARLY_LOGI(TAG2, "There is an info log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is an info log"));
esp_log_level_set("*", ESP_LOG_ERROR);
reset_buffer();
ESP_EARLY_LOGI(TAG2, "There is an info log");
TEST_ASSERT_EQUAL(0, get_counter());
reset_buffer();
ESP_EARLY_LOGE(TAG2, "There is an error log");
TEST_ASSERT_GREATER_THAN(17, get_counter());
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "There is an error log"));
esp_rom_install_uart_printf();
esp_log_level_set("*", ESP_LOG_INFO);
}

View File

@ -1,4 +1,5 @@
#define CONFIG_NVS_ENCRYPTION 1
#define CONFIG_LOG_DEFAULT_LEVEL 3
#define CONFIG_LOG_MAXIMUM_LEVEL 3
#define CONFIG_LOG_TIMESTAMP_SOURCE_RTOS 1
#define CONFIG_IDF_TARGET_LINUX 1

View File

@ -382,6 +382,7 @@ The following configuration options reduces the final binary size of almost any
- Set :ref:`CONFIG_COMPILER_OPTIMIZATION` to ``Optimize for size (-Os)``. In some cases, ``Optimize for performance (-O2)`` will also reduce the binary size compared to the default. Note that if your code contains C or C++ Undefined Behavior then increasing the compiler optimization level may expose bugs that otherwise do not happen.
- Reduce the compiled-in log output by lowering the app :ref:`CONFIG_LOG_DEFAULT_LEVEL`. If the :ref:`CONFIG_LOG_MAXIMUM_LEVEL` is changed from the default then this setting controls the binary size instead. Reducing compiled-in logging reduces the number of strings in the binary, and also the code size of the calls to logging functions.
- If your application doesn't 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` and changing :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`. It reduces IRAM usage by approximately 260 bytes, DRAM usage by approximately 264 bytes, and Flash usage by approximately 1K bytes compared to the default option, it also speeds up logging.
- Set the :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL` to ``Silent``. This avoids compiling in a dedicated assertion string and source file name for each assert that may fail. It is still possible to find the failed assert in the code by looking at the memory address where the assertion failed.
- Besides the :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL`, you can disable or silent the assertion for the HAL component separately by setting :ref:`CONFIG_HAL_DEFAULT_ASSERTION_LEVEL`. It is to notice that ESP-IDF lowers the HAL assertion level in bootloader to be silent even if :ref:`CONFIG_HAL_DEFAULT_ASSERTION_LEVEL` is set to full-assertion level. This is to reduce the bootloader size.
- Setting :ref:`CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT` removes specific error messages for particular internal ESP-IDF error check macros. This may make it harder to debug some error conditions by reading the log output.

View File

@ -98,6 +98,7 @@ Although standard output is buffered, it is possible for an application to be li
- Reduce the volume of log output by lowering the app :ref:`CONFIG_LOG_DEFAULT_LEVEL` (the equivalent bootloader setting is :ref:`CONFIG_BOOTLOADER_LOG_LEVEL`). This also reduces the binary size, and saves some CPU time spent on string formatting.
:not SOC_USB_OTG_SUPPORTED: - Increase the speed of logging output by increasing the :ref:`CONFIG_ESP_CONSOLE_UART_BAUDRATE`.
:SOC_USB_OTG_SUPPORTED: - Increase the speed of logging output by increasing the :ref:`CONFIG_ESP_CONSOLE_UART_BAUDRATE`. However, if you are using internal USB-CDC, the serial throughput is not dependent on the configured baud rate.
- If your application doesn't 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` and changing :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`. It helps to reduce memory usage and also contributes to speeding up log operations in your application about 10 times.
Not Recommended
^^^^^^^^^^^^^^^

View File

@ -10,7 +10,7 @@ The logging library provides three ways for setting log verbosity:
- **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.
- **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.
There are the following verbosity levels:
@ -72,6 +72,9 @@ At component scope, define it in the component CMakeLists:
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
@ -86,6 +89,19 @@ To configure logging output per module at runtime, add calls to the function :cp
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 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:
- "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.
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 doesn't 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 1K bytes 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.
.. note::
The "Linked list" and "Cache + Linked List" options will automatically enable :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL`.
Master Logging Level
^^^^^^^^^^^^^^^^^^^^