/* * This file is part of the EasyLogger Library. * * Copyright (c) 2015-2018, Armink, * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * 'Software'), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Function: Initialize function and other general function. * Created on: 2015-04-28 */ #define LOG_TAG "elog" #include #include #include #include #include #if !defined(ELOG_OUTPUT_LVL) #error "Please configure static output log level (in elog_cfg.h)" #endif #if !defined(ELOG_LINE_NUM_MAX_LEN) #error "Please configure output line number max length (in elog_cfg.h)" #endif #if !defined(ELOG_LINE_BUF_SIZE) #error "Please configure buffer size for every line's log (in elog_cfg.h)" #endif #if !defined(ELOG_FILTER_TAG_MAX_LEN) #error "Please configure output filter's tag max length (in elog_cfg.h)" #endif #if !defined(ELOG_FILTER_KW_MAX_LEN) #error "Please configure output filter's keyword max length (in elog_cfg.h)" #endif #if !defined(ELOG_NEWLINE_SIGN) #error "Please configure output newline sign (in elog_cfg.h)" #endif /* output filter's tag level max num */ #ifndef ELOG_FILTER_TAG_LVL_MAX_NUM #define ELOG_FILTER_TAG_LVL_MAX_NUM 4 #endif #ifdef ELOG_COLOR_ENABLE /** * CSI(Control Sequence Introducer/Initiator) sign * more information on https://en.wikipedia.org/wiki/ANSI_escape_code */ #define CSI_START "\033[" #define CSI_END "\033[0m" /* output log front color */ #define F_BLACK "30;" #define F_RED "31;" #define F_GREEN "32;" #define F_YELLOW "33;" #define F_BLUE "34;" #define F_MAGENTA "35;" #define F_CYAN "36;" #define F_WHITE "37;" /* output log background color */ #define B_NULL #define B_BLACK "40;" #define B_RED "41;" #define B_GREEN "42;" #define B_YELLOW "43;" #define B_BLUE "44;" #define B_MAGENTA "45;" #define B_CYAN "46;" #define B_WHITE "47;" /* output log fonts style */ #define S_BOLD "1m" #define S_UNDERLINE "4m" #define S_BLINK "5m" #define S_NORMAL "22m" /* output log default color definition: [front color] + [background color] + [show style] */ /* color output info */ static const char *color_output_info[] = { [ELOG_LVL_ASSERT] = (F_MAGENTA B_NULL S_NORMAL), [ELOG_LVL_ERROR] = (F_RED B_NULL S_NORMAL), [ELOG_LVL_WARN] = (F_YELLOW B_NULL S_NORMAL), [ELOG_LVL_INFO] = (F_CYAN B_NULL S_NORMAL), [ELOG_LVL_DEBUG] = (F_GREEN B_NULL S_NORMAL), [ELOG_LVL_VERBOSE] = (F_BLUE B_NULL S_NORMAL), }; #endif /* ELOG_COLOR_ENABLE */ /* EasyLogger object */ static EasyLogger elog; /* every line log's buffer */ static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 }; /* level output info */ static const char *level_output_info[] = { [ELOG_LVL_ASSERT] = "A/", [ELOG_LVL_ERROR] = "E/", [ELOG_LVL_WARN] = "W/", [ELOG_LVL_INFO] = "I/", [ELOG_LVL_DEBUG] = "D/", [ELOG_LVL_VERBOSE] = "V/", }; static bool get_fmt_enabled(uint8_t level, size_t set); static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg); static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg); static void elog_set_filter_tag_lvl_default(void); extern void elog_port_output(const char *log, size_t size); extern void elog_port_output_lock(void); extern void elog_port_output_unlock(void); /** * EasyLogger initialize. * * @return result */ int elog_init(void) { extern int elog_port_init(void); extern int elog_async_init(void); int result = 0; if (elog.init_ok == true) { return result; } /* port initialize */ result = elog_port_init(); if (result != 0) { return result; } #ifdef ELOG_ASYNC_OUTPUT_ENABLE result = elog_async_init(); if (result != 0) { return result; } #endif /* enable the output lock */ elog_output_lock_enabled(true); /* output locked status initialize */ elog.output_is_locked_before_enable = false; elog.output_is_locked_before_disable = false; #ifdef ELOG_COLOR_ENABLE /* enable text color by default */ elog_set_text_color_enabled(true); #endif /* set level is ELOG_LVL_VERBOSE */ elog_set_filter_lvl(ELOG_LVL_VERBOSE); /* set tag_level to default val */ elog_set_filter_tag_lvl_default(); elog.init_ok = true; return result; } /** * EasyLogger deinitialize. * */ void elog_deinit(void) { extern int elog_port_deinit(void); extern int elog_async_deinit(void); if (!elog.init_ok) { return ; } #ifdef ELOG_ASYNC_OUTPUT_ENABLE elog_async_deinit(); #endif /* port deinitialize */ elog_port_deinit(); elog.init_ok = false; } /** * EasyLogger start after initialize. */ void elog_start(void) { if (!elog.init_ok) { return ; } /* enable output */ elog_set_output_enabled(true); #if defined(ELOG_ASYNC_OUTPUT_ENABLE) elog_async_enabled(true); #elif defined(ELOG_BUF_OUTPUT_ENABLE) elog_buf_enabled(true); #endif /* show version */ log_i("EasyLogger V%s is initialize success.", ELOG_SW_VERSION); } /** * EasyLogger stop after initialize. */ void elog_stop(void) { if (!elog.init_ok) { return ; } /* disable output */ elog_set_output_enabled(false); #if defined(ELOG_ASYNC_OUTPUT_ENABLE) elog_async_enabled(false); #elif defined(ELOG_BUF_OUTPUT_ENABLE) elog_buf_enabled(false); #endif /* show version */ log_i("EasyLogger V%s is deinitialize success.", ELOG_SW_VERSION); } /** * set output enable or disable * * @param enabled TRUE: enable FALSE: disable */ void elog_set_output_enabled(bool enabled) { assert((enabled == false) || (enabled == true)); elog.output_enabled = enabled; } #ifdef ELOG_COLOR_ENABLE /** * set log text color enable or disable * * @param enabled TRUE: enable FALSE:disable */ void elog_set_text_color_enabled(bool enabled) { assert((enabled == false) || (enabled == true)); elog.text_color_enabled = enabled; } /** * get log text color enable status * * @return enable or disable */ bool elog_get_text_color_enabled(void) { return elog.text_color_enabled; } #endif /* ELOG_COLOR_ENABLE */ /** * get output is enable or disable * * @return enable or disable */ bool elog_get_output_enabled(void) { return elog.output_enabled; } /** * set log output format. only enable or disable * * @param level level * @param set format set */ void elog_set_fmt(uint8_t level, size_t set) { assert(level <= ELOG_LVL_VERBOSE); elog.enabled_fmt_set[level] = set; } /** * set log filter all parameter * * @param level level * @param tag tag * @param keyword keyword */ void elog_set_filter(uint8_t level, const char *tag, const char *keyword) { assert(level <= ELOG_LVL_VERBOSE); elog_set_filter_lvl(level); elog_set_filter_tag(tag); elog_set_filter_kw(keyword); } /** * set log filter's level * * @param level level */ void elog_set_filter_lvl(uint8_t level) { assert(level <= ELOG_LVL_VERBOSE); elog.filter.level = level; } /** * set log filter's tag * * @param tag tag */ void elog_set_filter_tag(const char *tag) { strncpy(elog.filter.tag, tag, ELOG_FILTER_TAG_MAX_LEN); } /** * set log filter's keyword * * @param keyword keyword */ void elog_set_filter_kw(const char *keyword) { strncpy(elog.filter.keyword, keyword, ELOG_FILTER_KW_MAX_LEN); } /** * lock output */ void elog_output_lock(void) { if (elog.output_lock_enabled) { elog_port_output_lock(); elog.output_is_locked_before_disable = true; } else { elog.output_is_locked_before_enable = true; } } /** * unlock output */ void elog_output_unlock(void) { if (elog.output_lock_enabled) { elog_port_output_unlock(); elog.output_is_locked_before_disable = false; } else { elog.output_is_locked_before_enable = false; } } /** * set log filter's tag level val to default */ static void elog_set_filter_tag_lvl_default(void) { uint8_t i = 0; for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; elog.filter.tag_lvl[i].tag_use_flag = false; } } /** * Set the filter's level by different tag. * The log on this tag which level is less than it will stop output. * * example: * // the example tag log enter silent mode * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_SILENT); * // the example tag log which level is less than INFO level will stop output * elog_set_filter_tag_lvl("example", ELOG_LVL_INFO); * // remove example tag's level filter, all level log will resume output * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_ALL); * * @param tag log tag * @param level The filter level. When the level is ELOG_FILTER_LVL_SILENT, the log enter silent mode. * When the level is ELOG_FILTER_LVL_ALL, it will remove this tag's level filer. * Then all level log will resume output. * */ void elog_set_filter_tag_lvl(const char *tag, uint8_t level) { assert(level <= ELOG_LVL_VERBOSE); assert(tag != ((void *)0)); uint8_t i = 0; if (!elog.init_ok) { return; } elog_output_lock(); /* find the tag in arr */ for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ if (elog.filter.tag_lvl[i].tag_use_flag == true && !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ break; } } if (i < ELOG_FILTER_TAG_LVL_MAX_NUM){ /* find OK */ if (level == ELOG_FILTER_LVL_ALL){ /* remove current tag's level filter when input level is the lowest level */ elog.filter.tag_lvl[i].tag_use_flag = false; memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; } else{ elog.filter.tag_lvl[i].level = level; } } else{ /* only add the new tag's level filer when level is not ELOG_FILTER_LVL_ALL */ if (level != ELOG_FILTER_LVL_ALL){ for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ if (elog.filter.tag_lvl[i].tag_use_flag == false){ strncpy(elog.filter.tag_lvl[i].tag, tag, ELOG_FILTER_TAG_MAX_LEN); elog.filter.tag_lvl[i].level = level; elog.filter.tag_lvl[i].tag_use_flag = true; break; } } } } elog_output_unlock(); } /** * get the level on tag's level filer * * @param tag tag * * @return It will return the lowest level when tag was not found. * Other level will return when tag was found. */ uint8_t elog_get_filter_tag_lvl(const char *tag) { assert(tag != ((void *)0)); uint8_t i = 0; uint8_t level = ELOG_FILTER_LVL_ALL; if (!elog.init_ok) { return level; } elog_output_lock(); /* find the tag in arr */ for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ if (elog.filter.tag_lvl[i].tag_use_flag == true && !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ level = elog.filter.tag_lvl[i].level; break; } } elog_output_unlock(); return level; } /** * output RAW format log * * @param format output format * @param ... args */ void elog_raw_output(const char *format, ...) { va_list args; size_t log_len = 0; int fmt_result; /* check output enabled */ if (!elog.output_enabled) { return; } /* args point to the first variable parameter */ va_start(args, format); /* lock output */ elog_output_lock(); /* package log data to buffer */ fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args); /* output converted log */ if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { log_len = fmt_result; } else { log_len = ELOG_LINE_BUF_SIZE; } /* output log */ #if defined(ELOG_ASYNC_OUTPUT_ENABLE) extern void elog_async_output(uint8_t level, const char *log, size_t size); /* raw log will using assert level */ elog_async_output(ELOG_LVL_ASSERT, log_buf, log_len); #elif defined(ELOG_BUF_OUTPUT_ENABLE) extern void elog_buf_output(const char *log, size_t size); elog_buf_output(log_buf, log_len); #else elog_port_output(log_buf, log_len); #endif /* unlock output */ elog_output_unlock(); va_end(args); } /** * output the log * * @param level level * @param tag tag * @param file file name * @param func function name * @param line line number * @param format output format * @param ... args * */ void elog_output(uint8_t level, const char *tag, const char *dirfile, const char *funcname, const long line, const char *format, ...) { extern const char *elog_port_get_time(void); extern const char *elog_port_get_p_info(void); extern const char *elog_port_get_t_info(void); char filename[128]; char function[128]; sprintf(filename, "%16s", basename((char*)dirfile)); sprintf(function, "%-32s", funcname); const char *file = filename; const char *func = function; size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN); char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 }; char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 }; va_list args; int fmt_result; assert(level <= ELOG_LVL_VERBOSE); /* check output enabled */ if (!elog.output_enabled) { return; } /* level filter */ if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) { return; } else if (!strstr(tag, elog.filter.tag)) { /* tag filter */ return; } /* args point to the first variable parameter */ va_start(args, format); /* lock output */ elog_output_lock(); #ifdef ELOG_COLOR_ENABLE /* add CSI start sign and color info */ if (elog.text_color_enabled) { log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START); log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]); } #endif /* package level info */ if (get_fmt_enabled(level, ELOG_FMT_LVL)) { log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]); } /* package tag info */ if (get_fmt_enabled(level, ELOG_FMT_TAG)) { log_len += elog_strcpy(log_len, log_buf + log_len, tag); /* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */ if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) { memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len); log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce); } log_len += elog_strcpy(log_len, log_buf + log_len, " "); } /* package time, process and thread info */ if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); /* package time info */ if (get_fmt_enabled(level, ELOG_FMT_TIME)) { log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time()); if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); } } /* package process info */ if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) { log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info()); if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); } } /* package thread info */ if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info()); } log_len += elog_strcpy(log_len, log_buf + log_len, " "); } /* package file directory and name, function name and line number info */ if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file) || get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func) || get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) { /* package func info */ if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) { log_len += elog_strcpy(log_len, log_buf + log_len, func); } // log_len += elog_strcpy(log_len, log_buf + log_len, "["); /* package file info */ if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file)) { log_len += elog_strcpy(log_len, log_buf + log_len, file); if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) { log_len += elog_strcpy(log_len, log_buf + log_len, ":"); } else if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); } } /* package line info */ if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) { snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%3ld", line); log_len += elog_strcpy(log_len, log_buf + log_len, line_num); if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); } } log_len += elog_strcpy(log_len, log_buf + log_len, "| "); } /* package other log data to buffer. '\0' must be added in the end by vsnprintf. */ fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len, format, args); va_end(args); /* calculate log length */ if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) { log_len += fmt_result; } else { /* using max length */ log_len = ELOG_LINE_BUF_SIZE; } /* overflow check and reserve some space for CSI end sign and newline sign */ #ifdef ELOG_COLOR_ENABLE if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) { /* using max length */ log_len = ELOG_LINE_BUF_SIZE; /* reserve some space for CSI end sign */ log_len -= (sizeof(CSI_END) - 1); #else if (log_len + newline_len > ELOG_LINE_BUF_SIZE) { /* using max length */ log_len = ELOG_LINE_BUF_SIZE; #endif /* ELOG_COLOR_ENABLE */ /* reserve some space for newline sign */ log_len -= newline_len; } /* keyword filter */ if (elog.filter.keyword[0] != '\0') { /* add string end sign */ log_buf[log_len] = '\0'; /* find the keyword */ if (!strstr(log_buf, elog.filter.keyword)) { /* unlock output */ elog_output_unlock(); return; } } #ifdef ELOG_COLOR_ENABLE /* add CSI end sign */ if (elog.text_color_enabled) { log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END); } #endif /* package newline sign */ log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); /* output log */ #if defined(ELOG_ASYNC_OUTPUT_ENABLE) extern void elog_async_output(uint8_t level, const char *log, size_t size); elog_async_output(level, log_buf, log_len); #elif defined(ELOG_BUF_OUTPUT_ENABLE) extern void elog_buf_output(const char *log, size_t size); elog_buf_output(log_buf, log_len); #else elog_port_output(log_buf, log_len); #endif /* unlock output */ elog_output_unlock(); } /** * get format enabled * * @param level level * @param set format set * * @return enable or disable */ static bool get_fmt_enabled(uint8_t level, size_t set) { assert(level <= ELOG_LVL_VERBOSE); if (elog.enabled_fmt_set[level] & set) { return true; } else { return false; } } static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg) { return arg && get_fmt_enabled(level, set); } static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg) { return arg && get_fmt_enabled(level, set); } /** * enable or disable logger output lock * @note disable this lock is not recommended except you want output system exception log * * @param enabled true: enable false: disable */ void elog_output_lock_enabled(bool enabled) { elog.output_lock_enabled = enabled; /* it will re-lock or re-unlock before output lock enable */ if (elog.output_lock_enabled) { if (!elog.output_is_locked_before_disable && elog.output_is_locked_before_enable) { /* the output lock is unlocked before disable, and the lock will unlocking after enable */ elog_port_output_lock(); } else if (elog.output_is_locked_before_disable && !elog.output_is_locked_before_enable) { /* the output lock is locked before disable, and the lock will locking after enable */ elog_port_output_unlock(); } } } /** * find the log level * @note make sure the log level is output on each format * * @param log log buffer * * @return log level, found failed will return -1 */ int8_t elog_find_lvl(const char *log) { assert(log); /* make sure the log level is output on each format */ assert(elog.enabled_fmt_set[ELOG_LVL_ASSERT] & ELOG_FMT_LVL); assert(elog.enabled_fmt_set[ELOG_LVL_ERROR] & ELOG_FMT_LVL); assert(elog.enabled_fmt_set[ELOG_LVL_WARN] & ELOG_FMT_LVL); assert(elog.enabled_fmt_set[ELOG_LVL_INFO] & ELOG_FMT_LVL); assert(elog.enabled_fmt_set[ELOG_LVL_DEBUG] & ELOG_FMT_LVL); assert(elog.enabled_fmt_set[ELOG_LVL_VERBOSE] & ELOG_FMT_LVL); #ifdef ELOG_COLOR_ENABLE uint8_t i; size_t csi_start_len = strlen(CSI_START); for(i = 0; i < ELOG_LVL_TOTAL_NUM; i ++) { if (!strncmp(color_output_info[i], log + csi_start_len, strlen(color_output_info[i]))) { return i; } } /* found failed */ return -1; #else switch (log[0]) { case 'A': return ELOG_LVL_ASSERT; case 'E': return ELOG_LVL_ERROR; case 'W': return ELOG_LVL_WARN; case 'I': return ELOG_LVL_INFO; case 'D': return ELOG_LVL_DEBUG; case 'V': return ELOG_LVL_VERBOSE; default: return -1; } #endif } /** * find the log tag * @note make sure the log tag is output on each format * @note the tag don't have space in it * * @param log log buffer * @param lvl log level, you can get it by @see elog_find_lvl * @param tag_len found tag length * * @return log tag, found failed will return NULL */ const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len) { const char *tag = NULL, *tag_end = NULL; assert(log); assert(tag_len); assert(lvl < ELOG_LVL_TOTAL_NUM); /* make sure the log tag is output on each format */ assert(elog.enabled_fmt_set[lvl] & ELOG_FMT_TAG); #ifdef ELOG_COLOR_ENABLE tag = log + strlen(CSI_START) + strlen(color_output_info[lvl]) + strlen(level_output_info[lvl]); #else tag = log + strlen(level_output_info[lvl]); #endif /* find the first space after tag */ if ((tag_end = memchr(tag, ' ', ELOG_FILTER_TAG_MAX_LEN)) != NULL) { *tag_len = tag_end - tag; } else { tag = NULL; } return tag; } /** * dump the hex format data to log * * @param name name for hex object, it will show on log header * @param width hex number for every line, such as: 16, 32 * @param buf hex buffer * @param size buffer size */ void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size) { #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') uint16_t i, j; uint16_t log_len = 0; const uint8_t *buf_p = buf; char dump_string[8] = {0}; int fmt_result; if (!elog.output_enabled) { return; } /* level filter */ if (ELOG_LVL_DEBUG > elog.filter.level) { return; } else if (!strstr(name, elog.filter.tag)) { /* tag filter */ return; } /* lock output */ elog_output_lock(); for (i = 0; i < size; i += width) { /* package header */ fmt_result = snprintf(log_buf, ELOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width - 1); /* calculate log length */ if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { log_len = fmt_result; } else { log_len = ELOG_LINE_BUF_SIZE; } /* dump hex */ for (j = 0; j < width; j++) { if (i + j < size) { snprintf(dump_string, sizeof(dump_string), "%02X ", buf_p[i + j]); } else { strncpy(dump_string, " ", sizeof(dump_string)); } log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); if ((j + 1) % 8 == 0) { log_len += elog_strcpy(log_len, log_buf + log_len, " "); } } log_len += elog_strcpy(log_len, log_buf + log_len, " "); /* dump char for hex */ for (j = 0; j < width; j++) { if (i + j < size) { snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf_p[i + j]) ? buf_p[i + j] : '.'); log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); } } /* overflow check and reserve some space for newline sign */ if (log_len + strlen(ELOG_NEWLINE_SIGN) > ELOG_LINE_BUF_SIZE) { log_len = ELOG_LINE_BUF_SIZE - strlen(ELOG_NEWLINE_SIGN); } /* package newline sign */ log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); /* do log output */ #if defined(ELOG_ASYNC_OUTPUT_ENABLE) extern void elog_async_output(uint8_t level, const char *log, size_t size); elog_async_output(ELOG_LVL_DEBUG, log_buf, log_len); #elif defined(ELOG_BUF_OUTPUT_ENABLE) extern void elog_buf_output(const char *log, size_t size); elog_buf_output(log_buf, log_len); #else elog_port_output(log_buf, log_len); #endif } /* unlock output */ elog_output_unlock(); }