1@page page_component_ulog Ulog Log
2
3# Ulog Introduction
4
5**Log definition**:The log is to output the status, process and other information of the software to different media (for example: file, console, display, etc.), display and save. Provide reference for software traceability, performance analysis, system monitoring, fault warning and other functions during software debugging and maintenance. It can be said that the use of logs consumes at least 80% of the software life cycle.
6
7**The importance of the log**:For the operating system, because the complexity of the software is very large, single-step debugging is not suitable in some scenarios, the log component is almost standard part on the operating system. A sophisticated logging system can also make the debugging of the operating system more effective.
8
9**The origin of ulog**: RT-Thread has always lacked a small, useful log component, and the birth of ulog complements this short board. It will be open sourced as a basic component of RT-Thread, allowing our developers to use a simple and easy-to-use logging system to improve development efficiency.
10
11Ulog is a very simple and easy to use C/C++ log component. The first letter u stands for μ, which means micro. It can achieve the lowest **ROM<1K, RAM<0.2K** resource usage. Ulog is not only small in size, but also has very comprehensive functions. Its design concept refers to another C/C++ open source log library: EasyLogger (referred to as elog), and has made many improvements in terms of functions and performance. The main features are as follows:
12
13* The backend of the log output is diversified and can support, for example, serial port, network, file, flash memory and other backend forms.
14
15* The log output is designed to be thread-safe and supports asynchronous output mode.
16
17* The logging system is highly reliable and is still available in complex environments such as interrupted ISRs and Hardfault.
18
19* The log supports runtime/compilation time to set the output level.
20
21* The log content supports global filtering by keyword and label.
22
23* The APIs and log formats are compatible with linux syslog.
24
25* Support for dumping debug data to the log in hex format.
26
27* Compatible with `rtdbg` (RTT's early log header file) and EasyLogger's log output API.
28
29## Ulog Architecture
30
31The following figure shows the ulog log component architecture diagram:
32
33![ulog architecture](figures/ulog_framework.png)
34
35* **Front end**:This layer is the closest layer to the application, and provides users with two types of API interfaces, `syslog` and `LOG_X`, which are convenient for users to use in different scenarios.
36
37* **Core**:The main work of the middle core layer is to format and filter the logs passed by the upper layer, and then generate log frames, and finally output them to the lowest-end back-end devices through different output modules.
38
39* **Back end**:After receiving the log frames sent from the core layer, the logs are output to the registered log backend devices, such as files, consoles, log servers, and so on.
40
41## Configuration Options
42
43The path to configure ulog using menuconfig in the ENV tool is as follows:
44
45```c
46 RT-Thread Components → Utilities → Enable ulog
47```
48
49 The ulog configuration options are described below. In general, the default configuration is used:
50
51```c
52[*] Enable ulog                   /* Enable ulog */
53      The static output log level./* Select a static log output level. After the selection is completed, the log level lower than the set level (here specifically the log using the LOG_X API) will not be compiled into the ROM. */
54[ ]   Enable ISR log.             /* Enable interrupted ISR log, ie log output API can also be used in ISR */
55[*]   Enable assert check.        /* Enable assertion checks. If fter disabled, the asserted log will not be compiled into ROM */
56(128) The log's max width.        /* The maximum length of the log. Since ulog's logging API is in units of rows, this length also represents the maximum length of a row of logs. */
57[ ]   Enable async output mode.   /* Enable asynchronous log output mode. When this mode is turned on, the log will not be output to the backend immediately, but will be cached first, and then handed to the log output thread (for example: idle thread) to output. */
58      log format  --->            /* Configure the format of the log, such as time information, color information, thread information, whether to support floating point, etc. */
59[*]   Enable console backend.     /* Enable the console as a backend. After enabling, the log can be output to the console serial port. It is recommended to keep it on. */
60[ ]   Enable runtime log filter.  /* Enable the runtime log filter, which is dynamic filtering. After enabling, the log will support dynamic filtering when the system is running, by means of tags, keywords, and so on. */
61```
62
63**The configuration log format option description is as follows:**
64
65```c
66[ ] Enable float number support. It will using more thread stack.   /* Supporting floating-point variables (traditional rtdbg/rt_kprintf does not support floating-point logs) */
67    [*] Enable color log.                   /* Colored log */
68    [*] Enable time information.            /* Time information */
69    [ ]   Enable timestamp format for time. /* Including timestamp */
70    [*] Enable level information.           /* Level information */
71    [*] Enable tag information.             /* Label Information */
72    [ ] Enable thread information.          /* Thread information */
73```
74
75## Log Level
76
77The log level represents the importance of the log, from high to low in ulog, with the following log levels:
78
79| Level    | Name | Description |
80| ------------ | ---- | ----------------------- |
81| LOG_LVL_ASSERT  | assertion | Unhandled and fatal errors occurred, so that the system could not continue to run. These are assertion logs. |
82| LOG_LVL_ERROR   | error | The log that is output when a serious, **unrepairable** error occurs is an error level log. |
83| LOG_LVL_WARNING | warning | These warning logs are output when there are some less important errors with **repairability**. |
84| LOG_LVL_INFO    | information | A log of important prompt information that is viewed by the upper-level user of the module, for example, initialization success, current working status, and so on. This level of log is generally **retained** during mass production. |
85| LOG_LVL_DBG     | debug | The debug log that is viewed by the developer of this module. This level of log is generally **closed** during mass production. |
86
87The log level in ulog also has the following classification:
88
89* **Static and dynamic levels**:Classify according to whether the log can be modified during the run phase. The dynamic level that can be modified during the run phase can only be called static level in the **compilation phase**. Logs that are lower than the static level (here specifically the logs using the `LOG_X` API) will not be compiled into the ROM and will not be output or displayed. The dynamic level can control logs that their level are higher than or equal to the static level. Logs that are lower than the dynamic level are filtered out when ulog is running.
90
91* **Global level and module level**:Classification by scope. Each file (module) can also be set to a separate log level in ulog. The global level scope is larger than the module level, that is, the module level can only control module logs higher than or equal to the global level.
92
93As can be seen from the above classification, the output level of the log can be set in the following four aspects of ulog:
94
95* **Global static **log level:Configured in menuconfig, corresponding to the `ULOG_OUTPUT_LVL` macro.
96
97* **Global Dynamics** log level:Use the `void ulog_global_filter_lvl_set(rt_uint32_t level)` function to set it.
98
99* **Module static** log level:The `LOG_LVL` macro is defined in the module (file), similar to the way the log tag macro `LOG_TAG` is defined.
100
101* **Module dynamics** log level:Use the `int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)` function to set it.
102
103Their scope of action is:**Global Static**>**Global Dynamics**>**Module Static**>**Module Dynamic**.
104
105## Log Label
106
107Due to the increasing log output, in order to avoid the log being outputted indiscriminately, it is necessary to use a tag to classify each log. The definition of the label is in the form of **modular**, for example: Wi-Fi components include device driver (wifi_driver), device management (wifi_mgnt) and other modules, Wi-Fi component internal module can use `wifi.driver`, `wifi.mgnt` is used as a label to perform classified output of logs.
108
109The tag attribute of each log can also be output and displayed. At the same time, ulog can also set the output level of each tag (module) corresponding to the log. The log of the current unimportant module can be selectively closed, which not only reduces ROM resources, but also helps developers filter irrelevant logs.
110
111See the `rt-thread\examples\ulog_example.c` ulog routine file with the `LOG_TAG` macro defined at the top of the file:
112
113```c
114#define LOG_TAG     "example"     // The label corresponding to this module. When not defined, default: NO_TAG
115#define LOG_LVL     LOG_LVL_DBG   // The log output level corresponding to this module. When not defined, default: debug level
116#include <ulog.h>                 // this header file Must be under LOG_TAG and LOG_LVL
117```
118
119Note that the definition log tag must be above `#include <ulog.h>`, otherwise the default `NO_TAG` will be used (not recommended to define these macros in the header file).
120
121The scope of the log tag is the current source file, and the project source code will usually be classified according to the module. Therefore, when defining a label, you can specify the module name and sub-module name as the label name. This is not only clear and intuitive when the log output is displayed, but also facilitates subsequent dynamic adjustment of the level or filtering by label.
122
123# Log Initialization
124
125## Initialization
126
127```c
128int ulog_init(void)
129```
130
131| **Return** | **Description** |
132| :-----   | :-----                        |
133|>=0     | Succeeded              |
134|-5        | Failed, insufficient memory |
135
136This function must be called to complete ulog initialization before using ulog. This function will also be called automatically if component auto-initialization is turned on.
137
138## Deinitialization
139
140```c
141void ulog_deinit(void)
142```
143
144This deinit release resource can be executed when ulog is no longer used.
145
146# Log Output API
147
148Ulog mainly has two log output macro APIs, which are defined in the source code as follows:
149
150```c
151#define LOG_E(...)                           ulog_e(LOG_TAG, __VA_ARGS__)
152#define LOG_W(...)                           ulog_w(LOG_TAG, __VA_ARGS__)
153#define LOG_I(...)                           ulog_i(LOG_TAG, __VA_ARGS__)
154#define LOG_D(...)                           ulog_d(LOG_TAG, __VA_ARGS__)
155#define LOG_RAW(...)                         ulog_raw(__VA_ARGS__)
156#define LOG_HEX(name, width, buf, size)      ulog_hex(name, width, buf, size)
157```
158
159* The macro `LOG_X(...)`:`X` corresponds to the first letter of the different levels. The parameter `...` is the log content, and the format is the same as printf. This method is preferred because on the one hand, because its API format is simple, only one log information is entered, and the static log level filtering by module is also supported.
160
161* The macro `ulog_x(LOG_TAG, __VA_ARGS__)`:  `x ` corresponds to a different level of shorthand. The parameter `LOG_TAG` is the log label, the parameter `...` is the log content, and the format is the same as printf. This API is useful when you use different tag output logs in one file.
162
163|  **API**  |**Description**                                |
164|-------------------------|--------------------------|
165| LOG_E(...)| Error level log          |
166| LOG_W(...) | Error level log |
167| LOG_I(...) | Prompt level log |
168| LOG_D(...)| Debug level log |
169| LOG_RAW(...) | Output raw log |
170| LOG_HEX(name, width, buf, size)| Output hexadecimal format data to the log |
171
172API such as ` LOG_X` and `ulog_x` , the output are formatted logs. When you need to output logs without any format, you can use `LOG_RAW` or `ulog_raw()`. E.g:
173
174```c
175LOG_RAW("\r");
176ulog_raw("\033[2A");
177```
178
179You can use `LOG_HEX()` or `ulog_hex` to dump data into the log in hexadecimal hex format. The function parameters and descriptions are as follows:
180
181| **Parameter** | **Description**             |
182| ---- | -------------------------- |
183| tag   | Log label           |
184| width | The width (number) of a line of hex content |
185| buf   | Data content to be output |
186| size  | Data size           |
187
188The `hexdump` log is DEBUG level, supports runtime level filtering. The tag corresponding to the hexdump log supports tag filtering during runtime.
189
190Ulog also provides the assertion API: `ASSERT(expression)`. When the assertion is triggered, the system will stop running, and `ulog_flush()` will be executed internally, and all log backends will execute flush. If asynchronous mode is turned on, all logs in the buffer will also be flushed. An example of the use of assertions is as follows:
191
192```c
193void show_string(const char *str)
194{
195    ASSERT(str);
196    ...
197}
198```
199
200# ULog Usage Example
201
202## Example
203
204The following is a description of the ulog routine. Open `rt-thread\examples\ulog_example.c` and you can see that there are labels and static priorities defined at the top.
205
206```c
207#define LOG_TAG              "example"
208#define LOG_LVL              LOG_LVL_DBG
209#include <ulog.h>
210```
211
212The `LOG_X` API is used in the `void ulog_example(void)` function, which is roughly as follows:
213
214```c
215/* output different level log by LOG_X API */
216LOG_D("LOG_D(%d): RT-Thread is an open source IoT operating system from China.", count);
217LOG_I("LOG_I(%d): RT-Thread is an open source IoT operating system from China.", count);
218LOG_W("LOG_W(%d): RT-Thread is an open source IoT operating system from China.", count);
219LOG_E("LOG_E(%d): RT-Thread is an open source IoT operating system from China.", count);
220```
221
222These log output APIs support the printf format and will automatically wrap lines at the end of the log.
223
224The following will show the effect of the ulog routine on qemu:
225
226- Copy `rt-thread\examples\ulog_example.c` to the `rt-thread\bsp\qemu-vexpress-a9\applications` folder.
227- Go to the `rt-thread\bsp\qemu-vexpress-a9` directory in Env
228- After determining that the configuration of ulog has been executed before, execute the `scons` command and wait for the compilation to complete.
229- Run `qemu.bat` to open RT-Thread's qemu simulator
230- Enter the `ulog_example` command to see the results of the ulog routine. The effect is as follows.
231
232![ulog routine](figures/ulog_example.png)
233
234You can see that each log is displayed in rows, and different levels of logs have different colors. At the top of the log is the tick of the current system, with the log level and label displayed in the middle, and the specific log content at the end. These log formats and configuration instructions are also highlighted later in this article.
235
236## Used in Interrupt ISR
237
238Many times you need to output a log in the interrupt ISR, but the ISR may interrupt the thread that is doing the log output. To ensure that the interrupt log and the thread log do not interfere with each other, special handling must be performed for the interrupt condition.
239
240Ulog has integrated interrupt log function, but it is not enabled by default. Open the `Enable ISR log` option when using it. The API of the log is the same as that used in the thread, for example:
241
242```c
243#define LOG_TAG              "driver.timer"
244#define LOG_LVL              LOG_LVL_DBG
245#include <ulog.h>
246
247void Timer2_Handler(void)
248{
249    /* enter interrupt */
250    rt_interrupt_enter();
251
252    LOG_D("I'm in timer2 ISR");
253
254    /* leave interrupt */
255    rt_interrupt_leave();
256}
257
258```
259
260Here are the different strategies for interrupt logging in ulog in synchronous mode and asynchronous mode:
261
262**In synchronous mode**:If the thread is interrupted when the log is being output at this time, and there is a log to be output in the interrupt, it will be directly output to the console, and output to other backends is not supported;
263
264**In asynchronous mode**:If the above situation occurs, the log in the interrupt will be put into the buffer first, and finally sent to the log output thread for processing together with the thread log.
265
266## Set the Log Format
267
268The log format supported by ulog can be configured in menuconfig, located in `RT-Thread Components` → `Utilities` → `ulog` → `log format`. The specific configuration is as follows:
269
270![ulog format configuration](figures/ulog_menuconfig_format.png)
271
272They can be configured separately: floating-point number support (traditional rtdbg/rt_kprintf does not support floating-point logs), colored logs, time information (including timestamps), level information, tag information, thread information. Below we will **select all of these options**, save and recompile and run the ulog routine again in qemu to see the actual effect:
273
274![ulog routine (all formats)](figures/ulog_example_all_format.png)
275
276It can be seen that the time information has been changed from the tick value of the system to the timestamp information compared to the first run routine, and the thread information has also been output.
277
278## Hexdump Output Using
279
280Hexdump is also a more common function when logging output. hexdump can output a piece of data in hex format. The corresponding API is: `void ulog_hexdump(const char *tag, rt_size_t width, rt_uint8_t *buf, rt_size_t size)` , see below the specific use method and operation effect:
281
282```c
283/* Define an array of 128 bytes in length */
284uint8_t i, buf[128];
285/* Fill the array with numbers */
286for (i = 0; i < sizeof(buf); i++)
287{
288    buf[i] = i;
289}
290/* Dumps the data in the array in hex format with a width of 16 */
291ulog_hexdump("buf_dump_test", 16, buf, sizeof(buf));
292```
293
294You can copy the above code into the ulog routine, and then look at the actual running results:
295
296![ulog routine (hexdump)](figures/ulog_example_hexdump.png)
297
298It can be seen that the middle is the hexadecimal information of the buf data, and the rightmost is the character information corresponding to each data.
299
300# Log Advanced Features
301
302After understanding the introduction of the log in the previous section, the basic functions of ulog can be mastered. In order to let everyone better use ulog, this application note will focus on the advanced features of ulog and some experience and skills in log debugging. After learning these advanced uses, developers can also greatly improve the efficiency of log debugging.
303
304It also introduces the advanced mode of ulog: syslog mode, which is fully compatible with the Linux syslog from the front-end API to the log format, greatly facilitating the migration of software from Linux.
305
306## Log Backend
307
308![Ulog framework](figures/ulog_framework_backend.png)
309
310Speaking of the backend, let's review the ulog's framework. As can be seen from the above figure, ulog is a design with front and back ends separated, and there is no dependence on the front and back ends. And the backends that are supported are diversified, no matter what kind of backends, as long as they are implemented, they can be registered.
311
312Currently ulog has integrated the console backend, the traditional device that outputs `rt_kprintf` print logs. Ulog also supports the Flash backend, which seamlessly integrates with EasyFlash. See its package for details.([Click to view](https://github.com/armink-rtt-pkgs/ulog_easyflash_be))。Later ulog will also increase the implementation of backends such as file backends and network backends. Of course, if there are special needs, users can also implement the backend themselves.
313
314### Register Backend Device
315
316```c
317rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color)
318```
319
320| **Parameter** | **Description**                |
321| :-----   | :-----                        |
322|backend  | Backend device handle |
323|name| Backend device name |
324|support_color| Whether it supports color logs |
325|**return**|--                     |
326|>=0     | Succeeded                  |
327
328This function is used to register the backend device into the ulog, ensuring that the function members in the backend device structure are set before registration.
329
330### Logout Backend Device
331
332```c
333rt_err_t ulog_backend_unregister(ulog_backend_t backend);
334```
335
336| **Parameter** | **Description**                |
337| :-----   | :-----                        |
338|backend  | Backend device handle |
339|**return**|--                      |
340|>=0     | Succeeded                  |
341
342This function is used to unregister a backend device that has already been registered.
343
344### Backend Implementation and Registration Examples
345
346The console backend is taken as an example to briefly introduce the implementation method and registration method of the backend.
347
348Open the `rt-thread/components/utilities/ulog/backend/console_be.c` file and you can see the following:
349
350```c
351#include <rthw.h>
352#include <ulog.h>
353
354/* Defining console backend devices */
355static struct ulog_backend console;
356/* Console backend output function */
357void 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)
358{
359    ...
360    /* Output log to the console */
361    ...
362}
363/* Console backend initialization */
364int ulog_console_backend_init(void)
365{
366    /* Set output function */
367    console.output = ulog_console_backend_output;
368    /* Registration backend */
369    ulog_backend_register(&console, "console", RT_TRUE);
370
371    return 0;
372}
373INIT_COMPONENT_EXPORT(ulog_console_backend_init);
374```
375
376Through the above code, it can be seen that the implementation of the console backend is very simple. Here, the `output` function of the backend device is implemented, and the backend is registered in the ulog, and then the log of ulog is output to the console.
377
378If you want to implement a more complex back-end device, you need to understand the back-end device structure, as follows:
379
380```c
381struct ulog_backend
382{
383    char name[RT_NAME_MAX];
384    rt_bool_t support_color;
385    void (*init)  (struct ulog_backend *backend);
386    void (*output)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, size_t len);
387    void (*flush) (struct ulog_backend *backend);
388    void (*deinit)(struct ulog_backend *backend);
389    rt_slist_t list;
390};
391```
392
393From the perspective of this structure, the requirements for implementing the backend device are as follows:
394
395* `The name` and `support_color` properties can be passed in through the `ulog_backend_register()` function.
396
397* `output` is the back-end specific output function, and all backends must implement the interface.
398
399* `init`/`deinit` is optional, `init` is called at `register`, and `deinit` is called at `ulog_deinit`.
400
401* `flush` is also optional, and some internal output cached backends need to implement this interface. For example, some file systems with RAM cache. The flush of the backend is usually called by `ulog_flush` in the case of an exception such as assertion or hardfault.
402
403## Asynchronous Log
404
405In ulog, the default output mode is synchronous mode, and in many scenarios users may also need asynchronous mode. When the user calls the log output API, the log is cached in the buffer, and the thread dedicated to the log output takes out the log and outputs it to the back end.
406
407Asynchronous mode and synchronous mode are the same for the user, there is no difference in the use of the log API, because ulog will distinguish between the underlying processing. The difference between the two works is as follows:
408
409![ulog asynchronous VS synchronization](figures/ulog_async_vs_sync.png)
410
411The advantages and disadvantages of asynchronous mode are as follows:
412
413**Advantage**:
414
415* First, the log output will not block the current thread, and some backend output rates are low, so using the synchronous output mode may affect the timing of the current thread. The asynchronous mode does not have this problem.
416
417* Secondly, since each thread that uses the log omits the action of the backend output, the stack overhead of these threads may also be reduced, and from this perspective, the resource consumption of the entire system can also be reduced.
418
419* Interrupt logs in synchronous mode can only be output to the console backend, while in asynchronous mode interrupt logs can be output to all backends.
420
421**Disadvantage**:First, the asynchronous mode requires a log buffer. Furthermore, the output of the asynchronous log needs to be completed by a special thread, such as an idle thread or a user-defined thread, which is slightly more complicated to use. The overall sense of asynchronous mode resource occupancy will be higher than the synchronous mode.
422
423### Configuration Option
424
425Use menuconfig in the Env tool to enter the ulog configuration options:
426
427```c
428 RT-Thread Components → Utilities → Enable ulog
429```
430
431The asynchronous mode related configuration options are described as follows:
432
433```c
434[*]   Enable async output mode.                 /* Enable asynchronous mode */
435(2048)  The async output buffer size.           /* Asynchronous buffer size, default is 2048*/
436[*]     Enable async output by thread.          /* Whether to enable the asynchronous log output thread in ulog, the thread will wait for log notification when it runs, and then output the log to all backends. This option is turned on by default, and can be turned off if you want to modify it to another thread, such as an idle thread. */
437(1024)    The async output thread stack size.   /* Asynchronous output thread stack size, default is 1024 */
438(30)      The async output thread stack priority./* The priority of the asynchronous output thread, the default is 30*/
439```
440
441When using the idle thread output, the implementation is simple, just call `rt_thread_idle_sethook(ulog_async_output)` at the application layer, but there are some limitations.
442
443* The idle thread stack size needs to be adjusted based on actual backend usage.
444
445* Because thread suspend operations are not allowed inside idle threads, backends such as Flash and networking may not be available based on idle threads.
446
447### Use Example
448
449Save the asynchronous output option configuration and copy `rt-thread\examples\ulog_example.c` to the `rt-thread\bsp\qemu-vexpress-a9\applications` folder.
450
451Execute the `scons` command and wait for the compilation to complete. Run `qemu.bat` to open the qemu emulator for RT-Thread.
452Enter the `ulog_example` command to see the results of the ulog routine. The approximate effect is as follows:
453
454![ulog asynchronous routine](figures/ulog_example_async.png)
455
456If you look carefully, you can see that after the asynchronous mode is turned on, the time information of these logs that are very close in code is almost the same. However, in synchronous mode, the log is output using the user thread. Since the log output takes a certain amount of time, there is a certain interval between each log. It also fully shows that the asynchronous log output is very efficient, and it takes almost no time for the caller.
457
458## Log Dynamic Filter
459
460In the previous section, some static filtering functions have been introduced. Static filtering has its advantages such as saving resources, but in many cases, users need to dynamically adjust the filtering mode of the log while the software is running. This allows the dynamic filter function of ulog to be used. To use the dynamic filter feature, turn on the `Enable runtime log filter.` option in menuconfig, which is **turned off by default**.
461
462There are four types of dynamic filtering supported by ulog, and there are corresponding API functions and Finsh/MSH commands, which will be introduced one by one.
463
464### Filter by Module Level
465
466```c
467int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)
468```
469
470| **Parameter** | **Description**                 |
471| ------- | ------------------------------ |
472| tag      | Log label             |
473| level    | Set log level |
474|**return**|--                       |
475| >=0    | Succeeded                   |
476| -5     | Failed, not enough memory |
477
478* Command format: `ulog_tag_lvl <tag> <level>`
479
480The **module** referred to here represents a class of log code with the same tag attributes. Sometimes it is necessary to dynamically modify the log output level of a module at runtime.
481
482The parameter log `level` can take the following values:
483
484|**Level**             |**Name**            |
485| --------------------- | ---------------- |
486| LOG_LVL_ASSERT        | Assertion |
487| LOG_LVL_ERROR         | Error      |
488| LOG_LVL_WARNING       | Warning     |
489| LOG_LVL_INFO          | Information |
490| LOG_LVL_DBG           | Debug   |
491| LOG_FILTER_LVL_SILENT | Silent (stop output) |
492| LOG_FILTER_LVL_ALL    | All          |
493
494An example of a function call and command is as follows:
495
496| Function | Function Call | Execute an order |
497| ---------------- | ------------------------------ | ------------------ |
498| Close all logs of `wifi` module | `ulog_tag_lvl_filter_set("wifi", LOG_FILTER_LVL_SILENT);` | `ulog_tag_lvl wifi 0` |
499| Open all logs of `wifi` module | `ulog_tag_lvl_filter_set("wifi", LOG_FILTER_LVL_ALL);`    | `ulog_tag_lvl wifi 7` |
500| Set the `wifi` module log level to warning | `ulog_tag_lvl_filter_set("wifi", LOG_LVL_WARNING);`       | `ulog_tag_lvl wifi 4` |
501
502### Global Filtering by Label
503
504```c
505void ulog_global_filter_tag_set(const char *tag)
506```
507
508| **Parameter** | **Description** |
509| :--- | :------------- |
510| tag  | Set filter label |
511
512* The command format: `ulog_tag [tag]`, when the tag is empty, the label filtering is canceled.
513
514This filtering method can perform label filtering on all logs, and only the **log containing the label information** is allowed to output.
515
516For example: there are 3 kinds of tags for `wifi.driver`, `wifi.mgnt`, `audio.driver`. When setting the filter tag to `wifi`, only the tags are `wifi.driver` and `wifi.mgnt. The log of ` will be output. Similarly, when the filter tag is set to `driver`, only logs with tags `wifi.driver` and `audio.driver` will be output. Examples of function calls and commands for common functions are as follows:
517
518| Function | Function Call | Execute an Order |
519| -------------| -------------------- | ---------- |
520| Set the filter tag to `wifi` | `ulog_global_filter_tag_set("wifi");`   | `ulog_tag wifi`   |
521| Set the filter tag to `driver` | `ulog_global_filter_tag_set("driver");` | `ulog_tag driver` |
522| Cancel label filtering | `ulog_global_filter_tag_set("");` | `ulog_tag`    |
523
524### Global Filtering by Level
525
526```c
527void ulog_global_filter_lvl_set(rt_uint32_t level)
528```
529
530| **Parameter** | **Description**    |
531| ---- | -------------------|
532| level | Set log level |
533
534* Command format: `ulog_lvl <level>` , level Refer to the following table:
535
536| **Value** | **Description** |
537| :------------ | :--------------- |
538| 0  | assertion |
539| 3  | error |
540| 4  | warning |
541| 6  | information |
542| 7  | debug |
543
544After setting the global filter level by function or command, the log below **setting level** will stop output. Examples of function calls and commands for common functions are as follows:
545
546| Function | Function Call | Execute an order |
547| ----------| ------------------------------ | ------- |
548| Close all logs | `ulog_global_filter_lvl_set(LOG_FILTER_LVL_SILENT);` | `ulog_lvl 0` |
549| Open all logs | `ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);`    | `ulog_lvl 7` |
550| Set the log level to warning | `ulog_global_filter_lvl_set(LOG_LVL_WARNING);`       | `ulog_lvl 4` |
551
552### Global Filtering by Keyword
553
554```c
555void ulog_global_filter_kw_set(const char *keyword)
556```
557
558| **Parameter** | **Description**  |
559| :------ | :--------------- |
560| keyword | Set filter keywords |
561
562* The command format: `ulog_kw [keyword]`, when the keyword is empty, the keyword filtering is canceled.
563
564This filtering method can filter all the logs by keyword, and the log **containing the keyword information** is allowed to output. Examples of function calls and commands for common functions are as follows:
565
566| Function | Function Call | Execute an order |
567| -------------- | ------------------- | --------- |
568| Set the filter keyword to `wifi` | `ulog_global_filter_kw_set("wifi");` | `ulog_kw wifi` |
569| Clear filter keywords | `ulog_global_filter_kw_set("");`     | `ulog_kw`      |
570
571### View Filter Information
572
573After setting the filter parameters, if you want to view the current filter information, you can enter the `ulog_filter` command. The approximate effect is as follows:
574
575```c
576msh />ulog_filter
577--------------------------------------
578ulog global filter:
579level   : Debug
580tag     : NULL
581keyword : NULL
582--------------------------------------
583ulog tag's level filter:
584wifi                   : Warning
585audio.driver           : Error
586msh />
587```
588
589> Filter parameters are also supported storing in Flash and also support boot autoload configuration. If you need this feature, please see the instructions for the **ulog_easyflash** package.([Click to check](https://github.com/armink-rtt-pkgs/ulog_easyflash_be)590
591### Use Example
592
593Still executing in qemu BSP, first open dynamic filter in menuconfig, then save the configuration and compile and run the routine. After the log output is about **20** times, the corresponding filter code in ulog_example.c will be executed:
594
595```c
596if (count == 20)
597{
598    /* Set the global filer level is INFO. All of DEBUG log will stop output */
599    ulog_global_filter_lvl_set(LOG_LVL_INFO);
600    /* Set the test tag's level filter's level is ERROR. The DEBUG, INFO, WARNING log will stop output. */
601    ulog_tag_lvl_filter_set("test", LOG_LVL_ERROR);
602}
603...
604```
605
606At this point, the global filter level is set to the INFO level, so you can no longer see logs lower than the INFO level. At the same time, the log output level of the `test` tag is set to ERROR, and the log lower than ERROR in the `test` tag is also stopped. In each log, there is a count value of the current log output count. The effect of the comparison is as follows:
607
608![ulog filter routine 20](figures/ulog_example_filter20.png)
609
610After the log output is about **30** times, the following filter code corresponding to ulog_example.c is executed:
611
612```c
613...
614else if (count == 30)
615{
616    /* Set the example tag's level filter's level is LOG_FILTER_LVL_SILENT, the log enter silent mode. */
617    ulog_tag_lvl_filter_set("example", LOG_FILTER_LVL_SILENT);
618    /* Set the test tag's level filter's level is WARNING. The DEBUG, INFO log will stop output. */
619    ulog_tag_lvl_filter_set("test", LOG_LVL_WARNING);
620}
621...
622```
623
624At this point, the filter of the `example` module has been added, and all the logs of this module are stopped, so the module log will not be visible next. At the same time, reduce the log output level of the `test` tag to WARING. At this point, you can only see the WARING and ERROR level logs of the `test` tag. The effect is as follows:
625
626![ulog filter routine 30](figures/ulog_example_filter30.png)
627
628After the log output is about **40** times, the following filter code corresponding to ulog_example.c is executed:
629
630```c
631...
632else if (count == 40)
633{
634    /* Set the test tag's level filter's level is LOG_FILTER_LVL_ALL. All level log will resume output. */
635    ulog_tag_lvl_filter_set("test", LOG_FILTER_LVL_ALL);
636    /* Set the global filer level is LOG_FILTER_LVL_ALL. All level log will resume output */
637    ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL);
638}
639```
640
641At this time, the log output level of the `test` module is adjusted to `LOG_FILTER_LVL_ALL`, that is, the log of any level of the module is no longer filtered. At the same time, the global filter level is set to `LOG_FILTER_LVL_ALL`, so all the logs of the `test` module will resume output. The effect is as follows:
642
643![ulog filter routine 40](figures/ulog_example_filter40.png)
644
645## Usage when the System is Abnormal
646
647Since the asynchronous mode of ulog has a caching mechanism, the registered backend may also have a cache inside. If there are error conditions such as hardfault and assertion in the system, but there are still logs in the cache that are not output, which may cause the log to be lost. It is impossible to find the cause of the exception.
648
649For this scenario, ulog provides a unified log flush function: `void ulog_flush(void)`. When an exception occurs, when the exception information log is output, the function is called at the same time to ensure that the remaining logs in the cache can also be output to the back end.
650
651The following is an example of RT-Thread assertion and CmBacktrace:
652
653### Assertion
654
655RT-Thread assertions support assertion callbacks. We define an assertion hook function similar to the following, and then set it to the system via the `rt_assert_set_hook(rtt_user_assert_hook);` function.
656
657```c
658static void rtt_user_assert_hook(const char* ex, const char* func, rt_size_t line)
659{
660    rt_enter_critical();
661
662    ulog_output(LOG_LVL_ASSERT, "rtt", RT_TRUE, "(%s) has assert failed at %s:%ld.", ex, func, line);
663    /* flush all log */
664    ulog_flush();
665    while(1);
666}
667```
668
669### CmBacktrace
670
671CmBacktrace is a fault diagnosis library for ARM Cortex-M series MCUs. It also has a corresponding RT-Thread package, and the latest version of the package has been adapted for ulog. The adaptation code is located in `cmb_cfg.h` :
672
673```c
674...
675/* print line, must config by user */
676#include <rtthread.h>
677#ifndef RT_USING_ULOG
678#define cmb_println(...)               rt_kprintf(__VA_ARGS__);rt_kprintf("\r\n")
679#else
680#include <ulog.h>
681#define cmb_println(...)               ulog_e("cmb", __VA_ARGS__);ulog_flush()
682#endif /* RT_USING_ULOG */
683...
684```
685
686It can be seen that when ulog is enabled, each log output of CmBacktrace will use the error level, and `ulog_flush` will be executed at the same time, and the user does not need to make any modifications.
687
688## Syslog Mode
689
690On Unix-like operating systems, syslog is widely used in system logs. The common backends of syslog are files and networks. The syslog logs can be recorded in local files or sent over the network to the server that receives the syslog.
691
692Ulog provides support for the syslog mode, not only the front-end API is exactly the same as the syslog API, but the log format is also RFC compliant. However, it should be noted that after the syslog mode is enabled, the log output format of the entire ulog will be in syslog format regardless of which log output API is used.
693
694To use the syslog configuration you need to enable the `Enable syslog format log and API.` option.
695
696### Log Format
697
698![ulog syslog format](figures/ulog_syslog_format.png)
699
700As shown in the figure above, the ulog syslog log format is divided into the following four parts:
701
702| Format | **Description**  |
703| ---- | --------------------- |
704| PRI | The PRI part consists of a number enclosed in angle brackets. This number contains the Facility and Severity information, which is made by Facility multiplying 8 and then adding Severity. Facility and Severity are passed in by the syslog function. For details, see syslog.h. |
705| Header | The Header part is mainly a timestamp indicating the time of the current log; |
706| TAG | The current log label can be passed in via the `openlog` function. If not specified, `rtt` will be used as the default label. |
707| Content | The specific content of the log |
708
709### Instruction
710
711The syslog option needs to be enabled in menuconfig before use. The main commonly used APIs are:
712
713* Open syslog:`void openlog(const char *ident, int option, int facility)`
714
715* Output syslog log:`void syslog(int priority, const char *format, ...)`
716
717> Hint: Calling `openlog` is optional. If you do not call `openlog`, the openlog is automatically called when syslog is called for the first time.
718
719
720syslog() is very simple to use. The input format is the same as the printf function. There are also syslog routines in ulog_example.c that work as follows in qemu:
721
722![ulog syslog routine](figures/ulog_example_syslog.png)
723
724## Migrate from *rt_dbg.h* or elog to ulog
725
726If the two types of log components were used in the project before, when ulog is to be used, it will involve how to make the previous code also support ulog. The following will focus on the migration process.
727
728### Migrate from rt_dbg.h
729
730Currently rtdbg has completed **seamless docking** ulog. After ulog is turned on, the code of rtdbg in the old project can be used to complete the log output without any modification.
731
732### Migrate from Elog(EasyLogger)
733
734If you are not sure that a source code file is running on a target platform that will use ulog, then it is recommended to add the following changes to the file:
735
736```c
737#ifdef RT_USING_ULOG
738#include <ulog.h>
739#else
740#include <elog.h>
741#endif /* RT_USING_ULOG */
742```
743
744If you explicitly only use the ulog component, then simply change the header file reference from `elog.h` to `ulog .h`, and no other code needs to be changed.
745
746## Log Usage Tip
747
748With the logging tool, if it is used improperly, it will also cause the log to be abused, and the log information cannot be highlighted. Here we will focus on sharing some tips on the use of the log component to make the log information more intuitive. The main concerns are:
749
750### Rational Use of Label Classification
751
752Reasonably use the label function. For each module code before the use of the log, first clear the module, sub-module name. This also allows the logs to be categorized at the very beginning and ready for later log filtering.
753
754### Rational Use of Log Levels
755
756When you first use the log library, you will often encounter warnings and errors. The logs cannot be distinguished. The information cannot be distinguished from the debug logs, which makes the log level selection inappropriate. Some important logs may not be visible, and unimportant logs are full of problems. Therefore, be sure to read the log level section carefully before using it. For each level, there are clear standards.
757
758### Avoid Repetitive and Redundant Logs
759
760In some cases, repeated or cyclic execution of code occurs, and the same, similar log problems are output multiple times. Such a log not only takes up a lot of system resources, but also affects the developer's positioning of the problem. Therefore, in this case, it is recommended to add special processing for repetitive logs, such as: let the upper layer output some business-related logs, the bottom layer only returns the specific result status; the same log at the same time point, can increase to deal with the re-processing, only output once and so on when the error state has not changed.
761
762### Open More Log Formats
763
764The timestamp and thread information are not turned on in the default log format of ulog. These two log messages are useful on RTOS. They can help developers to understand the running time and time difference of each log, and clearly see which thread is executing the current code. So if conditions permit, it is still recommended to open.
765
766### Close Unimportant Logs
767
768Ulog provides log switch and filtering functions in various dimensions, which can completely control the refinement. Therefore, if you debug a function module, you can properly close the log output of other unrelated modules, so that you can focus on the current debugging on the module.
769
770# Common Problems
771
772## Q: The log code has been executed, but there is no output.
773
774 **A:** Refer to the Log Levels section for the log level classification and check the log filtering parameters. There is also the possibility of accidentally closing the console backend and re-opening `Enable console backend`.
775
776## Q: After ulog is turned on, the system crashes, for example: thread stack overflow.
777
778 **A:** Ulog will occupy more part of the thread stack space than the previous rtdbg or `rt_kprintf` printout function. If floating-point printing support is enabled, it is recommended because it uses the internal memory of libc with a large amount of `vsnprintf`. Reserve more than 250 bytes. If the timestamp function is turned on, the stack recommends a reserve of 100 bytes.
779
780## Q: The end of the log content is missing.
781
782 **A:** This is because the log content exceeds the maximum width of the set log. Check the `The log's max width` option and increase it to the appropriate size.
783
784## Q: After turning on the timestamp, why can't I see the millisecond time?
785
786 **A:** This is because ulog currently only supports millisecond timestamps when the software emulation RTC state is turned on. To display, just turn on the RT-Thread software to simulate the RTC function.
787
788## Q: Define LOG_TAG and LOG_LVL before each include ulog header file, can it be simplified?
789
790**A:** If `LOG_TAG` is not defined, the `NO_TAG` tag will be used by default, so the output log will be easily misunderstood. Therefore the tag macro is not recommended to be omitted.
791
792If `LOG_LVL` is not defined, the debug level is used by default. If the module is in the development stage, this process can be omitted, but if the module code is stable, it is recommended to define the macro and modify the level to information level.
793
794## Q: Warning when running:Warning: There is no enough buffer for saving async log, please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option。
795
796**A:** When this prompt is encountered, it indicates that the buffer in the asynchronous mode has overflowed, which will cause some of the log to be lost. Increasing the ULOG_ASYNC_OUTPUT_BUF_SIZE option can solve the problem.
797
798## Q: Compile time prompt:The idle thread stack size must more than 384 when using async output by idle (ULOG_ASYNC_OUTPUT_BY_IDLE)。
799
800**A:** When using an idle thread as the output thread, the stack size of the idle thread needs to be increased, depending on the specific backend device. For example, when the console being a backend, the idle thread must be at least 384 bytes.
801