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 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 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 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 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 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 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