deltachat/
debug_logging.rs

1//! Forward log messages to logging webxdc
2use crate::chat::ChatId;
3use crate::config::Config;
4use crate::context::Context;
5use crate::events::EventType;
6use crate::log::{error, info};
7use crate::message::{Message, MsgId, Viewtype};
8use crate::param::Param;
9use crate::tools::time;
10use crate::webxdc::StatusUpdateItem;
11use async_channel::{self as channel, Receiver, Sender};
12use serde_json::json;
13use tokio::task;
14
15#[derive(Debug)]
16pub(crate) struct DebugLogging {
17    /// The message containing the logging xdc
18    pub(crate) msg_id: MsgId,
19    /// Handle to the background task responsible for sending
20    pub(crate) loop_handle: task::JoinHandle<()>,
21    /// Channel that log events should be sent to.
22    /// A background loop will receive and handle them.
23    pub(crate) sender: Sender<DebugEventLogData>,
24}
25
26impl DebugLogging {
27    pub(crate) fn log_event(&self, event: EventType) {
28        let event_data = DebugEventLogData {
29            time: time(),
30            msg_id: self.msg_id,
31            event,
32        };
33
34        self.sender.try_send(event_data).ok();
35    }
36}
37
38/// Store all information needed to log an event to a webxdc.
39pub struct DebugEventLogData {
40    pub time: i64,
41    pub msg_id: MsgId,
42    pub event: EventType,
43}
44
45/// Creates a loop which forwards all log messages send into the channel to the associated
46/// logging xdc.
47pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLogData>) {
48    while let Ok(DebugEventLogData {
49        time,
50        msg_id,
51        event,
52    }) = events.recv().await
53    {
54        match context
55            .write_status_update_inner(
56                &msg_id,
57                &StatusUpdateItem {
58                    payload: json!({
59                        "event": event,
60                        "time": time,
61                    }),
62                    info: None,
63                    href: None,
64                    summary: None,
65                    document: None,
66                    uid: None,
67                    notify: None,
68                },
69                time,
70            )
71            .await
72        {
73            Err(err) => {
74                eprintln!("Can't log event to webxdc status update: {err:#}");
75            }
76            Ok(serial) => {
77                if let Some(serial) = serial {
78                    if !matches!(event, EventType::WebxdcStatusUpdate { .. }) {
79                        context.emit_event(EventType::WebxdcStatusUpdate {
80                            msg_id,
81                            status_update_serial: serial,
82                        });
83                    }
84                } else {
85                    // This should not happen as the update has no `uid`.
86                    error!(context, "Debug logging update is not created.");
87                };
88            }
89        }
90    }
91}
92
93/// Set message as new logging webxdc if filename and chat_id fit
94pub async fn maybe_set_logging_xdc(
95    context: &Context,
96    msg: &Message,
97    chat_id: ChatId,
98) -> anyhow::Result<()> {
99    maybe_set_logging_xdc_inner(
100        context,
101        msg.get_viewtype(),
102        chat_id,
103        msg.param.get(Param::Filename),
104        msg.get_id(),
105    )
106    .await?;
107
108    Ok(())
109}
110
111/// Set message as new logging webxdc if filename and chat_id fit
112pub async fn maybe_set_logging_xdc_inner(
113    context: &Context,
114    viewtype: Viewtype,
115    chat_id: ChatId,
116    filename: Option<&str>,
117    msg_id: MsgId,
118) -> anyhow::Result<()> {
119    if viewtype == Viewtype::Webxdc {
120        if let Some(filename) = filename {
121            if filename.starts_with("debug_logging")
122                && filename.ends_with(".xdc")
123                && chat_id.is_self_talk(context).await?
124            {
125                set_debug_logging_xdc(context, Some(msg_id)).await?;
126            }
127        }
128    }
129    Ok(())
130}
131
132/// Set the webxdc contained in the msg as the current logging xdc on the context and save it to db
133/// If id is a `None` value, disable debug logging
134pub(crate) async fn set_debug_logging_xdc(ctx: &Context, id: Option<MsgId>) -> anyhow::Result<()> {
135    match id {
136        Some(msg_id) => {
137            ctx.sql
138                .set_raw_config(
139                    Config::DebugLogging.as_ref(),
140                    Some(msg_id.to_string().as_ref()),
141                )
142                .await?;
143            {
144                let debug_logging = &mut *ctx.debug_logging.write().expect("RwLock is poisoned");
145                match debug_logging {
146                    // Switch logging xdc
147                    Some(debug_logging) => debug_logging.msg_id = msg_id,
148                    // Bootstrap background loop for message forwarding
149                    None => {
150                        let (sender, debug_logging_recv) = channel::bounded(1000);
151                        let loop_handle = {
152                            let ctx = ctx.clone();
153                            task::spawn(async move {
154                                debug_logging_loop(&ctx, debug_logging_recv).await
155                            })
156                        };
157                        *debug_logging = Some(DebugLogging {
158                            msg_id,
159                            loop_handle,
160                            sender,
161                        });
162                    }
163                }
164            }
165            info!(ctx, "replacing logging webxdc");
166        }
167        // Delete current debug logging
168        None => {
169            ctx.sql
170                .set_raw_config(Config::DebugLogging.as_ref(), None)
171                .await?;
172            *ctx.debug_logging.write().expect("RwLock is poisoned") = None;
173            info!(ctx, "removing logging webxdc");
174        }
175    }
176    Ok(())
177}