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
26pub(crate) use info;
27
28#[macro_use]
30mod warn_macro_mod {
31 macro_rules! warn_macro {
32 ($ctx:expr, $msg:expr) => {
33 warn_macro!($ctx, $msg,)
34 };
35 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
36 let formatted = format!($msg, $($args),*);
37 let full = format!("{file}:{line}: {msg}",
38 file = file!(),
39 line = line!(),
40 msg = &formatted);
41 ::tracing::event!(::tracing::Level::WARN, account_id = $ctx.get_id(), "{}", &formatted);
42 $ctx.emit_event($crate::EventType::Warning(full));
43 }};
44 }
45
46 pub(crate) use warn_macro;
47}
48
49pub(crate) use warn_macro_mod::warn_macro as warn;
50
51macro_rules! error {
52 ($ctx:expr, $msg:expr) => {
53 error!($ctx, $msg,)
54 };
55 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
56 let formatted = format!($msg, $($args),*);
57 ::tracing::event!(::tracing::Level::ERROR, account_id = $ctx.get_id(), "{}", &formatted);
58 $ctx.set_last_error(&formatted);
59 $ctx.emit_event($crate::EventType::Error(formatted));
60 }};
61}
62
63pub(crate) use error;
64
65impl Context {
66 pub fn set_last_error(&self, error: &str) {
69 let mut last_error = self.last_error.write();
70 *last_error = error.to_string();
71 }
72
73 pub fn get_last_error(&self) -> String {
75 let last_error = &*self.last_error.read();
76 last_error.clone()
77 }
78
79 pub fn set_migration_error(&self, error: &str) {
80 let mut migration_error = self.migration_error.write();
81 *migration_error = Some(error.to_string());
82 }
83
84 pub fn get_migration_error(&self) -> Option<String> {
85 let migration_error = &*self.migration_error.read();
86 migration_error.clone()
87 }
88}
89
90pub trait LogExt<T, E>
91where
92 Self: std::marker::Sized,
93{
94 #[track_caller]
105 fn log_err(self, context: &Context) -> Result<T, E>;
106}
107
108impl<T, E: std::fmt::Display> LogExt<T, E> for Result<T, E> {
109 #[track_caller]
110 fn log_err(self, context: &Context) -> Result<T, E> {
111 if let Err(e) = &self {
112 let location = std::panic::Location::caller();
113
114 let full = format!(
116 "{file}:{line}: {e:#}",
117 file = location.file(),
118 line = location.line(),
119 e = e
120 );
121 tracing::event!(
124 ::tracing::Level::WARN,
125 account_id = context.get_id(),
126 "{}",
127 &full
128 );
129 context.emit_event(crate::EventType::Warning(full));
130 };
131 self
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 use anyhow::Result;
140
141 use crate::test_utils::TestContext;
142
143 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
144 async fn test_get_last_error() -> Result<()> {
145 let t = TestContext::new().await;
146
147 assert_eq!(t.get_last_error(), "");
148
149 error!(t, "foo-error");
150 assert_eq!(t.get_last_error(), "foo-error");
151
152 warn!(t, "foo-warning");
153 assert_eq!(t.get_last_error(), "foo-error");
154
155 info!(t, "foo-info");
156 assert_eq!(t.get_last_error(), "foo-error");
157
158 error!(t, "bar-error");
159 error!(t, "baz-error");
160 assert_eq!(t.get_last_error(), "baz-error");
161
162 Ok(())
163 }
164}