elog.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. /*
  2. * This file is part of the EasyLogger Library.
  3. *
  4. * Copyright (c) 2015-2018, Armink, <armink.ztl@gmail.com>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining
  7. * a copy of this software and associated documentation files (the
  8. * 'Software'), to deal in the Software without restriction, including
  9. * without limitation the rights to use, copy, modify, merge, publish,
  10. * distribute, sublicense, and/or sell copies of the Software, and to
  11. * permit persons to whom the Software is furnished to do so, subject to
  12. * the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  21. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  22. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  23. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. *
  25. * Function: Initialize function and other general function.
  26. * Created on: 2015-04-28
  27. */
  28. #define LOG_TAG "elog"
  29. #include <elog.h>
  30. #include <string.h>
  31. #include <stdarg.h>
  32. #include <stdio.h>
  33. #include <libgen.h>
  34. #if !defined(ELOG_OUTPUT_LVL)
  35. #error "Please configure static output log level (in elog_cfg.h)"
  36. #endif
  37. #if !defined(ELOG_LINE_NUM_MAX_LEN)
  38. #error "Please configure output line number max length (in elog_cfg.h)"
  39. #endif
  40. #if !defined(ELOG_LINE_BUF_SIZE)
  41. #error "Please configure buffer size for every line's log (in elog_cfg.h)"
  42. #endif
  43. #if !defined(ELOG_FILTER_TAG_MAX_LEN)
  44. #error "Please configure output filter's tag max length (in elog_cfg.h)"
  45. #endif
  46. #if !defined(ELOG_FILTER_KW_MAX_LEN)
  47. #error "Please configure output filter's keyword max length (in elog_cfg.h)"
  48. #endif
  49. #if !defined(ELOG_NEWLINE_SIGN)
  50. #error "Please configure output newline sign (in elog_cfg.h)"
  51. #endif
  52. /* output filter's tag level max num */
  53. #ifndef ELOG_FILTER_TAG_LVL_MAX_NUM
  54. #define ELOG_FILTER_TAG_LVL_MAX_NUM 4
  55. #endif
  56. #ifdef ELOG_COLOR_ENABLE
  57. /**
  58. * CSI(Control Sequence Introducer/Initiator) sign
  59. * more information on https://en.wikipedia.org/wiki/ANSI_escape_code
  60. */
  61. #define CSI_START "\033["
  62. #define CSI_END "\033[0m"
  63. /* output log front color */
  64. #define F_BLACK "30;"
  65. #define F_RED "31;"
  66. #define F_GREEN "32;"
  67. #define F_YELLOW "33;"
  68. #define F_BLUE "34;"
  69. #define F_MAGENTA "35;"
  70. #define F_CYAN "36;"
  71. #define F_WHITE "37;"
  72. /* output log background color */
  73. #define B_NULL
  74. #define B_BLACK "40;"
  75. #define B_RED "41;"
  76. #define B_GREEN "42;"
  77. #define B_YELLOW "43;"
  78. #define B_BLUE "44;"
  79. #define B_MAGENTA "45;"
  80. #define B_CYAN "46;"
  81. #define B_WHITE "47;"
  82. /* output log fonts style */
  83. #define S_BOLD "1m"
  84. #define S_UNDERLINE "4m"
  85. #define S_BLINK "5m"
  86. #define S_NORMAL "22m"
  87. /* output log default color definition: [front color] + [background color] + [show style] */
  88. /* color output info */
  89. static const char *color_output_info[] = {
  90. [ELOG_LVL_ASSERT] = (F_MAGENTA B_NULL S_NORMAL),
  91. [ELOG_LVL_ERROR] = (F_RED B_NULL S_NORMAL),
  92. [ELOG_LVL_WARN] = (F_YELLOW B_NULL S_NORMAL),
  93. [ELOG_LVL_INFO] = (F_CYAN B_NULL S_NORMAL),
  94. [ELOG_LVL_DEBUG] = (F_GREEN B_NULL S_NORMAL),
  95. [ELOG_LVL_VERBOSE] = (F_BLUE B_NULL S_NORMAL),
  96. };
  97. #endif /* ELOG_COLOR_ENABLE */
  98. /* EasyLogger object */
  99. static EasyLogger elog;
  100. /* every line log's buffer */
  101. static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 };
  102. /* level output info */
  103. static const char *level_output_info[] = {
  104. [ELOG_LVL_ASSERT] = "A/",
  105. [ELOG_LVL_ERROR] = "E/",
  106. [ELOG_LVL_WARN] = "W/",
  107. [ELOG_LVL_INFO] = "I/",
  108. [ELOG_LVL_DEBUG] = "D/",
  109. [ELOG_LVL_VERBOSE] = "V/",
  110. };
  111. static bool get_fmt_enabled(uint8_t level, size_t set);
  112. static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg);
  113. static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg);
  114. static void elog_set_filter_tag_lvl_default(void);
  115. extern void elog_port_output(const char *log, size_t size);
  116. extern void elog_port_output_lock(void);
  117. extern void elog_port_output_unlock(void);
  118. /**
  119. * EasyLogger initialize.
  120. *
  121. * @return result
  122. */
  123. int elog_init(void) {
  124. extern int elog_port_init(void);
  125. extern int elog_async_init(void);
  126. int result = 0;
  127. if (elog.init_ok == true) {
  128. return result;
  129. }
  130. /* port initialize */
  131. result = elog_port_init();
  132. if (result != 0) {
  133. return result;
  134. }
  135. #ifdef ELOG_ASYNC_OUTPUT_ENABLE
  136. result = elog_async_init();
  137. if (result != 0) {
  138. return result;
  139. }
  140. #endif
  141. /* enable the output lock */
  142. elog_output_lock_enabled(true);
  143. /* output locked status initialize */
  144. elog.output_is_locked_before_enable = false;
  145. elog.output_is_locked_before_disable = false;
  146. #ifdef ELOG_COLOR_ENABLE
  147. /* enable text color by default */
  148. elog_set_text_color_enabled(true);
  149. #endif
  150. /* set level is ELOG_LVL_VERBOSE */
  151. elog_set_filter_lvl(ELOG_LVL_VERBOSE);
  152. /* set tag_level to default val */
  153. elog_set_filter_tag_lvl_default();
  154. elog.init_ok = true;
  155. return result;
  156. }
  157. /**
  158. * EasyLogger deinitialize.
  159. *
  160. */
  161. void elog_deinit(void) {
  162. extern int elog_port_deinit(void);
  163. extern int elog_async_deinit(void);
  164. if (!elog.init_ok) {
  165. return ;
  166. }
  167. #ifdef ELOG_ASYNC_OUTPUT_ENABLE
  168. elog_async_deinit();
  169. #endif
  170. /* port deinitialize */
  171. elog_port_deinit();
  172. elog.init_ok = false;
  173. }
  174. /**
  175. * EasyLogger start after initialize.
  176. */
  177. void elog_start(void) {
  178. if (!elog.init_ok) {
  179. return ;
  180. }
  181. /* enable output */
  182. elog_set_output_enabled(true);
  183. #if defined(ELOG_ASYNC_OUTPUT_ENABLE)
  184. elog_async_enabled(true);
  185. #elif defined(ELOG_BUF_OUTPUT_ENABLE)
  186. elog_buf_enabled(true);
  187. #endif
  188. /* show version */
  189. log_i("EasyLogger V%s is initialize success.", ELOG_SW_VERSION);
  190. }
  191. /**
  192. * EasyLogger stop after initialize.
  193. */
  194. void elog_stop(void) {
  195. if (!elog.init_ok) {
  196. return ;
  197. }
  198. /* disable output */
  199. elog_set_output_enabled(false);
  200. #if defined(ELOG_ASYNC_OUTPUT_ENABLE)
  201. elog_async_enabled(false);
  202. #elif defined(ELOG_BUF_OUTPUT_ENABLE)
  203. elog_buf_enabled(false);
  204. #endif
  205. /* show version */
  206. log_i("EasyLogger V%s is deinitialize success.", ELOG_SW_VERSION);
  207. }
  208. /**
  209. * set output enable or disable
  210. *
  211. * @param enabled TRUE: enable FALSE: disable
  212. */
  213. void elog_set_output_enabled(bool enabled) {
  214. assert((enabled == false) || (enabled == true));
  215. elog.output_enabled = enabled;
  216. }
  217. #ifdef ELOG_COLOR_ENABLE
  218. /**
  219. * set log text color enable or disable
  220. *
  221. * @param enabled TRUE: enable FALSE:disable
  222. */
  223. void elog_set_text_color_enabled(bool enabled) {
  224. assert((enabled == false) || (enabled == true));
  225. elog.text_color_enabled = enabled;
  226. }
  227. /**
  228. * get log text color enable status
  229. *
  230. * @return enable or disable
  231. */
  232. bool elog_get_text_color_enabled(void) {
  233. return elog.text_color_enabled;
  234. }
  235. #endif /* ELOG_COLOR_ENABLE */
  236. /**
  237. * get output is enable or disable
  238. *
  239. * @return enable or disable
  240. */
  241. bool elog_get_output_enabled(void) {
  242. return elog.output_enabled;
  243. }
  244. /**
  245. * set log output format. only enable or disable
  246. *
  247. * @param level level
  248. * @param set format set
  249. */
  250. void elog_set_fmt(uint8_t level, size_t set) {
  251. assert(level <= ELOG_LVL_VERBOSE);
  252. elog.enabled_fmt_set[level] = set;
  253. }
  254. /**
  255. * set log filter all parameter
  256. *
  257. * @param level level
  258. * @param tag tag
  259. * @param keyword keyword
  260. */
  261. void elog_set_filter(uint8_t level, const char *tag, const char *keyword) {
  262. assert(level <= ELOG_LVL_VERBOSE);
  263. elog_set_filter_lvl(level);
  264. elog_set_filter_tag(tag);
  265. elog_set_filter_kw(keyword);
  266. }
  267. /**
  268. * set log filter's level
  269. *
  270. * @param level level
  271. */
  272. void elog_set_filter_lvl(uint8_t level) {
  273. assert(level <= ELOG_LVL_VERBOSE);
  274. elog.filter.level = level;
  275. }
  276. /**
  277. * set log filter's tag
  278. *
  279. * @param tag tag
  280. */
  281. void elog_set_filter_tag(const char *tag) {
  282. strncpy(elog.filter.tag, tag, ELOG_FILTER_TAG_MAX_LEN);
  283. }
  284. /**
  285. * set log filter's keyword
  286. *
  287. * @param keyword keyword
  288. */
  289. void elog_set_filter_kw(const char *keyword) {
  290. strncpy(elog.filter.keyword, keyword, ELOG_FILTER_KW_MAX_LEN);
  291. }
  292. /**
  293. * lock output
  294. */
  295. void elog_output_lock(void) {
  296. if (elog.output_lock_enabled) {
  297. elog_port_output_lock();
  298. elog.output_is_locked_before_disable = true;
  299. } else {
  300. elog.output_is_locked_before_enable = true;
  301. }
  302. }
  303. /**
  304. * unlock output
  305. */
  306. void elog_output_unlock(void) {
  307. if (elog.output_lock_enabled) {
  308. elog_port_output_unlock();
  309. elog.output_is_locked_before_disable = false;
  310. } else {
  311. elog.output_is_locked_before_enable = false;
  312. }
  313. }
  314. /**
  315. * set log filter's tag level val to default
  316. */
  317. static void elog_set_filter_tag_lvl_default(void)
  318. {
  319. uint8_t i = 0;
  320. for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
  321. memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1);
  322. elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT;
  323. elog.filter.tag_lvl[i].tag_use_flag = false;
  324. }
  325. }
  326. /**
  327. * Set the filter's level by different tag.
  328. * The log on this tag which level is less than it will stop output.
  329. *
  330. * example:
  331. * // the example tag log enter silent mode
  332. * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_SILENT);
  333. * // the example tag log which level is less than INFO level will stop output
  334. * elog_set_filter_tag_lvl("example", ELOG_LVL_INFO);
  335. * // remove example tag's level filter, all level log will resume output
  336. * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_ALL);
  337. *
  338. * @param tag log tag
  339. * @param level The filter level. When the level is ELOG_FILTER_LVL_SILENT, the log enter silent mode.
  340. * When the level is ELOG_FILTER_LVL_ALL, it will remove this tag's level filer.
  341. * Then all level log will resume output.
  342. *
  343. */
  344. void elog_set_filter_tag_lvl(const char *tag, uint8_t level)
  345. {
  346. assert(level <= ELOG_LVL_VERBOSE);
  347. assert(tag != ((void *)0));
  348. uint8_t i = 0;
  349. if (!elog.init_ok) {
  350. return;
  351. }
  352. elog_output_lock();
  353. /* find the tag in arr */
  354. for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
  355. if (elog.filter.tag_lvl[i].tag_use_flag == true &&
  356. !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){
  357. break;
  358. }
  359. }
  360. if (i < ELOG_FILTER_TAG_LVL_MAX_NUM){
  361. /* find OK */
  362. if (level == ELOG_FILTER_LVL_ALL){
  363. /* remove current tag's level filter when input level is the lowest level */
  364. elog.filter.tag_lvl[i].tag_use_flag = false;
  365. memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1);
  366. elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT;
  367. } else{
  368. elog.filter.tag_lvl[i].level = level;
  369. }
  370. } else{
  371. /* only add the new tag's level filer when level is not ELOG_FILTER_LVL_ALL */
  372. if (level != ELOG_FILTER_LVL_ALL){
  373. for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
  374. if (elog.filter.tag_lvl[i].tag_use_flag == false){
  375. strncpy(elog.filter.tag_lvl[i].tag, tag, ELOG_FILTER_TAG_MAX_LEN);
  376. elog.filter.tag_lvl[i].level = level;
  377. elog.filter.tag_lvl[i].tag_use_flag = true;
  378. break;
  379. }
  380. }
  381. }
  382. }
  383. elog_output_unlock();
  384. }
  385. /**
  386. * get the level on tag's level filer
  387. *
  388. * @param tag tag
  389. *
  390. * @return It will return the lowest level when tag was not found.
  391. * Other level will return when tag was found.
  392. */
  393. uint8_t elog_get_filter_tag_lvl(const char *tag)
  394. {
  395. assert(tag != ((void *)0));
  396. uint8_t i = 0;
  397. uint8_t level = ELOG_FILTER_LVL_ALL;
  398. if (!elog.init_ok) {
  399. return level;
  400. }
  401. elog_output_lock();
  402. /* find the tag in arr */
  403. for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
  404. if (elog.filter.tag_lvl[i].tag_use_flag == true &&
  405. !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){
  406. level = elog.filter.tag_lvl[i].level;
  407. break;
  408. }
  409. }
  410. elog_output_unlock();
  411. return level;
  412. }
  413. /**
  414. * output RAW format log
  415. *
  416. * @param format output format
  417. * @param ... args
  418. */
  419. void elog_raw_output(const char *format, ...) {
  420. va_list args;
  421. size_t log_len = 0;
  422. int fmt_result;
  423. /* check output enabled */
  424. if (!elog.output_enabled) {
  425. return;
  426. }
  427. /* args point to the first variable parameter */
  428. va_start(args, format);
  429. /* lock output */
  430. elog_output_lock();
  431. /* package log data to buffer */
  432. fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args);
  433. /* output converted log */
  434. if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) {
  435. log_len = fmt_result;
  436. } else {
  437. log_len = ELOG_LINE_BUF_SIZE;
  438. }
  439. /* output log */
  440. #if defined(ELOG_ASYNC_OUTPUT_ENABLE)
  441. extern void elog_async_output(uint8_t level, const char *log, size_t size);
  442. /* raw log will using assert level */
  443. elog_async_output(ELOG_LVL_ASSERT, log_buf, log_len);
  444. #elif defined(ELOG_BUF_OUTPUT_ENABLE)
  445. extern void elog_buf_output(const char *log, size_t size);
  446. elog_buf_output(log_buf, log_len);
  447. #else
  448. elog_port_output(log_buf, log_len);
  449. #endif
  450. /* unlock output */
  451. elog_output_unlock();
  452. va_end(args);
  453. }
  454. /**
  455. * output the log
  456. *
  457. * @param level level
  458. * @param tag tag
  459. * @param file file name
  460. * @param func function name
  461. * @param line line number
  462. * @param format output format
  463. * @param ... args
  464. *
  465. */
  466. void elog_output(uint8_t level, const char *tag, const char *dirfile, const char *funcname, const long line, const char *format, ...)
  467. {
  468. extern const char *elog_port_get_time(void);
  469. extern const char *elog_port_get_p_info(void);
  470. extern const char *elog_port_get_t_info(void);
  471. char filename[128];
  472. char function[128];
  473. sprintf(filename, "%16s", basename((char*)dirfile));
  474. sprintf(function, "%-32s", funcname);
  475. const char *file = filename;
  476. const char *func = function;
  477. size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN);
  478. char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 };
  479. char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 };
  480. va_list args;
  481. int fmt_result;
  482. assert(level <= ELOG_LVL_VERBOSE);
  483. /* check output enabled */
  484. if (!elog.output_enabled) {
  485. return;
  486. }
  487. /* level filter */
  488. if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) {
  489. return;
  490. } else if (!strstr(tag, elog.filter.tag)) { /* tag filter */
  491. return;
  492. }
  493. /* args point to the first variable parameter */
  494. va_start(args, format);
  495. /* lock output */
  496. elog_output_lock();
  497. #ifdef ELOG_COLOR_ENABLE
  498. /* add CSI start sign and color info */
  499. if (elog.text_color_enabled) {
  500. log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START);
  501. log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
  502. }
  503. #endif
  504. /* package level info */
  505. if (get_fmt_enabled(level, ELOG_FMT_LVL)) {
  506. log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
  507. }
  508. /* package tag info */
  509. if (get_fmt_enabled(level, ELOG_FMT_TAG)) {
  510. log_len += elog_strcpy(log_len, log_buf + log_len, tag);
  511. /* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */
  512. if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) {
  513. memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len);
  514. log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce);
  515. }
  516. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  517. }
  518. /* package time, process and thread info */
  519. if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
  520. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  521. /* package time info */
  522. if (get_fmt_enabled(level, ELOG_FMT_TIME)) {
  523. log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time());
  524. if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
  525. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  526. }
  527. }
  528. /* package process info */
  529. if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) {
  530. log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info());
  531. if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
  532. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  533. }
  534. }
  535. /* package thread info */
  536. if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
  537. log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info());
  538. }
  539. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  540. }
  541. /* package file directory and name, function name and line number info */
  542. if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file) ||
  543. get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func) ||
  544. get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
  545. /* package func info */
  546. if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
  547. log_len += elog_strcpy(log_len, log_buf + log_len, func);
  548. }
  549. // log_len += elog_strcpy(log_len, log_buf + log_len, "[");
  550. /* package file info */
  551. if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file)) {
  552. log_len += elog_strcpy(log_len, log_buf + log_len, file);
  553. if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
  554. log_len += elog_strcpy(log_len, log_buf + log_len, ":");
  555. } else if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
  556. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  557. }
  558. }
  559. /* package line info */
  560. if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
  561. snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%3ld", line);
  562. log_len += elog_strcpy(log_len, log_buf + log_len, line_num);
  563. if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
  564. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  565. }
  566. }
  567. log_len += elog_strcpy(log_len, log_buf + log_len, "| ");
  568. }
  569. /* package other log data to buffer. '\0' must be added in the end by vsnprintf. */
  570. fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len, format, args);
  571. va_end(args);
  572. /* calculate log length */
  573. if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) {
  574. log_len += fmt_result;
  575. } else {
  576. /* using max length */
  577. log_len = ELOG_LINE_BUF_SIZE;
  578. }
  579. /* overflow check and reserve some space for CSI end sign and newline sign */
  580. #ifdef ELOG_COLOR_ENABLE
  581. if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) {
  582. /* using max length */
  583. log_len = ELOG_LINE_BUF_SIZE;
  584. /* reserve some space for CSI end sign */
  585. log_len -= (sizeof(CSI_END) - 1);
  586. #else
  587. if (log_len + newline_len > ELOG_LINE_BUF_SIZE) {
  588. /* using max length */
  589. log_len = ELOG_LINE_BUF_SIZE;
  590. #endif /* ELOG_COLOR_ENABLE */
  591. /* reserve some space for newline sign */
  592. log_len -= newline_len;
  593. }
  594. /* keyword filter */
  595. if (elog.filter.keyword[0] != '\0') {
  596. /* add string end sign */
  597. log_buf[log_len] = '\0';
  598. /* find the keyword */
  599. if (!strstr(log_buf, elog.filter.keyword)) {
  600. /* unlock output */
  601. elog_output_unlock();
  602. return;
  603. }
  604. }
  605. #ifdef ELOG_COLOR_ENABLE
  606. /* add CSI end sign */
  607. if (elog.text_color_enabled) {
  608. log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END);
  609. }
  610. #endif
  611. /* package newline sign */
  612. log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN);
  613. /* output log */
  614. #if defined(ELOG_ASYNC_OUTPUT_ENABLE)
  615. extern void elog_async_output(uint8_t level, const char *log, size_t size);
  616. elog_async_output(level, log_buf, log_len);
  617. #elif defined(ELOG_BUF_OUTPUT_ENABLE)
  618. extern void elog_buf_output(const char *log, size_t size);
  619. elog_buf_output(log_buf, log_len);
  620. #else
  621. elog_port_output(log_buf, log_len);
  622. #endif
  623. /* unlock output */
  624. elog_output_unlock();
  625. }
  626. /**
  627. * get format enabled
  628. *
  629. * @param level level
  630. * @param set format set
  631. *
  632. * @return enable or disable
  633. */
  634. static bool get_fmt_enabled(uint8_t level, size_t set) {
  635. assert(level <= ELOG_LVL_VERBOSE);
  636. if (elog.enabled_fmt_set[level] & set) {
  637. return true;
  638. } else {
  639. return false;
  640. }
  641. }
  642. static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg) {
  643. return arg && get_fmt_enabled(level, set);
  644. }
  645. static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg) {
  646. return arg && get_fmt_enabled(level, set);
  647. }
  648. /**
  649. * enable or disable logger output lock
  650. * @note disable this lock is not recommended except you want output system exception log
  651. *
  652. * @param enabled true: enable false: disable
  653. */
  654. void elog_output_lock_enabled(bool enabled) {
  655. elog.output_lock_enabled = enabled;
  656. /* it will re-lock or re-unlock before output lock enable */
  657. if (elog.output_lock_enabled) {
  658. if (!elog.output_is_locked_before_disable && elog.output_is_locked_before_enable) {
  659. /* the output lock is unlocked before disable, and the lock will unlocking after enable */
  660. elog_port_output_lock();
  661. } else if (elog.output_is_locked_before_disable && !elog.output_is_locked_before_enable) {
  662. /* the output lock is locked before disable, and the lock will locking after enable */
  663. elog_port_output_unlock();
  664. }
  665. }
  666. }
  667. /**
  668. * find the log level
  669. * @note make sure the log level is output on each format
  670. *
  671. * @param log log buffer
  672. *
  673. * @return log level, found failed will return -1
  674. */
  675. int8_t elog_find_lvl(const char *log) {
  676. assert(log);
  677. /* make sure the log level is output on each format */
  678. assert(elog.enabled_fmt_set[ELOG_LVL_ASSERT] & ELOG_FMT_LVL);
  679. assert(elog.enabled_fmt_set[ELOG_LVL_ERROR] & ELOG_FMT_LVL);
  680. assert(elog.enabled_fmt_set[ELOG_LVL_WARN] & ELOG_FMT_LVL);
  681. assert(elog.enabled_fmt_set[ELOG_LVL_INFO] & ELOG_FMT_LVL);
  682. assert(elog.enabled_fmt_set[ELOG_LVL_DEBUG] & ELOG_FMT_LVL);
  683. assert(elog.enabled_fmt_set[ELOG_LVL_VERBOSE] & ELOG_FMT_LVL);
  684. #ifdef ELOG_COLOR_ENABLE
  685. uint8_t i;
  686. size_t csi_start_len = strlen(CSI_START);
  687. for(i = 0; i < ELOG_LVL_TOTAL_NUM; i ++) {
  688. if (!strncmp(color_output_info[i], log + csi_start_len, strlen(color_output_info[i]))) {
  689. return i;
  690. }
  691. }
  692. /* found failed */
  693. return -1;
  694. #else
  695. switch (log[0]) {
  696. case 'A': return ELOG_LVL_ASSERT;
  697. case 'E': return ELOG_LVL_ERROR;
  698. case 'W': return ELOG_LVL_WARN;
  699. case 'I': return ELOG_LVL_INFO;
  700. case 'D': return ELOG_LVL_DEBUG;
  701. case 'V': return ELOG_LVL_VERBOSE;
  702. default: return -1;
  703. }
  704. #endif
  705. }
  706. /**
  707. * find the log tag
  708. * @note make sure the log tag is output on each format
  709. * @note the tag don't have space in it
  710. *
  711. * @param log log buffer
  712. * @param lvl log level, you can get it by @see elog_find_lvl
  713. * @param tag_len found tag length
  714. *
  715. * @return log tag, found failed will return NULL
  716. */
  717. const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len) {
  718. const char *tag = NULL, *tag_end = NULL;
  719. assert(log);
  720. assert(tag_len);
  721. assert(lvl < ELOG_LVL_TOTAL_NUM);
  722. /* make sure the log tag is output on each format */
  723. assert(elog.enabled_fmt_set[lvl] & ELOG_FMT_TAG);
  724. #ifdef ELOG_COLOR_ENABLE
  725. tag = log + strlen(CSI_START) + strlen(color_output_info[lvl]) + strlen(level_output_info[lvl]);
  726. #else
  727. tag = log + strlen(level_output_info[lvl]);
  728. #endif
  729. /* find the first space after tag */
  730. if ((tag_end = memchr(tag, ' ', ELOG_FILTER_TAG_MAX_LEN)) != NULL) {
  731. *tag_len = tag_end - tag;
  732. } else {
  733. tag = NULL;
  734. }
  735. return tag;
  736. }
  737. /**
  738. * dump the hex format data to log
  739. *
  740. * @param name name for hex object, it will show on log header
  741. * @param width hex number for every line, such as: 16, 32
  742. * @param buf hex buffer
  743. * @param size buffer size
  744. */
  745. void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size)
  746. {
  747. #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
  748. uint16_t i, j;
  749. uint16_t log_len = 0;
  750. const uint8_t *buf_p = buf;
  751. char dump_string[8] = {0};
  752. int fmt_result;
  753. if (!elog.output_enabled) {
  754. return;
  755. }
  756. /* level filter */
  757. if (ELOG_LVL_DEBUG > elog.filter.level) {
  758. return;
  759. } else if (!strstr(name, elog.filter.tag)) { /* tag filter */
  760. return;
  761. }
  762. /* lock output */
  763. elog_output_lock();
  764. for (i = 0; i < size; i += width) {
  765. /* package header */
  766. fmt_result = snprintf(log_buf, ELOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width - 1);
  767. /* calculate log length */
  768. if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) {
  769. log_len = fmt_result;
  770. } else {
  771. log_len = ELOG_LINE_BUF_SIZE;
  772. }
  773. /* dump hex */
  774. for (j = 0; j < width; j++) {
  775. if (i + j < size) {
  776. snprintf(dump_string, sizeof(dump_string), "%02X ", buf_p[i + j]);
  777. } else {
  778. strncpy(dump_string, " ", sizeof(dump_string));
  779. }
  780. log_len += elog_strcpy(log_len, log_buf + log_len, dump_string);
  781. if ((j + 1) % 8 == 0) {
  782. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  783. }
  784. }
  785. log_len += elog_strcpy(log_len, log_buf + log_len, " ");
  786. /* dump char for hex */
  787. for (j = 0; j < width; j++) {
  788. if (i + j < size) {
  789. snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf_p[i + j]) ? buf_p[i + j] : '.');
  790. log_len += elog_strcpy(log_len, log_buf + log_len, dump_string);
  791. }
  792. }
  793. /* overflow check and reserve some space for newline sign */
  794. if (log_len + strlen(ELOG_NEWLINE_SIGN) > ELOG_LINE_BUF_SIZE) {
  795. log_len = ELOG_LINE_BUF_SIZE - strlen(ELOG_NEWLINE_SIGN);
  796. }
  797. /* package newline sign */
  798. log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN);
  799. /* do log output */
  800. #if defined(ELOG_ASYNC_OUTPUT_ENABLE)
  801. extern void elog_async_output(uint8_t level, const char *log, size_t size);
  802. elog_async_output(ELOG_LVL_DEBUG, log_buf, log_len);
  803. #elif defined(ELOG_BUF_OUTPUT_ENABLE)
  804. extern void elog_buf_output(const char *log, size_t size);
  805. elog_buf_output(log_buf, log_len);
  806. #else
  807. elog_port_output(log_buf, log_len);
  808. #endif
  809. }
  810. /* unlock output */
  811. elog_output_unlock();
  812. }