xref: /nrf52832-nimble/rt-thread/components/utilities/ulog/ulog.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, 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_OUTPUT_FLOAT
20 #include <stdio.h>
21 #endif
22 
23 #ifdef ULOG_TIME_USING_TIMESTAMP
24 #include <sys/time.h>
25 #endif
26 
27 #ifdef ULOG_USING_ASYNC_OUTPUT
28 #include <rtdevice.h>
29 #endif
30 
31 #ifdef RT_USING_ULOG
32 
33 /* the number which is max stored line logs */
34 #ifndef ULOG_ASYNC_OUTPUT_STORE_LINES
35 #define ULOG_ASYNC_OUTPUT_STORE_LINES  (ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / ULOG_LINE_BUF_SIZE)
36 #endif
37 
38 #ifdef ULOG_USING_COLOR
39 /**
40  * CSI(Control Sequence Introducer/Initiator) sign
41  * more information on https://en.wikipedia.org/wiki/ANSI_escape_code
42  */
43 #define CSI_START                      "\033["
44 #define CSI_END                        "\033[0m"
45 /* output log front color */
46 #define F_BLACK                        "30m"
47 #define F_RED                          "31m"
48 #define F_GREEN                        "32m"
49 #define F_YELLOW                       "33m"
50 #define F_BLUE                         "34m"
51 #define F_MAGENTA                      "35m"
52 #define F_CYAN                         "36m"
53 #define F_WHITE                        "37m"
54 
55 /* output log default color definition */
56 #ifndef ULOG_COLOR_DEBUG
57 #define ULOG_COLOR_DEBUG               NULL
58 #endif
59 #ifndef ULOG_COLOR_INFO
60 #define ULOG_COLOR_INFO                (F_GREEN)
61 #endif
62 #ifndef ULOG_COLOR_WARN
63 #define ULOG_COLOR_WARN                (F_YELLOW)
64 #endif
65 #ifndef ULOG_COLOR_ERROR
66 #define ULOG_COLOR_ERROR               (F_RED)
67 #endif
68 #ifndef ULOG_COLOR_ASSERT
69 #define ULOG_COLOR_ASSERT              (F_MAGENTA)
70 #endif
71 #endif /* ULOG_USING_COLOR */
72 
73 #if ULOG_LINE_BUF_SIZE < 80
74 #error "the log line buffer size must more than 80"
75 #endif
76 
77 struct rt_ulog
78 {
79     rt_bool_t init_ok;
80     struct rt_mutex output_locker;
81     /* all backends */
82     rt_slist_t backend_list;
83     /* the thread log's line buffer */
84     char log_buf_th[ULOG_LINE_BUF_SIZE];
85 
86 #ifdef ULOG_USING_ISR_LOG
87     /* the ISR log's line buffer */
88     rt_base_t output_locker_isr_lvl;
89     char log_buf_isr[ULOG_LINE_BUF_SIZE];
90 #endif /* ULOG_USING_ISR_LOG */
91 
92 #ifdef ULOG_USING_ASYNC_OUTPUT
93     rt_rbb_t async_rbb;
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 /* level output info */
112 static const char * const level_output_info[] =
113 {
114         "A/",
115         NULL,
116         NULL,
117         "E/",
118         "W/",
119         NULL,
120         "I/",
121         "D/",
122 };
123 
124 #ifdef ULOG_USING_COLOR
125 /* color output info */
126 static const char * const color_output_info[] =
127 {
128         ULOG_COLOR_ASSERT,
129         NULL,
130         NULL,
131         ULOG_COLOR_ERROR,
132         ULOG_COLOR_WARN,
133         NULL,
134         ULOG_COLOR_INFO,
135         ULOG_COLOR_DEBUG,
136 };
137 #endif /* ULOG_USING_COLOR */
138 
139 /* ulog local object */
140 static struct rt_ulog ulog = { 0 };
141 
ulog_strcpy(size_t cur_len,char * dst,const char * src)142 size_t ulog_strcpy(size_t cur_len, char *dst, const char *src)
143 {
144     const char *src_old = src;
145 
146     RT_ASSERT(dst);
147     RT_ASSERT(src);
148 
149     while (*src != 0)
150     {
151         /* make sure destination has enough space */
152         if (cur_len++ < ULOG_LINE_BUF_SIZE)
153         {
154             *dst++ = *src++;
155         }
156         else
157         {
158             break;
159         }
160     }
161     return src - src_old;
162 }
163 
ulog_ultoa(char * s,unsigned long int n)164 size_t ulog_ultoa(char *s, unsigned long int n)
165 {
166     size_t i = 0, j = 0, len = 0;
167     char swap;
168 
169     do
170     {
171         s[len++] = n % 10 + '0';
172     } while (n /= 10);
173     s[len] = '\0';
174     /* reverse string */
175     for (i = 0, j = len - 1; i < j; ++i, --j)
176     {
177         swap = s[i];
178         s[i] = s[j];
179         s[j] = swap;
180     }
181     return len;
182 }
183 
output_unlock(void)184 static void output_unlock(void)
185 {
186     /* is in thread context */
187     if (rt_interrupt_get_nest() == 0)
188     {
189         rt_mutex_release(&ulog.output_locker);
190     }
191     else
192     {
193 #ifdef ULOG_USING_ISR_LOG
194         rt_hw_interrupt_enable(ulog.output_locker_isr_lvl);
195 #endif
196     }
197 }
198 
output_lock(void)199 static void output_lock(void)
200 {
201     /* is in thread context */
202     if (rt_interrupt_get_nest() == 0)
203     {
204         rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
205     }
206     else
207     {
208 #ifdef ULOG_USING_ISR_LOG
209         ulog.output_locker_isr_lvl = rt_hw_interrupt_disable();
210 #endif
211     }
212 }
213 
get_log_buf(void)214 static char *get_log_buf(void)
215 {
216     /* is in thread context */
217     if (rt_interrupt_get_nest() == 0)
218     {
219         return ulog.log_buf_th;
220     }
221     else
222     {
223 #ifdef ULOG_USING_ISR_LOG
224         return ulog.log_buf_isr;
225 #else
226         rt_kprintf("Error: Current mode not supported run in ISR. Please enable ULOG_USING_ISR_LOG.\n");
227         return NULL;
228 #endif
229     }
230 }
231 
ulog_formater(char * log_buf,rt_uint32_t level,const char * tag,rt_bool_t newline,const char * format,va_list args)232 RT_WEAK rt_size_t ulog_formater(char *log_buf, rt_uint32_t level, const char *tag, rt_bool_t newline,
233         const char *format, va_list args)
234 {
235     /* the caller has locker, so it can use static variable for reduce stack usage */
236     static rt_size_t log_len, newline_len;
237     static int fmt_result;
238 
239     RT_ASSERT(log_buf);
240     RT_ASSERT(level <= LOG_LVL_DBG);
241     RT_ASSERT(tag);
242     RT_ASSERT(format);
243 
244     log_len = 0;
245     newline_len = rt_strlen(ULOG_NEWLINE_SIGN);
246 
247 #ifdef ULOG_USING_COLOR
248     /* add CSI start sign and color info */
249     if (color_output_info[level])
250     {
251         log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_START);
252         log_len += ulog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
253     }
254 #endif /* ULOG_USING_COLOR */
255 
256 #ifdef ULOG_OUTPUT_TIME
257     /* add time info */
258     {
259 #ifdef ULOG_TIME_USING_TIMESTAMP
260         static time_t now;
261         static struct tm *tm, tm_tmp;
262 
263         now = time(NULL);
264         tm = gmtime_r(&now, &tm_tmp);
265 
266 #ifdef RT_USING_SOFT_RTC
267         rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d.%03d", tm->tm_mon + 1,
268                 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, rt_tick_get() % 1000);
269 #else
270         rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm->tm_mon + 1,
271                 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
272 #endif /* RT_USING_SOFT_RTC */
273 
274 #else
275         static rt_size_t tick_len = 0;
276 
277         log_buf[log_len] = '[';
278         tick_len = ulog_ultoa(log_buf + log_len + 1, rt_tick_get());
279         log_buf[log_len + 1 + tick_len] = ']';
280         log_buf[log_len + 1 + tick_len + 1] = '\0';
281 #endif /* ULOG_TIME_USING_TIMESTAMP */
282 
283         log_len += rt_strlen(log_buf + log_len);
284     }
285 #endif /* ULOG_OUTPUT_TIME */
286 
287 #ifdef ULOG_OUTPUT_LEVEL
288 
289 #ifdef ULOG_OUTPUT_TIME
290     log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
291 #endif
292 
293     /* add level info */
294     log_len += ulog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
295 #endif /* ULOG_OUTPUT_LEVEL */
296 
297 #ifdef ULOG_OUTPUT_TAG
298 
299 #if !defined(ULOG_OUTPUT_LEVEL) && defined(ULOG_OUTPUT_TIME)
300     log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
301 #endif
302 
303     /* add tag info */
304     log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
305 #endif /* ULOG_OUTPUT_TAG */
306 
307 #ifdef ULOG_OUTPUT_THREAD_NAME
308     /* add thread info */
309     {
310 
311 #if defined(ULOG_OUTPUT_TIME) || defined(ULOG_OUTPUT_LEVEL) || defined(ULOG_OUTPUT_TAG)
312         log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
313 #endif
314 
315         /* is not in interrupt context */
316         if (rt_interrupt_get_nest() == 0)
317         {
318             log_len += ulog_strcpy(log_len, log_buf + log_len, rt_thread_self()->name);
319         }
320         else
321         {
322             log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
323         }
324     }
325 #endif /* ULOG_OUTPUT_THREAD_NAME */
326 
327     log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
328 
329 #ifdef ULOG_OUTPUT_FLOAT
330     fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
331 #else
332     fmt_result = rt_vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
333 #endif /* ULOG_OUTPUT_FLOAT */
334 
335     /* calculate log length */
336     if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
337     {
338         log_len += fmt_result;
339     }
340     else
341     {
342         /* using max length */
343         log_len = ULOG_LINE_BUF_SIZE;
344     }
345 
346     /* overflow check and reserve some space for CSI end sign and newline sign */
347 #ifdef ULOG_USING_COLOR
348     if (log_len + (sizeof(CSI_END) - 1) + newline_len > ULOG_LINE_BUF_SIZE)
349     {
350         /* using max length */
351         log_len = ULOG_LINE_BUF_SIZE;
352         /* reserve some space for CSI end sign */
353         log_len -= (sizeof(CSI_END) - 1);
354 #else
355     if (log_len + newline_len > ULOG_LINE_BUF_SIZE)
356     {
357         /* using max length */
358         log_len = ULOG_LINE_BUF_SIZE;
359 #endif /* ULOG_USING_COLOR */
360         /* reserve some space for newline sign */
361         log_len -= newline_len;
362     }
363 
364     /* package newline sign */
365     if (newline)
366     {
367         log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
368     }
369 
370 #ifdef ULOG_USING_COLOR
371     /* add CSI end sign  */
372     if (color_output_info[level])
373     {
374         log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_END);
375     }
376 #endif /* ULOG_USING_COLOR */
377 
378     return log_len;
379 }
380 
381 void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t size)
382 {
383     rt_slist_t *node;
384     ulog_backend_t backend;
385 
386     if (!ulog.init_ok)
387         return;
388 
389     /* output for all backends */
390     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
391     {
392         backend = rt_slist_entry(node, struct ulog_backend, list);
393 #if !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG)
394         backend->output(backend, level, tag, is_raw, log, size);
395 #else
396         if (backend->support_color)
397         {
398             backend->output(backend, level, tag, is_raw, log, size);
399         }
400         else
401         {
402             /* recalculate the log start address and log size when backend not supported color */
403             rt_size_t color_info_len = rt_strlen(color_output_info[level]);
404             if (color_info_len)
405             {
406                 rt_size_t color_hdr_len = rt_strlen(CSI_START) + color_info_len;
407 
408                 log += color_hdr_len;
409                 size -= (color_hdr_len + (sizeof(CSI_END) - 1));
410             }
411             backend->output(backend, level, tag, is_raw, log, size);
412         }
413 #endif /* !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) */
414     }
415 }
416 
417 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)
418 {
419 #ifdef ULOG_USING_ASYNC_OUTPUT
420     rt_rbb_blk_t log_blk;
421     ulog_frame_t log_frame;
422 
423     /* allocate log frame */
424     log_blk = rt_rbb_blk_alloc(ulog.async_rbb, RT_ALIGN(sizeof(struct ulog_frame) + log_len, RT_ALIGN_SIZE));
425     if (log_blk)
426     {
427         /* package the log frame */
428         log_frame = (ulog_frame_t) log_blk->buf;
429         log_frame->magic = ULOG_FRAME_MAGIC;
430         log_frame->is_raw = is_raw;
431         log_frame->level = level;
432         log_frame->log_len = log_len;
433         log_frame->tag = tag;
434         log_frame->log = (const char *)log_blk->buf + sizeof(struct ulog_frame);
435         /* copy log data */
436         rt_memcpy(log_blk->buf + sizeof(struct ulog_frame), log_buf, log_len);
437         /* put the block */
438         rt_rbb_blk_put(log_blk);
439         /* send a notice */
440         rt_sem_release(&ulog.async_notice);
441     }
442     else
443     {
444         static rt_bool_t already_output = RT_FALSE;
445         if (already_output == RT_FALSE)
446         {
447             rt_kprintf("Warning: There is no enough buffer for saving async log,"
448                     " please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option.\n");
449             already_output = RT_TRUE;
450         }
451     }
452 #else
453     /* is in thread context */
454     if (rt_interrupt_get_nest() == 0)
455     {
456         /* output to all backends */
457         ulog_output_to_all_backend(level, tag, is_raw, log_buf, log_len);
458     }
459     else
460     {
461 #ifdef ULOG_BACKEND_USING_CONSOLE
462         /* We can't ensure that all backends support ISR context output.
463          * So only using rt_kprintf when context is ISR */
464         extern void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag,
465                 rt_bool_t is_raw, const char *log, size_t len);
466         ulog_console_backend_output(NULL, level, tag, is_raw, log_buf, log_len);
467 #endif /* ULOG_BACKEND_USING_CONSOLE */
468     }
469 #endif /* ULOG_USING_ASYNC_OUTPUT */
470 }
471 
472 /**
473  * output the log by variable argument list
474  *
475  * @param level level
476  * @param tag tag
477  * @param newline has_newline
478  * @param format output format
479  * @param args variable argument list
480  */
481 void ulog_voutput(rt_uint32_t level, const char *tag, rt_bool_t newline, const char *format, va_list args)
482 {
483     char *log_buf = NULL;
484     rt_size_t log_len = 0;
485 
486 #ifndef ULOG_USING_SYSLOG
487     RT_ASSERT(level <= LOG_LVL_DBG);
488 #else
489     RT_ASSERT(LOG_PRI(level) <= LOG_DEBUG);
490 #endif /* ULOG_USING_SYSLOG */
491 
492     RT_ASSERT(tag);
493     RT_ASSERT(format);
494 
495     if (!ulog.init_ok)
496     {
497         return;
498     }
499 
500 #ifdef ULOG_USING_FILTER
501     /* level filter */
502 #ifndef ULOG_USING_SYSLOG
503     if (level > ulog.filter.level || level > ulog_tag_lvl_filter_get(tag))
504     {
505         return;
506     }
507 #else
508     if (((LOG_MASK(LOG_PRI(level)) & ulog.filter.level) == 0)
509             || ((LOG_MASK(LOG_PRI(level)) & ulog_tag_lvl_filter_get(tag)) == 0))
510     {
511         return;
512     }
513 #endif /* ULOG_USING_SYSLOG */
514     else if (!rt_strstr(tag, ulog.filter.tag))
515     {
516         /* tag filter */
517         return;
518     }
519 #endif /* ULOG_USING_FILTER */
520 
521     /* get log buffer */
522     log_buf = get_log_buf();
523 
524     /* lock output */
525     output_lock();
526 
527 #ifndef ULOG_USING_SYSLOG
528     log_len = ulog_formater(log_buf, level, tag, newline, format, args);
529 #else
530     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);
531     log_len = syslog_formater(log_buf, level, tag, newline, format, args);
532 #endif /* ULOG_USING_SYSLOG */
533 
534 #ifdef ULOG_USING_FILTER
535     /* keyword filter */
536     if (ulog.filter.keyword[0] != '\0')
537     {
538         /* add string end sign */
539         log_buf[log_len] = '\0';
540         /* find the keyword */
541         if (!rt_strstr(log_buf, ulog.filter.keyword))
542         {
543             /* unlock output */
544             output_unlock();
545             return;
546         }
547     }
548 #endif /* ULOG_USING_FILTER */
549     /* do log output */
550     do_output(level, tag, RT_FALSE, log_buf, log_len);
551 
552     /* unlock output */
553     output_unlock();
554 }
555 
556 /**
557  * output the log
558  *
559  * @param level level
560  * @param tag tag
561  * @param newline has newline
562  * @param format output format
563  * @param ... args
564  */
565 void ulog_output(rt_uint32_t level, const char *tag, rt_bool_t newline, const char *format, ...)
566 {
567     va_list args;
568 
569     /* args point to the first variable parameter */
570     va_start(args, format);
571 
572     ulog_voutput(level, tag, newline, format, args);
573 
574     va_end(args);
575 }
576 
577 /**
578  * output RAW string format log
579  *
580  * @param format output format
581  * @param ... args
582  */
583 void ulog_raw(const char *format, ...)
584 {
585     rt_size_t log_len = 0;
586     char *log_buf = NULL;
587     va_list args;
588     int fmt_result;
589 
590     RT_ASSERT(ulog.init_ok);
591 
592     /* get log buffer */
593     log_buf = get_log_buf();
594 
595     /* lock output */
596     output_lock();
597     /* args point to the first variable parameter */
598     va_start(args, format);
599 
600 #ifdef ULOG_OUTPUT_FLOAT
601     fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
602 #else
603     fmt_result = rt_vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
604 #endif /* ULOG_OUTPUT_FLOAT */
605 
606     va_end(args);
607 
608     /* calculate log length */
609     if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
610     {
611         log_len = fmt_result;
612     }
613     else
614     {
615         log_len = ULOG_LINE_BUF_SIZE;
616     }
617 
618     /* do log output */
619     do_output(LOG_LVL_DBG, NULL, RT_TRUE, log_buf, log_len);
620 
621     /* unlock output */
622     output_unlock();
623 }
624 
625 /**
626  * dump the hex format data to log
627  *
628  * @param tag name for hex object, it will show on log header
629  * @param width hex number for every line, such as: 16, 32
630  * @param buf hex buffer
631  * @param size buffer size
632  */
633 void ulog_hexdump(const char *tag, rt_size_t width, rt_uint8_t *buf, rt_size_t size)
634 {
635 #define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
636 
637     rt_size_t i, j;
638     rt_size_t log_len = 0, name_len = rt_strlen(tag);
639     char *log_buf = NULL, dump_string[8];
640     int fmt_result;
641 
642     RT_ASSERT(ulog.init_ok);
643 
644 #ifdef ULOG_USING_FILTER
645     /* level filter */
646 #ifndef ULOG_USING_SYSLOG
647     if (LOG_LVL_DBG > ulog.filter.level || LOG_LVL_DBG > ulog_tag_lvl_filter_get(tag))
648     {
649         return;
650     }
651 #else
652     if ((LOG_MASK(LOG_DEBUG) & ulog.filter.level) == 0)
653     {
654         return;
655     }
656 #endif /* ULOG_USING_SYSLOG */
657     else if (!rt_strstr(tag, ulog.filter.tag))
658     {
659         /* tag filter */
660         return;
661     }
662 #endif /* ULOG_USING_FILTER */
663 
664     /* get log buffer */
665     log_buf = get_log_buf();
666 
667     /* lock output */
668     output_lock();
669 
670     for (i = 0, log_len = 0; i < size; i += width)
671     {
672         /* package header */
673         if (i == 0)
674         {
675             log_len += ulog_strcpy(log_len, log_buf + log_len, "D/HEX ");
676             log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
677             log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
678         }
679         else
680         {
681             log_len = 6 + name_len + 2;
682             rt_memset(log_buf, ' ', log_len);
683         }
684         fmt_result = rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE, "%04X-%04X: ", i, i + width);
685         /* calculate log length */
686         if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
687         {
688             log_len += fmt_result;
689         }
690         else
691         {
692             log_len = ULOG_LINE_BUF_SIZE;
693         }
694         /* dump hex */
695         for (j = 0; j < width; j++)
696         {
697             if (i + j < size)
698             {
699                 rt_snprintf(dump_string, sizeof(dump_string), "%02X ", buf[i + j]);
700             }
701             else
702             {
703                 rt_strncpy(dump_string, "   ", sizeof(dump_string));
704             }
705             log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
706             if ((j + 1) % 8 == 0)
707             {
708                 log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
709             }
710         }
711         log_len += ulog_strcpy(log_len, log_buf + log_len, "  ");
712         /* dump char for hex */
713         for (j = 0; j < width; j++)
714         {
715             if (i + j < size)
716             {
717                 rt_snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
718                 log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
719             }
720         }
721         /* overflow check and reserve some space for newline sign */
722         if (log_len + rt_strlen(ULOG_NEWLINE_SIGN) > ULOG_LINE_BUF_SIZE)
723         {
724             log_len = ULOG_LINE_BUF_SIZE - rt_strlen(ULOG_NEWLINE_SIGN);
725         }
726         /* package newline sign */
727         log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
728         /* do log output */
729         do_output(LOG_LVL_DBG, NULL, RT_TRUE, log_buf, log_len);
730     }
731     /* unlock output */
732     output_unlock();
733 }
734 
735 #ifdef ULOG_USING_FILTER
736 /**
737  * Set the filter's level by different tag.
738  * The log on this tag which level is less than it will stop output.
739  *
740  * example:
741  *     // the example tag log enter silent mode
742  *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_SILENT);
743  *     // the example tag log which level is less than INFO level will stop output
744  *     ulog_set_filter_lvl("example", LOG_LVL_INFO);
745  *     // remove example tag's level filter, all level log will resume output
746  *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_ALL);
747  *
748  * @param tag log tag
749  * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode.
750  *        When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer.
751  *        Then all level log will resume output.
752  *
753  * @return  0 : success
754  *         -5 : no memory
755  *         -10: level is out of range
756  */
757 int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)
758 {
759     rt_slist_t *node;
760     ulog_tag_lvl_filter_t tag_lvl = NULL;
761     int result = RT_EOK;
762 
763     if (level > LOG_FILTER_LVL_ALL)
764         return -RT_EINVAL;
765 
766     if (!ulog.init_ok)
767         return result;
768 
769     /* lock output */
770     output_lock();
771     /* find the tag in list */
772     for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
773     {
774         tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
775         if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
776         {
777             break;
778         }
779         else
780         {
781             tag_lvl = NULL;
782         }
783     }
784     /* find OK */
785     if (tag_lvl)
786     {
787         if (level == LOG_FILTER_LVL_ALL)
788         {
789             /* remove current tag's level filter when input level is the lowest level */
790             rt_slist_remove(ulog_tag_lvl_list_get(), &tag_lvl->list);
791             rt_free(tag_lvl);
792         }
793         else
794         {
795             /* update level */
796             tag_lvl->level = level;
797         }
798     }
799     else
800     {
801         /* only add the new tag's level filer when level is not LOG_FILTER_LVL_ALL */
802         if (level != LOG_FILTER_LVL_ALL)
803         {
804             /* new a tag's level filter */
805             tag_lvl = (ulog_tag_lvl_filter_t)rt_malloc(sizeof(struct ulog_tag_lvl_filter));
806             if (tag_lvl)
807             {
808                 rt_memset(tag_lvl->tag, 0 , sizeof(tag_lvl->tag));
809                 rt_strncpy(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN);
810                 tag_lvl->level = level;
811                 rt_slist_append(ulog_tag_lvl_list_get(), &tag_lvl->list);
812             }
813             else
814             {
815                 result = -RT_ENOMEM;
816             }
817         }
818     }
819     /* unlock output */
820     output_unlock();
821 
822     return result;
823 }
824 
825 /**
826  * get the level on tag's level filer
827  *
828  * @param tag log tag
829  *
830  * @return It will return the lowest level when tag was not found.
831  *         Other level will return when tag was found.
832  */
833 rt_uint32_t ulog_tag_lvl_filter_get(const char *tag)
834 {
835     rt_slist_t *node;
836     ulog_tag_lvl_filter_t tag_lvl = NULL;
837     rt_uint32_t level = LOG_FILTER_LVL_ALL;
838 
839     if (!ulog.init_ok)
840         return level;
841 
842     /* lock output */
843     output_lock();
844     /* find the tag in list */
845     for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
846     {
847         tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
848         if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
849         {
850             level = tag_lvl->level;
851             break;
852         }
853     }
854     /* unlock output */
855     output_unlock();
856 
857     return level;
858 }
859 
860 /**
861  * get the tag's level list on filter
862  *
863  * @return tag's level list
864  */
865 rt_slist_t *ulog_tag_lvl_list_get(void)
866 {
867     return &ulog.filter.tag_lvl_list;
868 }
869 
870 /**
871  * set log global filter level
872  *
873  * @param level log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
874  *              LOG_FILTER_LVL_SILENT: disable all log output, except assert level
875  *              LOG_FILTER_LVL_ALL: enable all log output
876  */
877 void ulog_global_filter_lvl_set(rt_uint32_t level)
878 {
879     RT_ASSERT(level <= LOG_FILTER_LVL_ALL);
880 
881     ulog.filter.level = level;
882 }
883 
884 /**
885  * get log global filter level
886  *
887  * @return log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
888  *              LOG_FILTER_LVL_SILENT: disable all log output, except assert level
889  *              LOG_FILTER_LVL_ALL: enable all log output
890  */
891 rt_uint32_t ulog_global_filter_lvl_get(void)
892 {
893     return ulog.filter.level;
894 }
895 
896 /**
897  * set log global filter tag
898  *
899  * @param tag tag
900  */
901 void ulog_global_filter_tag_set(const char *tag)
902 {
903     RT_ASSERT(tag);
904 
905     rt_strncpy(ulog.filter.tag, tag, ULOG_FILTER_TAG_MAX_LEN);
906 }
907 
908 /**
909  * get log global filter tag
910  *
911  * @return tag
912  */
913 const char *ulog_global_filter_tag_get(void)
914 {
915     return ulog.filter.tag;
916 }
917 
918 /**
919  * set log global filter keyword
920  *
921  * @param keyword keyword
922  */
923 void ulog_global_filter_kw_set(const char *keyword)
924 {
925     RT_ASSERT(keyword);
926 
927     rt_strncpy(ulog.filter.keyword, keyword, ULOG_FILTER_KW_MAX_LEN);
928 }
929 
930 /**
931  * get log global filter keyword
932  *
933  * @return keyword
934  */
935 const char *ulog_global_filter_kw_get(void)
936 {
937     return ulog.filter.keyword;
938 }
939 
940 #if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
941 #include <finsh.h>
942 
943 static void ulog_tag_lvl(uint8_t argc, char **argv)
944 {
945     if (argc > 2)
946     {
947         if ((atoi(argv[2]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[2]) >= 0))
948         {
949             ulog_tag_lvl_filter_set(argv[1], atoi(argv[2]));
950         }
951         else
952         {
953             rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
954         }
955     }
956     else
957     {
958         rt_kprintf("Please input: ulog_tag_lvl <tag> <level>.\n");
959 #ifndef ULOG_USING_SYSLOG
960         rt_kprintf("Assert  : 0\n");
961         rt_kprintf("Error   : 3\n");
962         rt_kprintf("Warning : 4\n");
963         rt_kprintf("Info    : 6\n");
964         rt_kprintf("Debug   : 7\n");
965 #else
966         rt_kprintf("EMERG   :   1 (1 << 0)\n");
967         rt_kprintf("ALERT   :   2 (1 << 1)\n");
968         rt_kprintf("CRIT    :   4 (1 << 2)\n");
969         rt_kprintf("ERR     :   8 (1 << 3)\n");
970         rt_kprintf("WARNING :  16 (1 << 4)\n");
971         rt_kprintf("NOTICE  :  32 (1 << 5)\n");
972         rt_kprintf("INFO    :  64 (1 << 6)\n");
973         rt_kprintf("DEBUG   : 128 (1 << 7)\n");
974 #endif /* ULOG_USING_SYSLOG */
975     }
976 }
977 MSH_CMD_EXPORT(ulog_tag_lvl, Set ulog filter level by different tag.);
978 
979 static void ulog_lvl(uint8_t argc, char **argv)
980 {
981     if (argc > 1)
982     {
983         if ((atoi(argv[1]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[1]) >= 0))
984         {
985             ulog_global_filter_lvl_set(atoi(argv[1]));
986         }
987         else
988         {
989             rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
990         }
991     }
992     else
993     {
994         rt_kprintf("Please input: ulog_lvl <level>.\n");
995 #ifndef ULOG_USING_SYSLOG
996         rt_kprintf("Assert  : 0\n");
997         rt_kprintf("Error   : 3\n");
998         rt_kprintf("Warning : 4\n");
999         rt_kprintf("Info    : 6\n");
1000         rt_kprintf("Debug   : 7\n");
1001 #else
1002         rt_kprintf("EMERG   :   1 (1 << 0)\n");
1003         rt_kprintf("ALERT   :   2 (1 << 1)\n");
1004         rt_kprintf("CRIT    :   4 (1 << 2)\n");
1005         rt_kprintf("ERR     :   8 (1 << 3)\n");
1006         rt_kprintf("WARNING :  16 (1 << 4)\n");
1007         rt_kprintf("NOTICE  :  32 (1 << 5)\n");
1008         rt_kprintf("INFO    :  64 (1 << 6)\n");
1009         rt_kprintf("DEBUG   : 128 (1 << 7)\n");
1010 #endif /* ULOG_USING_SYSLOG */
1011     }
1012 }
1013 MSH_CMD_EXPORT(ulog_lvl, Set ulog global filter level.);
1014 
1015 static void ulog_tag(uint8_t argc, char **argv)
1016 {
1017     if (argc > 1)
1018     {
1019         if (rt_strlen(argv[1]) <= ULOG_FILTER_TAG_MAX_LEN)
1020         {
1021             ulog_global_filter_tag_set(argv[1]);
1022         }
1023         else
1024         {
1025             rt_kprintf("The tag length is too long. Max is %d.\n", ULOG_FILTER_TAG_MAX_LEN);
1026         }
1027     }
1028     else
1029     {
1030         ulog_global_filter_tag_set("");
1031     }
1032 }
1033 MSH_CMD_EXPORT(ulog_tag, Set ulog global filter tag);
1034 
1035 static void ulog_kw(uint8_t argc, char **argv)
1036 {
1037     if (argc > 1)
1038     {
1039         if (rt_strlen(argv[1]) <= ULOG_FILTER_KW_MAX_LEN)
1040         {
1041             ulog_global_filter_kw_set(argv[1]);
1042         }
1043         else
1044         {
1045             rt_kprintf("The keyword length is too long. Max is %d.\n", ULOG_FILTER_KW_MAX_LEN);
1046         }
1047     }
1048     else
1049     {
1050         ulog_global_filter_kw_set("");
1051     }
1052 }
1053 MSH_CMD_EXPORT(ulog_kw, Set ulog global filter keyword);
1054 
1055 static void ulog_filter(uint8_t argc, char **argv)
1056 {
1057 #ifndef ULOG_USING_SYSLOG
1058     const char *lvl_name[] = { "Assert ", "Error  ", "Error  ", "Error  ", "Warning", "Info   ", "Info   ", "Debug  " };
1059 #endif
1060     const char *tag = ulog_global_filter_tag_get(), *kw = ulog_global_filter_kw_get();
1061     rt_slist_t *node;
1062     ulog_tag_lvl_filter_t tag_lvl = NULL;
1063 
1064     rt_kprintf("--------------------------------------\n");
1065     rt_kprintf("ulog global filter:\n");
1066 
1067 #ifndef ULOG_USING_SYSLOG
1068     rt_kprintf("level   : %s\n", lvl_name[ulog_global_filter_lvl_get()]);
1069 #else
1070     rt_kprintf("level   : %d\n", ulog_global_filter_lvl_get());
1071 #endif
1072 
1073     rt_kprintf("tag     : %s\n", rt_strlen(tag) == 0 ? "NULL" : tag);
1074     rt_kprintf("keyword : %s\n", rt_strlen(kw) == 0 ? "NULL" : kw);
1075 
1076     rt_kprintf("--------------------------------------\n");
1077     rt_kprintf("ulog tag's level filter:\n");
1078     if (rt_slist_isempty(ulog_tag_lvl_list_get()))
1079     {
1080         rt_kprintf("settings not found\n");
1081     }
1082     else
1083     {
1084         /* lock output */
1085         output_lock();
1086         /* show the tag level list */
1087         for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
1088         {
1089             tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
1090             rt_kprintf("%-*.s: ", ULOG_FILTER_TAG_MAX_LEN, tag_lvl->tag);
1091 
1092 #ifndef ULOG_USING_SYSLOG
1093             rt_kprintf("%s\n", lvl_name[tag_lvl->level]);
1094 #else
1095             rt_kprintf("%d\n", tag_lvl->level);
1096 #endif
1097 
1098         }
1099         /* unlock output */
1100         output_unlock();
1101     }
1102 }
1103 MSH_CMD_EXPORT(ulog_filter, Show ulog filter settings);
1104 #endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
1105 #endif /* ULOG_USING_FILTER */
1106 
1107 rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color)
1108 {
1109     rt_base_t level;
1110 
1111     RT_ASSERT(backend);
1112     RT_ASSERT(name);
1113     RT_ASSERT(ulog.init_ok);
1114     RT_ASSERT(backend->output);
1115 
1116     if (backend->init)
1117     {
1118         backend->init(backend);
1119     }
1120 
1121     backend->support_color = support_color;
1122     rt_memcpy(backend->name, name, RT_NAME_MAX);
1123 
1124     level = rt_hw_interrupt_disable();
1125     rt_slist_append(&ulog.backend_list, &backend->list);
1126     rt_hw_interrupt_enable(level);
1127 
1128     return RT_EOK;
1129 }
1130 
1131 rt_err_t ulog_backend_unregister(ulog_backend_t backend)
1132 {
1133     rt_base_t level;
1134 
1135     RT_ASSERT(backend);
1136     RT_ASSERT(ulog.init_ok);
1137 
1138     if (backend->deinit)
1139     {
1140         backend->deinit(backend);
1141     }
1142 
1143     level = rt_hw_interrupt_disable();
1144     rt_slist_remove(&ulog.backend_list, &backend->list);
1145     rt_hw_interrupt_enable(level);
1146 
1147     return RT_EOK;
1148 }
1149 
1150 #ifdef ULOG_USING_ASYNC_OUTPUT
1151 /**
1152  * asynchronous output logs to all backends
1153  *
1154  * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
1155  */
1156 void ulog_async_output(void)
1157 {
1158     rt_rbb_blk_t log_blk;
1159     ulog_frame_t log_frame;
1160 
1161     while ((log_blk = rt_rbb_blk_get(ulog.async_rbb)) != NULL)
1162     {
1163         log_frame = (ulog_frame_t) log_blk->buf;
1164         if (log_frame->magic == ULOG_FRAME_MAGIC)
1165         {
1166             /* output to all backends */
1167             ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log,
1168                     log_frame->log_len);
1169         }
1170         rt_rbb_blk_free(ulog.async_rbb, log_blk);
1171     }
1172 }
1173 
1174 /**
1175  * waiting for get asynchronous output log
1176  *
1177  * @param time the waiting time
1178  */
1179 void ulog_async_waiting_log(rt_int32_t time)
1180 {
1181     rt_sem_control(&ulog.async_notice, RT_IPC_CMD_RESET, RT_NULL);
1182     rt_sem_take(&ulog.async_notice, time);
1183 }
1184 
1185 static void async_output_thread_entry(void *param)
1186 {
1187     while (1)
1188     {
1189         ulog_async_waiting_log(RT_WAITING_FOREVER);
1190         ulog_async_output();
1191     }
1192 }
1193 #endif /* ULOG_USING_ASYNC_OUTPUT */
1194 
1195 /**
1196  * flush all backends's log
1197  */
1198 void ulog_flush(void)
1199 {
1200     rt_slist_t *node;
1201     ulog_backend_t backend;
1202 
1203     if (!ulog.init_ok)
1204         return;
1205 
1206 #ifdef ULOG_USING_ASYNC_OUTPUT
1207     ulog_async_output();
1208 #endif
1209 
1210     /* flush all backends */
1211     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
1212     {
1213         backend = rt_slist_entry(node, struct ulog_backend, list);
1214         if (backend->flush)
1215         {
1216             backend->flush(backend);
1217         }
1218     }
1219 }
1220 
1221 int ulog_init(void)
1222 {
1223     if (ulog.init_ok)
1224         return 0;
1225 
1226     rt_mutex_init(&ulog.output_locker, "ulog lock", RT_IPC_FLAG_FIFO);
1227     rt_slist_init(&ulog.backend_list);
1228 
1229 #ifdef ULOG_USING_FILTER
1230     rt_slist_init(ulog_tag_lvl_list_get());
1231 #endif
1232 
1233 #ifdef ULOG_USING_ASYNC_OUTPUT
1234     RT_ASSERT(ULOG_ASYNC_OUTPUT_STORE_LINES >= 2);
1235     /* async output ring block buffer */
1236     ulog.async_rbb = rt_rbb_create(RT_ALIGN(ULOG_ASYNC_OUTPUT_BUF_SIZE, RT_ALIGN_SIZE), ULOG_ASYNC_OUTPUT_STORE_LINES);
1237     if (ulog.async_rbb == NULL)
1238     {
1239         rt_kprintf("Error: ulog init failed! No memory for async rbb.\n");
1240         rt_mutex_detach(&ulog.output_locker);
1241         return -RT_ENOMEM;
1242     }
1243     /* async output thread */
1244     ulog.async_th = rt_thread_create("ulog_async", async_output_thread_entry, &ulog, ULOG_ASYNC_OUTPUT_THREAD_STACK,
1245             ULOG_ASYNC_OUTPUT_THREAD_PRIORITY, 20);
1246     if (ulog.async_th == NULL)
1247     {
1248         rt_kprintf("Error: ulog init failed! No memory for async output thread.\n");
1249         rt_mutex_detach(&ulog.output_locker);
1250         rt_rbb_destroy(ulog.async_rbb);
1251         return -RT_ENOMEM;
1252     }
1253 
1254     rt_sem_init(&ulog.async_notice, "ulog", 0, RT_IPC_FLAG_FIFO);
1255     /* async output thread startup */
1256     rt_thread_startup(ulog.async_th);
1257 
1258 #endif /* ULOG_USING_ASYNC_OUTPUT */
1259 
1260 #ifdef ULOG_USING_FILTER
1261     ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
1262 #endif
1263 
1264     ulog.init_ok = RT_TRUE;
1265 
1266     return 0;
1267 }
1268 INIT_PREV_EXPORT(ulog_init);
1269 
1270 void ulog_deinit(void)
1271 {
1272     rt_slist_t *node;
1273     ulog_backend_t backend;
1274 
1275     if (!ulog.init_ok)
1276         return;
1277 
1278     /* deinit all backends */
1279     for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
1280     {
1281         backend = rt_slist_entry(node, struct ulog_backend, list);
1282         if (backend->deinit)
1283         {
1284             backend->deinit(backend);
1285         }
1286     }
1287 
1288 #ifdef ULOG_USING_FILTER
1289     /* deinit tag's level filter */
1290     {
1291         ulog_tag_lvl_filter_t tag_lvl;
1292         for (node = rt_slist_first(ulog_tag_lvl_list_get()); node; node = rt_slist_next(node))
1293         {
1294             tag_lvl = rt_slist_entry(node, struct ulog_tag_lvl_filter, list);
1295             rt_free(tag_lvl);
1296         }
1297     }
1298 #endif /* ULOG_USING_FILTER */
1299 
1300     rt_mutex_detach(&ulog.output_locker);
1301 
1302 #ifdef ULOG_USING_ASYNC_OUTPUT
1303     rt_rbb_destroy(ulog.async_rbb);
1304     rt_thread_delete(ulog.async_th);
1305 #endif
1306 
1307     ulog.init_ok = RT_FALSE;
1308 }
1309 
1310 #endif /* RT_USING_ULOG */
1311