27. 乾貨系列從零用Rust編寫正反向代理,Rust中紀錄檔庫的應用基礎準備

2023-11-15 12:03:26

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!的紀錄檔將不會做任何輸出。

以下是我用過的兩個紀錄檔庫:

  1. 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
  1. 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: 觸發大小的限制

以下是隻保留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.logarchive/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_logerror_log的功能。

點選 [關注][在看][點贊] 是對作者最大的支援