ulog.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. #include <stdarg.h>
  2. #include <FreeRTOS.h>
  3. #include "chip.h"
  4. #include "board.h"
  5. #include "ulog.h"
  6. #define ULOG_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
  7. #define ULOG_ALIGN_SIZE 4
  8. #ifdef ULOG_USING_SYSLOG
  9. #include <syslog.h>
  10. #endif
  11. #ifdef ULOG_OUTPUT_FLOAT
  12. #include <stdio.h>
  13. #endif
  14. #ifdef ULOG_USING_ASYNC_OUTPUT
  15. #include "ringblk_buf.h"
  16. #endif
  17. #ifdef USE_ULOG
  18. /* the number which is max stored line logs */
  19. #ifndef ULOG_ASYNC_OUTPUT_STORE_LINES
  20. #define ULOG_ASYNC_OUTPUT_STORE_LINES (ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / ULOG_LINE_BUF_SIZE)
  21. #endif
  22. #ifdef ULOG_USING_COLOR
  23. /**
  24. * CSI(Control Sequence Introducer/Initiator) sign
  25. * more information on https://en.wikipedia.org/wiki/ANSI_escape_code
  26. */
  27. #define CSI_START "\033["
  28. #define CSI_END "\033[0m"
  29. /* output log front color */
  30. #define F_BLACK "30m"
  31. #define F_RED "31m"
  32. #define F_GREEN "32m"
  33. #define F_YELLOW "33m"
  34. #define F_BLUE "34m"
  35. #define F_MAGENTA "35m"
  36. #define F_CYAN "36m"
  37. #define F_WHITE "37m"
  38. /* output log default color definition */
  39. #ifndef ULOG_COLOR_DEBUG
  40. #define ULOG_COLOR_DEBUG NULL
  41. #endif
  42. #ifndef ULOG_COLOR_INFO
  43. #define ULOG_COLOR_INFO (F_GREEN)
  44. #endif
  45. #ifndef ULOG_COLOR_WARN
  46. #define ULOG_COLOR_WARN (F_YELLOW)
  47. #endif
  48. #ifndef ULOG_COLOR_ERROR
  49. #define ULOG_COLOR_ERROR (F_RED)
  50. #endif
  51. #ifndef ULOG_COLOR_ASSERT
  52. #define ULOG_COLOR_ASSERT (F_MAGENTA)
  53. #endif
  54. #endif /* ULOG_USING_COLOR */
  55. #if ULOG_LINE_BUF_SIZE < 80
  56. #error "the log line buffer size must more than 80"
  57. #endif
  58. struct ulog
  59. {
  60. int init_ok;
  61. SemaphoreHandle_t output_locker;
  62. /* all backends */
  63. List_t backend_list;
  64. /* the thread log's line buffer */
  65. char log_buf_th[ULOG_LINE_BUF_SIZE + 1];
  66. #ifdef ULOG_USING_ISR_LOG
  67. /* the ISR log's line buffer */
  68. UBaseType_t output_locker_isr_lvl;
  69. char log_buf_isr[ULOG_LINE_BUF_SIZE + 1];
  70. #endif /* ULOG_USING_ISR_LOG */
  71. #ifdef ULOG_USING_ASYNC_OUTPUT
  72. rbb_t async_rbb;
  73. TaskHandle_t async_th;
  74. QueueHandle_t async_notice;
  75. #endif
  76. #ifdef ULOG_USING_FILTER
  77. struct
  78. {
  79. /* all tag's level filter */
  80. List_t tag_lvl_list;
  81. /* global filter level, tag and keyword */
  82. uint32_t level;
  83. char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
  84. char keyword[ULOG_FILTER_KW_MAX_LEN + 1];
  85. } filter;
  86. #endif /* ULOG_USING_FILTER */
  87. };
  88. #ifdef ULOG_OUTPUT_LEVEL
  89. /* level output info */
  90. static const char * const level_output_info[] =
  91. {
  92. "A/",
  93. NULL,
  94. NULL,
  95. "E/",
  96. "W/",
  97. NULL,
  98. "I/",
  99. "D/",
  100. };
  101. #endif
  102. #ifdef ULOG_USING_COLOR
  103. /* color output info */
  104. static const char * const color_output_info[] =
  105. {
  106. ULOG_COLOR_ASSERT,
  107. NULL,
  108. NULL,
  109. ULOG_COLOR_ERROR,
  110. ULOG_COLOR_WARN,
  111. NULL,
  112. ULOG_COLOR_INFO,
  113. ULOG_COLOR_DEBUG,
  114. };
  115. #endif /* ULOG_USING_COLOR */
  116. /* ulog local object */
  117. static struct ulog ulog = { 0 };
  118. extern uint8_t interrupt_get_nest(void);
  119. size_t ulog_strcpy(size_t cur_len, char *dst, const char *src)
  120. {
  121. const char *src_old = src;
  122. configASSERT(dst);
  123. configASSERT(src);
  124. while (*src != 0)
  125. {
  126. /* make sure destination has enough space */
  127. if (cur_len++ < ULOG_LINE_BUF_SIZE)
  128. {
  129. *dst++ = *src++;
  130. }
  131. else
  132. {
  133. break;
  134. }
  135. }
  136. return src - src_old;
  137. }
  138. size_t ulog_ultoa(char *s, unsigned long int n)
  139. {
  140. size_t i = 0, j = 0, len = 0;
  141. char swap;
  142. do
  143. {
  144. s[len++] = n % 10 + '0';
  145. } while (n /= 10);
  146. s[len] = '\0';
  147. /* reverse string */
  148. for (i = 0, j = len - 1; i < j; ++i, --j)
  149. {
  150. swap = s[i];
  151. s[i] = s[j];
  152. s[j] = swap;
  153. }
  154. return len;
  155. }
  156. static void output_unlock(void)
  157. {
  158. uint32_t cpsr = 0;
  159. uint32_t mode;
  160. asm("mrs %0, CPSR" : : "r"(cpsr));
  161. mode = cpsr & 0x1f;
  162. /* is in abort or undefine mode */
  163. if (mode == 0x17 || mode == 0x1b)
  164. {
  165. }
  166. /* is in thread context */
  167. else if (interrupt_get_nest() == 0)
  168. {
  169. xSemaphoreGive(ulog.output_locker);
  170. }
  171. else
  172. {
  173. #ifdef ULOG_USING_ISR_LOG
  174. portCLEAR_INTERRUPT_MASK_FROM_ISR(ulog.output_locker_isr_lvl);
  175. #endif
  176. }
  177. }
  178. static void output_lock(void)
  179. {
  180. uint32_t cpsr = 0;
  181. uint32_t mode;
  182. asm("mrs %0, CPSR" : : "r"(cpsr));
  183. mode = cpsr & 0x1f;
  184. /* is in abort or undefine mode */
  185. if (mode == 0x17 || mode == 0x1b)
  186. {
  187. }
  188. /* is in thread context */
  189. else if (interrupt_get_nest() == 0)
  190. {
  191. xSemaphoreTake(ulog.output_locker, portMAX_DELAY);
  192. }
  193. else
  194. {
  195. #ifdef ULOG_USING_ISR_LOG
  196. ulog.output_locker_isr_lvl = portSET_INTERRUPT_MASK_FROM_ISR();
  197. #endif
  198. }
  199. }
  200. static char *get_log_buf(void)
  201. {
  202. /* is in thread context */
  203. if (interrupt_get_nest() == 0)
  204. {
  205. return ulog.log_buf_th;
  206. }
  207. else
  208. {
  209. #ifdef ULOG_USING_ISR_LOG
  210. return ulog.log_buf_isr;
  211. #else
  212. printf("Error: Current mode not supported run in ISR. Please enable ULOG_USING_ISR_LOG.\n");
  213. return NULL;
  214. #endif
  215. }
  216. }
  217. WEAK size_t ulog_formater(char *log_buf, uint32_t level, const char *tag, int newline,
  218. const char *format, va_list args)
  219. {
  220. /* the caller has locker, so it can use static variable for reduce stack usage */
  221. static size_t log_len, newline_len;
  222. static int fmt_result;
  223. configASSERT(log_buf);
  224. configASSERT(level <= LOG_LVL_DBG);
  225. configASSERT(tag);
  226. configASSERT(format);
  227. log_len = 0;
  228. newline_len = strlen(ULOG_NEWLINE_SIGN);
  229. #ifdef ULOG_USING_COLOR
  230. /* add CSI start sign and color info */
  231. if (color_output_info[level])
  232. {
  233. log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_START);
  234. log_len += ulog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
  235. }
  236. #endif /* ULOG_USING_COLOR */
  237. #ifdef ULOG_OUTPUT_TIME
  238. /* add time info */
  239. {
  240. #ifdef ULOG_TIME_USING_TIMESTAMP
  241. SystemTime_t tm;
  242. #ifdef RTC_SUPPORT
  243. iGetLocalTime(&tm);
  244. snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm.tm_mon + 1,
  245. tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  246. #else
  247. tm.tm_year = YearDat;
  248. tm.tm_mon = MonthDat;
  249. tm.tm_mday = DayDat;
  250. tm.tm_hour = HourDat;
  251. tm.tm_min = MinuteDat;
  252. tm.tm_sec = SecondDat;
  253. snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm.tm_mon,
  254. tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  255. #endif
  256. #else
  257. static size_t tick_len = 0;
  258. log_buf[log_len] = '[';
  259. tick_len = ulog_ultoa(log_buf + log_len + 1, xTaskGetTickCount());
  260. log_buf[log_len + 1 + tick_len] = ']';
  261. log_buf[log_len + 1 + tick_len + 1] = '\0';
  262. #endif /* ULOG_TIME_USING_TIMESTAMP */
  263. log_len += strlen(log_buf + log_len);
  264. }
  265. #endif /* ULOG_OUTPUT_TIME */
  266. #ifdef ULOG_OUTPUT_LEVEL
  267. #ifdef ULOG_OUTPUT_TIME
  268. log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
  269. #endif
  270. /* add level info */
  271. log_len += ulog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
  272. #endif /* ULOG_OUTPUT_LEVEL */
  273. #ifdef ULOG_OUTPUT_TAG
  274. #if !defined(ULOG_OUTPUT_LEVEL) && defined(ULOG_OUTPUT_TIME)
  275. log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
  276. #endif
  277. /* add tag info */
  278. log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
  279. #endif /* ULOG_OUTPUT_TAG */
  280. #ifdef ULOG_OUTPUT_THREAD_NAME
  281. /* add thread info */
  282. {
  283. #if defined(ULOG_OUTPUT_TIME) || defined(ULOG_OUTPUT_LEVEL) || defined(ULOG_OUTPUT_TAG)
  284. log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
  285. #endif
  286. /* is not in interrupt context */
  287. if (interrupt_get_nest() == 0)
  288. {
  289. size_t name_len = strnlen(xTaskGetCurrentTaskHandle()->pcTaskName, ULOG_NAME_MAX);
  290. strncpy(log_buf + log_len, xTaskGetCurrentTaskHandle()->pcTaskName, name_len);
  291. log_len += name_len;
  292. }
  293. else
  294. {
  295. log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
  296. }
  297. }
  298. #endif /* ULOG_OUTPUT_THREAD_NAME */
  299. log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
  300. #ifdef ULOG_OUTPUT_FLOAT
  301. fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
  302. #else
  303. fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
  304. #endif /* ULOG_OUTPUT_FLOAT */
  305. /* calculate log length */
  306. if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
  307. {
  308. log_len += fmt_result;
  309. }
  310. else
  311. {
  312. /* using max length */
  313. log_len = ULOG_LINE_BUF_SIZE;
  314. }
  315. /* overflow check and reserve some space for CSI end sign and newline sign */
  316. #ifdef ULOG_USING_COLOR
  317. if (log_len + (sizeof(CSI_END) - 1) + newline_len > ULOG_LINE_BUF_SIZE)
  318. {
  319. /* using max length */
  320. log_len = ULOG_LINE_BUF_SIZE;
  321. /* reserve some space for CSI end sign */
  322. log_len -= (sizeof(CSI_END) - 1);
  323. #else
  324. if (log_len + newline_len > ULOG_LINE_BUF_SIZE)
  325. {
  326. /* using max length */
  327. log_len = ULOG_LINE_BUF_SIZE;
  328. #endif /* ULOG_USING_COLOR */
  329. /* reserve some space for newline sign */
  330. log_len -= newline_len;
  331. }
  332. /* package newline sign */
  333. if (newline)
  334. {
  335. log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
  336. }
  337. #ifdef ULOG_USING_COLOR
  338. /* add CSI end sign */
  339. if (color_output_info[level])
  340. {
  341. log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_END);
  342. }
  343. #endif /* ULOG_USING_COLOR */
  344. return log_len;
  345. }
  346. void ulog_output_to_all_backend(uint32_t level, const char *tag, int is_raw, const char *log, size_t size)
  347. {
  348. ListItem_t *node;
  349. ulog_backend_t backend;
  350. if (!ulog.init_ok)
  351. return;
  352. /* output for all backends */
  353. for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list);
  354. node = listGET_NEXT(node))
  355. {
  356. backend = listGET_LIST_ITEM_OWNER(node);
  357. #if !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG)
  358. backend->output(backend, level, tag, is_raw, log, size);
  359. #else
  360. if (backend->support_color || is_raw)
  361. {
  362. backend->output(backend, level, tag, is_raw, log, size);
  363. }
  364. else
  365. {
  366. /* recalculate the log start address and log size when backend not supported color */
  367. size_t color_info_len = strlen(color_output_info[level]), output_size = size;
  368. if (color_info_len)
  369. {
  370. size_t color_hdr_len = strlen(CSI_START) + color_info_len;
  371. log += color_hdr_len;
  372. output_size -= (color_hdr_len + (sizeof(CSI_END) - 1));
  373. }
  374. backend->output(backend, level, tag, is_raw, log, output_size);
  375. }
  376. #endif /* !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) */
  377. }
  378. }
  379. static void do_output(uint32_t level, const char *tag, int is_raw, const char *log_buf, size_t log_len)
  380. {
  381. #ifdef ULOG_USING_ASYNC_OUTPUT
  382. rbb_blk_t log_blk;
  383. ulog_frame_t log_frame;
  384. uint32_t cpsr = 0;
  385. uint32_t mode;
  386. /* allocate log frame */
  387. log_blk = rbb_blk_alloc(ulog.async_rbb, ULOG_ALIGN(sizeof(struct ulog_frame) + log_len, ULOG_ALIGN_SIZE));
  388. if (log_blk)
  389. {
  390. /* package the log frame */
  391. log_frame = (ulog_frame_t) log_blk->buf;
  392. log_frame->magic = ULOG_FRAME_MAGIC;
  393. log_frame->is_raw = is_raw;
  394. log_frame->level = level;
  395. log_frame->log_len = log_len;
  396. log_frame->tag = tag;
  397. log_frame->log = (const char *)log_blk->buf + sizeof(struct ulog_frame);
  398. /* copy log data */
  399. memcpy(log_blk->buf + sizeof(struct ulog_frame), log_buf, log_len);
  400. /* put the block */
  401. rbb_blk_put(log_blk);
  402. asm("mrs %0, CPSR" : : "r"(cpsr));
  403. mode = cpsr & 0x1f;
  404. /* is in abort or undefine mode */
  405. if (mode == 0x17 || mode == 0x1b)
  406. ;
  407. /* send a notice */
  408. else if (interrupt_get_nest() == 0)
  409. xQueueSend(ulog.async_notice, NULL, 0);
  410. else
  411. xQueueSendFromISR(ulog.async_notice, NULL, 0);
  412. }
  413. else
  414. {
  415. static int already_output = pdFALSE;
  416. if (already_output == pdFALSE)
  417. {
  418. printf("Warning: There is no enough buffer for saving async log,"
  419. " please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option.\n");
  420. already_output = pdTRUE;
  421. }
  422. }
  423. #else
  424. /* is in thread context */
  425. if (interrupt_get_nest() == 0)
  426. {
  427. /* output to all backends */
  428. ulog_output_to_all_backend(level, tag, is_raw, log_buf, log_len);
  429. }
  430. else
  431. {
  432. #ifdef ULOG_BACKEND_USING_CONSOLE
  433. /* We can't ensure that all backends support ISR context output.
  434. * So only using printf when context is ISR */
  435. extern void ulog_console_backend_output(struct ulog_backend *backend, uint32_t level, const char *tag,
  436. int is_raw, const char *log, size_t len);
  437. ulog_console_backend_output(NULL, level, tag, is_raw, log_buf, log_len);
  438. #endif /* ULOG_BACKEND_USING_CONSOLE */
  439. }
  440. #endif /* ULOG_USING_ASYNC_OUTPUT */
  441. }
  442. /**
  443. * output the log by variable argument list
  444. *
  445. * @param level level
  446. * @param tag tag
  447. * @param newline has_newline
  448. * @param format output format
  449. * @param args variable argument list
  450. */
  451. void ulog_voutput(uint32_t level, const char *tag, int newline, const char *format, va_list args)
  452. {
  453. char *log_buf = NULL;
  454. size_t log_len = 0;
  455. #ifndef ULOG_USING_SYSLOG
  456. configASSERT(level <= LOG_LVL_DBG);
  457. #else
  458. configASSERT(LOG_PRI(level) <= LOG_DEBUG);
  459. #endif /* ULOG_USING_SYSLOG */
  460. configASSERT(tag);
  461. configASSERT(format);
  462. if (!ulog.init_ok)
  463. {
  464. return;
  465. }
  466. #ifdef ULOG_USING_FILTER
  467. /* level filter */
  468. #ifndef ULOG_USING_SYSLOG
  469. if (level > ulog.filter.level || level > ulog_tag_lvl_filter_get(tag))
  470. {
  471. return;
  472. }
  473. #else
  474. if (((LOG_MASK(LOG_PRI(level)) & ulog.filter.level) == 0)
  475. || ((LOG_MASK(LOG_PRI(level)) & ulog_tag_lvl_filter_get(tag)) == 0))
  476. {
  477. return;
  478. }
  479. #endif /* ULOG_USING_SYSLOG */
  480. else if (!strstr(tag, ulog.filter.tag))
  481. {
  482. /* tag filter */
  483. return;
  484. }
  485. #endif /* ULOG_USING_FILTER */
  486. /* get log buffer */
  487. log_buf = get_log_buf();
  488. /* lock output */
  489. output_lock();
  490. #ifndef ULOG_USING_SYSLOG
  491. log_len = ulog_formater(log_buf, level, tag, newline, format, args);
  492. #else
  493. extern size_t syslog_formater(char *log_buf, uint8_t level, const char *tag, int newline, const char *format, va_list args);
  494. log_len = syslog_formater(log_buf, level, tag, newline, format, args);
  495. #endif /* ULOG_USING_SYSLOG */
  496. #ifdef ULOG_USING_FILTER
  497. /* keyword filter */
  498. if (ulog.filter.keyword[0] != '\0')
  499. {
  500. /* add string end sign */
  501. log_buf[log_len] = '\0';
  502. /* find the keyword */
  503. if (!strstr(log_buf, ulog.filter.keyword))
  504. {
  505. /* unlock output */
  506. output_unlock();
  507. return;
  508. }
  509. }
  510. #endif /* ULOG_USING_FILTER */
  511. /* do log output */
  512. do_output(level, tag, pdFALSE, log_buf, log_len);
  513. /* unlock output */
  514. output_unlock();
  515. }
  516. /**
  517. * output the log
  518. *
  519. * @param level level
  520. * @param tag tag
  521. * @param newline has newline
  522. * @param format output format
  523. * @param ... args
  524. */
  525. void ulog_output(uint32_t level, const char *tag, int newline, const char *format, ...)
  526. {
  527. va_list args;
  528. /* args point to the first variable parameter */
  529. va_start(args, format);
  530. ulog_voutput(level, tag, newline, format, args);
  531. va_end(args);
  532. }
  533. /**
  534. * output RAW string format log
  535. *
  536. * @param format output format
  537. * @param ... args
  538. */
  539. void ulog_raw(const char *format, ...)
  540. {
  541. size_t log_len = 0;
  542. char *log_buf = NULL;
  543. va_list args;
  544. int fmt_result;
  545. configASSERT(ulog.init_ok);
  546. /* get log buffer */
  547. log_buf = get_log_buf();
  548. /* lock output */
  549. output_lock();
  550. /* args point to the first variable parameter */
  551. va_start(args, format);
  552. #ifdef ULOG_OUTPUT_FLOAT
  553. fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
  554. #else
  555. fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
  556. #endif /* ULOG_OUTPUT_FLOAT */
  557. va_end(args);
  558. /* calculate log length */
  559. if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
  560. {
  561. log_len = fmt_result;
  562. }
  563. else
  564. {
  565. log_len = ULOG_LINE_BUF_SIZE;
  566. }
  567. /* do log output */
  568. do_output(LOG_LVL_DBG, NULL, pdTRUE, log_buf, log_len);
  569. /* unlock output */
  570. output_unlock();
  571. }
  572. /**
  573. * dump the hex format data to log
  574. *
  575. * @param tag name for hex object, it will show on log header
  576. * @param width hex number for every line, such as: 16, 32
  577. * @param buf hex buffer
  578. * @param size buffer size
  579. */
  580. void ulog_hexdump(const char *tag, size_t width, uint8_t *buf, size_t size)
  581. {
  582. #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
  583. size_t i, j;
  584. size_t log_len = 0, name_len = strlen(tag);
  585. char *log_buf = NULL, dump_string[8];
  586. int fmt_result;
  587. configASSERT(ulog.init_ok);
  588. #ifdef ULOG_USING_FILTER
  589. /* level filter */
  590. #ifndef ULOG_USING_SYSLOG
  591. if (LOG_LVL_DBG > ulog.filter.level || LOG_LVL_DBG > ulog_tag_lvl_filter_get(tag))
  592. {
  593. return;
  594. }
  595. #else
  596. if ((LOG_MASK(LOG_DEBUG) & ulog.filter.level) == 0)
  597. {
  598. return;
  599. }
  600. #endif /* ULOG_USING_SYSLOG */
  601. else if (!strstr(tag, ulog.filter.tag))
  602. {
  603. /* tag filter */
  604. return;
  605. }
  606. #endif /* ULOG_USING_FILTER */
  607. /* get log buffer */
  608. log_buf = get_log_buf();
  609. /* lock output */
  610. output_lock();
  611. for (i = 0, log_len = 0; i < size; i += width)
  612. {
  613. /* package header */
  614. if (i == 0)
  615. {
  616. log_len += ulog_strcpy(log_len, log_buf + log_len, "D/HEX ");
  617. log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
  618. log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
  619. }
  620. else
  621. {
  622. log_len = 6 + name_len + 2;
  623. memset(log_buf, ' ', log_len);
  624. }
  625. fmt_result = snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE, "%04X-%04X: ", i, i + width - 1);
  626. /* calculate log length */
  627. if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
  628. {
  629. log_len += fmt_result;
  630. }
  631. else
  632. {
  633. log_len = ULOG_LINE_BUF_SIZE;
  634. }
  635. /* dump hex */
  636. for (j = 0; j < width; j++)
  637. {
  638. if (i + j < size)
  639. {
  640. snprintf(dump_string, sizeof(dump_string), "%02X ", buf[i + j]);
  641. }
  642. else
  643. {
  644. strncpy(dump_string, " ", sizeof(dump_string));
  645. }
  646. log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
  647. if ((j + 1) % 8 == 0)
  648. {
  649. log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
  650. }
  651. }
  652. log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
  653. /* dump char for hex */
  654. for (j = 0; j < width; j++)
  655. {
  656. if (i + j < size)
  657. {
  658. snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
  659. log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
  660. }
  661. }
  662. /* overflow check and reserve some space for newline sign */
  663. if (log_len + strlen(ULOG_NEWLINE_SIGN) > ULOG_LINE_BUF_SIZE)
  664. {
  665. log_len = ULOG_LINE_BUF_SIZE - strlen(ULOG_NEWLINE_SIGN);
  666. }
  667. /* package newline sign */
  668. log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
  669. /*add string end sign*/
  670. log_buf[log_len] = '\0';
  671. /* do log output */
  672. do_output(LOG_LVL_DBG, NULL, pdTRUE, log_buf, log_len);
  673. }
  674. /* unlock output */
  675. output_unlock();
  676. }
  677. #ifdef ULOG_USING_FILTER
  678. /**
  679. * Set the filter's level by different tag.
  680. * The log on this tag which level is less than it will stop output.
  681. *
  682. * example:
  683. * // the example tag log enter silent mode
  684. * ulog_set_filter_lvl("example", LOG_FILTER_LVL_SILENT);
  685. * // the example tag log which level is less than INFO level will stop output
  686. * ulog_set_filter_lvl("example", LOG_LVL_INFO);
  687. * // remove example tag's level filter, all level log will resume output
  688. * ulog_set_filter_lvl("example", LOG_FILTER_LVL_ALL);
  689. *
  690. * @param tag log tag
  691. * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode.
  692. * When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer.
  693. * Then all level log will resume output.
  694. *
  695. * @return 0 : success
  696. * -5 : no memory
  697. * -10: level is out of range
  698. */
  699. int ulog_tag_lvl_filter_set(const char *tag, uint32_t level)
  700. {
  701. ListItem_t *node;
  702. ulog_tag_lvl_filter_t tag_lvl = NULL;
  703. int result = 0;
  704. if (level > LOG_FILTER_LVL_ALL)
  705. return -EINVAL;
  706. if (!ulog.init_ok)
  707. return result;
  708. /* lock output */
  709. output_lock();
  710. /* find the tag in list */
  711. for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get());
  712. node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node))
  713. {
  714. tag_lvl = listGET_LIST_ITEM_OWNER(node);
  715. if (!strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
  716. {
  717. break;
  718. }
  719. else
  720. {
  721. tag_lvl = NULL;
  722. }
  723. }
  724. /* find OK */
  725. if (tag_lvl)
  726. {
  727. if (level == LOG_FILTER_LVL_ALL)
  728. {
  729. /* remove current tag's level filter when input level is the lowest level */
  730. uxListRemove(&tag_lvl->list);
  731. vPortFree(tag_lvl);
  732. }
  733. else
  734. {
  735. /* update level */
  736. tag_lvl->level = level;
  737. }
  738. }
  739. else
  740. {
  741. /* only add the new tag's level filer when level is not LOG_FILTER_LVL_ALL */
  742. if (level != LOG_FILTER_LVL_ALL)
  743. {
  744. /* new a tag's level filter */
  745. tag_lvl = (ulog_tag_lvl_filter_t)pvPortMalloc(sizeof(struct ulog_tag_lvl_filter));
  746. if (tag_lvl)
  747. {
  748. memset(tag_lvl->tag, 0 , sizeof(tag_lvl->tag));
  749. strncpy(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN);
  750. tag_lvl->level = level;
  751. listSET_LIST_ITEM_OWNER(&tag_lvl->list, tag_lvl);
  752. vListInsertEnd(ulog_tag_lvl_list_get(), &tag_lvl->list);
  753. }
  754. else
  755. {
  756. result = -ENOMEM;
  757. }
  758. }
  759. }
  760. /* unlock output */
  761. output_unlock();
  762. return result;
  763. }
  764. /**
  765. * get the level on tag's level filer
  766. *
  767. * @param tag log tag
  768. *
  769. * @return It will return the lowest level when tag was not found.
  770. * Other level will return when tag was found.
  771. */
  772. uint32_t ulog_tag_lvl_filter_get(const char *tag)
  773. {
  774. ListItem_t *node;
  775. ulog_tag_lvl_filter_t tag_lvl = NULL;
  776. uint32_t level = LOG_FILTER_LVL_ALL;
  777. if (!ulog.init_ok)
  778. return level;
  779. /* lock output */
  780. output_lock();
  781. /* find the tag in list */
  782. for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get());
  783. node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node))
  784. {
  785. tag_lvl = listGET_LIST_ITEM_OWNER(node);
  786. if (!strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
  787. {
  788. level = tag_lvl->level;
  789. break;
  790. }
  791. }
  792. /* unlock output */
  793. output_unlock();
  794. return level;
  795. }
  796. /**
  797. * get the tag's level list on filter
  798. *
  799. * @return tag's level list
  800. */
  801. List_t *ulog_tag_lvl_list_get(void)
  802. {
  803. return &ulog.filter.tag_lvl_list;
  804. }
  805. /**
  806. * set log global filter level
  807. *
  808. * @param level log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
  809. * LOG_FILTER_LVL_SILENT: disable all log output, except assert level
  810. * LOG_FILTER_LVL_ALL: enable all log output
  811. */
  812. void ulog_global_filter_lvl_set(uint32_t level)
  813. {
  814. configASSERT(level <= LOG_FILTER_LVL_ALL);
  815. ulog.filter.level = level;
  816. }
  817. /**
  818. * get log global filter level
  819. *
  820. * @return log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
  821. * LOG_FILTER_LVL_SILENT: disable all log output, except assert level
  822. * LOG_FILTER_LVL_ALL: enable all log output
  823. */
  824. uint32_t ulog_global_filter_lvl_get(void)
  825. {
  826. return ulog.filter.level;
  827. }
  828. /**
  829. * set log global filter tag
  830. *
  831. * @param tag tag
  832. */
  833. void ulog_global_filter_tag_set(const char *tag)
  834. {
  835. configASSERT(tag);
  836. strncpy(ulog.filter.tag, tag, ULOG_FILTER_TAG_MAX_LEN);
  837. }
  838. /**
  839. * get log global filter tag
  840. *
  841. * @return tag
  842. */
  843. const char *ulog_global_filter_tag_get(void)
  844. {
  845. return ulog.filter.tag;
  846. }
  847. /**
  848. * set log global filter keyword
  849. *
  850. * @param keyword keyword
  851. */
  852. void ulog_global_filter_kw_set(const char *keyword)
  853. {
  854. configASSERT(keyword);
  855. strncpy(ulog.filter.keyword, keyword, ULOG_FILTER_KW_MAX_LEN);
  856. }
  857. /**
  858. * get log global filter keyword
  859. *
  860. * @return keyword
  861. */
  862. const char *ulog_global_filter_kw_get(void)
  863. {
  864. return ulog.filter.keyword;
  865. }
  866. #endif /* ULOG_USING_FILTER */
  867. int ulog_backend_register(ulog_backend_t backend, const char *name, int support_color)
  868. {
  869. UBaseType_t uxSavedInterruptStatus;
  870. configASSERT(backend);
  871. configASSERT(name);
  872. configASSERT(ulog.init_ok);
  873. configASSERT(backend->output);
  874. if (backend->init)
  875. {
  876. backend->init(backend);
  877. }
  878. backend->support_color = support_color;
  879. memcpy(backend->name, name, ULOG_NAME_MAX);
  880. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  881. listSET_LIST_ITEM_OWNER(&backend->list, backend);
  882. vListInsertEnd(&ulog.backend_list, &backend->list);
  883. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  884. return 0;
  885. }
  886. int ulog_backend_unregister(ulog_backend_t backend)
  887. {
  888. UBaseType_t uxSavedInterruptStatus;
  889. configASSERT(backend);
  890. configASSERT(ulog.init_ok);
  891. if (backend->deinit)
  892. {
  893. backend->deinit(backend);
  894. }
  895. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  896. uxListRemove(&backend->list);
  897. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  898. return 0;
  899. }
  900. #ifdef ULOG_USING_ASYNC_OUTPUT
  901. /**
  902. * asynchronous output logs to all backends
  903. *
  904. * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
  905. */
  906. void ulog_async_output(void)
  907. {
  908. rbb_blk_t log_blk;
  909. ulog_frame_t log_frame;
  910. while ((log_blk = rbb_blk_get(ulog.async_rbb)) != NULL)
  911. {
  912. log_frame = (ulog_frame_t) log_blk->buf;
  913. if (log_frame->magic == ULOG_FRAME_MAGIC)
  914. {
  915. /* output to all backends */
  916. ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log,
  917. log_frame->log_len);
  918. }
  919. rbb_blk_free(ulog.async_rbb, log_blk);
  920. }
  921. }
  922. /**
  923. * waiting for get asynchronous output log
  924. *
  925. * @param time the waiting time
  926. */
  927. void ulog_async_waiting_log(uint32_t time)
  928. {
  929. xQueueReset(ulog.async_notice);
  930. xQueueReceive(ulog.async_notice, NULL, time);
  931. }
  932. static void async_output_thread_entry(void *param)
  933. {
  934. ulog_async_output();
  935. while (1)
  936. {
  937. ulog_async_waiting_log(portMAX_DELAY);
  938. ulog_async_output();
  939. }
  940. }
  941. #endif /* ULOG_USING_ASYNC_OUTPUT */
  942. /**
  943. * flush all backends's log
  944. */
  945. void ulog_flush(void)
  946. {
  947. ListItem_t *node;
  948. ulog_backend_t backend;
  949. if (!ulog.init_ok)
  950. return;
  951. #ifdef ULOG_USING_ASYNC_OUTPUT
  952. ulog_async_output();
  953. #endif
  954. /* flush all backends */
  955. for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list);
  956. node = listGET_NEXT(node))
  957. {
  958. backend = listGET_LIST_ITEM_OWNER(node);
  959. if (backend->flush)
  960. {
  961. backend->flush(backend);
  962. }
  963. }
  964. }
  965. int ulog_init(void)
  966. {
  967. if (ulog.init_ok)
  968. return 0;
  969. ulog.output_locker = xSemaphoreCreateMutex();
  970. vListInitialise(&ulog.backend_list);
  971. #ifdef ULOG_USING_FILTER
  972. vListInitialise(ulog_tag_lvl_list_get());
  973. #endif
  974. #ifdef ULOG_USING_ASYNC_OUTPUT
  975. configASSERT(ULOG_ASYNC_OUTPUT_STORE_LINES >= 2);
  976. /* async output ring block buffer */
  977. ulog.async_rbb = rbb_create(ULOG_ALIGN(ULOG_ASYNC_OUTPUT_BUF_SIZE, ULOG_ALIGN_SIZE), ULOG_ASYNC_OUTPUT_STORE_LINES);
  978. if (ulog.async_rbb == NULL)
  979. {
  980. printf("Error: ulog init failed! No memory for async rbb.\n");
  981. vSemaphoreDelete(ulog.output_locker);
  982. return -ENOMEM;
  983. }
  984. ulog.async_notice = xQueueCreate(1, 0);
  985. /* async output thread startup */
  986. /* async output thread */
  987. xTaskCreate(async_output_thread_entry, "ulog_async", ULOG_ASYNC_OUTPUT_THREAD_STACK, &ulog,
  988. ULOG_ASYNC_OUTPUT_THREAD_PRIORITY, &ulog.async_th);
  989. if (ulog.async_th == NULL)
  990. {
  991. printf("Error: ulog init failed! No memory for async output thread.\n");
  992. vSemaphoreDelete(ulog.output_locker);
  993. rbb_destroy(ulog.async_rbb);
  994. return -ENOMEM;
  995. }
  996. #endif /* ULOG_USING_ASYNC_OUTPUT */
  997. #ifdef ULOG_USING_FILTER
  998. ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
  999. #endif
  1000. ulog.init_ok = pdTRUE;
  1001. return 0;
  1002. }
  1003. void ulog_deinit(void)
  1004. {
  1005. ListItem_t *node;
  1006. ulog_backend_t backend;
  1007. if (!ulog.init_ok)
  1008. return;
  1009. /* deinit all backends */
  1010. for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list);
  1011. node = listGET_NEXT(node))
  1012. {
  1013. backend = listGET_LIST_ITEM_OWNER(node);
  1014. if (backend->deinit)
  1015. {
  1016. backend->deinit(backend);
  1017. }
  1018. }
  1019. #ifdef ULOG_USING_FILTER
  1020. /* deinit tag's level filter */
  1021. {
  1022. ulog_tag_lvl_filter_t tag_lvl;
  1023. for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get());
  1024. node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node))
  1025. {
  1026. tag_lvl = listGET_LIST_ITEM_OWNER(node);
  1027. vPortFree(tag_lvl);
  1028. }
  1029. }
  1030. #endif /* ULOG_USING_FILTER */
  1031. vSemaphoreDelete(ulog.output_locker);
  1032. #ifdef ULOG_USING_ASYNC_OUTPUT
  1033. rbb_destroy(ulog.async_rbb);
  1034. vTaskDelete(ulog.async_th);
  1035. #endif
  1036. ulog.init_ok = pdFALSE;
  1037. }
  1038. #endif /* USE_ULOG */