如何在rust中输出日志:在rust中打印日志的各种方式对比

在这里插入图片描述

有许多库可以在 Rust 中输出日志,有时很难选择该使用哪一个。当 println!dbg!eprintln! 无法解决问题时,找到一种方便记录日志的方法就很重要,尤其是在生产级应用程序中。本文将帮助您深入了解在 Rust 日志记录方面最适合您的用例的日志 crate。

Rust 中的日志如何工作?

简而言之:Rust 中的日志依赖于一个库来充当“日志门面”——以便其他crate可以和日志 API 配合工作。例如,如果我们有一个像 log 这样的crate,它为我们提供了可以与日志记录器一起使用的日志记录实现,那么我们还需要添加一个实际执行日志记录的crate 例如, simple-logger 是可以使用 log 的众多crate之一。某些日志门面可能只能由特定日志记录器使用 - 例如, tracing 要求您使用 tracing-subscriber crate,要么实现您自己的自定义类型,该类型实现 tracing::Subscriber

话不多说,下边开始 Rust 日志crate的比较!

log

log 是一个自称为“轻量级日志记录门面”的crate。该crate将日志门面定义为一个库,“提供一个抽象实际日志记录实现的单一日志记录 API”——本质上,这意味着我们需要运行另一个提供实际日志记录的库,然后使用此crate来提供日志消息。 Log 也由 Rust 核心团队维护,并且可能是您在 Rust Cookbook 上看到的第一个 crate,所以就是这样。

下面是一个关于如何使用它的简单示例,取自 GitHub 存储库:

use log;

pub fn shave_the_yak(yak: &mut Yak) {
    log::trace!("Commencing yak shaving");

    loop {
        match find_a_razor() {
            Ok(razor) => {
                log::info!("Razor located: {}", razor);
                yak.shave(razor);
                break;
            }
            Err(err) => {
                log::warn!("Unable to locate a razor: {}, retrying", err);
            }
        }
    }
}

应该注意的是 log 还与许多日志记录器crate兼容 - 仅他们的 GitHub 存储库就列出了 20 多个日志记录器crate,并且是一个非详尽的列表!如果您正在寻找一款多功能记录器,这绝对适合您。然而,它也不像其他一些crate那么强大,要记住这一点。

对于大多数常见的用例,使用 log crate 是最简单的:只需设置消息级别,然后发送消息!

A quick summary: 快速总结:

  • 由官方 Rust 团队维护
  • 适用于几乎所有日志记录器crate
  • 不像其他一些特殊日志 crate 那么强大

env-logger 环境记录器

env-logger 是一个简单的 Rust 日志记录器,它易于使用,对于任何想要实现日志记录但不希望需要大量样板文件的 大型项目来说都非常方便。它由 Rust CLI 工作组 (WG) 所有,这意味着它将获得长期支持,这对我们来说非常好。

它可以在一行语句中设置:

let logger = Logger::from_default_env();

然后,只需像这样从 Cargo 运行程序,并在命令前面添加 RUST_LOG 环境变量:

# This command will run your program and only print out error messages from logs
RUST_LOG=ERROR cargo run

还可以在应用程序中硬编码最低日志级别,如下所示:

use env_logger::{Logger, Env};

let env = Env::new()
// filters out any messages that aren't at "info" log level or above
 .filter_or("MY_LOG", "info")
// always use styles when printing
 .write_style_or("MY_LOG_STYLE", "always");

let logger = Logger::from_env(env);

还可以设置特定依赖项的日志级别(这与 log crate结合使用):

use env_logger::Builder;
use log::LevelFilter;

let mut builder = Builder::new();

builder.filter_module("path::to::module", LevelFilter::Info);
 .unwrap();

然而,尽管它很方便, env-logger 确实遇到了您可能在生产级应用程序中寻找的一些问题:即,几乎没有关于为日志编写自己的管道的记录功能。可能会使其实现起来相当棘手,而且也不清楚这个crate是否是线程安全的。不用说,它对于任何快速而混乱的日志记录来说都是非常有用的!

A quick summary: 快速总结:

  • 由 Rust CLI 工作组所有
  • 使用简单,使用感觉良好
  • 缺乏有关日志附加/管道等更复杂功能的文档
  • 关于 crate 是否 100% 线程安全的一些不清楚的问题

log4rs

log4rs 是一个以 Java 的 log4j 为模型的日志包,log4j 是一个日志包,可能是部署最多的开源软件之一。该板条箱比其他板条箱需要更多的设置,并且可以使用 YAML 文件或以编程方式完成配置。 log4rslog 兼容,这对我们来说非常好,因为这意味着我们不必仅仅为了使用 log4rs 就采用新的范例。

如果您想创建一个配置文件来加载,您可以像这样设置 YAML 文件:

# set a refresh rate
refresh_rate: 30 seconds

# appenders
appenders:
# this appender will append to the console
  stdout:
    kind: console
# this appender will append to a log file
  requests:
    kind: file
    path: "log/requests.log"
# this is a simple string encoder - this will be explained below
    encoder:
      pattern: "{d} - {m}{n}"

# the appender that prints to stdout will only print if the log level of the message is warn or above
root:
  level: warn
  appenders:
    - stdout

# set minimum logging level - log messages below the mnimum won't be recorded
loggers:
  app::backend::db:
    level: info

  app::requests:
    level: info
    appenders:
      - requests
    additive: false

编码器encoder可以使用 JSON 编码或模式编码。在这里,我们决定使用模式编码,它与原始 log4j 模式类似,但使用 Rust 字符串格式 - 您可以在此处查看有关如何格式化编码器模式的更多信息。

然后可以在设置程序时初始化它,如下所示:

log4rs::init_file("log4rs.yml", Default::default()).unwrap();

还可以以编程方式创建配置:

use log::LevelFilter;
use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{Appender, Config, Logger, Root};

fn main() {
// set up ConsoleAppender to allow appending logs to the console (stdout)
    let stdout = ConsoleAppender::builder().build();

// set up FileAppender to allow appending logs to a log file
    let requests = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new("{d} - {m}{n}")))
        .build("log/requests.log")
        .unwrap();

    let config = Config::builder()
        .appender(Appender::builder().build("stdout", Box::new(stdout)))
        .appender(Appender::builder().build("requests", Box::new(requests)))
        .logger(Logger::builder().build("app::backend::db", LevelFilter::Info))
        .logger(Logger::builder()
            .appender("requests")
            .additive(false)
            .build("app::requests", LevelFilter::Info))
        .build(Root::builder().appender("stdout").build(LevelFilter::Warn))
        .unwrap();

    let handle = log4rs::init_config(config).unwrap();

    // use handle to change logger configuration at runtime
}

还可以使用 log4rs 自动归档日志,这非常棒!对于大多数(如果不是所有)其他记录器包,此功能必须由您自己手动实现,因此将此功能内置到记录器本身中会带来巨大的便利。我们可以通过将以下内容添加到 YAML 配置文件(在“appenders”下)来开始设置它:

rolling_appender:
 kind: rolling_file
 path: log/foo.log
 append: true
 encoder:
   kind: pattern
   pattern: "{d} - {m}{n}"
 policy:
   kind: compound
   trigger:
     kind: size
     limit: 10 mb
 # upon reaching the max log size, the file simply gets deleted on successful roll
   roller:
     kind: delete

现在我们有一个策略,写入主日志文件,然后在主日志文件达到 10 MB 的日志时将其附加到存档日志文件,然后删除当前活动的日志文件以准备接收更多日志。

正如您所看到的, log4rs 是一个极其通用的 crate,它可以与前面提到的 log crate 配合使用,提供有关 Rust 日志的强大功能,如果您是来自像 Java 这样的语言,您已经了解其心智模型,只是想了解如何在 Rust 中进行日志记录。然而,作为交换,你必须学习如何设置记录器,与其他记录箱相比,设置本身相当复杂,所以请记住这一点。

Summary: 概括:

  • 多功能的大型一体式crate
  • 需要大量的样板代码或配置文件
  • 轻松设置您自己的文件附加日志出口服务
  • 适配 log crate

tracing

tracing是一个自称为“用于检测 Rust 程序以收集结构化、基于事件的诊断信息的框架”的包,需要使用其对应的记录器 tracing-subscriber 或实现 tracing::Subscriber

tracing 使用“跨度(spans)”的概念,用于记录程序的执行流程。事件可以发生在跨度(spans)内部或外部,也可以类似于非结构化日志记录(即,仅以任何方式记录事件)使用,但也可以表示跨度(spans)内的时间点。见下文:

use tracing::Level;

// records an event outside of any span context:
tracing::event!(Level::DEBUG, "something happened");

// create the span while entering it
let span = tracing::span!(Level::INFO"my_span").entered();

// records an event within "my_span".
tracing::event!(Level::DEBUG, "something happened inside my_span");

跨度(spans)可以形成树结构,整个子树由其子级表示 - 因此,父级 Span 的持续时间将始终与其寿命最长的子级 Span 一样长(如果不是更长的话)。

因为所有这些可能有点过多, tracing 还包含了其他日志外观库中用于日志记录的常规宏 - 即 info!error!debug!warn!trace! 。每个宏都有一个跨度(spans)版本 - 但如果您来自 log 并且想要尝试 tracing 而不会迷失在尝试确保一切正常的复杂性中很快,tracing就为您提供了支持。

use tracing;

tracing::debug!("Looks just like the log crate!");

tracing::info_span!("a more convenient version of creating spans!");

Tracing-subscriber 是一个日志 create,旨在与 tracing 一起使用,让您定义一个实现 tracing 中的 Subscriber 特征的记录器。

您可以启动一个采用 RUST_LOG 环境变量的订阅者,如下所示:

tracing_subscriber::registry()
    .with(fmt::layer())
    .with(EnvFilter::from_default_env())
    .init();

还可以以编程方式应用硬编码过滤器:

use tracing_subscriber::filter::{EnvFilter, LevelFilter};

let my_filter = EnvFilter::builder()
    .with_default_directive(LevelFilter::ERROR.into())
    .from_env_lossy();

tracing_subscriber::registry()
    .with(fmt::layer())
    .with(filter)
    .init();

还可以将过滤器分层!如果想要同时拥有多个订阅者的效果,这非常有用。

如果需要将日志导出到某个地方,还有tracing_appender crate。需要使用 .with_writer() 方法将其添加到您的跟踪订阅者中,如下所示:

// create a file appender that rotates hourly
let file_appender = tracing_appender::rolling::hourly("/some/directory", "prefix.log");
// make the file appender non-blocking
// the guard exists to make sure buffered logs get flushed to output
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);

// add the file appender to your tracing subscriber
tracing_subscriber::fmt()
    .with_writer(non_blocking)
    .init();

non_blocking 编写器是使用实现 std::io::Write 的类型构建的 - 因此,如果想实现自己的实现 std::io::Write 的东西(假设想要一个日志记录表达自动将您的所有内容导出到 BetterStack 或 Datadog) - 想尝试一下。见下文:

use std::io::Error;

struct TestWriter;

impl std::io::Write for TestWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let buf_len = buf.len();
        println!("{:?}", buf);
        Ok(buf_len)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

let (non_blocking, _guard) = tracing_appender::non_blocking(TestWriter);
tracing_subscriber::fmt()
    .with_writer(non_blocking)
    .init();

正如您所看到的, tracing 系列 crate 提供了强大的功能,并且对于任何 Web 应用程序来说都足够强大,并且由 Tokio 团队维护,因此肯定会受到支持许久。但是,使用它需要了解 tracing 的工作原理,因为它使用其他日志crate中未使用的概念 - 因此,如果您出于某种原因需要从该crate迁移,并且您将被锁定,那么您将被锁定重新使用跨度span。但是,如果您问自己“Rust 中最好的日志crate是什么”,那么就这个crate系列的强大功能而言,您选择 tracing crate不会出错。

Summary: 概括:

  • 需要了解一些关于跨度span等的知识才能充分利用
  • 由 Tokio 团队维护,因此很可能会看到 LTS
  • 拆分crate 意味着不必安装不打算使用的东西
  • 由于其构建方式,可能是列表中最复杂的系统

Conclusions 结论

谢谢阅读!现在我们已经结束了,我希望您对 Rust 登录有更好的了解。日志箱如此之多,很难确定您应该使用哪一个,但希望本文能够让您清楚地了解哪个crate是最适合您的用例的 Rust 日志库。


Logging in Rust - How to Get Started

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/403836.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是Elasticsearch SQL

什么是Elasticsearch SQL 一. 介绍二. SQL 入门 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 介绍 Elasticsearch SQL 是一个 X-Pack 组件&#xff0c;允许针对 Elasticsea…

OpenAI文生视频大模型Sora概述

Sora&#xff0c;美国人工智能研究公司OpenAI发布的人工智能文生视频大模型&#xff08;但OpenAI并未单纯将其视为视频模型&#xff0c;而是作为“世界模拟器” &#xff09;&#xff0c;于2024年2月15日&#xff08;美国当地时间&#xff09;正式对外发布。 Sora可以根据用户…

张驰咨询:餐饮业如何通过六西格玛培训增加利润

在当前的餐饮业&#xff0c;企业面临着一系列挑战&#xff0c;这些挑战可能会阻碍业务的成长和盈利能力。六西格玛培训提供了一套解决方案&#xff0c;能够帮助企业克服这些困境。让我们深入探讨一下餐饮业的具体困境以及六西格玛如何提供帮助。 一、餐饮业的挑战 顾客满意度…

localhost和127.0.0.1的区别是什么?

localhost和127.0.0.1的区别是什么&#xff1f; 前端同学本地调试的时候&#xff0c;应该没少和localhost打交道吧&#xff0c;只需要执行 npm run 就能在浏览器中打开你的页面窗口&#xff0c;地址栏显示的就是这个 http://localhost:xxx/index.html 可能大家只是用&#xff…

跨越千年医学对话:用AI技术解锁中医古籍知识,构建能够精准问答的智能语言模型,成就专业级古籍解读助手(LLAMA)

跨越千年医学对话&#xff1a;用AI技术解锁中医古籍知识&#xff0c;构建能够精准问答的智能语言模型&#xff0c;成就专业级古籍解读助手&#xff08;LLAMA&#xff09; 介绍&#xff1a;首先在 Ziya-LLaMA-13B-V1基线模型的基础上加入中医教材、中医各类网站数据等语料库&am…

JavaScript中的内存泄漏

一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;由于设计错误&#xff0c;导致在释放该段内存之前就失…

目前最先进的家庭取暖设备 南方取暖用什么电器好

在寒冷的冬季&#xff0c;家庭取暖成为了每个人关注的焦点。为了迎合消费者对舒适取暖环境的需求&#xff0c;市场上涌现出了多种家庭取暖设备。其中&#xff0c;取暖器成为目前最先进的家庭取暖设备之一。小编将向大家推荐五个顶尖品牌的取暖器。 1. 斯帝沃取暖器 英国斯帝沃&…

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一)

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一) 大家好 我是寸铁&#x1f44a; 总结了一篇刷题关于树、dfs、bfs、回溯、递归的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 105. 从前序与中序遍历序列构造二叉树 模拟分析图 代码实现 /*** Definition for a binary tre…

Windows系统中定时执行python脚本

背景&#xff1a;本地Windows系统指定目录下会有文件的修改新增&#xff0c;这些变化的文件需要定时的被上传到git仓库中&#xff0c;这样不需要每次变更手动上传了。 首先编写一个检测文件夹下文件变化并且上传git仓库的python脚本(确保你已经在E:\edc_workspace\data_edc_et…

uniapp-提现功能(demo)

页面布局 提现页面 有一个输入框 一个提现按钮 一段提现全部的文字 首先用v-model 和data内的数据双向绑定 输入框逻辑分析 输入框的逻辑 为了符合日常输出 所以要对输入框加一些条件限制 因为是提现 所以对输入的字符做筛选,只允许出现小数点和数字 这里用正则实现的 小数点…

力扣面试经典150 —— 1-5题

力扣面试经典150题在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题&#xff0c;安装 Debug LeetCode 插件可以免费 debug本文使用 python 语言解题&#xff0c;文中 “数组” 通常指 python 列表&#xff1b;文中 “指针” 通常指 python 列表索引 文章目录 1. [简单] 合并…

TongWEB(东方通),部署WEB前后端项目步骤

我的系统: 银河麒麟桌面系统V10(SP1)(兆芯) 环境需要搭建好,什么redis,数据库等 1.准备项目前端war包 (我后端项目本就是war部署,jar转war自行百度一下吧) 进入前端打包好的dist文件夹,创建一个文件夹 WEB-INF ,再在 WEB-INF 里创建一个 web.xml 文件,文件内容: <web-…

谁说常量字符串不可修改

哈喽&#xff0c;我是子牙&#xff0c;一个很卷的硬核男人 深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果&#xff0c;一年磨一剑&#xff0c;三年先后做了这些课程&#xff1a;手写JVM、手写OS、…

接口性能优化的小技巧

目录 1.索引 1.1 没加索引 1.2 索引没生效 1.3 选错索引 2. sql优化 3. 远程调用 3.1 并行调用 3.2 数据异构 4. 重复调用 4.1 循环查数据库 4.2 死循环 4.3 无限递归 5. 异步处理 5.1 线程池 5.2 mq 6. 避免大事务 7. 锁粒度 7.1 synchronized 7.2 redis分…

git 使用总结

文章目录 git merge 和 git rebasegit mergegit rebase总结 git merge 和 git rebase git merge git merge 最终效果说明&#xff1a; 假设有一个仓库情况如下&#xff0c;现需要进行 merge&#xff1a; merge 操作流程&#xff1a; merge 的回退操作&#xff1a; git reba…

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

常见的芯片行业ERP:SAP Business One ERP系统

在现代企业管理中&#xff0c;企业资源规划(ERP)系统已成为不可或缺的工具。特别是在高度复杂和竞争激烈的芯片行业中&#xff0c;一款高效、全面的ERP系统更是助力企业实现精细管理、提升竞争力的关键。SAP Business One ERP系统便是其中一款备受推崇的选择。 SAP Business On…

2023 龙蜥操作系统大会演讲实录:《兼容龙蜥的云原生大模型数据计算系统——πDataCS》

本文主要分三部分内容&#xff1a;第一部分介绍拓数派公司&#xff0c;第二部分介绍 πDataCS 产品&#xff0c;最后介绍 πDataCS 与龙蜥在生态上的合作。 杭州拓数派科技发展有限公司&#xff08;简称“拓数派”&#xff0c;英文名称“OpenPie”&#xff09;是国内基础数据计…

alist修改密码(docker版)

rootarmbian:~# docker exec -it [docker名称] ./alist admin set abcd123456 INFO[2024-02-20 11:06:29] reading config file: data/config.json INFO[2024-02-20 11:06:29] load config from env with prefix: ALIST_ INFO[2024-02-20 11:06:29] init logrus..…

bilibili尚硅谷周阳老师JUC并发编程与源码分析课程笔记第十一章——Synchronized与锁升级

文章目录 先从阿里及其它大厂面试题说起本章路线总纲阿里手册对锁使用的强制要求Synchronized锁优化的背景Synchronized锁的升级过程Synchronized锁的升级标志 Synchronized的性能变化Java5以前&#xff0c;只有Synchronized&#xff0c;这个是操作系统级别的重量级锁为什么每一…