1 /*
2  * Copyright (c) 2006-2024 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-08-25     armink       the first version
9  */
10 
11 #include <stdarg.h>
12 #include "ulog.h"
13 #include "rthw.h"
14 
15 #ifdef ULOG_USING_SYSLOG
16 #include <syslog.h>
17 #endif
18 
19 #ifdef ULOG_TIME_USING_TIMESTAMP
20 #include <sys/time.h>
21 #endif
22 
23 #ifdef ULOG_USING_ASYNC_OUTPUT
24 #include <rtdevice.h>
25 #endif
26 
27 #ifdef RT_USING_ULOG
28 
29 /* the number which is max stored line logs */
30 #ifndef ULOG_ASYNC_OUTPUT_STORE_LINES
31 #define ULOG_ASYNC_OUTPUT_STORE_LINES  (ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / 80)
32 #endif
33 
34 #ifdef ULOG_USING_COLOR
35 /**
36  * CSI(Control Sequence Introducer/Initiator) sign
37  * more information on https://en.wikipedia.org/wiki/ANSI_escape_code
38  */
39 #define CSI_START                      "\033["
40 #define CSI_END                        "\033[0m"
41 /* output log front color */
42 #define F_BLACK                        "30m"
43 #define F_RED                          "31m"
44 #define F_GREEN                        "32m"
45 #define F_YELLOW                       "33m"
46 #define F_BLUE                         "34m"
47 #define F_MAGENTA                      "35m"
48 #define F_CYAN                         "36m"
49 #define F_WHITE                        "37m"
50 
51 /* output log default color definition */
52 #ifndef ULOG_COLOR_DEBUG
53 #define ULOG_COLOR_DEBUG               RT_NULL
54 #endif
55 #ifndef ULOG_COLOR_INFO
56 #define ULOG_COLOR_INFO                (F_GREEN)
57 #endif
58 #ifndef ULOG_COLOR_WARN
59 #define ULOG_COLOR_WARN                (F_YELLOW)
60 #endif
61 #ifndef ULOG_COLOR_ERROR
62 #define ULOG_COLOR_ERROR               (F_RED)
63 #endif
64 #ifndef ULOG_COLOR_ASSERT
65 #define ULOG_COLOR_ASSERT              (F_MAGENTA)
66 #endif
67 #endif /* ULOG_USING_COLOR */
68 
69 #if ULOG_LINE_BUF_SIZE < 80
70 #error "the log line buffer size must more than 80"
71 #endif
72 
73 struct rt_ulog
74 {
75     rt_bool_t init_ok;
76     rt_bool_t output_lock_enabled;
77     struct rt_mutex output_locker;
78     /* all backends */
79     rt_slist_t backend_list;
80     /* the thread log's line buffer */
81     char log_buf_th[ULOG_LINE_BUF_SIZE + 1];
82 
83 #ifdef ULOG_USING_ISR_LOG
84     /* the ISR log's line buffer */
85     rt_base_t output_locker_isr_lvl;
86     char log_buf_isr[ULOG_LINE_BUF_SIZE + 1];
87 #endif /* ULOG_USING_ISR_LOG */
88 
89 #ifdef ULOG_USING_ASYNC_OUTPUT
90     rt_bool_t async_enabled;
91     rt_rbb_t async_rbb;
92     /* ringbuffer for log_raw function only */
93     struct rt_ringbuffer *async_rb;
94     rt_thread_t async_th;
95     struct rt_semaphore async_notice;
96 #endif
97 
98 #ifdef ULOG_USING_FILTER
99     struct
100     {
101         /* all tag's level filter */
102         rt_slist_t tag_lvl_list;
103         /* global filter level, tag and keyword */
104         rt_uint32_t level;
105         char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
106         char keyword[ULOG_FILTER_KW_MAX_LEN + 1];
107     } filter;
108 #endif /* ULOG_USING_FILTER */
109 };
110 
111 #ifdef ULOG_OUTPUT_LEVEL
112 /* level output info */
113 static const char * const level_output_info[] =
114 {
115     "A/",
116     RT_NULL,
117     RT_NULL,
118     "E/",
119     "W/",
120     RT_NULL,
121     "I/",
122     "D/",
123 };
124 #endif /* ULOG_OUTPUT_LEVEL */
125 
126 #ifdef ULOG_USING_COLOR
127 /* color output info */
128 static const char * const color_output_info[] =
129 {
130     ULOG_COLOR_ASSERT,
131     RT_NULL,
132     RT_NULL,
133     ULOG_COLOR_ERROR,
134     ULOG_COLOR_WARN,
135     RT_NULL,
136     ULOG_COLOR_INFO,
137     ULOG_COLOR_DEBUG,
138 };
139 #endif /* ULOG_USING_COLOR */
140 
141 /* ulog local object */
142 static struct rt_ulog ulog = { 0 };
143 static RT_DEFINE_SPINLOCK(_spinlock);
144 
ulog_strcpy(rt_size_t cur_len,char * dst,const char * src)145 rt_size_t ulog_strcpy(rt_size_t cur_len, char *dst, const char *src)
146 {
147     const char *src_old = src;
148 
149     RT_ASSERT(dst);
150     RT_ASSERT(src);
151 
152     while (*src != 0)
153     {
154         /* make sure destination has enough space */
155         if (cur_len++ < ULOG_LINE_BUF_SIZE)
156         {
157             *dst++ = *src++;
158         }
159         else
160         {
161             break;
162         }
163     }
164     return src - src_old;
165 }
166 
ulog_ultoa(char * s,unsigned long int n)167 rt_size_t ulog_ultoa(char *s, unsigned long int n)
168 {
169     rt_size_t i = 0, j = 0, len = 0;
170     char swap;
171 
172     do
173     {
174         s[len++] = n % 10 + '0';
175     } while (n /= 10);
176     s[len] = '\0';
177     /* reverse string */
178     for (i = 0, j = len - 1; i < j; ++i, --j)
179     {
180         swap = s[i];
181         s[i] = s[j];
182         s[j] = swap;
183     }
184     return len;
185 }
186 
output_unlock(void)187 static void output_unlock(void)
188 {
189     /* earlier stage */
190     if (ulog.output_lock_enabled == RT_FALSE)
191     {
192         return;
193     }
194 
195     /* If the scheduler is started and in thread context */
196     if (rt_scheduler_is_available())
197     {
198         rt_mutex_release(&ulog.output_locker);
199     }
200     else
201     {
202 #ifdef ULOG_USING_ISR_LOG
203         rt_spin_unlock_irqrestore(&_spinlock, ulog.output_locker_isr_lvl);
204 #endif
205     }
206 }
207 
output_lock(void)208 static void output_lock(void)
209 {
210     /* earlier stage */
211     if (ulog.output_lock_enabled == RT_FALSE)
212     {
213         return;
214     }
215 
216     /* If the scheduler is started and in thread context */
217     if (rt_scheduler_is_available())
218     {
219         rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
220     }
221     else
222     {
223 #ifdef ULOG_USING_ISR_LOG
224         ulog.output_locker_isr_lvl = rt_spin_lock_irqsave(&_spinlock);
225 #endif
226     }
227 }
228 
ulog_output_lock_enabled(rt_bool_t enabled)229 void ulog_output_lock_enabled(rt_bool_t enabled)
230 {
231     ulog.output_lock_enabled = enabled;
232 }
233 
get_log_buf(void)234 static char *get_log_buf(void)
235 {
236     /* is in thread context */
237     if (rt_interrupt_get_nest() == 0)
238     {
239         return ulog.log_buf_th;
240     }
241     else
242     {
243 #ifdef ULOG_USING_ISR_LOG
244         return ulog.log_buf_isr;
245 #else
246         rt_kprintf("Error: Current mode not supported run in ISR. Please enable ULOG_USING_ISR_LOG.\n");
247         return RT_NULL;
248 #endif
249     }
250 }
251 
ulog_head_formater(char * log_buf,rt_uint32_t level,const char * tag)252 rt_weak rt_size_t ulog_head_formater(char *log_buf, rt_uint32_t level, const char *tag)
253 {
254     /* the caller has locker, so it can use static variable for reduce stack usage */
255     static rt_size_t log_len;
256 
257     RT_ASSERT(log_buf);
258     RT_ASSERT(level <= LOG_LVL_DBG);
259     RT_ASSERT(tag);
260 
261     log_len = 0;
262 
263 #ifdef ULOG_USING_COLOR
264     /* add CSI start sign and color info */
265     if (color_output_info[level])
266     {
267         log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_START);
268         log_len += ulog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
269     }
270 #endif /* ULOG_USING_COLOR */
271 
272     log_buf[log_len] = '\0';
273 
274 #ifdef ULOG_OUTPUT_TIME
275     /* add time info */
276     {
277 #ifdef ULOG_TIME_USING_TIMESTAMP
278         static struct timeval now;
279         static struct tm *tm, tm_tmp;
280         static rt_bool_t check_usec_support = RT_FALSE, usec_is_support = RT_FALSE;
281         time_t t = (time_t)0;
282 
283         if (gettimeofday(&now, RT_NULL) >= 0)
284         {
285             t = now.tv_sec;
286         }
287         tm = localtime_r(&t, &tm_tmp);
288         /* show the time format MM-DD HH:MM:SS */
289         rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm->tm_mon + 1,
290                 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
291         /* check the microseconds support when kernel is startup */
292         if (t > 0 && !check_usec_support && rt_thread_self() != RT_NULL)
293         {
294             long old_usec = now.tv_usec;
295             /* delay some time for wait microseconds changed */
296             rt_thread_mdelay(10);
297             gettimeofday(&now, RT_NULL);
298             check_usec_support = RT_TRUE;
299             /* the microseconds is not equal between two gettimeofday calls */
300             if (now.tv_usec != old_usec)
301                 usec_is_support = RT_TRUE;
302         }
303         if (usec_is_support)
304         {
305             /* show the millisecond */
306             log_len += rt_strlen(log_buf + log_len);
307             rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, ".%03d", now.tv_usec / 1000);
308         }
309 
310 #else
311         static rt_size_t tick_len = 0;
312 
313         log_buf[log_len] = '[';
314         tick_len = ulog_ultoa(log_buf + log_len + 1, rt_tick_get());
315         log_buf[log_len + 1 + tick_len] = ']';
316         log_buf[log_len + 1 + tick_len + 1] = '\0';
317 #endif /* ULOG_TIME_USING_TIMESTAMP */
318 
319         log_len += rt_strlen(log_buf + log_len);
320     }
321 #endif /* ULOG_OUTPUT_TIME */
322 
323 #ifdef ULOG_OUTPUT_LEVEL
324 
325 #ifdef ULOG_OUTPUT_TIME
326     log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
327 #endif
328 
329     /* add level info */
330     log_len += ulog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
331 #endif /* ULOG_OUTPUT_LEVEL */
332 
333 #ifdef ULOG_OUTPUT_TAG
334 
335 #if !defined(ULOG_OUTPUT_LEVEL) && defined(ULOG_OUTPUT_TIME)
336     log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
337 #endif
338 
339     /* add tag info */
340     log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
341 #endif /* ULOG_OUTPUT_TAG */
342 
343 #ifdef ULOG_OUTPUT_THREAD_NAME
344     /* add thread info */
345     {
346 
347 #if defined(ULOG_OUTPUT_TIME) || defined(ULOG_OUTPUT_LEVEL) || defined(ULOG_OUTPUT_TAG)
348         log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
349 #endif
350 
351         /* is not in interrupt context */
352         if (rt_interrupt_get_nest() == 0)
353         {
354             rt_size_t name_len = 0;
355             const char *thread_name = "N/A";
356             if (rt_thread_self())
357             {
358                 thread_name = rt_thread_self()->parent.name;
359             }
360             name_len = rt_strnlen(thread_name, RT_NAME_MAX);
361             rt_strncpy(log_buf + log_len, thread_name, name_len);
362             log_len += name_len;
363         }
364         else
365         {
366             log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
367         }
368     }
369 #endif /* ULOG_OUTPUT_THREAD_NAME */
370 
371     log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
372 
373     return log_len;
374 }
375 
376 
ulog_tail_formater(char * log_buf,rt_size_t log_len,rt_bool_t newline,rt_uint32_t level)377 rt_weak rt_size_t ulog_tail_formater(char *log_buf, rt_size_t log_len, rt_bool_t newline, rt_uint32_t level)
378 {
379     /* the caller has locker, so it can use static variable for reduce stack usage */
380     static rt_size_t newline_len;
381 
382     RT_ASSERT(log_buf);
383     newline_len = rt_strlen(ULOG_NEWLINE_SIGN);
384     /* overflow check and reserve some space for CSI end sign, newline sign and string end sign */
385 #ifdef ULOG_USING_COLOR
386     if (log_len + (sizeof(CSI_END) - 1) + newline_len + sizeof((char)'\0') > ULOG_LINE_BUF_SIZE)
387     {
388         /* using max length */
389         log_len = ULOG_LINE_BUF_SIZE;
390         /* reserve some space for CSI end sign */
391         log_len -= (sizeof(CSI_END) - 1);
392 #else
393     if (log_len + newline_len + sizeof((char)'\0') > ULOG_LINE_BUF_SIZE)
394     {
395         /* using max length */
396         log_len = ULOG_LINE_BUF_SIZE;
397 #endif /* ULOG_USING_COLOR */
398         /* reserve some space for newline sign */
399         log_len -= newline_len;
400         /* reserve some space for string end sign */
401         log_len -= sizeof((char)'\0');
402     }
403 
404     /* package newline sign */
405     if (newline)
406     {
407         log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
408     }
409 
410 #ifdef ULOG_USING_COLOR
411     /* add CSI end sign  */
412     if (color_output_info[level])
413     {
414         log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_END);
415     }
416 #endif /* ULOG_USING_COLOR */
417 
418     /* add string end sign */
419     log_buf[log_len] = '\0';
420 
421     return log_len;
422 }
423 
424 rt_weak rt_size_t ulog_formater(char *log_buf, rt_uint32_t level, const char *tag, rt_bool_t newline,
425         const char *format, va_list args)
426 {
427     /* the caller has locker, so it can use static variable for reduce stack usage */
428     static rt_size_t log_len;
429     static int fmt_result;
430 
431     RT_ASSERT(log_buf);
432     RT_ASSERT(format);
433 
434     /* log head */
435     log_len = ulog_head_formater(log_buf, level, tag);
436     /* log content */
437     fmt_result = rt_vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
438     /* calculate log length */
439     if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
440     {
441         log_len += fmt_result;
442     }
443     else
444     {
445         /* using max length */
446         log_len = ULOG_LINE_BUF_SIZE;
447     }
448     /* log tail */
449     return ulog_tail_formater(log_buf, log_len, newline, level);
450 }
451 
452 rt_weak rt_size_t ulog_hex_formater(char *log_buf, const char *tag, const rt_uint8_t *buf, rt_size_t size, rt_size_t width, rt_base_t addr)
453 {
454 #define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
455     /* the caller has locker, so it can use static variable for reduce stack usage */
456     static rt_size_t log_len, j;
457     static int fmt_result;
458     char dump_string[8];
459 
460     RT_ASSERT(log_buf);
461     RT_ASSERT(buf);
462 
463     /* log head */
464     log_len = ulog_head_formater(log_buf, LOG_LVL_DBG, tag);
465     /* log content */
466     fmt_result = rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE, "%04X-%04X: ", addr, addr + size);
467     /* calculate log length */
468     if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
469     {
470         log_len += fmt_result;
471     }
472     else
473     {
474         log_len = ULOG_LINE_BUF_SIZE;
475     }
476     /* dump hex */
477     for (j = 0; j < width; j++)
478     {
479         if (j < size)
480         {
481             rt_snprintf(dump_string, sizeof(dump_string), "%02X ", buf[j]);
482         }
483         else
484         {
485             rt_strncpy(dump_string, "   ", sizeof(dump_string));
486         }
487         log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
488         if ((j + 1) % 8 == 0)
489         {
490             log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
491         }
492     }
493     log_len += ulog_strcpy(log_len, log_buf + log_len, "  ");
494     /* dump char for hex */
495     for (j = 0; j < size; j++)
496     {
497         rt_snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[j]) ? buf[j] : '.');
498         log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
499     }
500     /* log tail */
501     return ulog_tail_formater(log_buf, log_len, RT_TRUE, LOG_LVL_DBG);
502 }
503 
504 static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
505 {
506     rt_slist_t *node;
507     ulog_backend_t backend;
508 
509     if (!ulog.init_ok)
510         return;
511 
512     /* if there is no backend */
513     if (!rt_slist_first(&ulog.backend_list))
514     {
515         rt_kputs(log);
516         return;
517     }
518 
519     /* output for all backends */
520     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
521     {
522         backend = rt_slist_entry(node, struct ulog_backend, list);
523         if (backend->out_level < level)
524         {
525             continue;
526         }
527 #if !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG)
528         backend->output(backend, level, tag, is_raw, log, len);
529 #else
530         if (backend->filter && backend->filter(backend, level, tag, is_raw, log, len) == RT_FALSE)
531         {
532             /* backend's filter is not match, so skip output */
533             continue;
534         }
535         if (backend->support_color || is_raw)
536         {
537             backend->output(backend, level, tag, is_raw, log, len);
538         }
539         else
540         {
541             /* recalculate the log start address and log size when backend not supported color */
542             rt_size_t color_info_len = 0, output_len = len;
543             const char *output_log = log;
544 
545             if (color_output_info[level] != RT_NULL)
546                 color_info_len = rt_strlen(color_output_info[level]);
547 
548             if (color_info_len)
549             {
550                 rt_size_t color_hdr_len = rt_strlen(CSI_START) + color_info_len;
551 
552                 output_log += color_hdr_len;
553                 output_len -= (color_hdr_len + (sizeof(CSI_END) - 1));
554             }
555             backend->output(backend, level, tag, is_raw, output_log, output_len);
556         }
557 #endif /* !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) */
558     }
559 }
560 
561 static void do_output(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log_buf, rt_size_t log_len)
562 {
563 #ifdef ULOG_USING_ASYNC_OUTPUT
564     rt_size_t log_buf_size = log_len + sizeof((char)'\0');
565     if (ulog.async_enabled)
566     {
567         if (is_raw == RT_FALSE)
568         {
569             rt_rbb_blk_t log_blk;
570             ulog_frame_t log_frame;
571 
572             /* allocate log frame */
573             log_blk = rt_rbb_blk_alloc(ulog.async_rbb, RT_ALIGN(sizeof(struct ulog_frame) + log_buf_size, RT_ALIGN_SIZE));
574             if (log_blk)
575             {
576                 /* package the log frame */
577                 log_frame = (ulog_frame_t) log_blk->buf;
578                 log_frame->magic = ULOG_FRAME_MAGIC;
579                 log_frame->is_raw = is_raw;
580                 log_frame->level = level;
581                 log_frame->log_len = log_len;
582                 log_frame->tag = tag;
583                 log_frame->log = (const char *)log_blk->buf + sizeof(struct ulog_frame);
584                 /* copy log data */
585                 rt_strncpy((char *)(log_blk->buf + sizeof(struct ulog_frame)), log_buf, log_buf_size);
586                 /* put the block */
587                 rt_rbb_blk_put(log_blk);
588                 /* send a notice */
589                 rt_sem_release(&ulog.async_notice);
590             }
591             else
592             {
593                 static rt_bool_t already_output = RT_FALSE;
594                 if (already_output == RT_FALSE)
595                 {
596                     rt_kprintf("Warning: There is no enough buffer for saving async log,"
597                             " please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option.\n");
598                     already_output = RT_TRUE;
599                 }
600             }
601         }
602         else if (ulog.async_rb)
603         {
604             /* log_buf_size contain the tail \0, which will lead discard follow char, so only put log_buf_size -1  */
605             rt_ringbuffer_put(ulog.async_rb, (const rt_uint8_t *)log_buf, (rt_uint16_t)log_buf_size - 1);
606             /* send a notice */
607             rt_sem_release(&ulog.async_notice);
608         }
609 
610         return;
611     }
612 #endif /* ULOG_USING_ASYNC_OUTPUT */
613     /* is in thread context */
614     if (rt_interrupt_get_nest() == 0)
615     {
616         /* output to all backends */
617         ulog_output_to_all_backend(level, tag, is_raw, log_buf, log_len);
618     }
619     else
620     {
621 #ifdef ULOG_BACKEND_USING_CONSOLE
622         /* We can't ensure that all backends support ISR context output.
623          * So only using rt_kprintf when context is ISR */
624         extern void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag,
625                 rt_bool_t is_raw, const char *log, rt_size_t len);
626         ulog_console_backend_output(RT_NULL, level, tag, is_raw, log_buf, log_len);
627 #endif /* ULOG_BACKEND_USING_CONSOLE */
628     }
629 }
630 
631 /**
632  * output the log by variable argument list
633  *
634  * @param level level
635  * @param tag tag
636  * @param newline has_newline
637  * @param hex_buf != RT_NULL: enable hex log mode, data buffer
638  * @param hex_size hex data buffer size
639  * @param hex_width hex log width
640  * @param hex_addr hex data address
641  * @param format output format
642  * @param args variable argument list
643  */
644 void ulog_voutput(rt_uint32_t level, const char *tag, rt_bool_t newline, const rt_uint8_t *hex_buf, rt_size_t hex_size,
645         rt_size_t hex_width, rt_base_t hex_addr, const char *format, va_list args)
646 {
647     static rt_bool_t ulog_voutput_recursion = RT_FALSE;
648     char *log_buf = RT_NULL;
649     static rt_size_t log_len = 0;
650 
651     RT_ASSERT(tag);
652     RT_ASSERT((format && !hex_buf) || (!format && hex_buf));
653 #ifndef ULOG_USING_SYSLOG
654     RT_ASSERT(level <= LOG_LVL_DBG);
655 #else
656     RT_ASSERT(LOG_PRI(level) <= LOG_DEBUG);
657 #endif /* ULOG_USING_SYSLOG */
658 
659     if (!ulog.init_ok)
660     {
661         return;
662     }
663 
664 #ifdef ULOG_USING_FILTER
665     /* level filter */
666 #ifndef ULOG_USING_SYSLOG
667     if (level > ulog.filter.level || level > ulog_tag_lvl_filter_get(tag))
668     {
669         return;
670     }
671 #else
672     if (((LOG_MASK(LOG_PRI(level)) & ulog.filter.level) == 0)
673             || ((LOG_MASK(LOG_PRI(level)) & ulog_tag_lvl_filter_get(tag)) == 0))
674     {
675         return;
676     }
677 #endif /* ULOG_USING_SYSLOG */
678     else if (!rt_strstr(tag, ulog.filter.tag))
679     {
680         /* tag filter */
681         return;
682     }
683 #endif /* ULOG_USING_FILTER */
684 
685     /* get log buffer */
686     log_buf = get_log_buf();
687 
688     /* lock output */
689     output_lock();
690 
691     /* If there is a recursion, we use a simple way */
692     if ((ulog_voutput_recursion == RT_TRUE) && (hex_buf == RT_NULL))
693     {
694         rt_kprintf(format, args);
695         if (newline == RT_TRUE)
696         {
697             rt_kprintf(ULOG_NEWLINE_SIGN);
698         }
699         output_unlock();
700         return;
701     }
702 
703     ulog_voutput_recursion = RT_TRUE;
704 
705     if (hex_buf == RT_NULL)
706     {
707 #ifndef ULOG_USING_SYSLOG
708         log_len = ulog_formater(log_buf, level, tag, newline, format, args);
709 #else
710         extern rt_size_t syslog_formater(char *log_buf, rt_uint8_t level, const char *tag, rt_bool_t newline, const char *format, va_list args);
711         log_len = syslog_formater(log_buf, level, tag, newline, format, args);
712 #endif /* ULOG_USING_SYSLOG */
713     }
714     else
715     {
716         /* hex mode */
717         log_len = ulog_hex_formater(log_buf, tag, hex_buf, hex_size, hex_width, hex_addr);
718     }
719 
720 #ifdef ULOG_USING_FILTER
721     /* keyword filter */
722     if (ulog.filter.keyword[0] != '\0')
723     {
724         /* add string end sign */
725         log_buf[log_len] = '\0';
726         /* find the keyword */
727         if (!rt_strstr(log_buf, ulog.filter.keyword))
728         {
729             ulog_voutput_recursion = RT_FALSE;
730             /* unlock output */
731             output_unlock();
732             return;
733         }
734     }
735 #endif /* ULOG_USING_FILTER */
736     /* do log output */
737     do_output(level, tag, RT_FALSE, log_buf, log_len);
738 
739     ulog_voutput_recursion = RT_FALSE;
740 
741     /* unlock output */
742     output_unlock();
743 }
744 
745 /**
746  * output the log
747  *
748  * @param level level
749  * @param tag tag
750  * @param newline has newline
751  * @param format output format
752  * @param ... args
753  */
754 void ulog_output(rt_uint32_t level, const char *tag, rt_bool_t newline, const char *format, ...)
755 {
756     va_list args;
757 
758     /* args point to the first variable parameter */
759     va_start(args, format);
760 
761     ulog_voutput(level, tag, newline, RT_NULL, 0, 0, 0, format, args);
762 
763     va_end(args);
764 }
765 
766 /**
767  * output RAW string format log
768  *
769  * @param format output format
770  * @param ... args
771  */
772 void ulog_raw(const char *format, ...)
773 {
774     rt_size_t log_len = 0;
775     char *log_buf = RT_NULL;
776     va_list args;
777     int fmt_result;
778 
779     RT_ASSERT(ulog.init_ok);
780 
781 #ifdef ULOG_USING_ASYNC_OUTPUT
782     if (ulog.async_rb == RT_NULL)
783     {
784         ulog.async_rb = rt_ringbuffer_create(ULOG_ASYNC_OUTPUT_BUF_SIZE);
785     }
786 #endif
787 
788     /* get log buffer */
789     log_buf = get_log_buf();
790 
791     /* lock output */
792     output_lock();
793 
794     /* args point to the first variable parameter */
795     va_start(args, format);
796     fmt_result = rt_vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
797     va_end(args);
798 
799     /* calculate log length
800      * rt_vsnprintf would add \0 to the end, push \0 to ulog.async_rb will discard the follow char
801      * if fmt_result = ULOG_LINE_BUF_SIZE, then the last char must be \0
802      */
803     if ((fmt_result > -1) && (fmt_result < ULOG_LINE_BUF_SIZE))
804     {
805         log_len = fmt_result;
806     }
807     else
808     {
809         log_len = ULOG_LINE_BUF_SIZE;
810     }
811 
812     /* do log output */
813     do_output(LOG_LVL_DBG, "", RT_TRUE, log_buf, log_len);
814 
815     /* unlock output */
816     output_unlock();
817 }
818 
819 /**
820  * dump the hex format data to log
821  *
822  * @param tag name for hex object, it will show on log header
823  * @param width hex number for every line, such as: 16, 32
824  * @param buf hex buffer
825  * @param size buffer size
826  */
827 void ulog_hexdump(const char *tag, rt_size_t width, const rt_uint8_t *buf, rt_size_t size, ...)
828 {
829     rt_size_t i, len;
830     va_list args;
831 
832     va_start(args, size);
833 
834     for (i = 0; i < size; i += width, buf += width)
835     {
836         if (i + width > size)
837             len = size - i;
838         else
839             len = width;
840         ulog_voutput(LOG_LVL_DBG, tag, RT_TRUE, buf, len, width, i, RT_NULL, args);
841     }
842 
843     va_end(args);
844 }
845 
846 #ifdef ULOG_USING_FILTER
847 /**
848  * Set the filter's level by different backend.
849  * The log on this backend which level is less than it will stop output.
850  *
851  * @param be_name backend name
852  * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode.
853  *        When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer.
854  *        Then all level log will resume output.
855  *
856  * @return  0 : success
857  *         -10: level is out of range
858  */
859 int ulog_be_lvl_filter_set(const char *be_name, rt_uint32_t level)
860 {
861     rt_slist_t *node = RT_NULL;
862     ulog_backend_t backend;
863     int result = RT_EOK;
864 
865     if (level > LOG_FILTER_LVL_ALL)
866         return -RT_EINVAL;
867 
868     if (!ulog.init_ok)
869         return result;
870 
871     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
872     {
873         backend = rt_slist_entry(node, struct ulog_backend, list);
874         if (rt_strncmp(backend->name, be_name, RT_NAME_MAX) == 0)
875         {
876             backend->out_level = level;
877         }
878     }
879 
880     return result;
881 }
882 
883 /**
884  * Set the filter's level by different tag.
885  * The log on this tag which level is less than it will stop output.
886  *
887  * example:
888  *     // the example tag log enter silent mode
889  *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_SILENT);
890  *     // the example tag log which level is less than INFO level will stop output
891  *     ulog_set_filter_lvl("example", LOG_LVL_INFO);
892  *     // remove example tag's level filter, all level log will resume output
893  *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_ALL);
894  *
895  * @param tag log tag
896  * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode.
897  *        When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer.
898  *        Then all level log will resume output.
899  *
900  * @return  0 : success
901  *         -5 : no memory
902  *         -10: level is out of range
903  */
904 int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)
905 {
906     rt_slist_t *node;
907     ulog_tag_lvl_filter_t tag_lvl = RT_NULL;
908     int result = RT_EOK;
909 
910     if (level > LOG_FILTER_LVL_ALL)
911         return -RT_EINVAL;
912 
913     if (!ulog.init_ok)
914         return result;
915 
916     /* lock output */
917     output_lock();
918     /* find the tag in list */
919     for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
920     {
921         tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
922         if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
923         {
924             break;
925         }
926         else
927         {
928             tag_lvl = RT_NULL;
929         }
930     }
931     /* find OK */
932     if (tag_lvl)
933     {
934         if (level == LOG_FILTER_LVL_ALL)
935         {
936             /* remove current tag's level filter when input level is the lowest level */
937             rt_slist_remove(ulog_tag_lvl_list_get(), &tag_lvl->list);
938             rt_free(tag_lvl);
939         }
940         else
941         {
942             /* update level */
943             tag_lvl->level = level;
944         }
945     }
946     else
947     {
948         /* only add the new tag's level filer when level is not LOG_FILTER_LVL_ALL */
949         if (level != LOG_FILTER_LVL_ALL)
950         {
951             /* new a tag's level filter */
952             tag_lvl = (ulog_tag_lvl_filter_t)rt_malloc(sizeof(struct ulog_tag_lvl_filter));
953             if (tag_lvl)
954             {
955                 rt_memset(tag_lvl->tag, 0 , sizeof(tag_lvl->tag));
956                 rt_strncpy(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN);
957                 tag_lvl->level = level;
958                 rt_slist_append(ulog_tag_lvl_list_get(), &tag_lvl->list);
959             }
960             else
961             {
962                 result = -RT_ENOMEM;
963             }
964         }
965     }
966     /* unlock output */
967     output_unlock();
968 
969     return result;
970 }
971 
972 /**
973  * get the level on tag's level filer
974  *
975  * @param tag log tag
976  *
977  * @return It will return the lowest level when tag was not found.
978  *         Other level will return when tag was found.
979  */
980 rt_uint32_t ulog_tag_lvl_filter_get(const char *tag)
981 {
982     rt_slist_t *node;
983     ulog_tag_lvl_filter_t tag_lvl = RT_NULL;
984     rt_uint32_t level = LOG_FILTER_LVL_ALL;
985 
986     if (!ulog.init_ok)
987         return level;
988 
989     /* lock output */
990     output_lock();
991     /* find the tag in list */
992     for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
993     {
994         tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
995         if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
996         {
997             level = tag_lvl->level;
998             break;
999         }
1000     }
1001     /* unlock output */
1002     output_unlock();
1003 
1004     return level;
1005 }
1006 
1007 /**
1008  * get the tag's level list on filter
1009  *
1010  * @return tag's level list
1011  */
1012 rt_slist_t *ulog_tag_lvl_list_get(void)
1013 {
1014     return &ulog.filter.tag_lvl_list;
1015 }
1016 
1017 /**
1018  * set log global filter level
1019  *
1020  * @param level log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
1021  *              LOG_FILTER_LVL_SILENT: disable all log output, except assert level
1022  *              LOG_FILTER_LVL_ALL: enable all log output
1023  */
1024 void ulog_global_filter_lvl_set(rt_uint32_t level)
1025 {
1026     RT_ASSERT(level <= LOG_FILTER_LVL_ALL);
1027 
1028     ulog.filter.level = level;
1029 }
1030 
1031 /**
1032  * get log global filter level
1033  *
1034  * @return log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
1035  *              LOG_FILTER_LVL_SILENT: disable all log output, except assert level
1036  *              LOG_FILTER_LVL_ALL: enable all log output
1037  */
1038 rt_uint32_t ulog_global_filter_lvl_get(void)
1039 {
1040     return ulog.filter.level;
1041 }
1042 
1043 /**
1044  * set log global filter tag
1045  *
1046  * @param tag tag
1047  */
1048 void ulog_global_filter_tag_set(const char *tag)
1049 {
1050     RT_ASSERT(tag);
1051 
1052     rt_strncpy(ulog.filter.tag, tag, ULOG_FILTER_TAG_MAX_LEN);
1053 }
1054 
1055 /**
1056  * get log global filter tag
1057  *
1058  * @return tag
1059  */
1060 const char *ulog_global_filter_tag_get(void)
1061 {
1062     return ulog.filter.tag;
1063 }
1064 
1065 /**
1066  * set log global filter keyword
1067  *
1068  * @param keyword keyword
1069  */
1070 void ulog_global_filter_kw_set(const char *keyword)
1071 {
1072     RT_ASSERT(keyword);
1073 
1074     rt_strncpy(ulog.filter.keyword, keyword, ULOG_FILTER_KW_MAX_LEN);
1075 }
1076 
1077 /**
1078  * get log global filter keyword
1079  *
1080  * @return keyword
1081  */
1082 const char *ulog_global_filter_kw_get(void)
1083 {
1084     return ulog.filter.keyword;
1085 }
1086 
1087 #ifdef RT_USING_FINSH
1088 #include <finsh.h>
1089 
1090 static void _print_lvl_info(void)
1091 {
1092 #ifndef ULOG_USING_SYSLOG
1093     rt_kprintf("Assert  : 0\n");
1094     rt_kprintf("Error   : 3\n");
1095     rt_kprintf("Warning : 4\n");
1096     rt_kprintf("Info    : 6\n");
1097     rt_kprintf("Debug   : 7\n");
1098 #else
1099     rt_kprintf("EMERG   :   1 (1 << 0)\n");
1100     rt_kprintf("ALERT   :   2 (1 << 1)\n");
1101     rt_kprintf("CRIT    :   4 (1 << 2)\n");
1102     rt_kprintf("ERR     :   8 (1 << 3)\n");
1103     rt_kprintf("WARNING :  16 (1 << 4)\n");
1104     rt_kprintf("NOTICE  :  32 (1 << 5)\n");
1105     rt_kprintf("INFO    :  64 (1 << 6)\n");
1106     rt_kprintf("DEBUG   : 128 (1 << 7)\n");
1107 #endif /* ULOG_USING_SYSLOG */
1108 }
1109 
1110 static void ulog_be_lvl(uint8_t argc, char **argv)
1111 {
1112     if (argc > 2)
1113     {
1114         if ((atoi(argv[2]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[2]) >= 0))
1115         {
1116             ulog_be_lvl_filter_set(argv[1], atoi(argv[2]));
1117         }
1118         else
1119         {
1120             rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
1121         }
1122     }
1123     else
1124     {
1125         rt_kprintf("Please input: ulog_be_lvl <be_name> <level>.\n");
1126         _print_lvl_info();
1127     }
1128 }
1129 MSH_CMD_EXPORT(ulog_be_lvl, Set ulog filter level by different backend.);
1130 
1131 static void ulog_tag_lvl(uint8_t argc, char **argv)
1132 {
1133     if (argc > 2)
1134     {
1135         if ((atoi(argv[2]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[2]) >= 0))
1136         {
1137             ulog_tag_lvl_filter_set(argv[1], atoi(argv[2]));
1138         }
1139         else
1140         {
1141             rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
1142         }
1143     }
1144     else
1145     {
1146         rt_kprintf("Please input: ulog_tag_lvl <tag> <level>.\n");
1147         _print_lvl_info();
1148     }
1149 }
1150 MSH_CMD_EXPORT(ulog_tag_lvl, Set ulog filter level by different tag.);
1151 
1152 static void ulog_lvl(uint8_t argc, char **argv)
1153 {
1154     if (argc > 1)
1155     {
1156         if ((atoi(argv[1]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[1]) >= 0))
1157         {
1158             ulog_global_filter_lvl_set(atoi(argv[1]));
1159         }
1160         else
1161         {
1162             rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
1163         }
1164     }
1165     else
1166     {
1167         rt_kprintf("Please input: ulog_lvl <level>.\n");
1168         _print_lvl_info();
1169     }
1170 }
1171 MSH_CMD_EXPORT(ulog_lvl, Set ulog global filter level.);
1172 
1173 static void ulog_tag(uint8_t argc, char **argv)
1174 {
1175     if (argc > 1)
1176     {
1177         if (rt_strlen(argv[1]) <= ULOG_FILTER_TAG_MAX_LEN)
1178         {
1179             ulog_global_filter_tag_set(argv[1]);
1180         }
1181         else
1182         {
1183             rt_kprintf("The tag length is too long. Max is %d.\n", ULOG_FILTER_TAG_MAX_LEN);
1184         }
1185     }
1186     else
1187     {
1188         ulog_global_filter_tag_set("");
1189     }
1190 }
1191 MSH_CMD_EXPORT(ulog_tag, Set ulog global filter tag);
1192 
1193 static void ulog_kw(uint8_t argc, char **argv)
1194 {
1195     if (argc > 1)
1196     {
1197         if (rt_strlen(argv[1]) <= ULOG_FILTER_KW_MAX_LEN)
1198         {
1199             ulog_global_filter_kw_set(argv[1]);
1200         }
1201         else
1202         {
1203             rt_kprintf("The keyword length is too long. Max is %d.\n", ULOG_FILTER_KW_MAX_LEN);
1204         }
1205     }
1206     else
1207     {
1208         ulog_global_filter_kw_set("");
1209     }
1210 }
1211 MSH_CMD_EXPORT(ulog_kw, Set ulog global filter keyword);
1212 
1213 static void ulog_filter(uint8_t argc, char **argv)
1214 {
1215 #ifndef ULOG_USING_SYSLOG
1216     const char *lvl_name[] = { "Assert ", "Error  ", "Error  ", "Error  ", "Warning", "Info   ", "Info   ", "Debug  " };
1217 #endif
1218     const char *tag = ulog_global_filter_tag_get(), *kw = ulog_global_filter_kw_get();
1219     rt_slist_t *node;
1220     ulog_tag_lvl_filter_t tag_lvl = RT_NULL;
1221 
1222     rt_kprintf("--------------------------------------\n");
1223     rt_kprintf("ulog global filter:\n");
1224 
1225 #ifndef ULOG_USING_SYSLOG
1226     rt_kprintf("level   : %s\n", lvl_name[ulog_global_filter_lvl_get()]);
1227 #else
1228     rt_kprintf("level   : %d\n", ulog_global_filter_lvl_get());
1229 #endif
1230 
1231     rt_kprintf("tag     : %s\n", rt_strlen(tag) == 0 ? "NULL" : tag);
1232     rt_kprintf("keyword : %s\n", rt_strlen(kw) == 0 ? "NULL" : kw);
1233 
1234     rt_kprintf("--------------------------------------\n");
1235     rt_kprintf("ulog tag's level filter:\n");
1236     if (rt_slist_isempty(ulog_tag_lvl_list_get()))
1237     {
1238         rt_kprintf("settings not found\n");
1239     }
1240     else
1241     {
1242         /* lock output */
1243         output_lock();
1244         /* show the tag level list */
1245         for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
1246         {
1247             tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
1248             rt_kprintf("%-*.*s: ", ULOG_FILTER_TAG_MAX_LEN, ULOG_FILTER_TAG_MAX_LEN, tag_lvl->tag);
1249 
1250 #ifndef ULOG_USING_SYSLOG
1251             rt_kprintf("%s\n", lvl_name[tag_lvl->level]);
1252 #else
1253             rt_kprintf("%d\n", tag_lvl->level);
1254 #endif
1255 
1256         }
1257         /* unlock output */
1258         output_unlock();
1259     }
1260 }
1261 MSH_CMD_EXPORT(ulog_filter, Show ulog filter settings);
1262 #endif /* RT_USING_FINSH */
1263 #endif /* ULOG_USING_FILTER */
1264 
1265 /**
1266  * @brief register the backend device into the ulog.
1267  *
1268  * @param backend Backend device handle, a pointer to a "struct ulog_backend" obj.
1269  * @param name Backend device name.
1270  * @param support_color Whether it supports color logs.
1271  * @return rt_err_t - return 0 on success.
1272  *
1273  * @note - This function is used to register the backend device into the ulog,
1274  *       ensuring that the function members in the backend device structure are set before registration.
1275  *       - about struct ulog_backend:
1276  *        1. The name and support_color properties can be passed in through the ulog_backend_register() function.
1277  *        2. output is the back-end specific output function, and all backends must implement the interface.
1278  *        3. init/deinit is optional, init is called at register, and deinit is called at unregister or ulog_deinit.
1279  *        4. flush is also optional, and some internal output cached backends need to implement this interface.
1280  *           For example, some file systems with RAM cache. The flush of the backend is usually called by
1281  *           ulog_flush in the case of an exception such as assertion or hardfault.
1282  */
1283 rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color)
1284 {
1285     rt_base_t level;
1286 
1287     RT_ASSERT(backend);
1288     RT_ASSERT(name);
1289     RT_ASSERT(ulog.init_ok);
1290     RT_ASSERT(backend->output);
1291 
1292     if (backend->init)
1293     {
1294         backend->init(backend);
1295     }
1296 
1297     backend->support_color = support_color;
1298     backend->out_level = LOG_FILTER_LVL_ALL;
1299     rt_strncpy(backend->name, name, RT_NAME_MAX - 1);
1300 
1301     level = rt_spin_lock_irqsave(&_spinlock);
1302     rt_slist_append(&ulog.backend_list, &backend->list);
1303     rt_spin_unlock_irqrestore(&_spinlock, level);
1304 
1305     return RT_EOK;
1306 }
1307 
1308 /**
1309  * @brief unregister a backend device that has already been registered.
1310  *
1311  * @param backend Backend device handle
1312  * @return rt_err_t - return 0 on success.
1313  * @note deinit function will be called at unregister.
1314  */
1315 rt_err_t ulog_backend_unregister(ulog_backend_t backend)
1316 {
1317     rt_base_t level;
1318 
1319     RT_ASSERT(backend);
1320     RT_ASSERT(ulog.init_ok);
1321 
1322     if (backend->deinit)
1323     {
1324         backend->deinit(backend);
1325     }
1326 
1327     level = rt_spin_lock_irqsave(&_spinlock);
1328     rt_slist_remove(&ulog.backend_list, &backend->list);
1329     rt_spin_unlock_irqrestore(&_spinlock, level);
1330 
1331     return RT_EOK;
1332 }
1333 
1334 rt_err_t ulog_backend_set_filter(ulog_backend_t backend, ulog_backend_filter_t filter)
1335 {
1336     rt_base_t level;
1337     RT_ASSERT(backend);
1338 
1339     level = rt_spin_lock_irqsave(&_spinlock);
1340     backend->filter = filter;
1341     rt_spin_unlock_irqrestore(&_spinlock, level);
1342 
1343     return RT_EOK;
1344 }
1345 
1346 ulog_backend_t ulog_backend_find(const char *name)
1347 {
1348     rt_base_t level;
1349     rt_slist_t *node;
1350     ulog_backend_t backend;
1351 
1352     RT_ASSERT(ulog.init_ok);
1353 
1354     level = rt_spin_lock_irqsave(&_spinlock);
1355     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
1356     {
1357         backend = rt_slist_entry(node, struct ulog_backend, list);
1358         if (rt_strncmp(backend->name, name, RT_NAME_MAX) == 0)
1359         {
1360             rt_spin_unlock_irqrestore(&_spinlock, level);
1361             return backend;
1362         }
1363     }
1364 
1365     rt_spin_unlock_irqrestore(&_spinlock, level);
1366     return RT_NULL;
1367 }
1368 
1369 #ifdef ULOG_USING_ASYNC_OUTPUT
1370 /**
1371  * asynchronous output logs to all backends
1372  *
1373  * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
1374  */
1375 void ulog_async_output(void)
1376 {
1377     rt_rbb_blk_t log_blk;
1378     ulog_frame_t log_frame;
1379 
1380     if (!ulog.async_enabled)
1381     {
1382         return;
1383     }
1384 
1385     while ((log_blk = rt_rbb_blk_get(ulog.async_rbb)) != RT_NULL)
1386     {
1387         log_frame = (ulog_frame_t) log_blk->buf;
1388         if (log_frame->magic == ULOG_FRAME_MAGIC)
1389         {
1390             /* output to all backends */
1391             ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log,
1392                     log_frame->log_len);
1393         }
1394         rt_rbb_blk_free(ulog.async_rbb, log_blk);
1395     }
1396     /* output the log_raw format log */
1397     if (ulog.async_rb)
1398     {
1399         rt_size_t log_len = rt_ringbuffer_data_len(ulog.async_rb);
1400         char *log = rt_malloc(log_len + 1);
1401         if (log)
1402         {
1403             rt_size_t len = rt_ringbuffer_get(ulog.async_rb, (rt_uint8_t *)log, (rt_uint16_t)log_len);
1404             log[log_len] = '\0';
1405             ulog_output_to_all_backend(LOG_LVL_DBG, "", RT_TRUE, log, len);
1406             rt_free(log);
1407         }
1408     }
1409 }
1410 
1411 /**
1412  * enable or disable asynchronous output mode
1413  * the log will be output directly when mode is disabled
1414  *
1415  * @param enabled RT_TRUE: enabled, RT_FALSE: disabled
1416  */
1417 void ulog_async_output_enabled(rt_bool_t enabled)
1418 {
1419     ulog.async_enabled = enabled;
1420 }
1421 
1422 /**
1423  * waiting for get asynchronous output log
1424  *
1425  * @param time the waiting time
1426  *
1427  * @return the operation status, RT_EOK on successful
1428  */
1429 rt_err_t ulog_async_waiting_log(rt_int32_t time)
1430 {
1431     rt_sem_control(&ulog.async_notice, RT_IPC_CMD_RESET, RT_NULL);
1432     return rt_sem_take(&ulog.async_notice, time);
1433 }
1434 
1435 static void async_output_thread_entry(void *param)
1436 {
1437     ulog_async_output();
1438 
1439     while (1)
1440     {
1441         ulog_async_waiting_log(RT_WAITING_FOREVER);
1442         while (1)
1443         {
1444             ulog_async_output();
1445             /* If there is no log output for a certain period of time,
1446              * refresh the log buffer
1447              */
1448             if (ulog_async_waiting_log(RT_TICK_PER_SECOND * 2) == RT_EOK)
1449             {
1450                 continue;
1451             }
1452             else
1453             {
1454                 ulog_flush();
1455                 break;
1456             }
1457         }
1458     }
1459 }
1460 #endif /* ULOG_USING_ASYNC_OUTPUT */
1461 
1462 /**
1463  * flush all backends's log
1464  */
1465 void ulog_flush(void)
1466 {
1467     rt_slist_t *node;
1468     ulog_backend_t backend;
1469 
1470     if (!ulog.init_ok)
1471         return;
1472 
1473 #ifdef ULOG_USING_ASYNC_OUTPUT
1474     ulog_async_output();
1475 #endif
1476 
1477     /* flush all backends */
1478     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
1479     {
1480         backend = rt_slist_entry(node, struct ulog_backend, list);
1481         if (backend->flush)
1482         {
1483             backend->flush(backend);
1484         }
1485     }
1486 }
1487 
1488 /**
1489  * @brief ulog initialization
1490  *
1491  * @return int return 0 on success, return -5 when failed of insufficient memory.
1492  *
1493  * @note This function must be called to complete ulog initialization before using ulog.
1494  *       This function will also be called automatically if component auto-initialization is turned on.
1495  */
1496 int ulog_init(void)
1497 {
1498     if (ulog.init_ok)
1499         return 0;
1500 
1501     rt_mutex_init(&ulog.output_locker, "ulog", RT_IPC_FLAG_PRIO);
1502     ulog.output_lock_enabled = RT_TRUE;
1503     rt_slist_init(&ulog.backend_list);
1504 
1505 #ifdef ULOG_USING_FILTER
1506     rt_slist_init(ulog_tag_lvl_list_get());
1507 #endif
1508 
1509 #ifdef ULOG_USING_ASYNC_OUTPUT
1510     RT_ASSERT(ULOG_ASYNC_OUTPUT_STORE_LINES >= 2);
1511     ulog.async_enabled = RT_TRUE;
1512     /* async output ring block buffer */
1513     ulog.async_rbb = rt_rbb_create(RT_ALIGN(ULOG_ASYNC_OUTPUT_BUF_SIZE, RT_ALIGN_SIZE), ULOG_ASYNC_OUTPUT_STORE_LINES);
1514     if (ulog.async_rbb == RT_NULL)
1515     {
1516         rt_kprintf("Error: ulog init failed! No memory for async rbb.\n");
1517         rt_mutex_detach(&ulog.output_locker);
1518         return -RT_ENOMEM;
1519     }
1520     rt_sem_init(&ulog.async_notice, "ulog", 0, RT_IPC_FLAG_FIFO);
1521 #endif /* ULOG_USING_ASYNC_OUTPUT */
1522 
1523 #ifdef ULOG_USING_FILTER
1524     ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
1525 #endif
1526 
1527     ulog.init_ok = RT_TRUE;
1528 
1529     return 0;
1530 }
1531 INIT_BOARD_EXPORT(ulog_init);
1532 
1533 #ifdef ULOG_USING_ASYNC_OUTPUT
1534 int ulog_async_init(void)
1535 {
1536     if (ulog.async_th == RT_NULL)
1537     {
1538         /* async output thread */
1539         ulog.async_th = rt_thread_create("ulog_async", async_output_thread_entry, &ulog, ULOG_ASYNC_OUTPUT_THREAD_STACK,
1540                 ULOG_ASYNC_OUTPUT_THREAD_PRIORITY, 20);
1541         if (ulog.async_th == RT_NULL)
1542         {
1543             rt_kprintf("Error: ulog init failed! No memory for async output thread.\n");
1544             return -RT_ENOMEM;
1545         }
1546         /* async output thread startup */
1547         rt_thread_startup(ulog.async_th);
1548     }
1549     return 0;
1550 }
1551 INIT_PREV_EXPORT(ulog_async_init);
1552 #endif /* ULOG_USING_ASYNC_OUTPUT */
1553 
1554 /**
1555  * @brief ulog deinitialization
1556  *
1557  * @note This deinit release resource can be executed when ulog is no longer used.
1558  */
1559 void ulog_deinit(void)
1560 {
1561     rt_slist_t *node;
1562     ulog_backend_t backend;
1563 
1564     if (!ulog.init_ok)
1565         return;
1566 
1567     /* deinit all backends */
1568     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
1569     {
1570         backend = rt_slist_entry(node, struct ulog_backend, list);
1571         if (backend->deinit)
1572         {
1573             backend->deinit(backend);
1574         }
1575     }
1576 
1577 #ifdef ULOG_USING_FILTER
1578     /* deinit tag's level filter */
1579     {
1580         ulog_tag_lvl_filter_t tag_lvl;
1581         for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
1582         {
1583             tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
1584             rt_free(tag_lvl);
1585         }
1586     }
1587 #endif /* ULOG_USING_FILTER */
1588 
1589     rt_mutex_detach(&ulog.output_locker);
1590 
1591 #ifdef ULOG_USING_ASYNC_OUTPUT
1592     rt_rbb_destroy(ulog.async_rbb);
1593     rt_thread_delete(ulog.async_th);
1594     if (ulog.async_rb)
1595         rt_ringbuffer_destroy(ulog.async_rb);
1596 #endif
1597 
1598     ulog.init_ok = RT_FALSE;
1599 }
1600 
1601 #endif /* RT_USING_ULOG */
1602