Rust 在不同的环境中是否可能有不同的记录器?

Rust 在不同的环境中是否可能有不同的记录器?,rust,Rust,用途: 已注册单个全局静态记录器(Send+Sync)实例 所有的信息,警告,等等。宏从所有线程发送到全局记录器 您可以使用set\u max\u level 是否有可能以某种方式解开这个问题,并在不同的上下文中使用不同的记录器,例如,针对不同的线程 以下是我想要实现的具体示例: 目标“A”、“B”和“C”是不同的日志记录目标,例如A的控制台、B的文件和C的网络日志端点。A/B/C是什么并不重要,只是它们彼此不同 #[macro_use] extern crate log; use std

用途:

  • 已注册单个全局静态
    记录器
    Send+Sync
    )实例
  • 所有的
    信息
    警告,等等。宏从所有线程发送到全局记录器
  • 您可以使用
    set\u max\u level
是否有可能以某种方式解开这个问题,并在不同的上下文中使用不同的记录器,例如,针对不同的线程

以下是我想要实现的具体示例:

目标“A”、“B”和“C”是不同的日志记录目标,例如A的控制台、B的文件和C的网络日志端点。A/B/C是什么并不重要,只是它们彼此不同

#[macro_use]
extern crate log;

use std::thread::spawn;

use log::{set_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record};

struct Logger {}

impl Log for Logger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        println!("{}", record.args());
    }

    fn flush(&self) {}
}

static LOGGER: Logger = Logger {};

fn init() {
    set_logger(&LOGGER).unwrap();
    set_max_level(LevelFilter::Info);
}

fn foo() {
    // Should always log to target 'B'
    info!("B");
}

fn main() {
    init();

    // Should log to target 'A'
    info!("A");
    foo();

    spawn(move || {
        // It would be nice, to be able to also, say, use a different
        // logging level here, e.g. only log error!() in this thread.

        // should log to target 'C'
        info!("C");

        // should still log to target 'B'
        foo();
    }).join()
        .unwrap();
}
#[宏使用]
外部板条箱日志;
使用std::thread::spawn;
使用日志:{set_logger,set_max_level,level,LevelFilter,log,Metadata,Record};
结构记录器{}
记录器的impl日志{
启用fn(&self,元数据:&metadata)->bool{
metadata.level()最大级别;文档似乎是错误的,因此通常您会有一个函数来包装
set\u max\u level
set\u logger
,但这只是一个简单的示例。

因为,您可以使用线程本地存储来建立一个记录器堆栈。然后您可以始终在堆栈顶部登录记录器:

use std::cell::RefCell;

trait Logger {
    fn log(&self, message: &str);
}

struct StderrLogger;

impl Logger for StderrLogger {
    fn log(&self, message: &str) {
        eprintln!("Logger: {}", message);
    }
}

struct NetworkLogger;

impl Logger for NetworkLogger {
    fn log(&self, message: &str) {
        eprintln!("The cloud: {}", message);
    }
}

thread_local! {
    static LOGGER: RefCell<Vec<Box<Logger>>> = RefCell::new(vec![Box::new(StderrLogger)]);
}

fn push_context<L, F, R>(l: L, f: F) -> R
where
    L: Logger + 'static,
    F: FnOnce() -> R,
{
    LOGGER.with(|logger| logger.borrow_mut().push(Box::new(l)));

    let r = f();

    LOGGER.with(|logger| logger.borrow_mut().pop());

    r
}

macro_rules! log {
    ($msg:expr) => {
        LOGGER.with(|logger| {
            if let Some(logger) = logger.borrow().last() {
                logger.log($msg)
            }
        })
    }
}

fn main() {
    log!("a");
    push_context(NetworkLogger, || {
        log!("b");
    });
    log!("c");
}
使用std::cell::RefCell;
特征记录器{
fn日志(&self,消息:&str);
}
struct stderlogger;
stderlogger的impl记录器{
fn日志(&self,消息:&str){
eprintln!(“记录器:{}”,消息);
}
}
结构网络记录器;
网络记录器的impl记录器{
fn日志(&self,消息:&str){
eprintln!(“云:{}”,消息);
}
}
本地线程{
静态记录器:RefCell=RefCell::new(vec![Box::new(StderrLogger)];
}
fn推送上下文(l:l,f:f)->R
哪里
L:记录器+'静态,
F:FnOnce()->R,
{
LOGGER.with(| LOGGER | LOGGER.borrow_mut().push(Box::new(l));
设r=f();
LOGGER.with(| LOGGER | LOGGER.borrow_mut().pop());
R
}
宏规则!日志{
($msg:expr)=>{
记录器。带(|记录器|{
如果让一些(记录器)=logger.borrow().last(){
logger.log($msg)
}
})
}
}
fn main(){
日志!(“a”);
推送上下文(网络记录器,| |{
日志!(“b”);
});
日志!(“c”);
}
您需要有向堆栈添加新记录器并将其删除的方法(
push\u context

我没有花时间将其与原木板条箱真正集成,但我相信这应该是直接完成的。您必须实现原木板条箱需要的任何特性,才能完成此
log
宏中的工作


不过,我的观点是:全球任何东西,包括日志记录者,都是一种代码味道。当你开始想要在一个简单的抽象上强加越来越多的细节时,尤其如此

这确实帮助我澄清了我的想法:

日志记录是一项功能
  • 支持日志记录(错误和信息)为 应用程序用户界面的一部分。这些消息是 计划由支持人员以及系统人员跟踪 管理员和操作员,以诊断故障或监视 运行系统的进度

  • 诊断日志记录(调试和跟踪) 是程序员的基础设施。这些消息不应该被转换 因为他们的目的是帮助程序员 了解他们正在开发的系统内部的情况

前者应该是系统域的一部分,而不应该是二等公民。使用对应用程序有意义的方法创建自定义特性,并使用依赖项注入来传递


后者是我发现的类似于日志板条箱的东西,适合于倾倒原始数据来诊断野外的问题。

您可以实现并注册一个
记录器
,它向每个线程
记录器
(可能跟踪),并且(假设您控制线程)让每个线程注册自己的
记录器
(它不必是
记录器
特征;您可以定义自己的特征,而不需要
发送+同步
)。环境记录器中没有不安全的代码:✓&q=safesafe&type=@BurntSushi5我从来没有说过有。我所说的就是日志()一些日志程序实现也是如此。使用单例程序强制使用某种不安全的代码,即使你将其包装在一个不安全的代码后面。这是一个糟糕的设计,完全是这样,但我现在真正感兴趣的是试图找到一种实用的方法来使用它。/耸耸肩“日志包和各种实现中都有一堆不安全的代码”---内部可变性并不是天生的坏设计,它可以通过使用标准库中的安全抽象以多种不同的方式实现。如果您扩展此问题来解释您认为的“上下文记录器”是什么,也可能会有所帮助。您能否展示一个代码示例,以近似您希望使用记录器编写的代码?