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