目前為止第一支API已經可以紀錄基本的訂閱資料,這時候就可以先準備發布了。不過發布之前,我們要先幫系統加上紀錄log的功能,當產品發佈到雲平台後就不像在本機一樣容易偵錯,所以當問題發生時,我們希望有日誌的功能能夠幫助釐清問題。rust中有幾個紀錄log的套件,一般而言常使用log
,但在多執行緒非同步的系統中,因為一個任務不一定是由同一個執行緒完成,就會造成難以追蹤,這時候log
就不敷使用了。今天要介紹的是同為tokio生態系中的套件tracing
。
這是tracing中的第一個基本概念。最簡的紀錄log的方式是用printf!()
,這樣便會在日誌中紀錄一筆資料,這樣的log是單點的。Span與單純的印出log不同,它表示的是一段區間,像是一個HTTP Request的開始到結束。tracing提供了span!
巨集來簡的的產生一個span
:
fn main() {
// 建立span
let span = span!(Level::TRACE, "這是一個span");
// 開始進入span的區間
let enter = span.enter();
// do something
}
// 離開main的作用域後,因為rust的特性會drop作用域內的東西
// 換句話說就是這個span的結束
span的設計符合rust的哲學,他的生命週期會在離開作用域後回收,也就是離開main()
的作用範圍,在這個span的生命週期內可以記錄一些內容。
那麼要如何紀錄自定義的log呢?tracing提供了event
,event可以單獨存在,但需要在span
的作用範圍內才會被span捕捉:
event!(Level::INFO, "不在span區間內");
let span = span!(Level::INFO, "my_span");
let _guard = span.enter();
event!(Level::DEBUG, "在span區間內");
上面的這段程式碼紀錄的結果如下:
某個時間 INFO 應用程式: 不在span區間內
某個時間 DEBUG my_span: 應用程式: 在span區間內
----------------^^^
另外tracing也提供了info!
、error!
等巨集來簡單的建立event。
subscribers
是一個很巧妙的設計,不同於前面兩者是結構體,Subscriber
是一個trait,這個trait定義了一個Subscriber
的行為。這個設計其實跟.Net中的ILoggerProvider
很像,我們可以藉由ILogger
通知紀錄log,並由ILoggerProvider
決定要如何處理這些log。
Subscriber
中定義了三個方法
enter
: 進入span時會呼叫event
: 紀錄event時會呼叫exit
: 離開span時會呼叫既然Subscribers是一個trait,也就代表了可以有多種不同的實做,事實上一個系統中可以通時具有多個Subscriber,tracing本身不實做訂閱者,但官方提供了幾個實做:
另外也有一些第三方的實做像是著名的tracing-opentelemetry
,喜歡grafana的也可以用tracing-loki
。