日誌在程式中的重要性非常的重要,當系統發生故障時,我們要隨時能排查出相關的日誌,細數日誌在Rust中的定義依賴及其實現。 ...
wmproxy
wmproxy
已用Rust
實現http/https
代理, socks5
代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,內網穿透,後續將實現websocket
代理等,會將實現過程分享出來,感興趣的可以一起造個輪子
項目地址
國內: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
日誌
日誌在程式中的重要性非常的重要,當系統發生故障時,我們要隨時能排查出相關的日誌,所以通常有了日誌分級的概念(如錯誤error,警告warn,信息info,調試debug,追蹤trace),如果系統出了嚴重的Bug,那我們此時應該排查warn及error看是否有相應的錯誤信息。如果是程式流程上的問題,可能會很瑣碎,那我們可能需要排查trace的消息。
相對來說,trace的日誌數據量極大,當然也最瑣碎,排查起來會極難排查,要根據相關的關鍵字來定位相關日誌,通常一個類型的日誌會定義相同的關鍵字,在Java中類似Tag這種,在Rust庫中日誌會根據庫自動顯示出來,我們可以輕鬆的根據庫名定位同一個庫的問題。同一個庫內可根據關鍵字來定位。
日誌分類
標準輸出
程式中存在標準輸出
(stdout)
及錯誤輸出(stderr)
,通常程式的運行中會將標準輸出的內容顯示在控制臺上,我們就可以根據輸出的內容來判定程式是否正常執行。在Rust中,最常用的是巨集println!()
。
文件輸出
如果是服務類型的程式,常常會將數據輸出到文件中,因為它運行時間通常按天或者月甚至是年來計算。產生的日誌量也極為龐大,通常要按天來切割日誌,且單文件可能上GB大小,在後臺運行時,通過也會通過重定向將控制台的內容輸出到文件上。
後臺執行command命令,並將內容輸出到myout.log,且將stderr重定向到stdout。如:
nohup command > myout.log 2>&1 &
Rust中的日誌庫
通常在Rust中有日誌庫基本上都是基於log
庫去做實現,他定義了日誌的等級,總共5個級別
pub enum Level {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
通常用巨集來進行輸出,下麵來看下輸出巨集的定義:
#[macro_export]
macro_rules! info {
(target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Info, $($arg)+));
($($arg:tt)+) => ($crate::log!($crate::Level::Info, $($arg)+))
}
他分為兩種情況,一種是有目標target
,一種是預設target為root
在wmproxy中,我們通過服務的類型,找到日誌輸出的不同target,就可以控制輸出目錄一致或者不一致的控制,如:
log::info!(target: "access", "{}", String::from_utf8_lossy(&buf[..]));
此外設定日誌的等級,如果設定為等級為Level::Info
那麼log::trace!
及log::debug!
的日誌將不會做任何輸出。
以下是我用過的兩個日誌庫:
- env_logger
這是一個根據環境變數來控制日誌等級,並可以自由設置target輸出的日誌庫,我們需要手動調用初始化:
env_logger::init();
以下是如何啟動控制最簡單版:
RUST_LOG=debug cargo run
# windows
$env:RUST_LOG="debug"
cargo run
此外還可以設定特定庫的日誌等級:
當前庫預設的日誌等級是info,但是wenmeng及webparse庫的日誌等級為warn,可以有效的過濾第三方庫日誌信息又不影響自身的信息
RUST_LOG="info,wenmeng=warn,webparse=warn" cargo run
- log4rs
這是一個根據配置文件來控制日誌輸出到控制台或者輸出到文件及文件是否壓縮是否切割等數據,也可以同時輸出到控制台及文件中。
他通常通過配置文件來載入配置,以下是他的配置內容
# 每隔30秒檢查當前文件
refresh_rate: 30 seconds
appenders:
# 標準輸出的類型設置為控制台
stdout:
kind: console
# 請求數據放置在log/requests.log文件中
requests:
kind: file
path: "log/requests.log"
encoder:
# 輸出格式d為日期,m為內容,n為換行符
pattern: "{d} - {m}{n}"
root:
# 預設的日誌等級為warn
level: warn
# 標準輸出只輸到stdout的配置,即上面配置的控制台
appenders:
- stdout
loggers:
app::backend::db:
level: info
app::requests:
level: info
appenders:
- requests
# 是否追加到root里,如果配置為true他將同時輸出兩個
additive: false
然後調用初始化:
log4rs::init_file("log4rs.yml", Default::default()).unwrap();
log4rs規則
應用程式獲取日誌記錄並將其記錄到某個地方,例如,將其記錄到文件、控制台或系統日誌中。
實現格式:
- console: 控制台輸出,需要
console_appender
特征 - file: 文件輸出,需要
file_appender
特征 - rolling_file: 滾動文件輸出,需要
rolling_file_appender
特征且必須配置compound_policy
- compound: 如何觸發文件切割
- Rollers
- delete: 觸發時刪除舊數據
- fixed_window: 將舊數據如何進行存儲配置
- Triggers
- size: 觸發大小的限制
- Rollers
- compound: 如何觸發文件切割
以下是只保留10m大小的日誌,多餘數據全部刪除的配置:
kind: rolling_file
path: log/foo.log
append: true
encoder:
kind: pattern
policy:
kind: compound
trigger:
kind: size
limit: 10 mb
roller:
kind: delete
如果要保留舊數據,可以修改roller
的配置:
kind: fixed_window
pattern: archive/foo.{}.log
count: 5
base: 1
舊文件將會保持archive/foo.0.log
,archive/foo.1.log
……archive/foo.4.log
如果配置的尾碼名稱為.gz
且開始了gzip的特征,那麼目標文件將會被自動壓縮成gz格式,如archive/foo.{}.log.gz
。如:
log4rs = { version ="1.0.0", features = ["gzip"] }
匹配模型:
這是log4rs的簡單模型輸出,以{}
裡面包含目標數據及可能攜帶的參數:
d
,date
- 當前的日期格式.
一個自定義的格式,依賴於chrono
,獲取的日誌的寫入的當前時間,第一個參數為時間的自定為格式,第二個參數 是utc
或者為local
。以下參數{d}
-2022-11-15T14:22:20.644420340-08:00
{d(%Y-%m-%d %H:%M:%S)}
-2022-11-15 14:22:20
{d(%Y-%m-%d %H:%M:%S %Z)(utc)}
-2022-11-15 22:22:20 UTC
f
,file
- 當前列印的源文件,如果沒有顯示???
。h
,highlight
- 高亮當前日誌,如果有錯誤顯示紅色,藍色顯示info等{h(the level is {l})}
-
<code style="color: red; font-weight: bold">the level is ERROR</code>
l
,level
- 當前日誌等級.L
,line
- 當前列印的源文件的行數,如果沒有顯示???
m
,message
- 當前的消息詳情.M
,module
- 當前列印的源模塊,如果沒有顯示???
P
,pid
- 當前的進程id.i
,tid
- 當前的線程id.n
- 換行符.t
,target
- 目標輸出的target.T
,thread
- 當前的線程名稱.I
,thread_id
- pthread的線程id.
結語
log4rs提供了自定義的一些功能,可以友好的設置一些日誌的功能,下一篇將講解如何自定義實現access_log
和error_log
的功能。
點擊 [關註],[在看],[點贊] 是對作者最大的支持