@@ -12,6 +12,9 @@ use spin_factors::RuntimeFactors;
1212use spin_factors_executor:: ExecutorHooks ;
1313use tokio:: io:: AsyncWrite ;
1414
15+ pub const STDOUT_LOG_FILE_SUFFIX : & str = "stdout" ;
16+ pub const STDERR_LOG_FILE_SUFFIX : & str = "stderr" ;
17+
1518/// Which components should have their logs followed on stdout/stderr.
1619#[ derive( Clone , Debug , Default ) ]
1720pub enum FollowComponents {
@@ -39,13 +42,19 @@ impl FollowComponents {
3942pub struct StdioLoggingExecutorHooks {
4043 follow_components : FollowComponents ,
4144 log_dir : Option < PathBuf > ,
45+ truncate_log : bool ,
4246}
4347
4448impl StdioLoggingExecutorHooks {
45- pub fn new ( follow_components : FollowComponents , log_dir : Option < PathBuf > ) -> Self {
49+ pub fn new (
50+ follow_components : FollowComponents ,
51+ log_dir : Option < PathBuf > ,
52+ truncate_log : bool ,
53+ ) -> Self {
4654 Self {
4755 follow_components,
4856 log_dir,
57+ truncate_log,
4958 }
5059 }
5160
@@ -56,8 +65,7 @@ impl StdioLoggingExecutorHooks {
5665 log_dir : Option < & Path > ,
5766 ) -> Result < ComponentStdioWriter > {
5867 let sanitized_component_id = sanitize_filename:: sanitize ( component_id) ;
59- let log_path = log_dir
60- . map ( |log_dir| log_dir. join ( format ! ( "{sanitized_component_id}_{log_suffix}.txt" , ) ) ) ;
68+ let log_path = sanitized_log_path ( log_dir, & sanitized_component_id, log_suffix) ;
6169 let log_path = log_path. as_deref ( ) ;
6270
6371 let follow = self . follow_components . should_follow ( component_id) ;
@@ -86,6 +94,35 @@ impl StdioLoggingExecutorHooks {
8694 _ => Ok ( ( ) ) ,
8795 }
8896 }
97+
98+ fn truncate_log_files < F : RuntimeFactors > (
99+ & self ,
100+ configured_app : & spin_factors:: ConfiguredApp < F > ,
101+ ) {
102+ let sanitized_component_ids: Vec < String > = configured_app
103+ . app ( )
104+ . components ( )
105+ . map ( |c| sanitize_filename:: sanitize ( c. locked . id . clone ( ) ) )
106+ . collect ( ) ;
107+
108+ for sanitized_component_id in sanitized_component_ids {
109+ if let Some ( stdout_log_path) = sanitized_log_path (
110+ self . log_dir . as_deref ( ) ,
111+ & sanitized_component_id,
112+ STDOUT_LOG_FILE_SUFFIX ,
113+ ) {
114+ _ = std:: fs:: File :: create ( stdout_log_path) ;
115+ }
116+
117+ if let Some ( stderr_log_path) = sanitized_log_path (
118+ self . log_dir . as_deref ( ) ,
119+ & sanitized_component_id,
120+ STDERR_LOG_FILE_SUFFIX ,
121+ ) {
122+ _ = std:: fs:: File :: create ( stderr_log_path) ;
123+ }
124+ }
125+ }
89126}
90127
91128#[ async_trait]
@@ -95,11 +132,16 @@ impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for StdioLoggingExecutorHooks {
95132 configured_app : & spin_factors:: ConfiguredApp < F > ,
96133 ) -> anyhow:: Result < ( ) > {
97134 self . validate_follows ( configured_app. app ( ) ) ?;
135+
98136 if let Some ( dir) = & self . log_dir {
99137 // Ensure log dir exists if set
100138 std:: fs:: create_dir_all ( dir)
101139 . with_context ( || format ! ( "Failed to create log dir {}" , quoted_path( dir) ) ) ?;
102140
141+ if self . truncate_log {
142+ self . truncate_log_files ( configured_app) ;
143+ }
144+
103145 println ! ( "Logging component stdio to {}" , quoted_path( dir. join( "" ) ) )
104146 }
105147 Ok ( ( ) )
@@ -115,12 +157,12 @@ impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for StdioLoggingExecutorHooks {
115157 } ;
116158 wasi_builder. stdout_pipe ( self . component_stdio_writer (
117159 & component_id,
118- "stdout" ,
160+ STDOUT_LOG_FILE_SUFFIX ,
119161 self . log_dir . as_deref ( ) ,
120162 ) ?) ;
121163 wasi_builder. stderr_pipe ( self . component_stdio_writer (
122164 & component_id,
123- "stderr" ,
165+ STDERR_LOG_FILE_SUFFIX ,
124166 self . log_dir . as_deref ( ) ,
125167 ) ?) ;
126168 Ok ( ( ) )
@@ -328,3 +370,11 @@ fn bullet_list<S: std::fmt::Display>(items: impl IntoIterator<Item = S>) -> Stri
328370 . collect :: < Vec < _ > > ( )
329371 . join ( "\n " )
330372}
373+
374+ fn sanitized_log_path (
375+ log_dir : Option < & Path > ,
376+ sanitized_component_id : & str ,
377+ log_suffix : & str ,
378+ ) -> Option < PathBuf > {
379+ log_dir. map ( |log_dir| log_dir. join ( format ! ( "{sanitized_component_id}_{log_suffix}.txt" , ) ) )
380+ }
0 commit comments