@@ -25,13 +25,14 @@ use tower::ServiceBuilder;
2525use tracing:: Instrument ;
2626
2727use crate :: {
28- MIN_CORE_VERSION , VERSION , acme,
28+ LogsReceiver , MIN_CORE_VERSION , VERSION , acme,
2929 acme:: Port80Permit ,
3030 error:: ApiError ,
3131 http:: { GRPC_CERT_NAME , GRPC_KEY_NAME } ,
3232 proto:: {
33- AcmeCertificate , AcmeChallenge , AcmeIssueEvent , AcmeProgress , AcmeStep , CoreRequest ,
34- CoreResponse , DeviceInfo , acme_issue_event, core_request, core_response, proxy_server,
33+ AcmeCertificate , AcmeChallenge , AcmeIssueEvent , AcmeLogs , AcmeProgress , AcmeStep ,
34+ CoreRequest , CoreResponse , DeviceInfo , acme_issue_event, core_request, core_response,
35+ proxy_server,
3536 } ,
3637} ;
3738
@@ -58,6 +59,9 @@ pub(crate) struct ProxyServer {
5859 /// `Some` only when the main HTTP server is bound to port 80.
5960 /// Used to hand off port 80 gracefully during ACME HTTP-01 challenges.
6061 port80_pause_tx : Option < mpsc:: Sender < ( oneshot:: Sender < ( ) > , oneshot:: Receiver < ( ) > ) > > ,
62+ /// Shared log receiver - written by `GrpcLogLayer` for every tracing event.
63+ /// Drained during ACME execution to collect proxy log lines for error reporting.
64+ logs_rx : LogsReceiver ,
6165}
6266
6367impl ProxyServer {
@@ -69,6 +73,7 @@ impl ProxyServer {
6973 reset_tx : broadcast:: Sender < ( ) > ,
7074 https_cert_tx : broadcast:: Sender < ( String , String ) > ,
7175 port80_pause_tx : Option < mpsc:: Sender < ( oneshot:: Sender < ( ) > , oneshot:: Receiver < ( ) > ) > > ,
76+ logs_rx : LogsReceiver ,
7277 ) -> Self {
7378 Self {
7479 cookie_key,
@@ -82,6 +87,7 @@ impl ProxyServer {
8287 reset_tx,
8388 https_cert_tx,
8489 port80_pause_tx,
90+ logs_rx,
8591 }
8692 }
8793
@@ -205,6 +211,7 @@ impl Clone for ProxyServer {
205211 reset_tx : self . reset_tx . clone ( ) ,
206212 https_cert_tx : self . https_cert_tx . clone ( ) ,
207213 port80_pause_tx : self . port80_pause_tx . clone ( ) ,
214+ logs_rx : Arc :: clone ( & self . logs_rx ) ,
208215 }
209216 }
210217}
@@ -460,14 +467,21 @@ impl proxy_server::Proxy for ProxyServer {
460467
461468 let ( tx, rx) = mpsc:: unbounded_channel :: < Result < AcmeIssueEvent , Status > > ( ) ;
462469
463- // Emit the first progress step immediately — we are connected and about to start.
470+ // Emit the first progress step immediately - we are connected and about to start.
464471 let _ = tx. send ( Ok ( AcmeIssueEvent {
465472 payload : Some ( acme_issue_event:: Payload :: Progress ( AcmeProgress {
466473 step : AcmeStep :: Connecting as i32 ,
467474 } ) ) ,
468475 } ) ) ;
469476
477+ // Drain any stale log entries accumulated before this ACME run started.
478+ {
479+ let mut rx_guard = self . logs_rx . lock ( ) . await ;
480+ while rx_guard. try_recv ( ) . is_ok ( ) { }
481+ }
482+
470483 let pause_tx = self . port80_pause_tx . clone ( ) ;
484+ let logs_rx = Arc :: clone ( & self . logs_rx ) ;
471485 tokio:: spawn ( async move {
472486 // Request a graceful hand-off of port 80 from the main HTTP server if it is bound
473487 // there, so the ACME challenge listener can bind.
@@ -505,7 +519,7 @@ impl proxy_server::Proxy for ProxyServer {
505519 } ) ) ,
506520 } ;
507521 if tx_fwd. send ( Ok ( event) ) . is_err ( ) {
508- // Core disconnected — stop forwarding.
522+ // Core disconnected - stop forwarding.
509523 break ;
510524 }
511525 }
@@ -538,6 +552,30 @@ impl proxy_server::Proxy for ProxyServer {
538552 }
539553 Err ( err) => {
540554 error ! ( "ACME HTTP-01 failed for domain '{domain}': {err}" ) ;
555+
556+ // Drain collected log lines and forward them to Core before the error status.
557+ let log_lines: Vec < String > = {
558+ let mut rx_guard = logs_rx. lock ( ) . await ;
559+ let mut lines = Vec :: new ( ) ;
560+ while let Ok ( entry) = rx_guard. try_recv ( ) {
561+ lines. push ( format ! (
562+ "{} {} {}: message={}" ,
563+ entry. timestamp,
564+ entry. level. to_uppercase( ) ,
565+ entry. target,
566+ entry. message
567+ ) ) ;
568+ }
569+ lines
570+ } ;
571+ if !log_lines. is_empty ( ) {
572+ let _ = tx. send ( Ok ( AcmeIssueEvent {
573+ payload : Some ( acme_issue_event:: Payload :: Logs ( AcmeLogs {
574+ lines : log_lines,
575+ } ) ) ,
576+ } ) ) ;
577+ }
578+
541579 let _ = tx. send ( Err ( Status :: internal ( format ! (
542580 "ACME HTTP-01 certificate issuance failed: {err}"
543581 ) ) ) ) ;
0 commit comments