Rust 的 Logging 推薦

Rust 的 Logging 推薦

內容整理自 Reddit 的討論:What is the current recommendation for logging in Rust? : rust。

問題簡述:除了標準的 log,還有不少選擇:env_loggertracingslogsimplelog 等等,最佳實踐是什麼?

來自 Koxiaet 的答覆:通常有兩類與日誌相關的 crate:日誌接口和日誌消費者。接口提供了想要記錄某些東西時調用的函數,消費者處理將結構化日誌數據格式化到某個地方(stderr 或文件)。兩個主要的接口是 log 和 tracing,後者功能更強大因爲它支持結構化日誌記錄,但前者更普遍。還有另一個結構化日誌接口 slog,比 tracing 更古老但用的較少。每個日誌接口都有自己生態系統,可以根據自己的需要選擇。如果在寫一個庫,log 是個不錯的選擇,因爲所有的日誌記錄接口都與它兼容。但如果你確實需要結構化日誌記錄,則可以改用 tracing,這取決於你的需求,比如你是需要寫到文件還是隻是終端。

其他網友的推薦:

Rust 全棧

本文是一篇博客翻譯,來自:Full Stack Rust - Blog。

一年前,我的首選語言如下:

當時只聽過 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 或構建系統沒有一致的標準:

然後還有 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 是一種輕量級關係編程語言

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