1#![allow(missing_docs)]
4
5use crate::context::Context;
6
7mod stream;
8
9pub(crate) use stream::LoggingStream;
10
11macro_rules! info {
12 ($ctx:expr, $msg:expr) => {
13 info!($ctx, $msg,)
14 };
15 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
16 let formatted = format!($msg, $($args),*);
17 let full = format!("{file}:{line}: {msg}",
18 file = file!(),
19 line = line!(),
20 msg = &formatted);
21 ::tracing::event!(::tracing::Level::INFO, account_id = $ctx.get_id(), "{}", &formatted);
22 $ctx.emit_event($crate::EventType::Info(full));
23 }};
24}
25
26#[macro_use]
28mod warn_macro_mod {
29 macro_rules! warn_macro {
30 ($ctx:expr, $msg:expr) => {
31 warn_macro!($ctx, $msg,)
32 };
33 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
34 let formatted = format!($msg, $($args),*);
35 let full = format!("{file}:{line}: {msg}",
36 file = file!(),
37 line = line!(),
38 msg = &formatted);
39 ::tracing::event!(::tracing::Level::WARN, account_id = $ctx.get_id(), "{}", &formatted);
40 $ctx.emit_event($crate::EventType::Warning(full));
41 }};
42 }
43
44 pub(crate) use warn_macro;
45}
46
47pub(crate) use warn_macro_mod::warn_macro as warn;
48
49macro_rules! error {
50 ($ctx:expr, $msg:expr) => {
51 error!($ctx, $msg,)
52 };
53 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
54 let formatted = format!($msg, $($args),*);
55 ::tracing::event!(::tracing::Level::ERROR, account_id = $ctx.get_id(), "{}", &formatted);
56 $ctx.set_last_error(&formatted);
57 $ctx.emit_event($crate::EventType::Error(formatted));
58 }};
59}
60
61impl Context {
62 pub fn set_last_error(&self, error: &str) {
65 let mut last_error = self.last_error.write();
66 *last_error = error.to_string();
67 }
68
69 pub fn get_last_error(&self) -> String {
71 let last_error = &*self.last_error.read();
72 last_error.clone()
73 }
74
75 pub fn set_migration_error(&self, error: &str) {
76 let mut migration_error = self.migration_error.write();
77 *migration_error = Some(error.to_string());
78 }
79
80 pub fn get_migration_error(&self) -> Option<String> {
81 let migration_error = &*self.migration_error.read();
82 migration_error.clone()
83 }
84}
85
86pub trait LogExt<T, E>
87where
88 Self: std::marker::Sized,
89{
90 #[track_caller]
101 fn log_err(self, context: &Context) -> Result<T, E>;
102}
103
104impl<T, E: std::fmt::Display> LogExt<T, E> for Result<T, E> {
105 #[track_caller]
106 fn log_err(self, context: &Context) -> Result<T, E> {
107 if let Err(e) = &self {
108 let location = std::panic::Location::caller();
109
110 let full = format!(
112 "{file}:{line}: {e:#}",
113 file = location.file(),
114 line = location.line(),
115 e = e
116 );
117 tracing::event!(
120 ::tracing::Level::WARN,
121 account_id = context.get_id(),
122 "{}",
123 &full
124 );
125 context.emit_event(crate::EventType::Warning(full));
126 };
127 self
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 use anyhow::Result;
136
137 use crate::test_utils::TestContext;
138
139 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
140 async fn test_get_last_error() -> Result<()> {
141 let t = TestContext::new().await;
142
143 assert_eq!(t.get_last_error(), "");
144
145 error!(t, "foo-error");
146 assert_eq!(t.get_last_error(), "foo-error");
147
148 warn!(t, "foo-warning");
149 assert_eq!(t.get_last_error(), "foo-error");
150
151 info!(t, "foo-info");
152 assert_eq!(t.get_last_error(), "foo-error");
153
154 error!(t, "bar-error");
155 error!(t, "baz-error");
156 assert_eq!(t.get_last_error(), "baz-error");
157
158 Ok(())
159 }
160}