Explorar o código

[component] Add ulog logger basic component.

armink %!s(int64=6) %!d(string=hai) anos
pai
achega
12a803bcc9

+ 180 - 0
components/utilities/Kconfig

@@ -46,4 +46,184 @@ config RT_USING_RYM
     bool "Enable Ymodem"
     default n
 
+config RT_USING_ULOG
+    bool "Enable ulog"
+    default n
+
+    if RT_USING_ULOG
+        if !ULOG_USING_SYSLOG
+            choice
+                prompt "The static output log level."
+                default ULOG_OUTPUT_LVL_DEBUG
+                help
+                    When the log level is less than this option and it will stop output.
+                    These log will not compile into ROM when using LOG_X api.
+                    NOTE: It's not available on syslog mode.
+                config ULOG_OUTPUT_LVL_ASSERT
+                    bool "Assert"
+                config ULOG_OUTPUT_LVL_ERROR
+                    bool "Error"
+                config ULOG_OUTPUT_LVL_WARNING
+                    bool "Warning"
+                config ULOG_OUTPUT_LVL_INFO
+                    bool "Information"
+                config ULOG_OUTPUT_LVL_DEBUG
+                    bool "Debug"
+            endchoice
+        endif
+
+        if ULOG_USING_SYSLOG
+            choice
+                prompt "The static output log level."
+                default ULOG_OUTPUT_LVL_DEBUG
+                help
+                    When the log level is less than this option and it will stop output.
+                    These log will not compile into ROM when using LOG_X api.
+                    NOTE: It's not available on syslog mode.
+                config ULOG_OUTPUT_LVL_EMERG
+                    bool "EMERG"
+                config ULOG_OUTPUT_LVL_ALERT
+                    bool "ALERT"
+                config ULOG_OUTPUT_LVL_CRIT
+                    bool "CRIT"
+                config ULOG_OUTPUT_LVL_ERROR
+                    bool "ERR"
+                config ULOG_OUTPUT_LVL_WARNING
+                    bool "WARNING"
+                config ULOG_OUTPUT_LVL_NOTICE
+                    bool "NOTICE"
+                config ULOG_OUTPUT_LVL_INFO
+                    bool "INFO"
+                config ULOG_OUTPUT_LVL_DEBUG
+                    bool "DEBUG"
+            endchoice
+        endif
+
+        config ULOG_OUTPUT_LVL
+            int
+            default 0 if ULOG_OUTPUT_LVL_ASSERT
+            default 0 if ULOG_OUTPUT_LVL_EMERG
+            default 1 if ULOG_OUTPUT_LVL_ALERT
+            default 2 if ULOG_OUTPUT_LVL_CRIT
+            default 3 if ULOG_OUTPUT_LVL_ERROR
+            default 4 if ULOG_OUTPUT_LVL_WARNING
+            default 5 if ULOG_OUTPUT_LVL_NOTICE
+            default 6 if ULOG_OUTPUT_LVL_INFO
+            default 7 if ULOG_OUTPUT_LVL_DEBUG
+            default 7
+
+        config ULOG_USING_ISR_LOG
+            bool "Enable ISR log."
+            default n
+            help
+                The log output API can using in ISR (Interrupt Service Routines) also.
+
+        config ULOG_ASSERT_ENABLE
+            bool "Enable assert check."
+            default y
+
+        config ULOG_LINE_BUF_SIZE
+            int "The log's max width."
+            default 128
+            help
+               The buffer size for every line log.
+
+        config ULOG_USING_ASYNC_OUTPUT
+            bool "Enable async output mode."
+            default n
+            help
+                When enable asynchronous output mode. The log output is not immediately and the log will stored to buffer.
+                The another thread (Such as idle) will read the buffer and output the log. So it will using more RAM.
+
+        if ULOG_USING_ASYNC_OUTPUT
+            config ULOG_ASYNC_OUTPUT_BUF_SIZE
+                int "The async output buffer size."
+                default 2048
+
+            config ULOG_ASYNC_OUTPUT_BY_THREAD
+                bool "Enable async output by thread."
+                default y
+                help
+                    This thread will output the asynchronous logs. The logs can output by other user thread when this option is disable.
+
+                if ULOG_ASYNC_OUTPUT_BY_THREAD
+
+                    config ULOG_ASYNC_OUTPUT_THREAD_STACK
+                        int "The async output thread stack size."
+                        default 1024
+
+                    config ULOG_ASYNC_OUTPUT_THREAD_PRIORITY
+                        int "The async output thread stack priority."
+                        range 0 RT_THREAD_PRIORITY_MAX
+                        default 30
+
+                endif
+        endif
+
+        menu "log format"
+            config ULOG_OUTPUT_FLOAT
+                bool "Enable float number support."
+                select RT_USING_LIBC
+                default n
+                help
+                    The default formater is using rt_vsnprint and it not supported float number.
+                    When enable this option then it will enable libc. The formater will change to vsnprint on libc.
+
+            if !ULOG_USING_SYSLOG
+                config ULOG_USING_COLOR
+                    bool "Enable color log."
+                    default y
+                    help
+                        The log will has different color by level.
+            endif
+
+            config ULOG_OUTPUT_TIME
+                bool "Enable time information."
+                default y
+
+            config ULOG_TIME_USING_TIMESTAMP
+                bool "Enable timestamp format for time."
+                default n
+                select RT_USING_LIBC
+                depends on ULOG_OUTPUT_TIME
+
+            config ULOG_OUTPUT_LEVEL
+                bool "Enable level information."
+                default y
+
+            config ULOG_OUTPUT_TAG
+                bool "Enable tag information."
+                default y
+
+            config ULOG_OUTPUT_THREAD_NAME
+                bool "Enable thread information."
+                default n
+        endmenu
+
+        config ULOG_BACKEND_USING_CONSOLE
+            bool "Enable console backend."
+            default y
+            help
+                The low level output using rt_kprintf().
+
+        config ULOG_USING_FILTER
+            bool "Enable runtime log filter."
+            default n
+            help
+                It will enable the log filter.
+                Such as level filter, log tag filter, log kw filter and tag's level filter.
+
+        config ULOG_USING_SYSLOG
+            bool "Enable syslog format log and API."
+            select ULOG_OUTPUT_TIME
+            select ULOG_USING_FILTER
+            default n
+            
+        config ULOG_SW_VERSION_NUM
+            int
+            default 0x00100
+            help
+                sfotware module version number
+    endif
+
 endmenu

+ 16 - 0
components/utilities/ulog/SConscript

@@ -0,0 +1,16 @@
+from building import *
+
+cwd  = GetCurrentDir()
+src  = Glob('*.c')
+path = [cwd]
+
+if GetDepend('ULOG_BACKEND_USING_CONSOLE'):
+    src += ['backend/console_be.c']
+    
+if GetDepend('ULOG_USING_SYSLOG'):
+    path +=  [cwd + '/syslog']
+    src  += Glob('syslog/*.c')
+
+group = DefineGroup('Utilities', src, depend = ['RT_USING_ULOG'], CPPPATH = path)
+
+Return('group')

+ 56 - 0
components/utilities/ulog/backend/console_be.c

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-04     armink       the first version
+ */
+
+#include <rthw.h>
+#include <ulog.h>
+
+#ifdef ULOG_BACKEND_USING_CONSOLE
+
+#if defined(ULOG_ASYNC_OUTPUT_BY_THREAD) && ULOG_ASYNC_OUTPUT_THREAD_STACK < 384
+#error "The thread stack size must more than 384 when using async output by thread (ULOG_ASYNC_OUTPUT_BY_THREAD)"
+#endif
+
+static struct ulog_backend console;
+
+void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,
+        const char *log, size_t len)
+{
+    rt_device_t dev = rt_console_get_device();
+
+#ifdef RT_USING_DEVICE
+    if (dev == RT_NULL)
+    {
+        rt_hw_console_output(log);
+    }
+    else
+    {
+        rt_uint16_t old_flag = dev->open_flag;
+
+        dev->open_flag |= RT_DEVICE_FLAG_STREAM;
+        rt_device_write(dev, 0, log, len);
+        dev->open_flag = old_flag;
+    }
+#else
+    rt_hw_console_output(log);
+#endif
+
+}
+
+int ulog_console_backend_init(void)
+{
+    console.output = ulog_console_backend_output;
+
+    ulog_backend_register(&console, "console", RT_TRUE);
+
+    return 0;
+}
+INIT_COMPONENT_EXPORT(ulog_console_backend_init);
+
+#endif /* ULOG_BACKEND_USING_CONSOLE */

+ 260 - 0
components/utilities/ulog/syslog/syslog.c

@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-07     armink       the first version
+ */
+
+#include <stdarg.h>
+#include <ulog.h>
+#include <rthw.h>
+#include "syslog.h"
+
+#ifdef ULOG_OUTPUT_FLOAT
+#include <stdio.h>
+#endif
+
+/*
+ * reference:
+ * http://pubs.opengroup.org/onlinepubs/7908799/xsh/syslog.h.html
+ * https://www.gnu.org/software/libc/manual/html_node/Submitting-Syslog-Messages.html
+ * http://man7.org/linux/man-pages/man3/syslog.3.html
+ */
+
+#ifdef ULOG_USING_SYSLOG
+
+#ifndef ULOG_SYSLOG_IDENT_MAX_LEN
+#define ULOG_SYSLOG_IDENT_MAX_LEN      ULOG_FILTER_TAG_MAX_LEN
+#endif
+
+static char local_ident[ULOG_SYSLOG_IDENT_MAX_LEN + 1];
+static int local_facility = LOG_USER;
+static int local_option = LOG_USER;
+static rt_bool_t is_open = RT_FALSE;
+
+/**
+ * open connection to syslog
+ *
+ * @param ident is an arbitrary identification string which future syslog invocations will prefix to each message.
+ * @param option is not using on ulog.
+ * @param facility is the default facility code for this connection.
+ */
+void openlog(const char *ident, int option, int facility)
+{
+    rt_base_t level;
+
+    ulog_init();
+
+    level = rt_hw_interrupt_disable();
+
+    rt_memset(local_ident, 0, sizeof(local_ident));
+    if (ident)
+    {
+        rt_strncpy(local_ident, ident, ULOG_SYSLOG_IDENT_MAX_LEN);
+    }
+    else
+    {
+        rt_strncpy(local_ident, "rtt", ULOG_SYSLOG_IDENT_MAX_LEN);
+    }
+
+    local_option = option;
+
+    if (facility)
+    {
+        local_facility = facility;
+    }
+    else
+    {
+        /* default facility is LOG_USER */
+        local_facility = LOG_USER;
+    }
+    /* output all level log */
+    setlogmask(LOG_UPTO(LOG_DEBUG));
+
+    is_open = RT_TRUE;
+
+    rt_hw_interrupt_enable(level);
+
+}
+
+/**
+ * This is functionally identical to syslog.
+ *
+ * @param priority log priority, can be generated by the macro LOG_MAKEPRI
+ * @param format log format
+ * @param args log arguments
+ */
+void vsyslog(int priority, const char *format, va_list args)
+{
+    if (LOG_FAC(priority) == 0)
+    {
+        /* using local facility */
+        priority |= local_facility;
+    }
+
+    ulog_voutput(priority, local_ident, format, args);
+}
+
+/**
+ * generates a log message
+ *
+ * @param priority log priority, can be generated by the macro LOG_MAKEPRI
+ * @param format log format, like printf()
+ */
+void syslog(int priority, const char *format, ...)
+{
+    va_list args;
+
+    if (!is_open)
+    {
+        openlog(0, 0, 0);
+    }
+    /* args point to the first variable parameter */
+    va_start(args, format);
+
+    vsyslog(priority, format, args);
+
+    va_end(args);
+}
+
+/**
+ * close the syslog
+ */
+void closelog(void)
+{
+    ulog_deinit();
+
+    is_open = RT_FALSE;
+}
+
+/**
+ * set log priority mask
+ *
+ * @param mask The log priority mask which generate by macro LOG_MASK and LOG_UPTO.
+ *
+ * @return This function returns the previous log priority mask.
+ */
+int setlogmask(int mask)
+{
+    static int old_mask = 0;
+    int return_mask = old_mask;
+
+    ulog_tag_lvl_filter_set(local_ident, mask);
+
+    old_mask = mask;
+
+    return return_mask;
+}
+
+static const char *get_month_str(uint8_t month)
+{
+    switch(month)
+    {
+    case 1: return "Jan";
+    case 2: return "Feb";
+    case 3: return "Mar";
+    case 4: return "Apr";
+    case 5: return "May";
+    case 6: return "June";
+    case 7: return "July";
+    case 8: return "Aug";
+    case 9: return "Sept";
+    case 10: return "Oct";
+    case 11: return "Nov";
+    case 12: return "Dec";
+    default: return "Unknown";
+    }
+}
+
+RT_WEAK rt_size_t syslog_formater(char *log_buf, int level, const char *tag, const char *format, va_list args)
+{
+    extern size_t ulog_strcpy(size_t cur_len, char *dst, const char *src);
+
+    rt_size_t log_len = 0, newline_len = rt_strlen(ULOG_NEWLINE_SIGN);
+    int fmt_result;
+
+    RT_ASSERT(log_buf);
+    RT_ASSERT(LOG_PRI(level) <= LOG_DEBUG);
+    RT_ASSERT(tag);
+    RT_ASSERT(format);
+
+    /* add time and priority (level) info */
+    {
+        time_t now = time(RT_NULL);
+        struct tm *tm, tm_tmp;
+
+        tm = gmtime_r(&now, &tm_tmp);
+
+#ifdef ULOG_OUTPUT_LEVEL
+        rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "<%d>%s%3d %02d:%02d:%02d", level,
+                get_month_str(tm->tm_mon + 1), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, rt_tick_get() % 1000);
+#else
+        rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%s%3d %02d:%02d:%02d",
+                get_month_str(tm->tm_mon + 1), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, rt_tick_get() % 1000);
+#endif /* ULOG_OUTPUT_LEVEL */
+
+        log_len += rt_strlen(log_buf + log_len);
+    }
+
+#ifdef ULOG_OUTPUT_TAG
+    /* add identification (tag) info */
+    {
+        log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+        log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
+    }
+#endif /* ULOG_OUTPUT_TAG */
+
+#ifdef ULOG_OUTPUT_THREAD_NAME
+    /* add thread info */
+    {
+        log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+        /* is not in interrupt context */
+        if (rt_interrupt_get_nest() == 0)
+        {
+            log_len += ulog_strcpy(log_len, log_buf + log_len, rt_thread_self()->name);
+        }
+        else
+        {
+            log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
+        }
+    }
+#endif /* ULOG_OUTPUT_THREAD_NAME */
+
+    log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
+
+#ifdef ULOG_OUTPUT_FLOAT
+    fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
+#else
+    fmt_result = rt_vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
+#endif /* ULOG_OUTPUT_FLOAT */
+
+    /* calculate log length */
+    if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
+    {
+        log_len += fmt_result;
+    }
+    else
+    {
+        /* using max length */
+        log_len = ULOG_LINE_BUF_SIZE;
+    }
+
+    /* overflow check and reserve some space for newline sign */
+    if (log_len + newline_len > ULOG_LINE_BUF_SIZE)
+    {
+        /* using max length */
+        log_len = ULOG_LINE_BUF_SIZE;
+        /* reserve some space for newline sign */
+        log_len -= newline_len;
+    }
+
+    /* package newline sign */
+    log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
+
+    return log_len;
+}
+
+#endif /* ULOG_USING_SYSLOG */

+ 100 - 0
components/utilities/ulog/syslog/syslog.h

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-07     armink       the first version
+ */
+
+#ifndef _SYSLOG_H_
+#define _SYSLOG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number).  Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code.  This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+#define LOG_EMERG   0   /* system is unusable */
+#define LOG_ALERT   1   /* action must be taken immediately */
+#define LOG_CRIT    2   /* critical conditions */
+#define LOG_ERR     3   /* error conditions */
+#define LOG_WARNING 4   /* warning conditions */
+#define LOG_NOTICE  5   /* normal but significant condition */
+#define LOG_INFO    6   /* informational */
+#define LOG_DEBUG   7   /* debug-level messages */
+
+#define LOG_PRIMASK 0x07
+
+#define LOG_PRI(p)              ((p) & LOG_PRIMASK)
+#define LOG_MAKEPRI(fac, pri)   (((fac) << 3) | (pri))
+
+/* facility codes */
+#define LOG_KERN        (0<<3)  /* kernel messages */
+#define LOG_USER        (1<<3)  /* random user-level messages */
+#define LOG_MAIL        (2<<3)  /* mail system */
+#define LOG_DAEMON      (3<<3)  /* system daemons */
+#define LOG_AUTH        (4<<3)  /* security/authorization messages */
+#define LOG_SYSLOG      (5<<3)  /* messages generated internally by syslogd */
+#define LOG_LPR         (6<<3)  /* line printer subsystem */
+#define LOG_NEWS        (7<<3)  /* network news subsystem */
+#define LOG_UUCP        (8<<3)  /* UUCP subsystem */
+#define LOG_CRON        (9<<3)  /* clock daemon */
+#define LOG_AUTHPRIV    (10<<3) /* security/authorization messages (private) */
+
+/* other codes through 15 reserved for system use */
+#define LOG_LOCAL0      (16<<3) /* reserved for local use */
+#define LOG_LOCAL1      (17<<3) /* reserved for local use */
+#define LOG_LOCAL2      (18<<3) /* reserved for local use */
+#define LOG_LOCAL3      (19<<3) /* reserved for local use */
+#define LOG_LOCAL4      (20<<3) /* reserved for local use */
+#define LOG_LOCAL5      (21<<3) /* reserved for local use */
+#define LOG_LOCAL6      (22<<3) /* reserved for local use */
+#define LOG_LOCAL7      (23<<3) /* reserved for local use */
+
+#define LOG_NFACILITIES 24  /* current number of facilities */
+#define LOG_FACMASK     0x03f8  /* mask to extract facility part */
+/* facility of pri */
+#define LOG_FAC(p)      (((p) & LOG_FACMASK) >> 3)
+
+/*
+ * arguments to setlogmask.
+ */
+#define LOG_MASK(pri)   (1 << (pri))        /* mask for one priority */
+#define LOG_UPTO(pri)   ((1 << ((pri)+1)) - 1)  /* all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * LOG_ODELAY no longer does anything.
+ * LOG_NDELAY is the inverse of what it used to be.
+ */
+#define LOG_PID         0x01    /* log the pid with each message */
+#define LOG_CONS        0x02    /* log on the console if errors in sending */
+#define LOG_ODELAY      0x04    /* delay open until first syslog() (default) */
+#define LOG_NDELAY      0x08    /* don't delay open */
+#define LOG_NOWAIT      0x10    /* don't wait for console forks: DEPRECATED */
+#define LOG_PERROR      0x20    /* log to stderr as well */
+
+#include <stdarg.h>
+
+void closelog(void);
+void openlog(const char *ident, int option, int facility);
+int setlogmask(int mask);
+void syslog(int priority, const char *format, ...);
+void vsyslog(int priority, const char *format, va_list args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYSLOG_H_ */

+ 1152 - 0
components/utilities/ulog/ulog.c

@@ -0,0 +1,1152 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+
+#include <stdarg.h>
+#include "ulog.h"
+#include "rthw.h"
+
+#ifdef ULOG_USING_SYSLOG
+#include <syslog.h>
+#endif
+
+#ifdef ULOG_OUTPUT_FLOAT
+#include <stdio.h>
+#endif
+
+#ifdef ULOG_TIME_USING_TIMESTAMP
+#include <time.h>
+#endif
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+#include <rtdevice.h>
+#endif
+
+#ifdef RT_USING_ULOG
+
+/* the number which is max stored line logs */
+#ifndef ULOG_ASYNC_OUTPUT_STORE_LINES
+#define ULOG_ASYNC_OUTPUT_STORE_LINES  (ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / ULOG_LINE_BUF_SIZE)
+#endif
+
+#ifdef ULOG_USING_COLOR
+/**
+ * CSI(Control Sequence Introducer/Initiator) sign
+ * more information on https://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+#define CSI_START                      "\033["
+#define CSI_END                        "\033[0m"
+/* output log front color */
+#define F_BLACK                        "30m"
+#define F_RED                          "31m"
+#define F_GREEN                        "32m"
+#define F_YELLOW                       "33m"
+#define F_BLUE                         "34m"
+#define F_MAGENTA                      "35m"
+#define F_CYAN                         "36m"
+#define F_WHITE                        "37m"
+
+/* output log default color definition */
+#ifndef ULOG_COLOR_DEBUG
+#define ULOG_COLOR_DEBUG               NULL
+#endif
+#ifndef ULOG_COLOR_INFO
+#define ULOG_COLOR_INFO                (F_GREEN)
+#endif
+#ifndef ULOG_COLOR_WARN
+#define ULOG_COLOR_WARN                (F_YELLOW)
+#endif
+#ifndef ULOG_COLOR_ERROR
+#define ULOG_COLOR_ERROR               (F_RED)
+#endif
+#ifndef ULOG_COLOR_ASSERT
+#define ULOG_COLOR_ASSERT              (F_MAGENTA)
+#endif
+#endif /* ULOG_USING_COLOR */
+
+#if ULOG_LINE_BUF_SIZE < 80
+#error "the log line buffer size must more than 80"
+#endif
+
+/* tag's level filter */
+struct tag_lvl_filter
+{
+    char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
+    rt_uint32_t level;
+    rt_slist_t list;
+};
+typedef struct tag_lvl_filter *tag_lvl_filter_t;
+
+struct rt_ulog
+{
+    rt_bool_t init_ok;
+    struct rt_mutex output_locker;
+    /* all backends */
+    rt_slist_t backend_list;
+    /* the thread log's line buffer */
+    char log_buf_th[ULOG_LINE_BUF_SIZE];
+
+#ifdef ULOG_USING_ISR_LOG
+    /* the ISR log's line buffer */
+    rt_base_t output_locker_isr_lvl;
+    char log_buf_isr[ULOG_LINE_BUF_SIZE];
+#endif /* ULOG_USING_ISR_LOG */
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+    rt_rbb_t async_rbb;
+    rt_thread_t async_th;
+    struct rt_semaphore async_notice;
+#endif
+
+#ifdef ULOG_USING_FILTER
+    struct
+    {
+        /* all tag's level filter */
+        rt_slist_t tag_lvl_list;
+        /* global filter level, tag and keyword */
+        rt_uint32_t level;
+        char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
+        char keyword[ULOG_FILTER_KW_MAX_LEN + 1];
+    } filter;
+#endif /* ULOG_USING_FILTER */
+};
+
+/* level output info */
+static const char * const level_output_info[] =
+{
+        "A/",
+        NULL,
+        NULL,
+        "E/",
+        "W/",
+        NULL,
+        "I/",
+        "D/",
+};
+
+#ifdef ULOG_USING_COLOR
+/* color output info */
+static const char * const color_output_info[] =
+{
+        ULOG_COLOR_ASSERT,
+        NULL,
+        NULL,
+        ULOG_COLOR_ERROR,
+        ULOG_COLOR_WARN,
+        NULL,
+        ULOG_COLOR_INFO,
+        ULOG_COLOR_DEBUG,
+};
+#endif /* ULOG_USING_COLOR */
+
+/* ulog local object */
+static struct rt_ulog ulog = { 0 };
+
+size_t ulog_strcpy(size_t cur_len, char *dst, const char *src)
+{
+    const char *src_old = src;
+
+    RT_ASSERT(dst);
+    RT_ASSERT(src);
+
+    while (*src != 0)
+    {
+        /* make sure destination has enough space */
+        if (cur_len++ <= ULOG_LINE_BUF_SIZE)
+        {
+            *dst++ = *src++;
+        }
+        else
+        {
+            break;
+        }
+    }
+    return src - src_old;
+}
+
+static void output_unlock(void)
+{
+    /* is in thread context */
+    if (rt_interrupt_get_nest() == 0)
+    {
+        rt_mutex_release(&ulog.output_locker);
+    }
+    else
+    {
+#ifdef ULOG_USING_ISR_LOG
+        rt_hw_interrupt_enable(ulog.output_locker_isr_lvl);
+#endif
+    }
+}
+
+static void output_lock(void)
+{
+    /* is in thread context */
+    if (rt_interrupt_get_nest() == 0)
+    {
+        rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
+    }
+    else
+    {
+#ifdef ULOG_USING_ISR_LOG
+        ulog.output_locker_isr_lvl = rt_hw_interrupt_disable();
+#endif
+    }
+}
+
+static char *get_log_buf(void)
+{
+    /* is in thread context */
+    if (rt_interrupt_get_nest() == 0)
+    {
+        return ulog.log_buf_th;
+    }
+    else
+    {
+#ifdef ULOG_USING_ISR_LOG
+        return ulog.log_buf_isr;
+#else
+        rt_kprintf("Error: Current mode not supported run in ISR. Please enable ULOG_USING_ISR_LOG.\n");
+        return NULL;
+#endif
+    }
+}
+
+RT_WEAK rt_size_t ulog_formater(char *log_buf, rt_uint32_t level, const char *tag, const char *format, va_list args)
+{
+    rt_size_t log_len = 0, newline_len = rt_strlen(ULOG_NEWLINE_SIGN);
+    int fmt_result;
+
+    RT_ASSERT(log_buf);
+    RT_ASSERT(level <= LOG_LVL_DBG);
+    RT_ASSERT(tag);
+    RT_ASSERT(format);
+
+#ifdef ULOG_USING_COLOR
+    /* add CSI start sign and color info */
+    if (color_output_info[level])
+    {
+        log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_START);
+        log_len += ulog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
+    }
+#endif /* ULOG_USING_COLOR */
+
+#ifdef ULOG_OUTPUT_TIME
+    /* add time info */
+    {
+#ifdef ULOG_TIME_USING_TIMESTAMP
+        time_t now = time(NULL);
+        struct tm *tm, tm_tmp;
+
+        tm = gmtime_r(&now, &tm_tmp);
+
+#ifdef RT_USING_SOFT_RTC
+        rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d.%03d", tm->tm_mon + 1,
+                tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, rt_tick_get() % 1000);
+#else
+        rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm->tm_mon + 1,
+                tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+#endif /* RT_USING_SOFT_RTC */
+
+#else
+        rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "[%d]", rt_tick_get());
+#endif /* ULOG_TIME_USING_TIMESTAMP */
+
+        log_len += rt_strlen(log_buf + log_len);
+    }
+#endif /* ULOG_OUTPUT_TIME */
+
+#ifdef ULOG_OUTPUT_LEVEL
+
+#ifdef ULOG_OUTPUT_TIME
+    log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+#endif
+
+    /* add level info */
+    log_len += ulog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
+#endif /* ULOG_OUTPUT_LEVEL */
+
+#ifdef ULOG_OUTPUT_TAG
+
+#if !defined(ULOG_OUTPUT_LEVEL) && defined(ULOG_OUTPUT_TIME)
+    log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+#endif
+
+    /* add tag info */
+    log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
+#endif /* ULOG_OUTPUT_TAG */
+
+#ifdef ULOG_OUTPUT_THREAD_NAME
+    /* add thread info */
+    {
+
+#if defined(ULOG_OUTPUT_TIME) || defined(ULOG_OUTPUT_LEVEL) || defined(ULOG_OUTPUT_TAG)
+        log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+#endif
+
+        /* is not in interrupt context */
+        if (rt_interrupt_get_nest() == 0)
+        {
+            log_len += ulog_strcpy(log_len, log_buf + log_len, rt_thread_self()->name);
+        }
+        else
+        {
+            log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
+        }
+    }
+#endif /* ULOG_OUTPUT_THREAD_NAME */
+
+    log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
+
+#ifdef ULOG_OUTPUT_FLOAT
+    fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
+#else
+    fmt_result = rt_vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
+#endif /* ULOG_OUTPUT_FLOAT */
+
+    /* calculate log length */
+    if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
+    {
+        log_len += fmt_result;
+    }
+    else
+    {
+        /* using max length */
+        log_len = ULOG_LINE_BUF_SIZE;
+    }
+
+    /* overflow check and reserve some space for CSI end sign and newline sign */
+#ifdef ULOG_USING_COLOR
+    if (log_len + (sizeof(CSI_END) - 1) + newline_len > ULOG_LINE_BUF_SIZE)
+    {
+        /* using max length */
+        log_len = ULOG_LINE_BUF_SIZE;
+        /* reserve some space for CSI end sign */
+        log_len -= (sizeof(CSI_END) - 1);
+#else
+    if (log_len + newline_len > ULOG_LINE_BUF_SIZE)
+    {
+        /* using max length */
+        log_len = ULOG_LINE_BUF_SIZE;
+#endif /* ULOG_USING_COLOR */
+        /* reserve some space for newline sign */
+        log_len -= newline_len;
+    }
+
+    /* package newline sign */
+    log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
+
+#ifdef ULOG_USING_COLOR
+    /* add CSI end sign  */
+    if (color_output_info[level])
+    {
+        log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_END);
+    }
+#endif /* ULOG_USING_COLOR */
+
+    return log_len;
+}
+
+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)
+{
+    rt_slist_t *node;
+    ulog_backend_t backend;
+
+    if (!ulog.init_ok)
+        return;
+
+    /* output for all backends */
+    for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
+    {
+        backend = rt_slist_entry(node, struct ulog_backend, list);
+#if !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG)
+        backend->output(backend, level, tag, is_raw, log, size);
+#else
+        if (backend->support_color)
+        {
+            backend->output(backend, level, tag, is_raw, log, size);
+        }
+        else
+        {
+            /* recalculate the log start address and log size when backend not supported color */
+            rt_size_t color_info_len = rt_strlen(color_output_info[level]);
+            if (color_info_len)
+            {
+                rt_size_t color_hdr_len = rt_strlen(CSI_START) + color_info_len;
+
+                log += color_hdr_len;
+                size -= (color_hdr_len + (sizeof(CSI_END) - 1));
+            }
+            backend->output(backend, level, tag, is_raw, log, size);
+        }
+#endif /* !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) */
+    }
+}
+
+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)
+{
+#ifdef ULOG_USING_ASYNC_OUTPUT
+    rt_rbb_blk_t log_blk;
+    ulog_frame_t log_frame;
+
+    /* allocate log frame */
+    log_blk = rt_rbb_blk_alloc(ulog.async_rbb, RT_ALIGN(sizeof(struct ulog_frame) + log_len, RT_ALIGN_SIZE));
+    if (log_blk)
+    {
+        /* package the log frame */
+        log_frame = (ulog_frame_t) log_blk->buf;
+        log_frame->magic = ULOG_FRAME_MAGIC;
+        log_frame->is_raw = is_raw;
+        log_frame->level = level;
+        log_frame->log_len = log_len;
+        log_frame->tag = tag;
+        log_frame->log = (const char *)log_blk->buf + sizeof(struct ulog_frame);
+        /* copy log data */
+        rt_memcpy(log_blk->buf + sizeof(struct ulog_frame), log_buf, log_len);
+        /* put the block */
+        rt_rbb_blk_put(log_blk);
+        /* send a notice */
+        rt_sem_release(&ulog.async_notice);
+    }
+    else
+    {
+        static rt_bool_t already_output = RT_FALSE;
+        if (already_output == RT_FALSE)
+        {
+            rt_kprintf("Warning: There is no enough buffer for saving async log,"
+                    " please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option.\n");
+            already_output = RT_TRUE;
+        }
+    }
+#else
+    /* is in thread context */
+    if (rt_interrupt_get_nest() == 0)
+    {
+        /* output to all backends */
+        ulog_output_to_all_backend(level, tag, is_raw, log_buf, log_len);
+    }
+    else
+    {
+#ifdef ULOG_BACKEND_USING_CONSOLE
+        /* We can't ensure that all backends support ISR context output.
+         * So only using rt_kprintf when context is ISR */
+        extern void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag,
+                rt_bool_t is_raw, const char *log, size_t len);
+        ulog_console_backend_output(NULL, level, tag, is_raw, log_buf, log_len);
+#endif /* ULOG_BACKEND_USING_CONSOLE */
+    }
+#endif /* ULOG_USING_ASYNC_OUTPUT */
+}
+
+/**
+ * output the log by variable argument list
+ *
+ * @param level level
+ * @param tag tag
+ * @param format output format
+ * @param args variable argument list
+ */
+void ulog_voutput(rt_uint32_t level, const char *tag, const char *format, va_list args)
+{
+    char *log_buf = NULL;
+    rt_size_t log_len = 0;
+
+#ifndef ULOG_USING_SYSLOG
+    RT_ASSERT(level <= LOG_LVL_DBG);
+#else
+    RT_ASSERT(LOG_PRI(level) <= LOG_DEBUG);
+#endif /* ULOG_USING_SYSLOG */
+
+    RT_ASSERT(tag);
+    RT_ASSERT(format);
+
+    if (!ulog.init_ok)
+    {
+        return;
+    }
+
+#ifdef ULOG_USING_FILTER
+    /* level filter */
+#ifndef ULOG_USING_SYSLOG
+    if (level > ulog.filter.level || level > ulog_tag_lvl_filter_get(tag))
+    {
+        return;
+    }
+#else
+    if (((LOG_MASK(LOG_PRI(level)) & ulog.filter.level) == 0)
+            || ((LOG_MASK(LOG_PRI(level)) & ulog_tag_lvl_filter_get(tag)) == 0))
+    {
+        return;
+    }
+#endif /* ULOG_USING_SYSLOG */
+    else if (!rt_strstr(tag, ulog.filter.tag))
+    {
+        /* tag filter */
+        return;
+    }
+#endif /* ULOG_USING_FILTER */
+
+    /* get log buffer */
+    log_buf = get_log_buf();
+
+    /* lock output */
+    output_lock();
+
+#ifndef ULOG_USING_SYSLOG
+    log_len = ulog_formater(log_buf, level, tag, format, args);
+#else
+    extern rt_size_t syslog_formater(char *log_buf, rt_uint8_t level, const char *tag, const char *format, va_list args);
+    log_len = syslog_formater(log_buf, level, tag, format, args);
+#endif /* ULOG_USING_SYSLOG */
+
+#ifdef ULOG_USING_FILTER
+    /* keyword filter */
+    if (ulog.filter.keyword[0] != '\0')
+    {
+        /* add string end sign */
+        log_buf[log_len] = '\0';
+        /* find the keyword */
+        if (!rt_strstr(log_buf, ulog.filter.keyword))
+        {
+            /* unlock output */
+            output_unlock();
+            return;
+        }
+    }
+#endif /* ULOG_USING_FILTER */
+    /* do log output */
+    do_output(level, tag, RT_FALSE, log_buf, log_len);
+
+    /* unlock output */
+    output_unlock();
+}
+
+/**
+ * output the log
+ *
+ * @param level level
+ * @param tag tag
+ * @param format output format
+ * @param ... args
+ */
+void ulog_output(rt_uint32_t level, const char *tag, const char *format, ...)
+{
+    va_list args;
+
+    /* args point to the first variable parameter */
+    va_start(args, format);
+
+    ulog_voutput(level, tag, format, args);
+
+    va_end(args);
+}
+
+/**
+ * output RAW string format log
+ *
+ * @param format output format
+ * @param ... args
+ */
+void ulog_raw(const char *format, ...)
+{
+    rt_size_t log_len = 0;
+    char *log_buf = NULL;
+    va_list args;
+    int fmt_result;
+
+    RT_ASSERT(ulog.init_ok);
+
+    /* get log buffer */
+    log_buf = get_log_buf();
+
+    /* lock output */
+    output_lock();
+    /* args point to the first variable parameter */
+    va_start(args, format);
+
+#ifdef ULOG_OUTPUT_FLOAT
+    fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
+#else
+    fmt_result = rt_vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args);
+#endif /* ULOG_OUTPUT_FLOAT */
+
+    va_end(args);
+
+    /* calculate log length */
+    if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
+    {
+        log_len = fmt_result;
+    }
+    else
+    {
+        log_len = ULOG_LINE_BUF_SIZE;
+    }
+
+    /* do log output */
+    do_output(LOG_LVL_DBG, NULL, RT_TRUE, log_buf, log_len);
+
+    /* unlock output */
+    output_unlock();
+}
+
+/**
+ * dump the hex format data to log
+ *
+ * @param name name for hex object, it will show on log header
+ * @param width hex number for every line, such as: 16, 32
+ * @param buf hex buffer
+ * @param size buffer size
+ */
+void ulog_hexdump(const char *name, rt_size_t width, rt_uint8_t *buf, rt_size_t size)
+{
+#define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
+
+    rt_size_t i, j;
+    rt_size_t log_len = 0;
+    char *log_buf = NULL, dump_string[8];
+    int fmt_result;
+
+    RT_ASSERT(ulog.init_ok);
+
+    /* get log buffer */
+    log_buf = get_log_buf();
+
+    /* lock output */
+    output_lock();
+
+    for (i = 0, log_len = 0; i < size; i += width)
+    {
+        /* package header */
+        fmt_result = rt_snprintf(log_buf, ULOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width);
+        /* calculate log length */
+        if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE))
+        {
+            log_len = fmt_result;
+        }
+        else
+        {
+            log_len = ULOG_LINE_BUF_SIZE;
+        }
+        /* dump hex */
+        for (j = 0; j < width; j++)
+        {
+            if (i + j < size)
+            {
+                rt_snprintf(dump_string, sizeof(dump_string), "%02X ", buf[i + j]);
+            }
+            else
+            {
+                rt_strncpy(dump_string, "   ", sizeof(dump_string));
+            }
+            log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
+            if ((j + 1) % 8 == 0)
+            {
+                log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
+            }
+        }
+        log_len += ulog_strcpy(log_len, log_buf + log_len, "  ");
+        /* dump char for hex */
+        for (j = 0; j < width; j++)
+        {
+            if (i + j < size)
+            {
+                rt_snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
+                log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string);
+            }
+        }
+        /* overflow check and reserve some space for newline sign */
+        if (log_len + rt_strlen(ULOG_NEWLINE_SIGN) > ULOG_LINE_BUF_SIZE)
+        {
+            log_len = ULOG_LINE_BUF_SIZE - rt_strlen(ULOG_NEWLINE_SIGN);
+        }
+        /* package newline sign */
+        log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
+        /* do log output */
+        do_output(LOG_LVL_DBG, NULL, RT_TRUE, log_buf, log_len);
+    }
+    /* unlock output */
+    output_unlock();
+}
+
+#ifdef ULOG_USING_FILTER
+/**
+ * Set the filter's level by different tag.
+ * The log on this tag which level is less than it will stop output.
+ *
+ * example:
+ *     // the example tag log enter silent mode
+ *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_SILENT);
+ *     // the example tag log which level is less than INFO level will stop output
+ *     ulog_set_filter_lvl("example", LOG_LVL_INFO);
+ *     // remove example tag's level filter, all level log will resume output
+ *     ulog_set_filter_lvl("example", LOG_FILTER_LVL_ALL);
+ *
+ * @param tag log tag
+ * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode.
+ *        When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer.
+ *        Then all level log will resume output.
+ *
+ * @return  0: success
+ *         -5: no memory
+ */
+int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)
+{
+    rt_slist_t *node;
+    tag_lvl_filter_t tag_lvl = NULL;
+    int result = RT_EOK;
+
+    RT_ASSERT(level <= LOG_FILTER_LVL_ALL);
+
+    if (!ulog.init_ok)
+        return result;
+
+    /* lock output */
+    output_lock();
+    /* find the tag in list */
+    for (node = rt_slist_first(&ulog.filter.tag_lvl_list); node; node = rt_slist_next(node))
+    {
+        tag_lvl = rt_slist_entry(node, struct tag_lvl_filter, list);
+        if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
+        {
+            break;
+        }
+        else
+        {
+            tag_lvl = NULL;
+        }
+    }
+    /* find OK */
+    if (tag_lvl)
+    {
+        if (level == LOG_FILTER_LVL_ALL)
+        {
+            /* remove current tag's level filter when input level is the lowest level */
+            rt_slist_remove(&ulog.filter.tag_lvl_list, &tag_lvl->list);
+        }
+        else
+        {
+            /* update level */
+            tag_lvl->level = level;
+        }
+    }
+    else
+    {
+        /* only add the new tag's level filer when level is not LOG_FILTER_LVL_ALL */
+        if (level != LOG_FILTER_LVL_ALL)
+        {
+            /* new a tag's level filter */
+            tag_lvl = (tag_lvl_filter_t)rt_malloc(sizeof(struct tag_lvl_filter));
+            if (tag_lvl)
+            {
+                rt_memset(tag_lvl->tag, 0 , sizeof(tag_lvl->tag));
+                rt_strncpy(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN);
+                tag_lvl->level = level;
+                rt_slist_append(&ulog.filter.tag_lvl_list, &tag_lvl->list);
+            }
+            else
+            {
+                result = -RT_ENOMEM;
+            }
+        }
+    }
+    /* unlock output */
+    output_unlock();
+
+    return result;
+}
+
+/**
+ * get the level on tag's level filer
+ *
+ * @param tag log tag
+ *
+ * @return It will return the lowest level when tag was not found.
+ *         Other level will return when tag was found.
+ */
+rt_uint32_t ulog_tag_lvl_filter_get(const char *tag)
+{
+    rt_slist_t *node;
+    tag_lvl_filter_t tag_lvl = NULL;
+    rt_uint32_t level = LOG_FILTER_LVL_ALL;
+
+    if (!ulog.init_ok)
+        return level;
+
+    /* lock output */
+    output_lock();
+    /* find the tag in list */
+    for (node = rt_slist_first(&ulog.filter.tag_lvl_list); node; node = rt_slist_next(node))
+    {
+        tag_lvl = rt_slist_entry(node, struct tag_lvl_filter, list);
+        if (!rt_strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN))
+        {
+            level = tag_lvl->level;
+            break;
+        }
+    }
+    /* unlock output */
+    output_unlock();
+
+    return level;
+}
+
+/**
+ * set log global filter level
+ *
+ * @param level log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG
+ *              LOG_FILTER_LVL_SILENT: disable all log output, except assert level
+ *              LOG_FILTER_LVL_ALL: enable all log output
+ */
+void ulog_global_filter_lvl_set(rt_uint32_t level)
+{
+    RT_ASSERT(level <= LOG_FILTER_LVL_ALL);
+
+    ulog.filter.level = level;
+}
+
+/**
+ * set log global filter tag
+ *
+ * @param tag tag
+ */
+void ulog_global_filter_tag_set(const char *tag)
+{
+    RT_ASSERT(tag);
+
+    rt_strncpy(ulog.filter.tag, tag, ULOG_FILTER_TAG_MAX_LEN);
+}
+
+/**
+ * set log global filter keyword
+ *
+ * @param keyword keyword
+ */
+void ulog_global_filter_kw_set(const char *keyword)
+{
+    RT_ASSERT(keyword);
+
+    rt_strncpy(ulog.filter.keyword, keyword, ULOG_FILTER_KW_MAX_LEN);
+}
+
+#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
+#include <finsh.h>
+
+static void ulog_tag_lvl(uint8_t argc, char **argv)
+{
+    if (argc > 2)
+    {
+        if ((atoi(argv[2]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[2]) >= 0))
+        {
+            ulog_tag_lvl_filter_set(argv[1], atoi(argv[2]));
+        }
+        else
+        {
+            rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
+        }
+    }
+    else
+    {
+        rt_kprintf("Please input: ulog_tag_lvl <tag> <level>.\n");
+#ifndef ULOG_USING_SYSLOG
+        rt_kprintf("Assert  : 0\n");
+        rt_kprintf("Error   : 3\n");
+        rt_kprintf("Warning : 4\n");
+        rt_kprintf("Info    : 6\n");
+        rt_kprintf("Debug   : 7\n");
+#else
+        rt_kprintf("EMERG   :   1 (1 << 0)\n");
+        rt_kprintf("ALERT   :   2 (1 << 1)\n");
+        rt_kprintf("CRIT    :   4 (1 << 2)\n");
+        rt_kprintf("ERR     :   8 (1 << 3)\n");
+        rt_kprintf("WARNING :  16 (1 << 4)\n");
+        rt_kprintf("NOTICE  :  32 (1 << 5)\n");
+        rt_kprintf("INFO    :  64 (1 << 6)\n");
+        rt_kprintf("DEBUG   : 128 (1 << 7)\n");
+#endif /* ULOG_USING_SYSLOG */
+    }
+}
+MSH_CMD_EXPORT(ulog_tag_lvl, Set ulog filter level by different tag.);
+
+static void ulog_lvl(uint8_t argc, char **argv)
+{
+    if (argc > 1)
+    {
+        if ((atoi(argv[1]) <= LOG_FILTER_LVL_ALL) && (atoi(argv[1]) >= 0))
+        {
+            ulog_global_filter_lvl_set(atoi(argv[1]));
+        }
+        else
+        {
+            rt_kprintf("Please input correct level (0-%d).\n", LOG_FILTER_LVL_ALL);
+        }
+    }
+    else
+    {
+        rt_kprintf("Please input: ulog_lvl <level>.\n");
+#ifndef ULOG_USING_SYSLOG
+        rt_kprintf("Assert  : 0\n");
+        rt_kprintf("Error   : 3\n");
+        rt_kprintf("Warning : 4\n");
+        rt_kprintf("Info    : 6\n");
+        rt_kprintf("Debug   : 7\n");
+#else
+        rt_kprintf("EMERG   :   1 (1 << 0)\n");
+        rt_kprintf("ALERT   :   2 (1 << 1)\n");
+        rt_kprintf("CRIT    :   4 (1 << 2)\n");
+        rt_kprintf("ERR     :   8 (1 << 3)\n");
+        rt_kprintf("WARNING :  16 (1 << 4)\n");
+        rt_kprintf("NOTICE  :  32 (1 << 5)\n");
+        rt_kprintf("INFO    :  64 (1 << 6)\n");
+        rt_kprintf("DEBUG   : 128 (1 << 7)\n");
+#endif /* ULOG_USING_SYSLOG */
+    }
+}
+MSH_CMD_EXPORT(ulog_lvl, Set ulog global filter level.);
+
+static void ulog_tag(uint8_t argc, char **argv)
+{
+    if (argc > 1)
+    {
+        if (rt_strlen(argv[1]) <= ULOG_FILTER_TAG_MAX_LEN)
+        {
+            ulog_global_filter_tag_set(argv[1]);
+        }
+        else
+        {
+            rt_kprintf("The tag length is too long. Max is %d.\n", ULOG_FILTER_TAG_MAX_LEN);
+        }
+    }
+    else
+    {
+        ulog_global_filter_tag_set("");
+    }
+}
+MSH_CMD_EXPORT(ulog_tag, Set ulog global filter tag);
+
+static void ulog_kw(uint8_t argc, char **argv)
+{
+    if (argc > 1)
+    {
+        if (rt_strlen(argv[1]) <= ULOG_FILTER_KW_MAX_LEN)
+        {
+            ulog_global_filter_kw_set(argv[1]);
+        }
+        else
+        {
+            rt_kprintf("The keyword length is too long. Max is %d.\n", ULOG_FILTER_KW_MAX_LEN);
+        }
+    }
+    else
+    {
+        ulog_global_filter_kw_set("");
+    }
+}
+MSH_CMD_EXPORT(ulog_kw, Set ulog global filter keyword);
+#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
+#endif /* ULOG_USING_FILTER */
+
+rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color)
+{
+    rt_base_t level;
+
+    RT_ASSERT(backend);
+    RT_ASSERT(name);
+    RT_ASSERT(ulog.init_ok);
+    RT_ASSERT(backend->output);
+
+    if (backend->init)
+    {
+        backend->init(backend);
+    }
+
+    backend->support_color = support_color;
+    rt_memcpy(backend->name, name, RT_NAME_MAX);
+
+    level = rt_hw_interrupt_disable();
+    rt_slist_append(&ulog.backend_list, &backend->list);
+    rt_hw_interrupt_enable(level);
+
+    return RT_EOK;
+}
+
+rt_err_t ulog_backend_unregister(ulog_backend_t backend)
+{
+    rt_base_t level;
+
+    RT_ASSERT(backend);
+    RT_ASSERT(ulog.init_ok);
+
+    level = rt_hw_interrupt_disable();
+    rt_slist_remove(&ulog.backend_list, &backend->list);
+    rt_hw_interrupt_enable(level);
+
+    return RT_EOK;
+}
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+/**
+ * asynchronous output logs to all backends
+ *
+ * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
+ */
+void ulog_async_output(void)
+{
+    rt_rbb_blk_t log_blk;
+    ulog_frame_t log_frame;
+
+    while ((log_blk = rt_rbb_blk_get(ulog.async_rbb)) != NULL)
+    {
+        log_frame = (ulog_frame_t) log_blk->buf;
+        if (log_frame->magic == ULOG_FRAME_MAGIC)
+        {
+            /* output to all backends */
+            ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log,
+                    log_frame->log_len);
+        }
+        rt_rbb_blk_free(ulog.async_rbb, log_blk);
+    }
+}
+
+/**
+ * waiting for get asynchronous output log
+ *
+ * @param time the waiting time
+ */
+void ulog_async_waiting_log(rt_int32_t time)
+{
+    rt_sem_control(&ulog.async_notice, RT_IPC_CMD_RESET, RT_NULL);
+    rt_sem_take(&ulog.async_notice, time);
+}
+
+static void async_output_thread_entry(void *param)
+{
+    while (1)
+    {
+        ulog_async_waiting_log(RT_WAITING_FOREVER);
+        ulog_async_output();
+    }
+}
+#endif /* ULOG_USING_ASYNC_OUTPUT */
+
+/**
+ * flush all backends's log
+ */
+void ulog_flush(void)
+{
+    rt_slist_t *node;
+    ulog_backend_t backend;
+
+    if (!ulog.init_ok)
+        return;
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+    ulog_async_output();
+#endif
+
+    /* flush all backends */
+    for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
+    {
+        backend = rt_slist_entry(node, struct ulog_backend, list);
+        if (backend->flush)
+        {
+            backend->flush(backend);
+        }
+    }
+}
+
+int ulog_init(void)
+{
+    if (ulog.init_ok)
+        return 0;
+
+    rt_mutex_init(&ulog.output_locker, "ulog lock", RT_IPC_FLAG_FIFO);
+    rt_slist_init(&ulog.backend_list);
+
+#ifdef ULOG_USING_FILTER
+    rt_slist_init(&ulog.filter.tag_lvl_list);
+#endif
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+    RT_ASSERT(ULOG_ASYNC_OUTPUT_STORE_LINES >= 2);
+    /* async output ring block buffer */
+    ulog.async_rbb = rt_rbb_create(RT_ALIGN(ULOG_ASYNC_OUTPUT_BUF_SIZE, RT_ALIGN_SIZE), ULOG_ASYNC_OUTPUT_STORE_LINES);
+    if (ulog.async_rbb == NULL)
+    {
+        rt_kprintf("Error: ulog init failed! No memory for async rbb.\n");
+        rt_mutex_detach(&ulog.output_locker);
+        return -RT_ENOMEM;
+    }
+    /* async output thread */
+    ulog.async_th = rt_thread_create("ulog_async", async_output_thread_entry, &ulog, ULOG_ASYNC_OUTPUT_THREAD_STACK,
+            ULOG_ASYNC_OUTPUT_THREAD_PRIORITY, 20);
+    if (ulog.async_th == NULL)
+    {
+        rt_kprintf("Error: ulog init failed! No memory for async output thread.\n");
+        rt_mutex_detach(&ulog.output_locker);
+        rt_rbb_destroy(ulog.async_rbb);
+        return -RT_ENOMEM;
+    }
+
+    rt_sem_init(&ulog.async_notice, "ulog", 0, RT_IPC_FLAG_FIFO);
+    /* async output thread startup */
+    rt_thread_startup(ulog.async_th);
+
+#endif /* ULOG_USING_ASYNC_OUTPUT */
+
+#ifdef ULOG_USING_FILTER
+    ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
+#endif
+
+    ulog.init_ok = RT_TRUE;
+
+    return 0;
+}
+INIT_PREV_EXPORT(ulog_init);
+
+void ulog_deinit(void)
+{
+    rt_slist_t *node;
+    ulog_backend_t backend;
+
+    if (!ulog.init_ok)
+        return;
+
+    /* deinit all backends */
+    for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
+    {
+        backend = rt_slist_entry(node, struct ulog_backend, list);
+        if (backend->deinit)
+        {
+            backend->deinit(backend);
+        }
+    }
+
+#ifdef ULOG_USING_FILTER
+    /* deinit tag's level filter */
+    {
+        tag_lvl_filter_t tag_lvl;
+        for (node = rt_slist_first(&ulog.filter.tag_lvl_list); node; node = rt_slist_next(node))
+        {
+            tag_lvl = rt_slist_entry(node, struct tag_lvl_filter, list);
+            rt_free(tag_lvl);
+        }
+    }
+#endif /* ULOG_USING_FILTER */
+
+    rt_mutex_detach(&ulog.output_locker);
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+    rt_rbb_destroy(ulog.async_rbb);
+    rt_thread_delete(ulog.async_th);
+#endif
+
+    ulog.init_ok = RT_FALSE;
+}
+
+#endif /* RT_USING_ULOG */

+ 95 - 0
components/utilities/ulog/ulog.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+
+#ifndef _ULOG_H_
+#define _ULOG_H_
+
+#include <rtthread.h>
+#include "ulog_def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ULOG_VERSION_STR               "0.1.0"
+
+/*
+ * ulog init and deint
+ */
+int ulog_init(void);
+void ulog_deinit(void);
+
+/*
+ * output different level log by LOG_X API
+ *
+ * NOTE: The `LOG_TAG` and `LOG_LVL` must be defined before including the <ulog.h> when you want to use LOG_X API.
+ *
+ * #define LOG_TAG              "example"
+ * #define LOG_LVL              LOG_LVL_DBG
+ * #include <ulog.h>
+ *
+ * Then you can using LOG_X API to output log
+ *
+ * LOG_D("this is a debug log!");
+ * LOG_E("this is a error log!");
+ */
+#define LOG_E(...)                     ulog_e(LOG_TAG, __VA_ARGS__)
+#define LOG_W(...)                     ulog_w(LOG_TAG, __VA_ARGS__)
+#define LOG_I(...)                     ulog_i(LOG_TAG, __VA_ARGS__)
+#define LOG_D(...)                     ulog_d(LOG_TAG, __VA_ARGS__)
+#define LOG_RAW(...)                   ulog_raw(__VA_ARGS__)
+
+/*
+ * backend register and unregister
+ */
+rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color);
+rt_err_t ulog_backend_unregister(ulog_backend_t backend);
+
+#ifdef ULOG_USING_FILTER
+/*
+ * log filter setting
+ */
+int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level);
+rt_uint32_t ulog_tag_lvl_filter_get(const char *tag);
+void ulog_global_filter_lvl_set(rt_uint32_t level);
+void ulog_global_filter_tag_set(const char *tag);
+void ulog_global_filter_kw_set(const char *keyword);
+#endif /* ULOG_USING_FILTER */
+
+/*
+ * flush all backends's log
+ */
+void ulog_flush(void);
+
+#ifdef ULOG_USING_ASYNC_OUTPUT
+/*
+ * asynchronous output API
+ */
+void ulog_async_output(void);
+void ulog_async_waiting_log(rt_int32_t time);
+#endif
+
+/*
+ * dump the hex format data to log
+ */
+void ulog_hexdump(const char *name, rt_size_t width, rt_uint8_t *buf, rt_size_t size);
+
+/*
+ * Another log output API. This API is difficult to use than LOG_X API.
+ */
+void ulog_voutput(rt_uint32_t level, const char *tag, const char *format, va_list args);
+void ulog_output(rt_uint32_t level, const char *tag, const char *format, ...);
+void ulog_raw(const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ULOG_H_ */

+ 189 - 0
components/utilities/ulog/ulog_def.h

@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+#ifndef _ULOG_DEF_H_
+#define _ULOG_DEF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* logger level, the number is compatible for syslog */
+#define LOG_LVL_ASSERT                 0
+#define LOG_LVL_ERROR                  3
+#define LOG_LVL_WARNING                4
+#define LOG_LVL_INFO                   6
+#define LOG_LVL_DBG                    7
+
+/* the output silent level and all level for filter setting */
+#ifndef ULOG_USING_SYSLOG
+#define LOG_FILTER_LVL_SILENT         0
+#define LOG_FILTER_LVL_ALL            7
+#else
+#define LOG_FILTER_LVL_SILENT         1
+#define LOG_FILTER_LVL_ALL            255
+#endif /* ULOG_USING_SYSLOG */
+
+/* compatible for rtdbg */
+#undef LOG_D
+#undef LOG_I
+#undef LOG_W
+#undef LOG_E
+#undef LOG_RAW
+#undef DBG_ERROR
+#undef DBG_WARNING
+#undef DBG_INFO
+#undef DBG_LOG
+#define DBG_ERROR                      LOG_LVL_ERROR
+#define DBG_WARNING                    LOG_LVL_WARNING
+#define DBG_INFO                       LOG_LVL_INFO
+#define DBG_LOG                        LOG_LVL_DBG
+
+#if !defined(LOG_TAG)
+    /* compatible for rtdbg */
+    #if defined(DBG_SECTION_NAME)
+        #define LOG_TAG                DBG_SECTION_NAME
+    #else
+        #define LOG_TAG                "NO_TAG"
+    #endif
+#endif /* !defined(LOG_TAG) */
+
+#if !defined(LOG_LVL)
+    /* compatible for rtdbg */
+    #if defined(DBG_LEVEL)
+        #define LOG_LVL                    DBG_LEVEL
+    #else
+        #define LOG_LVL                    LOG_LVL_DBG
+    #endif
+#endif /* !defined(LOG_LVL) */
+
+#if (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG)
+    #define ulog_d(TAG, ...)           ulog_output(LOG_LVL_DBG, TAG, __VA_ARGS__)
+#else
+    #define ulog_d(TAG, ...)
+#endif /* (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG) */
+
+#if (LOG_LVL >= LOG_LVL_INFO) && (ULOG_OUTPUT_LVL >= LOG_LVL_INFO)
+    #define ulog_i(TAG, ...)           ulog_output(LOG_LVL_INFO, TAG, __VA_ARGS__)
+#else
+    #define ulog_i(TAG, ...)
+#endif /* (LOG_LVL >= LOG_LVL_INFO) && (ULOG_OUTPUT_LVL >= LOG_LVL_INFO) */
+
+#if (LOG_LVL >= LOG_LVL_WARNING) && (ULOG_OUTPUT_LVL >= LOG_LVL_WARNING)
+    #define ulog_w(TAG, ...)           ulog_output(LOG_LVL_WARNING, TAG, __VA_ARGS__)
+#else
+    #define ulog_w(TAG, ...)
+#endif /* (LOG_LVL >= LOG_LVL_WARNING) && (ULOG_OUTPUT_LVL >= LOG_LVL_WARNING) */
+
+#if (LOG_LVL >= LOG_LVL_ERROR) && (ULOG_OUTPUT_LVL >= LOG_LVL_ERROR)
+    #define ulog_e(TAG, ...)           ulog_output(LOG_LVL_ERROR, TAG, __VA_ARGS__)
+#else
+    #define ulog_e(TAG, ...)
+#endif /* (LOG_LVL >= LOG_LVL_ERROR) && (ULOG_OUTPUT_LVL >= LOG_LVL_ERROR) */
+
+/* assert for developer. */
+#ifdef ULOG_ASSERT_ENABLE
+    #define ULOG_ASSERT(EXPR)                                                 \
+    if (!(EXPR))                                                              \
+    {                                                                         \
+        ulog_output(LOG_LVL_ASSERT, LOG_TAG, "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \
+        ulog_flush();                                                         \
+        while (1);                                                            \
+    }
+#else
+    #define ULOG_ASSERT(EXPR)
+#endif
+
+/* ASSERT API definition */
+#if !defined(ASSERT)
+    #define ASSERT           ULOG_ASSERT
+#endif
+
+/* compatible for elog */
+#undef assert
+#undef log_e
+#undef log_w
+#undef log_i
+#undef log_d
+#undef log_v
+#undef ELOG_LVL_ASSERT
+#undef ELOG_LVL_ERROR
+#undef ELOG_LVL_WARN
+#undef ELOG_LVL_INFO
+#undef ELOG_LVL_DEBUG
+#undef ELOG_LVL_VERBOSE
+#define assert                         ASSERT
+#define log_e                          LOG_E
+#define log_w                          LOG_W
+#define log_i                          LOG_I
+#define log_d                          LOG_D
+#define log_v                          LOG_D
+#define log_raw                        LOG_RAW
+#define ELOG_LVL_ASSERT                LOG_LVL_ASSERT
+#define ELOG_LVL_ERROR                 LOG_LVL_ERROR
+#define ELOG_LVL_WARN                  LOG_LVL_WARNING
+#define ELOG_LVL_INFO                  LOG_LVL_INFO
+#define ELOG_LVL_DEBUG                 LOG_LVL_DBG
+#define ELOG_LVL_VERBOSE               LOG_LVL_DBG
+
+/* setting static output log level */
+#ifndef ULOG_OUTPUT_LVL
+#define ULOG_OUTPUT_LVL                LOG_LVL_DBG
+#endif
+
+/* buffer size for every line's log */
+#ifndef ULOG_LINE_BUF_SIZE
+#define ULOG_LINE_BUF_SIZE             128
+#endif
+
+/* output filter's tag max length */
+#ifndef ULOG_FILTER_TAG_MAX_LEN
+#define ULOG_FILTER_TAG_MAX_LEN        23
+#endif
+
+/* output filter's keyword max length */
+#ifndef ULOG_FILTER_KW_MAX_LEN
+#define ULOG_FILTER_KW_MAX_LEN         15
+#endif
+
+#ifndef ULOG_NEWLINE_SIGN
+#define ULOG_NEWLINE_SIGN              "\r\n"
+#endif
+
+#define ULOG_FRAME_MAGIC               0x10
+
+struct ulog_frame
+{
+    /* magic word is 0x10 ('lo') */
+    rt_uint32_t magic:8;
+    rt_uint32_t is_raw:1;
+    rt_uint32_t log_len:23;
+    rt_uint32_t level;
+    const char *log;
+    const char *tag;
+};
+typedef struct ulog_frame *ulog_frame_t;
+
+struct ulog_backend
+{
+    char name[RT_NAME_MAX];
+    rt_bool_t support_color;
+    void (*init)  (struct ulog_backend *backend);
+    void (*output)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, size_t len);
+    void (*flush) (struct ulog_backend *backend);
+    void (*deinit)(struct ulog_backend *backend);
+    rt_slist_t list;
+};
+typedef struct ulog_backend *ulog_backend_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ULOG_DEF_H_ */

+ 93 - 0
examples/ulog/ulog_example.c

@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-02     armink       the first version
+ */
+
+#include <stdlib.h>
+#include <rtthread.h>
+
+#ifndef ULOG_USING_SYSLOG
+#define LOG_TAG              "example"
+#define LOG_LVL              LOG_LVL_DBG
+#include <ulog.h>
+#else
+#include <syslog.h>
+#endif /* ULOG_USING_SYSLOG */
+
+void ulog_example(void)
+{
+    int count = 0;
+
+#ifdef ULOG_USING_SYSLOG
+    openlog("example1", 0, 0);
+#endif
+
+    while (count++ < 50)
+    {
+#ifndef ULOG_USING_SYSLOG
+        /* output different level log by LOG_X API */
+        LOG_D("LOG_D(%d): RT-Thread is an open source IoT operating system from China.", count);
+        LOG_I("LOG_I(%d): RT-Thread is an open source IoT operating system from China.", count);
+        LOG_W("LOG_W(%d): RT-Thread is an open source IoT operating system from China.", count);
+        LOG_E("LOG_E(%d): RT-Thread is an open source IoT operating system from China.", count);
+        ulog_d("test", "ulog_d(%d): RT-Thread is an open source IoT operating system from China.", count);
+        ulog_i("test", "ulog_i(%d): RT-Thread is an open source IoT operating system from China.", count);
+        ulog_w("test", "ulog_w(%d): RT-Thread is an open source IoT operating system from China.", count);
+        ulog_e("test", "ulog_e(%d): RT-Thread is an open source IoT operating system from China.", count);
+
+#ifdef ULOG_USING_FILTER
+        if (count == 20)
+        {
+            /* Set the global filer level is INFO. All of DEBUG log will stop output */
+            ulog_global_filter_lvl_set(LOG_LVL_INFO);
+            /* Set the test tag's level filter's level is ERROR. The DEBUG, INFO, WARNING log will stop output. */
+            ulog_tag_lvl_filter_set("test", LOG_LVL_ERROR);
+        }
+        else if (count == 30)
+        {
+            /* Set the example tag's level filter's level is LOG_FILTER_LVL_SILENT, the log enter silent mode. */
+            ulog_tag_lvl_filter_set("example", LOG_FILTER_LVL_SILENT);
+            /* Set the test tag's level filter's level is WARNING. The DEBUG, INFO log will stop output. */
+            ulog_tag_lvl_filter_set("test", LOG_LVL_WARNING);
+        }
+        else if (count == 40)
+        {
+            /* Set the test tag's level filter's level is LOG_FILTER_LVL_ALL. All level log will resume output. */
+            ulog_tag_lvl_filter_set("test", LOG_FILTER_LVL_ALL);
+            /* Set the global filer level is LOG_FILTER_LVL_ALL. All level log will resume output */
+            ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
+        }
+#endif /* ULOG_USING_FILTER */
+
+#else
+        /* output different priority log by syslog API */
+        syslog(LOG_INFO, "syslog(%d) LOG_INFO: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_DEBUG, "syslog(%d) LOG_DEBUG: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_WARNING, "syslog(%d) LOG_WARNING: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_ERR, "syslog(%d) LOG_ERR: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_INFO | LOG_MAIL, "syslog(%d) LOG_INFO | LOG_MAIL: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_DEBUG | LOG_DAEMON, "syslog(%d) LOG_DEBUG | LOG_DAEMON: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_WARNING | LOG_AUTH, "syslog(%d) LOG_WARNING | LOG_AUTH: RT-Thread is an open source IoT operating system from China.", count);
+        syslog(LOG_ERR | LOG_SYSLOG, "syslog(%d) LOG_ERR | LOG_SYSLOG: RT-Thread is an open source IoT operating system from China.", count);
+
+        if (count == 20)
+        {
+            /* Set log priority mask. Only output ERR and WARNING log. */
+            setlogmask(LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING));
+        }
+        else if (count == 40)
+        {
+            /* Set log priority mask. The log which level is less than ERROR will stop output. */
+            setlogmask(LOG_UPTO(LOG_ERR));
+        }
+#endif /* ULOG_USING_SYSLOG */
+
+        rt_thread_delay(rt_tick_from_millisecond(rand() % 1000));
+    }
+}
+MSH_CMD_EXPORT(ulog_example, run ulog example)