@@ -20,14 +20,13 @@ use crate::{
20
20
prelude:: { app_dir, CONFIG_DIR , VERSION } ,
21
21
} ;
22
22
23
- /// The logger must be assigned to a variable because we're using async logging.
24
- /// We should also avoid doing this if we're just going to relaunch into detached mode anyway.
23
+ /// The logger handle must be retained until the application closes.
25
24
/// https://docs.rs/flexi_logger/0.23.1/flexi_logger/error_info/index.html#write
26
25
fn prepare_logging ( ) -> Result < flexi_logger:: LoggerHandle , flexi_logger:: FlexiLoggerError > {
27
26
flexi_logger:: Logger :: try_with_env_or_str ( "ludusavi=warn" )
28
27
. unwrap ( )
29
28
. log_to_file ( flexi_logger:: FileSpec :: default ( ) . directory ( app_dir ( ) . as_std_path_buf ( ) . unwrap ( ) ) )
30
- . write_mode ( flexi_logger:: WriteMode :: Async )
29
+ . write_mode ( flexi_logger:: WriteMode :: BufferAndFlush )
31
30
. rotate (
32
31
flexi_logger:: Criterion :: Size ( 1024 * 1024 * 10 ) ,
33
32
flexi_logger:: Naming :: Timestamps ,
@@ -47,6 +46,37 @@ fn prepare_logging() -> Result<flexi_logger::LoggerHandle, flexi_logger::FlexiLo
47
46
. start ( )
48
47
}
49
48
49
+ /// Based on: https://github.com/Traverse-Research/panic-log/blob/874a61b24a8bc8f9b07f9c26dc10b13cbc2622f9/src/lib.rs#L26
50
+ /// Modified to flush a provided log handle.
51
+ fn prepare_panic_hook ( handle : Option < flexi_logger:: LoggerHandle > ) {
52
+ let original_hook = std:: panic:: take_hook ( ) ;
53
+ std:: panic:: set_hook ( Box :: new ( move |info| {
54
+ let thread_name = std:: thread:: current ( ) . name ( ) . unwrap_or ( "<unnamed thread>" ) . to_owned ( ) ;
55
+
56
+ let location = if let Some ( panic_location) = info. location ( ) {
57
+ format ! (
58
+ "{}:{}:{}" ,
59
+ panic_location. file( ) ,
60
+ panic_location. line( ) ,
61
+ panic_location. column( )
62
+ )
63
+ } else {
64
+ "<unknown location>" . to_owned ( )
65
+ } ;
66
+ let message = info. payload ( ) . downcast_ref :: < & str > ( ) . unwrap_or ( & "" ) ;
67
+
68
+ let backtrace = std:: backtrace:: Backtrace :: force_capture ( ) ;
69
+
70
+ log:: error!( "thread '{thread_name}' panicked at {location}:\n {message}\n stack backtrace:\n {backtrace}" ) ;
71
+
72
+ if let Some ( handle) = handle. clone ( ) {
73
+ handle. flush ( ) ;
74
+ }
75
+
76
+ original_hook ( info) ;
77
+ } ) ) ;
78
+ }
79
+
50
80
/// Detach the current process from its console on Windows.
51
81
///
52
82
/// ## Testing
@@ -87,33 +117,69 @@ fn prepare_logging() -> Result<flexi_logger::LoggerHandle, flexi_logger::FlexiLo
87
117
/// ("The request is not supported (os error 50)"),
88
118
/// but that has been solved by resetting the standard device handles:
89
119
/// https://github.com/rust-lang/rust/issues/113277
120
+ ///
121
+ /// Watch out for non-obvious code paths that may defeat detachment.
122
+ /// flexi_logger's `colors` feature would cause the console to stick around
123
+ /// if logging was enabled before detaching.
90
124
#[ cfg( target_os = "windows" ) ]
91
125
unsafe fn detach_console ( ) {
92
126
use windows:: Win32 :: {
93
127
Foundation :: HANDLE ,
94
128
System :: Console :: { FreeConsole , SetStdHandle , STD_ERROR_HANDLE , STD_INPUT_HANDLE , STD_OUTPUT_HANDLE } ,
95
129
} ;
96
130
131
+ fn tell ( msg : & str ) {
132
+ eprintln ! ( "{}" , msg) ;
133
+ log:: error!( "{}" , msg) ;
134
+ }
135
+
97
136
if FreeConsole ( ) . is_err ( ) {
98
- eprintln ! ( "Unable to detach the console" ) ;
137
+ tell ( "Unable to detach the console" ) ;
99
138
std:: process:: exit ( 1 ) ;
100
139
}
101
140
if SetStdHandle ( STD_INPUT_HANDLE , HANDLE :: default ( ) ) . is_err ( ) {
102
- eprintln ! ( "Unable to reset stdin handle" ) ;
141
+ tell ( "Unable to reset stdin handle" ) ;
103
142
std:: process:: exit ( 1 ) ;
104
143
}
105
144
if SetStdHandle ( STD_OUTPUT_HANDLE , HANDLE :: default ( ) ) . is_err ( ) {
106
- eprintln ! ( "Unable to reset stdout handle" ) ;
145
+ tell ( "Unable to reset stdout handle" ) ;
107
146
std:: process:: exit ( 1 ) ;
108
147
}
109
148
if SetStdHandle ( STD_ERROR_HANDLE , HANDLE :: default ( ) ) . is_err ( ) {
110
- eprintln ! ( "Unable to reset stderr handle" ) ;
149
+ tell ( "Unable to reset stderr handle" ) ;
111
150
std:: process:: exit ( 1 ) ;
112
151
}
113
152
}
114
153
115
154
fn main ( ) {
116
- let args = cli:: parse ( ) ;
155
+ let mut failed = false ;
156
+
157
+ let mut logger = prepare_logging ( ) ;
158
+ #[ allow( clippy:: useless_asref) ]
159
+ prepare_panic_hook ( logger. as_ref ( ) . map ( |x| x. clone ( ) ) . ok ( ) ) ;
160
+ let mut flush_logger = || {
161
+ if let Ok ( logger) = & mut logger {
162
+ logger. flush ( ) ;
163
+ }
164
+ } ;
165
+
166
+ log:: debug!( "Version: {}" , * VERSION ) ;
167
+ log:: debug!( "Invocation: {:?}" , std:: env:: args( ) ) ;
168
+
169
+ let args = match cli:: parse ( ) {
170
+ Ok ( x) => x,
171
+ Err ( e) => {
172
+ match e. kind ( ) {
173
+ clap:: error:: ErrorKind :: DisplayHelp | clap:: error:: ErrorKind :: DisplayVersion => { }
174
+ _ => {
175
+ log:: error!( "CLI failed to parse: {e}" ) ;
176
+ }
177
+ }
178
+ flush_logger ( ) ;
179
+ e. exit ( )
180
+ }
181
+ } ;
182
+
117
183
if let Some ( config_dir) = args. config . as_deref ( ) {
118
184
* CONFIG_DIR . lock ( ) . unwrap ( ) = Some ( config_dir. to_path_buf ( ) ) ;
119
185
}
@@ -126,30 +192,25 @@ fn main() {
126
192
}
127
193
}
128
194
129
- // We must do this after detaching the console, or else it will still be present, somehow.
130
- #[ allow( unused) ]
131
- let logger = prepare_logging ( ) ;
132
-
133
- log:: debug!( "Version: {}" , * VERSION ) ;
134
-
135
195
let flags = Flags {
136
196
update_manifest : !args. no_manifest_update ,
137
197
} ;
138
198
gui:: run ( flags) ;
139
199
}
140
200
Some ( sub) => {
141
- #[ allow( unused) ]
142
- let logger = prepare_logging ( ) ;
143
-
144
201
let gui = sub. gui ( ) ;
145
202
let force = sub. force ( ) ;
146
203
147
- log:: debug!( "Version: {}" , * VERSION ) ;
148
-
149
204
if let Err ( e) = cli:: run ( sub, args. no_manifest_update , args. try_manifest_update ) {
205
+ failed = true ;
150
206
cli:: show_error ( & e, gui, force) ;
151
- std:: process:: exit ( 1 ) ;
152
207
}
153
208
}
154
209
} ;
210
+
211
+ flush_logger ( ) ;
212
+
213
+ if failed {
214
+ std:: process:: exit ( 1 ) ;
215
+ }
155
216
}
0 commit comments