Skip to main content

ua_client/
logger.rs

1use std::fmt::Write as _;
2
3use tokio::sync::mpsc::UnboundedSender;
4use tracing::field::{Field, Visit};
5use tracing::{Event, Level, Subscriber};
6use tracing_subscriber::layer::{Context, SubscriberExt};
7use tracing_subscriber::util::SubscriberInitExt;
8use tracing_subscriber::{EnvFilter, Layer};
9
10use crate::messages::UiUpdate;
11use crate::types::{LogLevel, LogLine};
12
13pub fn init_tracing(tx: UnboundedSender<UiUpdate>) {
14    let filter = EnvFilter::try_from_default_env()
15        .unwrap_or_else(|_| EnvFilter::new("info,opcua=info,ua_client=debug"));
16    tracing_subscriber::registry()
17        .with(filter)
18        .with(ChannelLogLayer::new(tx))
19        .init();
20}
21
22pub struct ChannelLogLayer {
23    tx: UnboundedSender<UiUpdate>,
24}
25
26impl ChannelLogLayer {
27    pub fn new(tx: UnboundedSender<UiUpdate>) -> Self {
28        Self { tx }
29    }
30}
31
32impl<S: Subscriber> Layer<S> for ChannelLogLayer {
33    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
34        let metadata = event.metadata();
35        let level = to_level(*metadata.level());
36        let mut visitor = MessageVisitor::default();
37        event.record(&mut visitor);
38        let line = LogLine {
39            level,
40            target: metadata.target().to_string(),
41            message: visitor.message,
42        };
43        let _ = self.tx.send(UiUpdate::Log(line));
44    }
45}
46
47fn to_level(level: Level) -> LogLevel {
48    match level {
49        Level::ERROR => LogLevel::Error,
50        Level::WARN => LogLevel::Warn,
51        Level::INFO => LogLevel::Info,
52        Level::DEBUG => LogLevel::Debug,
53        Level::TRACE => LogLevel::Trace,
54    }
55}
56
57#[derive(Default)]
58struct MessageVisitor {
59    message: String,
60}
61
62impl Visit for MessageVisitor {
63    fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
64        if field.name() == "message" {
65            let _ = write!(&mut self.message, "{value:?}");
66        } else {
67            if !self.message.is_empty() {
68                self.message.push(' ');
69            }
70            let _ = write!(&mut self.message, "{}={value:?}", field.name());
71        }
72    }
73
74    fn record_str(&mut self, field: &Field, value: &str) {
75        if field.name() == "message" {
76            self.message.push_str(value);
77        } else {
78            if !self.message.is_empty() {
79                self.message.push(' ');
80            }
81            let _ = write!(&mut self.message, "{}={value}", field.name());
82        }
83    }
84}