deltachat/
debug_logging.rs1use 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 pub(crate) msg_id: MsgId,
19 pub(crate) loop_handle: task::JoinHandle<()>,
21 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
38pub struct DebugEventLogData {
40 pub time: i64,
41 pub msg_id: MsgId,
42 pub event: EventType,
43}
44
45pub 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 error!(context, "Debug logging update is not created.");
87 };
88 }
89 }
90 }
91}
92
93pub 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
111pub 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
132pub(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 Some(debug_logging) => debug_logging.msg_id = msg_id,
148 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 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}