Rust 的 Logging 推薦
Rust 的 Logging 推薦
內容整理自 Reddit 的討論:What is the current recommendation for logging in Rust? : rust。
問題簡述:除了標準的 log
,還有不少選擇:env_logger
,tracing
,slog
,simplelog
等等,最佳實踐是什麼?
來自 Koxiaet 的答覆:通常有兩類與日誌相關的 crate:日誌接口和日誌消費者。接口提供了想要記錄某些東西時調用的函數,消費者處理將結構化日誌數據格式化到某個地方(stderr 或文件)。兩個主要的接口是 log
和 tracing
,後者功能更強大因爲它支持結構化日誌記錄,但前者更普遍。還有另一個結構化日誌接口 slog,比 tracing
更古老但用的較少。每個日誌接口都有自己生態系統,可以根據自己的需要選擇。如果在寫一個庫,log
是個不錯的選擇,因爲所有的日誌記錄接口都與它兼容。但如果你確實需要結構化日誌記錄,則可以改用 tracing
,這取決於你的需求,比如你是需要寫到文件還是隻是終端。
其他網友的推薦:
-
File Logging:emabee/flexi_logger: A flexible logger for rust programs that can write to stderr or to log files。(來自 cfsamson)
-
tracing
的接口:tracing_log - Rust,有多個同時操作交錯日誌消息時特別方便,可以按某些屬性對它們進行分組並單獨查看它們。(來自 class_two_perversion) -
estk/log4rs: A highly configurable logging framework for Rust,log4rs 是一個高度可配置的日誌框架,以 Java 的 Logback 和 log4j 庫爲模型。通過 Yaml 配置,到 sdout 和文件,帶有文件大小限制選項,還可以配置不同級別的日誌。(來自 tms102)
-
tracing-appender - crates.io: Rust Package Registry,推薦者所知道的唯一線程外日誌記錄解決方案,不僅適用於異步應用程序。(來自 Pand9)
-
daboross/fern: Simple, efficient logging for Rust,像 Python 的
logging
和 JS 的Winston
。(來自 RapBeautician)
Rust 全棧
本文是一篇博客翻譯,來自:Full Stack Rust - Blog。
一年前,我的首選語言如下:
-
Python 用於高級代碼快速原型設計,或用於需要第三方功能的代碼
-
C/C++ 用於長期的 low-level 項目
當時只聽過 Rust 並簡單使用過,我的經驗來自用 Rust 寫了一個處理大文件(>4GB)的事務並從中挖掘一些統計信息的小工具。我用了一個庫將文件映射到內存,繽瑞按照順序對其進行分析。有一些很酷的概念,比如編譯器靜態地強制內存映射在它被取消映射後無法訪問——如果你不小心,C++ 中可能就會發生這種錯誤。
不過當時並沒有真正吸引我,因爲那只是一個小新奇。當我向 pdblister 添加新功能以並行獲取數千個 PDB 文件時訣竅來了。由於 GIL,在 CPython 中幾乎不可能,而在 C/C++ 中做到不面臨並行錯誤是極其困難的。然而 Rust 讓這變得容易。我添加了 tokio 驅動的異步,使用 tokio::spawn
生成新任務來下載 PDB,並修復了編譯器報的錯誤,它可以正常工作了。Rust 編譯器輸出一個二進制文件,它可以在任何地方運行,沒有運行時依賴。
取代 Python
這是第一點,Rust 是 Python 作爲中長期工具語言的絕佳替代品。Python 的好處是龐大的庫和生態系統,通過 pip 可以直接拿到,想要快速製作與 API 交互的原型,可以使用 requests
,只要 import requests
就可以使用了。Rust 的 reqwest
也是如此,只要輸入 cargo add reqwest
就可以在代碼中使用它。
然而當進入更長期的生命週期時,Python 就顯示出劣勢,requests
是程序的依賴,用戶需要後去後才能使用。此外,由於弱類型和錯誤處理能力(與 Rust 比),Python 變得更加劣勢。這一點上,我可以使用 Rust 比使用 Python 更快地編寫原型工具,並且我可以自信地知道我的工具比等效的 Python 更易於維護且壽命更長。但是,對於短期工具,Python 可能仍然更好,因爲它不需要啓動項目即可在 VSCode 中獲得智能感知支持。 Rust 的 cargo-script 接近將 Rust 推入腳本語言的領域,但不幸的是,我還沒有在 VSCode 中找到與之集成的插件。
取代 C
Rust 也是 C 的直接替代品,它在各方面都更好,並且可以與遺留 C 代碼原生互操作以進行增量替換。Rust 最大的改進是生態系統:如上所述,利用 Rust 生態中已有的庫是很容易的。如果你從未使用過 C,那很幸運,實際上 C 中使用高級功能的最佳方法是自己寫。
C 生態系統是支離破碎的,而且很脆弱。ABI 或構建系統沒有一致的標準:
-
由於缺乏 ABI 一致性,你不能跨平臺或操作系統使用相同的二進制文件。 所以你必須從源代碼構建。
-
由於缺乏一致的構建系統,你不能簡單地和應用程序一起構建 C 庫,必須修補或重寫要使其與你的庫兼容的庫的構建系統。
-
C 庫很少跨平臺兼容,因爲它們缺乏可以依賴的共享抽象。
然後還有 Rust 最特色的安全改進——我就不展開了。但根據我的經驗 - 安全性在很大程度上是一種工具,可以讓第三方庫開發人員更容易強迫我正確使用他們的庫,這是 C 庫不能做的事情。
全棧 Rust
總而言之,在過去的一年中,我一直在堆棧的所有部分使用 Rust,而我之前使用過其他語言。我已經使用 Rust 來實現引導加載程序:xenia-project/xell-rs: Xell Bootloader, rewritten in Rust because ¯_(ツ)_/¯,我已經使用它通過 pdblister 和 panamax 中的高級 HTTP/HTTPS 和其他技術來鏡像文件。我利用並貢獻了優秀的 gdbstub 庫,用於控制由自定義 VMM 運行的 VM。這些項目都是在堆棧的不同級別完成的,而 Rust 非常適合所有級別。 我已經開始在我的個人項目中專門使用 Rust,並在適合的時候推動它在我的工作中使用。
tagged_cell:快速、可初始化和線程安全的靜態變量
通過 TaggedCell
和 Tag
類型實現,爲了安全操作,TaggedCell
的每個實例都必須是唯一的。然後必須通過 TaggedCell::init ()
初始化 TaggedCell
,它使用用戶提供的函數或閉包初始化底層數據,然後返回一個特殊的零大小的 Init<Tag>
用於訪問 Cell 的數據。爲了確保每個單元格使用唯一的標籤類型,tagged_cell!
提供宏。該宏根據變量的名稱創建一個新的標記類型,並將其應用到聲明中。
use tagged_cell::tagged_cell;
tagged_cell!{
static BAR: TaggedCell<Vec<usize>, _> = TaggedCell::new();
}
let tag = BAR.init(|| vec![0, 10, 20]);
let vec = BAR.get(tag);
assert_eq!(vec[2], 20);
爲了允許跨線程使用,只有第一次調用 TaggedCell::init
纔會初始化 Cell 的數據。所有未來的 TaggedCell::init
調用都將返回一個新標籤。未確定哪個線程將初始化 Cell 的數據。
use std::thread;
use tagged_cell::tagged_cell;
tagged_cell!{
static TABLE: TaggedCell<Vec<usize>, _> = TaggedCell::new();
}
thread::spawn(move || {
let tag = TABLE.init(|| vec![0, 10, 20]);
let table = TABLE.get(tag);
assert_eq!(table[2], 20);
});
thread::spawn(move || {
let tag = TABLE.init(|| vec![0, 10, 20]);
let table = TABLE.get(tag);
assert_eq!(table[1], 10);
});
GitHub:Dasch0/tagged_cell: Fast, initializable, and thread safe static variables
ukanren-rs:µKanren 的 Rust 實現
µKanren 是一種輕量級關係編程語言
-
原始的 Schema 實現在這裏:jasonhemann/microKanren: The implementation of microKanren, a featherweight relational programming language
-
相關參考:miniKanren.org
use ukanren::*;
fn appendo(first: Value, second: Value, out: Value) -> BoxedGoal<impl Iterator<Item = State>> {
eq(&first, &())
.and(eq(&second, &out))
.or(fresh(move |a: Value, d: Value, res: Value| {
eq(&(a.clone(), d.clone()), &first)
.and(eq(&(a.clone(), res.clone()), &out))
.and(appendo(d.clone(), second.clone(), res))
}))
.boxed()
}
let goal = fresh(|x, y| appendo(x, y, [1, 2, 3, 4, 5].to_value()));
assert_eq!(
goal.run(2).collect::<Vec<_>>(),
vec![
state![(), [1, 2, 3, 4, 5]],
state![[1], [2, 3, 4, 5]],
state![[1, 2], [3, 4, 5]],
state![[1, 2, 3], [4, 5]],
state![[1, 2, 3, 4], [5]],
state![[1, 2, 3, 4, 5], ()],
],
);
GitHub:ekzhang/ukanren-rs: Rust implementation of µKanren, a featherweight relational programming language.
rust-counter-strings:快速定位字符串位置
字符串中的每個星號都出現在由緊接前面的數字指定的位置。因此,29 後面的星號是該字符串中的第 29 個字符。可以在任何地方砍掉字符串的末尾,並且確切地知道它在哪裏被剪掉了。比如不用數就知道字符串 2*4*6*8*11*14*17*2
正好有 18 個字符。當處理 50 萬個字符時會比較省事。
$ ./rust-counter-strings 50
# 2*4*6*8*11*14*17*20*23*26*29*32*35*38*41*44*47*50*
這就是個小工具,代碼也只有幾十行。
GitHub:thomaschaplin/rust-counter-strings: 🧵 Generate self-describing strings of a given length to help aid software testing
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/KuKlbMbCDocAGpNMsU-Uzg