摘要前端
本應用筆記介紹了 RT-Thread ulog 組件的基本知識和 ulog 的基本使用方法,幫助開發者更好地上手、入門 RT-Thread ulog 組件。更多 ulog 組件的高級用法,詳見《RT-Thread ulog 日誌組件應用筆記 - 進階篇》。
linux
一、本文的目的和結構
本文的目的和背景
日誌的定義 :日誌是將軟件運行的狀態、過程等信息,輸出到不一樣的介質中(例如:文件、控制檯、顯示屏等),並進行顯示和保存。爲軟件調試、維護過程當中的問題追溯、性能分析、系統監控、故障預警等功能,提供參考依據。能夠說,日誌的使用,幾乎佔用的軟件生命週期的至少 80% 的時間。web
日誌的重要性 :對於操做系統而言,因爲其軟件的複雜度很是大,單步調試在一些場景下並不適合,因此日誌組件在操做系統上幾乎都是標配。完善的日誌系統也能讓操做系統的調試事半功倍。express
ulog 的起源 : RT-Thread 一直缺乏小巧、實用的日誌組件,而 ulog 的誕生補全了這塊的短板。它將做爲 RT-Thread 的基礎組件被開源出來,讓咱們的開發者也能用上簡潔易用的日誌系統,提升開發效率。後端
本文的結構
本應用筆記將從如下幾個方面來介紹 RT-Thread ulog 組件:數組
ulog 組件簡介、框架總覽緩存
ulog 組件的配置安全
ulog 組件基本功能的使用服務器
二、問題闡述
本應用筆記將圍繞下面幾個問題來介紹 RT-Thread ulog 組件。微信
ulog 組件的主要功能有哪些?
經常使用的日誌接口有哪些?
如何使用 ulog?
想要解決這些問題,首先須要認識 RT-Thread ulog 組件基本功能,而後熟悉經常使用的日誌 API,最後將在 qemu 平臺上演示 ulog 的使用方法。
三、問題的解決
ulog 簡介
ulog 是一個很是簡潔、易用的 C/C++ 日誌組件,第一個字母 u 表明 μ,即微型的意思。它能作到最低 ROM<1K, RAM<0.2K 的資源佔用。ulog 不只有小巧體積,一樣也有很是全面的功能,其設計理念參考的是另一款 C/C++ 開源日誌庫:EasyLogger(簡稱 elog),並在功能和性能等方面作了很是多的改進。主要特性以下:
日誌輸出的 後端多樣化 ,可支持例如:串口、網絡,文件、閃存等後端形式;
日誌輸出被設計爲 線程安全 的方式,並支持 異步輸出 模式;
日誌系統 高可靠,在中斷 ISR 、Hardfault 等複雜環境下依舊可用;
支持 運行期/編譯期 開關控制全局的日誌輸出級別;
各模塊的日誌支持 運行期/編譯期 設置輸出級別;
日誌內容支持按 關鍵詞及標籤 方式進行全局過濾;
API 和日誌格式可兼容 linux syslog ;
支持以 hex 格式 dump 調試數據到日誌中;
兼容 rtdbg (RTT 早期的日誌頭文件)及 EasyLogger 的日誌輸出 API。
ulog 框架總覽
上圖爲 ulog 的內部框架圖,因而可知:
前端 :該層做爲離應用最近的一層,給用戶提供了 syslog 及 LOG_X 兩類 API 接口,方便用戶在不一樣的場景中使用;
核心 :中間核心層的主要工做是將上層傳遞過來的日誌,按照不一樣的配置要求進行格式化與過濾而後生成日誌幀,最終經過不一樣的輸出模塊,輸出到最底層的後端設備上;
後端 :接收到核心層發來的日誌幀後,將日誌輸出到已經註冊的日誌後端設備上,例如:文件、控制檯、日誌服務器等等。
配置說明
下載 RT-Thread 源碼,使用 env 工具進入 rt-thread\bsp\qemu-vexpress-a9
文件夾,輸入 menuconfig
打開配置菜單,在 RT-Thread Components
→ Utilities
下能夠看到 ulog 的配置項,將其使能能夠看到以下配置界面:
每一個選項配置說明以下:
The static output log level. (Debug)
:選擇靜態的日誌輸出級別。選擇完成後,比設定級別低的日誌(這裏特指使用 LOG_X API 的日誌)將不會被編譯到 ROM 中。Enable ISR log
:使能中斷 ISR日誌,即在 ISR 中也可使用日誌輸出 API 。Enable assert check
:使能斷言檢查。關閉後,斷言的日誌將不會被編譯到 ROM 中。The log's max width
:日誌的最大長度。因爲 ulog 的日誌 API 按行做爲單位,因此這個長度也表明一行日誌的最大長度。Enable async output mode
:使能異步日誌輸出模式。開啓這個模式後,日誌不會馬上輸出到後端,而是先緩存起來,而後交給日誌輸出線程(例如:idle 線程)去輸出。該模式的好處有不少,將在 《RT-Thread ulog 日誌組件應用筆記 - 進階篇》 中詳細介紹。log format
:配置日誌的格式,例如:時間信息,顏色信息,線程信息,是否支持浮點等等。Enable console backend
:使能控制檯做爲後端。使能後日志能夠輸出到控制檯串口上。建議保持開啓。Enable runtime log filter
:使能運行時的日誌過濾器,即動態過濾。使能後,日誌將支持按標籤、關鍵詞等方式,在系統運行時進行動態過濾。Enable syslog format log and API
:使能 linux syslog API 及對應的日誌格式。
使用默認配置便可,保存並退出 menuconfig 。
日誌標籤
標籤(tag)是一種常見的分類方式,ulog 也給每條 log 賦予了標籤的屬性,便於分類管理。
使用標籤保證日誌模塊化
因爲日誌輸出量的不斷增大,爲了不日誌被雜亂無章的輸出出來,就須要使用標籤給每條日誌進行分類。標籤的定義是按照 模塊化 的方式,例如:Wi-Fi 組件包括設備驅動(wifi_driver)、設備管理(wifi_mgnt)等模塊,則 Wi-Fi 組件內部模塊可使用 wifi.driver
、wifi.mgnt
等做爲標籤,進行日誌的分類輸出。
每條日誌的標籤屬性也能夠被輸出並顯示出來,同時 ulog 還能夠設置每一個標籤(模塊)對應日誌的輸出級別,當前不重要模塊的日誌能夠選擇性關閉,不只下降 ROM 資源,還能幫助開發者過濾無關日誌。
各個標籤(模塊)對應日誌的輸出級別也支持在運行時動態調整,詳見《RT-Thread ulog 日誌組件應用筆記 - 進階篇》。
標籤的定義方法
參見 rt-thread\examples\ulog_example.c
ulog 例程文件,在文件頂部有定義 LOG_TAG
宏:
1#define LOG_TAG "example" //該模塊對應的標籤。不定義時,默認:NO_TAG
2#define LOG_LVL LOG_LVL_DBG //該模塊對應的日誌輸出級別。不定義時,默認:調試級別
3#include <ulog.h> //必須在 LOG_TAG 與 LOG_LVL 下面
須要注意的,定義日誌標籤時,必須 位於 #include <ulog.h>
的上方,不然會使用默認的 NO_TAG
(不推薦定義在頭文件中定義這些宏)。
日誌標籤的做用域是當前源碼文件,項目源代碼一般也會按照模塊進行文件分類。因此在定義標籤時,能夠指定模塊名、子模塊名做爲標籤名稱,這樣不只在日誌輸出顯示時清晰直觀,也能方便後續按標籤方式動態調整級別或過濾。
日誌級別
日誌級別表明了日誌的重要性,在 ulog 中 由高到低 ,有以下幾個日誌級別
級別 | 名稱 | 描述 |
---|---|---|
LOG_LVL_ASSERT | 斷言 | 發生沒法處理、致命性的的錯誤,以致於系統沒法繼續運行的斷言日誌 |
LOG_LVL_ERROR | 錯誤 | 發生嚴重的、不可修復 的錯誤時輸出的日誌屬於錯誤級別日誌 |
LOG_LVL_WARNING | 警告 | 出現一些不過重要的、具備 可修復性 的錯誤時,會輸出這些警告日誌 |
LOG_LVL_INFO | 信息 | 給本模塊上層使用人員查看的重要提示信息日誌,例如:初始化成功,當前工做狀態等。該級別日誌通常在量產時依舊 保留 |
LOG_LVL_DBG | 調試 | 給本模塊開發人員查看的調試日誌,該級別日誌通常在量產時 關閉 |
設定級別的分類
在 ulog 中,可分爲以下幾類日誌級別
靜態級別與動態級別:是按照日誌是否能夠在 運行階段修改 進行分類,可在運行階段修改的稱之爲動態級別,只能在 編譯階段 修改的稱之爲靜態級別。比靜態級別低的日誌(這裏特指使用 LOG_X API 的日誌)將不會被編譯到 ROM 中,最終也不會輸出、顯示出來。而動態級別能夠管控的是高於或等於靜態級別的日誌。在 ulog 運行時,比動態級別低的日誌會被過濾掉。
全局級別與模塊級別:是按照 做用域 進行的分類。在 ulog 中每一個文件(模塊)也能夠設定獨立的日誌級別。全局級別做用域大於模塊級別,也就是模塊級別只能管控那些高於或等於全局級別的模塊日誌。
綜合上面分類能夠看出,在 ulog 能夠經過如下 4 個方面來設定日誌的輸出級別
全局靜態 日誌級別:在 menuconfig 中配置,對應
ULOG_OUTPUT_LVL
宏全局動態 日誌級別:使用
void ulog_global_filter_lvl_set(rt_uint32_t level)
函數來設定模塊靜態 日誌級別:在模塊(文件)內定義
LOG_LVL
宏,與日誌標籤宏LOG_TAG
定義方式相似模塊動態 日誌級別:使用
int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)
函數來設定
它們的做用範圍關係以下:
全局靜態 > 全局動態 > 模塊靜態 > 模塊動態
日誌輸出 API 的使用
ulog 主要有兩種日誌輸出 API
LOG_X("msg")
宏 API :X
對應的是不一樣級別的第一個字母大寫,API 爲LOG_D
、LOG_E
等。這種方式是首選,一方面由於其 API 格式簡單,入參只有一個即日誌信息,再者還支持按模塊靜態日誌級別過濾。ulog_x("tag", "msg")
宏 API:x
對應的是不一樣級別的簡寫,這個 API 適用於在一個文件中使用不一樣 tag 輸出日誌的狀況。
日誌輸出 API 的使用方法
下面將以 ulog 例程進行介紹,打開 rt-thread\examples\ulog_example.c
能夠看到,頂部有定義該文件的標籤及靜態優先級
1#define LOG_TAG "example"
2#define LOG_LVL LOG_LVL_DBG
3#include <ulog.h>
在 void ulog_example(void)
函數中有使用 LOG_X
API ,大體以下:
1/* output different level log by LOG_X API */
2LOG_D("LOG_D(%d): RT-Thread is an open source IoT operating system from China.", count);
3LOG_I("LOG_I(%d): RT-Thread is an open source IoT operating system from China.", count);
4LOG_W("LOG_W(%d): RT-Thread is an open source IoT operating system from China.", count);
5LOG_E("LOG_E(%d): RT-Thread is an open source IoT operating system from China.", count);
這些日誌輸出 API 均支持 printf 格式,而且會在日誌末尾自動換行。
下面將在 qemu 上展現下 ulog 例程的運行效果:
將
rt-thread\examples\ulog_example.c
拷貝至rt-thread\bsp\qemu-vexpress-a9\applications
文件夾下在 env 中進入
rt-thread\bsp\qemu-vexpress-a9
目錄肯定以前已執行過 ulog 的配置後,執行
scons
命令並等待編譯完成運行
qemu.bat
來打開 RT-Thread 的 qemu 模擬器輸入
ulog_example
命令,便可看到 ulog 例程運行結果,大體效果以下圖
能夠看到每條日誌都是按行顯示,不一樣級別日誌也有着不一樣的顏色。在日誌最前面有當前系統的 tick ,中間有顯示日誌級別及標籤,最後面是具體的日誌內容。在本文後面也會重點介紹這些日誌格式及配置說明。
輸出 raw 日誌
LOG_X
及 ulog_x
這類 API 輸出都是帶格式日誌,有些時候須要輸出不帶任何格式的日誌時,可使用LOG_RAW
或 void ulog_raw(const char *format, ...)
函數。例如:
1LOG_RAW("\r");
2ulog_raw("\033[2A");
在中斷 ISR 中使用
不少時候須要在中斷 ISR 中輸出日誌,可是中斷 ISR 可能會打斷正在進行日誌輸出的線程。要保證中斷日誌與線程日誌互不干涉,就得針對於中斷狀況進行特殊處理。
ulog 已集成中斷日誌的功能,可是默認沒有開啓,使用時打開 Enable ISR log
選項便可,日誌的 API 與線程中使用的方式一致,例如:
1#define LOG_TAG "driver.timer"
2#define LOG_LVL LOG_LVL_DBG
3#include <ulog.h>
4
5void Timer2_Handler(void)
6{
7 /* enter interrupt */
8 rt_interrupt_enter();
9
10 LOG_D("I'm in timer2 ISR");
11
12 /* leave interrupt */
13 rt_interrupt_leave();
14}
這裏說明下中斷日誌在 ulog 處於同步模式與異步模式下的不一樣策略:
同步模式下 :若是線程此時正在輸出日誌時來了中斷,此時若是中斷裏也有日誌要輸出,會直接輸出到控制檯上,不支持輸出到其餘後端;
異步模式下 :若是發生上面的狀況,中斷裏的日誌會先放入緩衝區中,最終和線程日誌一塊兒交給日誌輸出線程來處理。
斷言
ulog 也提供裏斷言 API :ASSERT(表達式)
,當斷言觸發時,系統會中止運行,內部也會執行 ulog_flush()
,全部日誌後端將執行 flush 。若是開啓了異步模式,緩衝區中全部的日誌也將被 flush 。斷言的使用示例以下:
1void show_string(const char *str)
2{
3 ASSERT(str);
4 ...
5}
設置日誌格式
ulog 支持的日誌格式能夠在 menuconfig 中配置,位於RT-Thread Components
→ Utilities
→ ulog
→ log format
,具體配置以下:
能夠看出,相比第一次運行例程,時間信息已經由系統的 tick 數值變爲時間戳信息,而且線程信息也已被輸出出來。
hexdump
hexdump 也是日誌輸出時較爲經常使用的功能,經過 hexdump 能夠將一段數據以 hex 格式輸出出來,對應的 API 爲:void ulog_hexdump(const char *tag, rt_size_t width, rt_uint8_t *buf, rt_size_t size)
,下面看下具體的使用方法及運行效果:
1/* 定義一個 128 個字節長度的數組 */
2uint8_t i, buf[128];
3/* 在數組內填充上數字 */
4for (i = 0; i < sizeof(buf); i++)
5{
6 buf[i] = i;
7}
8/* 以 hex 格式 dump 數組內的數據,寬度爲 16 */
9ulog_hexdump("buf_dump_test", 16, buf, sizeof(buf));
能夠將上面的代碼拷貝到 ulog 例程中運行,而後再看下實際運行結果:
能夠看出,中部爲 buf 數據的 16 進制 hex 信息,最右側爲各個數據對應的字符信息。
至此,關於 ulog 的入門基礎已經介紹完了,想要了解更多關於 ulog 的進階使用,能夠繼續查看 《RT-Thread ulog 日誌組件應用筆記 - 進階篇》
四、常見問題
一、日誌代碼已執行,可是無輸出
參考 日誌級別 章節,瞭解日誌級別分類,並檢查日誌過濾參數。還有種多是不當心將控制檯後端給關閉了,從新開啓
Enable console backend
便可。二、開啓 ulog 後,系統運行崩潰,例如:線程堆棧溢出
ulog 比起之前用的 rtdbg 或者
rt_kprintf
打印輸出函數會多佔一部分線程堆棧空間,若是是開啓了浮點數打印支持,因爲其內部使用了 libc 裏資源佔用加大的vsnprintf
,因此堆棧建議多預留 250 字節。若是開啓了時間戳功能,堆棧建議多預留 100 字節。三、日誌內容的末尾缺失
這是因爲日誌內容超出設定的日誌的最大寬度。檢查
The log's max width
選項,並增大其至合適的大小。四、開啓時間戳之後,爲何看不到毫秒級時間
這是由於 ulog 目前只支持在開啓軟件模擬 RTC 狀態下,顯示毫秒級時間戳。如需顯示,只要開啓 RT-Thread 軟件模擬 RTC 功能便可。
五、每次 include ulog 頭文件前,都要定義
LOG_TAG
及LOG_LVL
,能否簡化LOG_TAG
若是不定義,默認會使用NO_TAG
標籤,這樣輸出的日誌會容易產生誤解,因此標籤的宏不建議省略。LOG_LVL
若是不定義,默認會使用調試級別,若是該模塊處於開發階段這個過程能夠省略,可是模塊代碼若是已經穩定,建議定義該宏,並修改級別爲信息級別。
五、參考
本文全部相關的 API
API 列表
API | 位置 |
---|---|
int ulog_init(void) | ulog.c |
void ulog_deinit(void) | ulog.c |
LOG_E(…) / LOG_W(…) / LOG_I(…) / LOG_D(…) / LOG_RAW(…) | ulog.h |
ulog_e(TAG, …) / ulog_w(TAG, …) / ulog_i(TAG, …) / ulog_d(TAG, …) | ulog_def.h |
void ulog_hexdump(const char *name, rt_size_t width, rt_uint8_t *buf, rt_size_t size) | ulog.c |
核心 API 詳解
ulog 初始化
1int ulog_init(void)
2
在使用 ulog 前必須調用該函數完成 ulog 初始化。若是開啓了組件自動初始化, API 也將被自動調用。
返回 | 描述 |
---|---|
>=0 |
成功 |
-5 | 失敗,內存不足 |
ulog 反初始化
1void ulog_deinit(void)
當 ulog 再也不使用時,能夠執行該 deinit 釋放資源。
LOG_X 日誌輸出 API
1LOG_X(...)
該 API 是一個宏,X
對應的是不一樣級別的第一個字母大寫。
注意:使用這個 API 前需在先在 ulog.h 頭文件上方,定義
LOG_TAG
及LOG_LVL
宏,詳見:日誌輸出 API 的使用章節。
這個 API 使用就能夠基於已定義好的標籤,輸出對應級別的日誌。
參數 | 描述 |
---|---|
… | 日誌內容,格式與 printf 一致 |
ulog_x 日誌輸出 API
1ulog_x(TAG, ...)
該 API 是一個宏,x
對應的是不一樣級別的第一個字母小寫。
注意:使用這個 API 前需在先在 ulog.h 頭文件上方,定義
LOG_LVL
宏,詳見:日誌輸出 API 的使用章節。
這個 API 可在輸出對應級別的日誌時指定標籤,比 LOG_X
多一個入參,不推薦使用。
參數 | 描述 |
---|---|
TAG | 日誌標籤 |
… | 日誌內容,格式與 printf 一致 |
輸出 hex 格式日誌
1void ulog_hexdump(const char *tag, rt_size_t width, rt_uint8_t *buf, rt_size_t size)
以 16 進制 hex 格式 dump 數據到日誌中,使用方法及效果詳見 hexdump 章節
注意:
hexdump 日誌爲 DEBUG 級別,並支持運行期的級別過濾
hexdump 日誌對應的 tag ,支持運行期的標籤過濾
參數 | 描述 |
---|---|
tag | 日誌標籤 |
width | 一行 hex 內容的寬度(數量) |
buf | 待輸出的數據內容 |
size | 數據大小 |
近期活動
活動1
資料下載:2018深圳開發者大會-PPT及培訓資料下載,請在公衆號後臺回覆關鍵詞「深圳開發者大會」,請勿錯字、漏字、多打空格,不然系統沒法識別。
活動2
柿餅GUI直播資料:
請添加小師妹(微信:RT-Thread2006)回覆「柿餅」下載。
加我爲好友
你能夠添加微信13924608367爲好友,註明:公司+姓名,拉進 RT-Thread 官方微信交流羣
RT-Thread
讓物聯網終端的開發變得簡單、快速,芯片的價值獲得最大化發揮。GPLv2+協議,可免費在商業產品中使用。
長按二維碼,關注咱們
本文分享自微信公衆號 - RTThread物聯網操做系統(RTThread)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。