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