summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/utilities/Kconfig180
-rw-r--r--components/utilities/ulog/SConscript16
-rw-r--r--components/utilities/ulog/backend/console_be.c56
-rw-r--r--components/utilities/ulog/syslog/syslog.c260
-rw-r--r--components/utilities/ulog/syslog/syslog.h100
-rw-r--r--components/utilities/ulog/ulog.c1152
-rw-r--r--components/utilities/ulog/ulog.h95
-rw-r--r--components/utilities/ulog/ulog_def.h189
-rw-r--r--examples/ulog/ulog_example.c93
9 files changed, 2141 insertions, 0 deletions
diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig
index 09ab2cfe8e..45e0791c9e 100644
--- a/components/utilities/Kconfig
+++ b/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
diff --git a/components/utilities/ulog/SConscript b/components/utilities/ulog/SConscript
new file mode 100644
index 0000000000..03ea1b5528
--- /dev/null
+++ b/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')
diff --git a/components/utilities/ulog/backend/console_be.c b/components/utilities/ulog/backend/console_be.c
new file mode 100644
index 0000000000..b2ed3d565d
--- /dev/null
+++ b/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 */
diff --git a/components/utilities/ulog/syslog/syslog.c b/components/utilities/ulog/syslog/syslog.c
new file mode 100644
index 0000000000..0df99c77f2
--- /dev/null
+++ b/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 */
diff --git a/components/utilities/ulog/syslog/syslog.h b/components/utilities/ulog/syslog/syslog.h
new file mode 100644
index 0000000000..43eec7125f
--- /dev/null
+++ b/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_ */
diff --git a/components/utilities/ulog/ulog.c b/components/utilities/ulog/ulog.c
new file mode 100644
index 0000000000..2f950fe0d9
--- /dev/null
+++ b/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 */
diff --git a/components/utilities/ulog/ulog.h b/components/utilities/ulog/ulog.h
new file mode 100644
index 0000000000..85b76c9cdd
--- /dev/null
+++ b/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_ */
diff --git a/components/utilities/ulog/ulog_def.h b/components/utilities/ulog/ulog_def.h
new file mode 100644
index 0000000000..64962fd5c4
--- /dev/null
+++ b/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_ */
diff --git a/examples/ulog/ulog_example.c b/examples/ulog/ulog_example.c
new file mode 100644
index 0000000000..88572f5152
--- /dev/null
+++ b/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)