1+ use slog:: { crit, error, Logger } ;
12use std:: fmt:: Display ;
23
34use mithril_common:: entities:: EpochError ;
@@ -40,39 +41,37 @@ impl RuntimeError {
4041 }
4142 )
4243 }
44+
45+ /// Write the error to the given logger.
46+ pub fn write_to_log ( & self , logger : & Logger ) {
47+ match self {
48+ Self :: KeepState { nested_error, .. } => match nested_error {
49+ None => error ! ( logger, "{self}" ) ,
50+ Some ( err) => error ! ( logger, "{self}" ; "nested_error" => ?err) ,
51+ } ,
52+ Self :: Critical { nested_error, .. } => match nested_error {
53+ None => crit ! ( logger, "{self}" ) ,
54+ Some ( err) => crit ! ( logger, "{self}" ; "nested_error" => ?err) ,
55+ } ,
56+ }
57+ }
4358}
4459
4560impl std:: error:: Error for RuntimeError { }
4661
4762impl Display for RuntimeError {
4863 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
4964 match self {
50- Self :: KeepState {
51- message,
52- nested_error,
53- } => {
54- let nested = if let Some ( error) = nested_error {
55- format ! ( "nested error = {error:?}" )
56- } else {
57- String :: new ( )
58- } ;
65+ Self :: KeepState { message, .. } => {
5966 write ! (
6067 f,
61- "An error occurred, runtime state kept. message = '{message}'. {nested} "
68+ "An error occurred, runtime state kept. message = '{message}'"
6269 )
6370 }
64- Self :: Critical {
65- message,
66- nested_error,
67- } => {
68- let nested = if let Some ( error) = nested_error {
69- format ! ( "nested error = {error:?}" )
70- } else {
71- String :: new ( )
72- } ;
71+ Self :: Critical { message, .. } => {
7372 write ! (
7473 f,
75- "A critical error occurred, aborting runtime. message = '{message}'. {nested} "
74+ "A critical error occurred, aborting runtime. message = '{message}'"
7675 )
7776 }
7877 }
@@ -96,3 +95,109 @@ impl From<EpochError> for RuntimeError {
9695 }
9796 }
9897}
98+
99+ #[ cfg( test) ]
100+ mod tests {
101+ use anyhow:: anyhow;
102+ use std:: path:: Path ;
103+
104+ use mithril_common:: test_utils:: TempDir ;
105+
106+ use crate :: test_tools:: TestLogger ;
107+
108+ use super :: * ;
109+
110+ /// Separate function so the logger is dropped and flushed before the assertion.
111+ fn write_log ( log_file : & Path , error : & RuntimeError ) {
112+ let logger = TestLogger :: file ( log_file) ;
113+ error. write_to_log ( & logger) ;
114+ }
115+
116+ fn nested_error_debug_string ( error : & RuntimeError ) -> String {
117+ let error = match error {
118+ RuntimeError :: KeepState { nested_error, .. } => nested_error,
119+ RuntimeError :: Critical { nested_error, .. } => nested_error,
120+ } ;
121+ match error {
122+ None => String :: new ( ) ,
123+ Some ( err) => {
124+ format ! ( "{err:?}" )
125+ }
126+ }
127+ }
128+
129+ #[ test]
130+ fn log_critical_without_nested_error ( ) {
131+ let log_file = TempDir :: create ( "signer_runtime_error" , "log_critical_without_nested_error" )
132+ . join ( "file.log" ) ;
133+
134+ let error = RuntimeError :: Critical {
135+ message : "Critical error" . to_string ( ) ,
136+ nested_error : None ,
137+ } ;
138+ write_log ( & log_file, & error) ;
139+
140+ let log_content = std:: fs:: read_to_string ( & log_file) . unwrap ( ) ;
141+ assert ! ( log_content. contains( & format!( "{error}" ) ) ) ;
142+ assert ! ( !log_content. contains( "nested_error" ) ) ;
143+ }
144+
145+ #[ test]
146+ fn log_critical_with_nested_error ( ) {
147+ let log_file = TempDir :: create ( "signer_runtime_error" , "log_critical_with_nested_error" )
148+ . join ( "file.log" ) ;
149+
150+ let error = RuntimeError :: Critical {
151+ message : "Critical error" . to_string ( ) ,
152+ nested_error : Some (
153+ anyhow ! ( "Another context error" )
154+ . context ( "Context error" )
155+ . context ( "Critical nested error" ) ,
156+ ) ,
157+ } ;
158+ write_log ( & log_file, & error) ;
159+
160+ let log_content = std:: fs:: read_to_string ( & log_file) . unwrap ( ) ;
161+ assert ! ( log_content. contains( & format!( "{error}" ) ) ) ;
162+ assert ! ( log_content. contains( & nested_error_debug_string( & error) ) ) ;
163+ }
164+
165+ #[ test]
166+ fn log_keep_state_without_nested_error ( ) {
167+ let log_file = TempDir :: create (
168+ "signer_runtime_error" ,
169+ "log_keep_state_without_nested_error" ,
170+ )
171+ . join ( "file.log" ) ;
172+
173+ let error = RuntimeError :: KeepState {
174+ message : "KeepState error" . to_string ( ) ,
175+ nested_error : None ,
176+ } ;
177+ write_log ( & log_file, & error) ;
178+
179+ let log_content = std:: fs:: read_to_string ( & log_file) . unwrap ( ) ;
180+ assert ! ( log_content. contains( & format!( "{error}" ) ) ) ;
181+ assert ! ( !log_content. contains( "nested_error" ) ) ;
182+ }
183+
184+ #[ test]
185+ fn log_keep_state_with_nested_error ( ) {
186+ let log_file = TempDir :: create ( "signer_runtime_error" , "log_keep_state_with_nested_error" )
187+ . join ( "file.log" ) ;
188+
189+ let error = RuntimeError :: KeepState {
190+ message : "KeepState error" . to_string ( ) ,
191+ nested_error : Some (
192+ anyhow ! ( "Another context error" )
193+ . context ( "Context error" )
194+ . context ( "KeepState nested error" ) ,
195+ ) ,
196+ } ;
197+ write_log ( & log_file, & error) ;
198+
199+ let log_content = std:: fs:: read_to_string ( & log_file) . unwrap ( ) ;
200+ assert ! ( log_content. contains( & format!( "{error}" ) ) ) ;
201+ assert ! ( log_content. contains( & nested_error_debug_string( & error) ) ) ;
202+ }
203+ }
0 commit comments