1
- use mithril_common :: StdError ;
1
+ use slog :: { crit , error , Logger } ;
2
2
use thiserror:: Error ;
3
3
4
+ use mithril_common:: StdError ;
5
+
4
6
/// Error encountered or produced by the Runtime.
5
7
/// This enum represents the faith of the errors produced during the state
6
8
/// transitions.
7
9
#[ derive( Error , Debug ) ]
8
10
pub enum RuntimeError {
9
11
/// 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}' " ) ]
11
13
KeepState {
12
14
/// error message
13
15
message : String ,
@@ -18,7 +20,7 @@ pub enum RuntimeError {
18
20
} ,
19
21
/// A Critical error means the Runtime stops and the software exits with an
20
22
/// error code.
21
- #[ error( "Critical error: '{message}'. " ) ]
23
+ #[ error( "A critical error occurred, aborting runtime. message = '{message}'" ) ]
22
24
Critical {
23
25
/// error message
24
26
message : String ,
@@ -28,7 +30,7 @@ pub enum RuntimeError {
28
30
nested_error : Option < StdError > ,
29
31
} ,
30
32
/// 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}' " ) ]
32
34
ReInit {
33
35
/// error message
34
36
message : String ,
@@ -40,6 +42,11 @@ pub enum RuntimeError {
40
42
}
41
43
42
44
impl RuntimeError {
45
+ /// Easy matching Critical errors.
46
+ pub fn is_critical ( & self ) -> bool {
47
+ matches ! ( self , RuntimeError :: Critical { .. } )
48
+ }
49
+
43
50
/// Create a new KeepState error
44
51
pub fn keep_state ( message : & str , error : Option < StdError > ) -> Self {
45
52
Self :: KeepState {
@@ -55,6 +62,22 @@ impl RuntimeError {
55
62
nested_error : error,
56
63
}
57
64
}
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
+ }
58
81
}
59
82
60
83
impl From < StdError > for RuntimeError {
@@ -65,3 +88,156 @@ impl From<StdError> for RuntimeError {
65
88
}
66
89
}
67
90
}
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
+ }
0 commit comments