diff --git a/components/log/Kconfig b/components/log/Kconfig index 43e3a523..639fd520 100644 --- a/components/log/Kconfig +++ b/components/log/Kconfig @@ -44,5 +44,10 @@ config LOG_COLORS In order to view these, your terminal program must support ANSI color codes. +config LOG_SET_LEVEL + bool "Enable log set level" + default n + help + Enable this option, user can set tag level. endmenu diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index b2e4b6af..05283386 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -41,6 +41,30 @@ typedef enum { typedef int (*putchar_like_t)(int ch); +#ifdef CONFIG_LOG_SET_LEVEL +/** + * @brief Set log level for given tag + * + * If logging for given component has already been enabled, changes previous setting. + * + * Note that this function can not raise log level above the level set using + * CONFIG_LOG_DEFAULT_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); +#else +#define esp_log_level_set(tag, level) +#endif /* CONFIG_LOG_SET_LEVEL */ + /** * @brief Set function used to output log entries * diff --git a/components/log/log.c b/components/log/log.c index d1842192..fc59a4c1 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include "esp_libc.h" @@ -55,9 +58,126 @@ static uint32_t IRAM_ATTR esp_log_early_timestamp() } #ifndef BOOTLOADER_BUILD + +#ifdef CONFIG_LOG_SET_LEVEL +#define GLOBAL_TAG "*" + +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 esp_log_level_t s_global_tag_level = ESP_LOG_VERBOSE; +static SLIST_HEAD(log_tags_head , uncached_tag_entry_) s_log_uncached_tags = SLIST_HEAD_INITIALIZER(s_log_uncached_tags); +static uncached_tag_entry_t *s_uncached_tag_entry_prev; +#endif /* CONFIG_LOG_SET_LEVEL */ + static _lock_t s_lock; static putchar_like_t s_putchar_func = &putchar; +#ifdef CONFIG_LOG_SET_LEVEL +/** + * @brief get entry by inputting tag + */ +static bool esp_log_get_tag_entry(const char *tag, uncached_tag_entry_t **entry) +{ + uncached_tag_entry_t *it = NULL; + + SLIST_FOREACH(it, &s_log_uncached_tags, entries) { + if (!strcmp(it->tag, tag)) { + //one tag in the linked list match, update the level + *entry = it; + //quit with it != NULL + return true; + } + } + + return false; +} + +static void clear_log_level_list(void) +{ + uncached_tag_entry_t *it = NULL; + + SLIST_FOREACH(it, &s_log_uncached_tags, entries) { + SLIST_REMOVE(&s_log_uncached_tags, it, uncached_tag_entry_, entries); + free(it); + } +} + +/** + * @brief get level by inputting tag + */ +static esp_log_level_t esp_log_get_level(const char *tag) +{ + esp_log_level_t out_level; + uncached_tag_entry_t *entry; + + _lock_acquire_recursive(&s_lock); + + if (s_uncached_tag_entry_prev && !strcmp(s_uncached_tag_entry_prev->tag, tag)) { + out_level = (esp_log_level_t)s_uncached_tag_entry_prev->level; + goto exit; + } + + if (esp_log_get_tag_entry(tag, &entry) == true) { + out_level = (esp_log_level_t)entry->level; + s_uncached_tag_entry_prev = entry; + goto exit; + } else + out_level = s_global_tag_level; + +exit: + _lock_release_recursive(&s_lock); + return out_level; +} + +/** + * @brief check if system should output data + */ +static inline bool should_output(esp_log_level_t user_level, esp_log_level_t system_level) +{ + return user_level <= system_level; +} + +/** + * @brief set log level for given tag + */ +void esp_log_level_set(const char *tag, esp_log_level_t level) +{ + size_t bytes; + uncached_tag_entry_t *entry, *new_entry; + + _lock_acquire_recursive(&s_lock); + + if (!strcmp(tag, GLOBAL_TAG)) { + s_global_tag_level = level; + clear_log_level_list(); + goto exit; + } + + if (esp_log_get_tag_entry(tag, &entry) == true) { + entry->level = level; + goto exit; + } + + bytes = strlen(tag) + 1; + + new_entry = malloc(sizeof(uncached_tag_entry_t) + bytes); + if (!new_entry) + goto exit; + + new_entry->level = level; + memcpy(new_entry->tag, tag, bytes); + + SLIST_INSERT_HEAD(&s_log_uncached_tags, new_entry, entries); + +exit: + _lock_release_recursive(&s_lock); +} +#endif /* CONFIG_LOG_SET_LEVEL */ + static int esp_log_write_str(const char *s) { int ret; @@ -114,9 +234,15 @@ void esp_log_write(esp_log_level_t level, const char *tag, const char *fmt, ... int ret; va_list va; char *pbuf; - char prefix = level >= ESP_LOG_MAX ? 'N' : s_log_prefix[level]; + char prefix; + + _lock_acquire_recursive(&s_lock); + +#ifdef CONFIG_LOG_SET_LEVEL + if (!should_output(level, esp_log_get_level(tag))) + goto exit; +#endif - _lock_acquire(&s_lock); #ifdef CONFIG_LOG_COLORS static char buf[16]; uint32_t color = level >= ESP_LOG_MAX ? 0 : s_log_color[level]; @@ -128,7 +254,7 @@ void esp_log_write(esp_log_level_t level, const char *tag, const char *fmt, ... goto exit; } #endif - + prefix = level >= ESP_LOG_MAX ? 'N' : s_log_prefix[level]; ret = asprintf(&pbuf, "%c (%d) %s: ", prefix, esp_log_timestamp(), tag); if (ret < 0) goto out; @@ -159,7 +285,7 @@ out: s_putchar_func('\n'); exit: - _lock_release(&s_lock); + _lock_release_recursive(&s_lock); } /** @@ -169,10 +295,10 @@ putchar_like_t esp_log_set_putchar(putchar_like_t func) { putchar_like_t tmp; - _lock_acquire(&s_lock); + _lock_acquire_recursive(&s_lock); tmp = s_putchar_func; s_putchar_func = func; - _lock_release(&s_lock); + _lock_release_recursive(&s_lock); return tmp; }