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}