@@ -112,11 +112,15 @@ pub enum Event {
112112 Error {
113113 error : String ,
114114 } ,
115+ Print {
116+ message : String ,
117+ } ,
115118 Stopping {
116119 inflight : usize ,
117120 } ,
118121 Stopped ,
119122 StopTimedOut ,
123+ Shutdown ,
120124}
121125
122126// --- Broadcast channel ---
@@ -192,6 +196,12 @@ pub fn log_error(error: &str) {
192196 } ) ;
193197}
194198
199+ pub fn log_print ( message : & str ) {
200+ emit ( Event :: Print {
201+ message : message. to_string ( ) ,
202+ } ) ;
203+ }
204+
195205pub fn log_stopping ( inflight : usize ) {
196206 emit ( Event :: Stopping { inflight } ) ;
197207}
@@ -204,9 +214,13 @@ pub fn log_stop_timed_out() {
204214 emit ( Event :: StopTimedOut ) ;
205215}
206216
217+ pub fn shutdown ( ) {
218+ emit ( Event :: Shutdown ) ;
219+ }
220+
207221// --- JSONL handler (dedicated writer thread does serialization + IO) ---
208222
209- pub fn run_jsonl_handler ( rx : broadcast:: Receiver < Event > ) {
223+ pub fn run_jsonl_handler ( rx : broadcast:: Receiver < Event > ) -> std :: thread :: JoinHandle < ( ) > {
210224 use std:: io:: Write ;
211225
212226 std:: thread:: spawn ( move || {
@@ -231,13 +245,19 @@ pub fn run_jsonl_handler(rx: broadcast::Receiver<Event>) {
231245 Err ( broadcast:: error:: RecvError :: Closed ) => break ,
232246 } ;
233247
248+ if matches ! ( event, Event :: Shutdown ) {
249+ let _ = stdout. flush ( ) ;
250+ break ;
251+ }
252+
234253 let needs_flush = matches ! (
235254 & event,
236255 Event :: Started { .. }
237256 | Event :: Stopped
238257 | Event :: StopTimedOut
239258 | Event :: Reloaded
240259 | Event :: Error { .. }
260+ | Event :: Print { .. }
241261 ) ;
242262
243263 let stamp = scru128:: new ( ) . to_string ( ) ;
@@ -309,6 +329,13 @@ pub fn run_jsonl_handler(rx: broadcast::Receiver<Event>) {
309329 "error" : error,
310330 } )
311331 }
332+ Event :: Print { message } => {
333+ serde_json:: json!( {
334+ "stamp" : stamp,
335+ "message" : "print" ,
336+ "content" : message,
337+ } )
338+ }
312339 Event :: Stopping { inflight } => {
313340 serde_json:: json!( {
314341 "stamp" : stamp,
@@ -328,6 +355,7 @@ pub fn run_jsonl_handler(rx: broadcast::Receiver<Event>) {
328355 "message" : "stop_timed_out" ,
329356 } )
330357 }
358+ Event :: Shutdown => unreachable ! ( ) ,
331359 } ;
332360
333361 if let Ok ( line) = serde_json:: to_string ( & json) {
@@ -341,7 +369,7 @@ pub fn run_jsonl_handler(rx: broadcast::Receiver<Event>) {
341369 }
342370
343371 let _ = stdout. flush ( ) ;
344- } ) ;
372+ } )
345373}
346374
347375// --- Human-readable handler (dedicated thread) ---
@@ -445,7 +473,7 @@ fn format_complete_line(state: &RequestState, duration_ms: u64, bytes: u64) -> S
445473 )
446474}
447475
448- pub fn run_human_handler ( rx : broadcast:: Receiver < Event > ) {
476+ pub fn run_human_handler ( rx : broadcast:: Receiver < Event > ) -> std :: thread :: JoinHandle < ( ) > {
449477 std:: thread:: spawn ( move || {
450478 let mut rx = rx;
451479 let mut zone = ActiveZone :: new ( ) ;
@@ -498,6 +526,10 @@ pub fn run_human_handler(rx: broadcast::Receiver<Event>) {
498526 eprintln ! ( "ERROR: {error}" ) ;
499527 zone. redraw ( & active_ids, & requests) ;
500528 }
529+ Event :: Print { message } => {
530+ zone. print_permanent ( & format ! ( "PRINT: {message}" ) ) ;
531+ zone. redraw ( & active_ids, & requests) ;
532+ }
501533 Event :: Stopping { inflight } => {
502534 zone. print_permanent ( & format ! (
503535 "stopping, {inflight} connection(s) in flight..."
@@ -567,6 +599,7 @@ pub fn run_human_handler(rx: broadcast::Receiver<Event>) {
567599 zone. redraw ( & active_ids, & requests) ;
568600 }
569601 }
602+ Event :: Shutdown => break ,
570603 }
571604 }
572605
@@ -578,7 +611,7 @@ pub fn run_human_handler(rx: broadcast::Receiver<Event>) {
578611 if lagged > 0 {
579612 println ! ( "⚠ total lagged: {lagged} events dropped" ) ;
580613 }
581- } ) ;
614+ } )
582615}
583616
584617// --- RequestGuard: ensures Complete fires even on abort ---
0 commit comments