Skip to content

Commit e86fa7e

Browse files
committed
refactor(aggregator): move errors away from main state machine logs msg
And write them to an isolated `error` property.
1 parent 9080e21 commit e86fa7e

File tree

2 files changed

+184
-45
lines changed

2 files changed

+184
-45
lines changed

mithril-aggregator/src/runtime/error.rs

Lines changed: 180 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
use mithril_common::StdError;
1+
use slog::{crit, error, Logger};
22
use thiserror::Error;
33

4+
use mithril_common::StdError;
5+
46
/// Error encountered or produced by the Runtime.
57
/// This enum represents the faith of the errors produced during the state
68
/// transitions.
79
#[derive(Error, Debug)]
810
pub enum RuntimeError {
911
/// Errors that need the runtime to try again without changing its state.
10-
#[error("An error occured: {message}. This runtime cycle will be skipped.")]
12+
#[error("An error occurred, runtime state kept. message = '{message}'")]
1113
KeepState {
1214
/// error message
1315
message: String,
@@ -18,7 +20,7 @@ pub enum RuntimeError {
1820
},
1921
/// A Critical error means the Runtime stops and the software exits with an
2022
/// error code.
21-
#[error("Critical error:'{message}'.")]
23+
#[error("A critical error occurred, aborting runtime. message = '{message}'")]
2224
Critical {
2325
/// error message
2426
message: String,
@@ -28,7 +30,7 @@ pub enum RuntimeError {
2830
nested_error: Option<StdError>,
2931
},
3032
/// An error that needs to re-initialize the state machine.
31-
#[error("An error occured: {message}. The state machine will be re-initialized.")]
33+
#[error("An error occurred, runtime will be re-initialized. message = '{message}'")]
3234
ReInit {
3335
/// error message
3436
message: String,
@@ -40,6 +42,11 @@ pub enum RuntimeError {
4042
}
4143

4244
impl RuntimeError {
45+
/// Easy matching Critical errors.
46+
pub fn is_critical(&self) -> bool {
47+
matches!(self, RuntimeError::Critical { .. })
48+
}
49+
4350
/// Create a new KeepState error
4451
pub fn keep_state(message: &str, error: Option<StdError>) -> Self {
4552
Self::KeepState {
@@ -55,6 +62,22 @@ impl RuntimeError {
5562
nested_error: error,
5663
}
5764
}
65+
66+
/// Write the error to the given logger.
67+
pub fn write_to_log(&self, logger: &Logger) {
68+
match self {
69+
Self::KeepState { nested_error, .. } | Self::ReInit { nested_error, .. } => {
70+
match nested_error {
71+
None => error!(logger, "{self}"),
72+
Some(err) => error!(logger, "{self}"; "nested_error" => ?err),
73+
}
74+
}
75+
Self::Critical { nested_error, .. } => match nested_error {
76+
None => crit!(logger, "{self}"),
77+
Some(err) => crit!(logger, "{self}"; "nested_error" => ?err),
78+
},
79+
}
80+
}
5881
}
5982

6083
impl From<StdError> for RuntimeError {
@@ -65,3 +88,156 @@ impl From<StdError> for RuntimeError {
6588
}
6689
}
6790
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use anyhow::anyhow;
95+
use std::path::Path;
96+
97+
use mithril_common::test_utils::TempDir;
98+
99+
use crate::test_tools::TestLogger;
100+
101+
use super::*;
102+
103+
/// Separate function so the logger is dropped and flushed before the assertion.
104+
fn write_log(log_file: &Path, error: &RuntimeError) {
105+
let logger = TestLogger::file(log_file);
106+
error.write_to_log(&logger);
107+
}
108+
109+
fn nested_error_debug_string(error: &RuntimeError) -> String {
110+
let error = match error {
111+
RuntimeError::KeepState { nested_error, .. } => nested_error,
112+
RuntimeError::Critical { nested_error, .. } => nested_error,
113+
RuntimeError::ReInit { nested_error, .. } => nested_error,
114+
};
115+
match error {
116+
None => String::new(),
117+
Some(err) => {
118+
format!("{err:?}")
119+
}
120+
}
121+
}
122+
123+
#[test]
124+
fn log_critical_without_nested_error() {
125+
let log_file = TempDir::create(
126+
"aggregator_runtime_error",
127+
"log_critical_without_nested_error",
128+
)
129+
.join("file.log");
130+
131+
let error = RuntimeError::Critical {
132+
message: "Critical error".to_string(),
133+
nested_error: None,
134+
};
135+
write_log(&log_file, &error);
136+
137+
let log_content = std::fs::read_to_string(&log_file).unwrap();
138+
assert!(log_content.contains(&format!("{error}")));
139+
assert!(!log_content.contains("nested_error"));
140+
}
141+
142+
#[test]
143+
fn log_critical_with_nested_error() {
144+
let log_file =
145+
TempDir::create("aggregator_runtime_error", "log_critical_with_nested_error")
146+
.join("file.log");
147+
148+
let error = RuntimeError::Critical {
149+
message: "Critical error".to_string(),
150+
nested_error: Some(
151+
anyhow!("Another context error")
152+
.context("Context error")
153+
.context("Critical nested error"),
154+
),
155+
};
156+
write_log(&log_file, &error);
157+
158+
let log_content = std::fs::read_to_string(&log_file).unwrap();
159+
assert!(log_content.contains(&format!("{error}")));
160+
assert!(log_content.contains(&nested_error_debug_string(&error)));
161+
}
162+
163+
#[test]
164+
fn log_keep_state_without_nested_error() {
165+
let log_file = TempDir::create(
166+
"aggregator_runtime_error",
167+
"log_keep_state_without_nested_error",
168+
)
169+
.join("file.log");
170+
171+
let error = RuntimeError::KeepState {
172+
message: "KeepState error".to_string(),
173+
nested_error: None,
174+
};
175+
write_log(&log_file, &error);
176+
177+
let log_content = std::fs::read_to_string(&log_file).unwrap();
178+
assert!(log_content.contains(&format!("{error}")));
179+
assert!(!log_content.contains("nested_error"));
180+
}
181+
182+
#[test]
183+
fn log_keep_state_with_nested_error() {
184+
let log_file = TempDir::create(
185+
"aggregator_runtime_error",
186+
"log_keep_state_with_nested_error",
187+
)
188+
.join("file.log");
189+
190+
let error = RuntimeError::KeepState {
191+
message: "KeepState error".to_string(),
192+
nested_error: Some(
193+
anyhow!("Another context error")
194+
.context("Context error")
195+
.context("KeepState nested error"),
196+
),
197+
};
198+
write_log(&log_file, &error);
199+
200+
let log_content = std::fs::read_to_string(&log_file).unwrap();
201+
assert!(log_content.contains(&format!("{error}")));
202+
assert!(log_content.contains(&nested_error_debug_string(&error)));
203+
}
204+
205+
#[test]
206+
fn log_reinit_without_nested_error() {
207+
let log_file = TempDir::create(
208+
"aggregator_runtime_error",
209+
"log_reinit_without_nested_error",
210+
)
211+
.join("file.log");
212+
213+
let error = RuntimeError::ReInit {
214+
message: "ReInit error".to_string(),
215+
nested_error: None,
216+
};
217+
write_log(&log_file, &error);
218+
219+
let log_content = std::fs::read_to_string(&log_file).unwrap();
220+
assert!(log_content.contains(&format!("{error}")));
221+
assert!(!log_content.contains("nested_error"));
222+
}
223+
224+
#[test]
225+
fn log_reinit_with_nested_error() {
226+
let log_file = TempDir::create("aggregator_runtime_error", "log_reinit_with_nested_error")
227+
.join("file.log");
228+
229+
let error = RuntimeError::ReInit {
230+
message: "ReInit error".to_string(),
231+
nested_error: Some(
232+
anyhow!("Another context error")
233+
.context("Context error")
234+
.context("ReInit nested error"),
235+
),
236+
};
237+
write_log(&log_file, &error);
238+
239+
let log_content = std::fs::read_to_string(&log_file).unwrap();
240+
assert!(log_content.contains(&format!("{error}")));
241+
assert!(log_content.contains(&nested_error_debug_string(&error)));
242+
}
243+
}

mithril-aggregator/src/runtime/state_machine.rs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
use anyhow::Context;
88
use mithril_common::entities::TimePoint;
99
use mithril_common::logging::LoggerExtensions;
10-
use slog::{crit, info, trace, warn, Logger};
10+
use slog::{info, trace, Logger};
1111
use std::fmt::Display;
1212
use std::sync::Arc;
1313
use tokio::time::sleep;
@@ -108,46 +108,9 @@ impl AggregatorRuntime {
108108

109109
loop {
110110
if let Err(e) = self.cycle().await {
111-
warn!(self.logger, "State machine issued an error"; "error" => ?e);
112-
113-
match &e {
114-
RuntimeError::Critical {
115-
message: _,
116-
nested_error: _,
117-
} => {
118-
crit!(self.logger, "A critical error occurred"; "error" => ?e);
119-
120-
return Err(e);
121-
}
122-
RuntimeError::KeepState {
123-
message,
124-
nested_error,
125-
} => {
126-
warn!(
127-
self.logger,
128-
"KeepState Error: {message}. Nested error: «{}».",
129-
nested_error
130-
.as_ref()
131-
.map(|e| format!("{e:?}"))
132-
.unwrap_or("None".into())
133-
);
134-
}
135-
RuntimeError::ReInit {
136-
message,
137-
nested_error,
138-
} => {
139-
warn!(
140-
self.logger,
141-
"ReInit Error: {message}. Nested error: «{}».",
142-
nested_error
143-
.as_ref()
144-
.map(|e| format!("{e:?}"))
145-
.unwrap_or("None".into())
146-
);
147-
self.state = AggregatorState::Idle(IdleState {
148-
current_time_point: None,
149-
});
150-
}
111+
e.write_to_log(&self.logger);
112+
if e.is_critical() {
113+
return Err(e);
151114
}
152115
}
153116

0 commit comments

Comments
 (0)