@@ -56,13 +56,19 @@ impl StdioLoggingTriggerHooks {
56
56
& self ,
57
57
component_id : & str ,
58
58
log_suffix : & str ,
59
- log_dir : & Path ,
59
+ log_dir : Option < & Path > ,
60
60
) -> Result < ComponentStdioWriter > {
61
61
let sanitized_component_id = sanitize_filename:: sanitize ( component_id) ;
62
- let log_path = log_dir. join ( format ! ( "{sanitized_component_id}_{log_suffix}.txt" ) ) ;
62
+ let log_path = log_dir
63
+ . map ( |log_dir| log_dir. join ( format ! ( "{sanitized_component_id}_{log_suffix}.txt" , ) ) ) ;
64
+ let log_path = log_path. as_deref ( ) ;
65
+
63
66
let follow = self . follow_components . should_follow ( component_id) ;
64
- ComponentStdioWriter :: new ( & log_path, follow)
65
- . with_context ( || format ! ( "Failed to open log file {}" , quoted_path( & log_path) ) )
67
+ match log_path {
68
+ Some ( log_path) => ComponentStdioWriter :: new_forward ( log_path, follow)
69
+ . with_context ( || format ! ( "Failed to open log file {}" , quoted_path( log_path) ) ) ,
70
+ None => ComponentStdioWriter :: new_inherit ( ) ,
71
+ }
66
72
}
67
73
68
74
fn validate_follows ( & self , app : & spin_app:: App ) -> anyhow:: Result < ( ) > {
@@ -112,27 +118,37 @@ impl TriggerHooks for StdioLoggingTriggerHooks {
112
118
component : & spin_app:: AppComponent ,
113
119
builder : & mut spin_core:: StoreBuilder ,
114
120
) -> anyhow:: Result < ( ) > {
115
- match & self . log_dir {
116
- Some ( l ) => {
117
- builder . stdout_pipe ( self . component_stdio_writer ( component . id ( ) , "stdout" , l ) ? ) ;
118
- builder . stderr_pipe ( self . component_stdio_writer ( component . id ( ) , "stderr" , l ) ? ) ;
119
- }
120
- None => {
121
- builder . inherit_stdout ( ) ;
122
- builder . inherit_stderr ( ) ;
123
- }
124
- }
121
+ builder . stdout_pipe ( self . component_stdio_writer (
122
+ component . id ( ) ,
123
+ "stdout" ,
124
+ self . log_dir . as_deref ( ) ,
125
+ ) ? ) ;
126
+ builder . stderr_pipe ( self . component_stdio_writer (
127
+ component . id ( ) ,
128
+ "stderr" ,
129
+ self . log_dir . as_deref ( ) ,
130
+ ) ? ) ;
125
131
126
132
Ok ( ( ) )
127
133
}
128
134
}
129
135
130
- /// ComponentStdioWriter forwards output to a log file and (optionally) stderr
136
+ /// ComponentStdioWriter forwards output to a log file, (optionally) stderr, and (optionally) to a
137
+ /// tracing compatibility layer.
131
138
pub struct ComponentStdioWriter {
132
- sync_file : std:: fs:: File ,
133
- async_file : tokio:: fs:: File ,
134
- state : ComponentStdioWriterState ,
135
- follow : bool ,
139
+ inner : ComponentStdioWriterInner ,
140
+ }
141
+
142
+ enum ComponentStdioWriterInner {
143
+ /// Inherit stdout/stderr from the parent process.
144
+ Inherit ,
145
+ /// Forward stdout/stderr to a file in addition to the inherited stdout/stderr.
146
+ Forward {
147
+ sync_file : std:: fs:: File ,
148
+ async_file : tokio:: fs:: File ,
149
+ state : ComponentStdioWriterState ,
150
+ follow : bool ,
151
+ } ,
136
152
}
137
153
138
154
#[ derive( Debug ) ]
@@ -142,20 +158,30 @@ enum ComponentStdioWriterState {
142
158
}
143
159
144
160
impl ComponentStdioWriter {
145
- pub fn new ( log_path : & Path , follow : bool ) -> anyhow:: Result < Self > {
161
+ fn new_forward ( log_path : & Path , follow : bool ) -> anyhow:: Result < Self > {
146
162
let sync_file = std:: fs:: File :: options ( )
147
163
. create ( true )
148
164
. append ( true )
149
165
. open ( log_path) ?;
166
+
150
167
let async_file = sync_file
151
168
. try_clone ( )
152
169
. context ( "could not get async file handle" ) ?
153
170
. into ( ) ;
171
+
154
172
Ok ( Self {
155
- async_file,
156
- sync_file,
157
- state : ComponentStdioWriterState :: File ,
158
- follow,
173
+ inner : ComponentStdioWriterInner :: Forward {
174
+ sync_file,
175
+ async_file,
176
+ state : ComponentStdioWriterState :: File ,
177
+ follow,
178
+ } ,
179
+ } )
180
+ }
181
+
182
+ fn new_inherit ( ) -> anyhow:: Result < Self > {
183
+ Ok ( Self {
184
+ inner : ComponentStdioWriterInner :: Inherit ,
159
185
} )
160
186
}
161
187
}
@@ -167,38 +193,56 @@ impl AsyncWrite for ComponentStdioWriter {
167
193
buf : & [ u8 ] ,
168
194
) -> Poll < std:: result:: Result < usize , std:: io:: Error > > {
169
195
let this = self . get_mut ( ) ;
196
+
170
197
loop {
171
- match & this. state {
172
- ComponentStdioWriterState :: File => {
198
+ match & mut this. inner {
199
+ ComponentStdioWriterInner :: Inherit => {
173
200
let written = futures:: ready!(
174
- std:: pin:: Pin :: new( & mut this . async_file ) . poll_write( cx, buf)
201
+ std:: pin:: Pin :: new( & mut tokio :: io :: stderr ( ) ) . poll_write( cx, buf)
175
202
) ;
176
203
let written = match written {
177
- Ok ( e ) => e ,
204
+ Ok ( w ) => w ,
178
205
Err ( e) => return Poll :: Ready ( Err ( e) ) ,
179
206
} ;
180
- if this. follow {
181
- this. state = ComponentStdioWriterState :: Follow ( 0 ..written) ;
182
- } else {
183
- return Poll :: Ready ( Ok ( written) ) ;
184
- }
185
- }
186
- ComponentStdioWriterState :: Follow ( range) => {
187
- let written = futures:: ready!( std:: pin:: Pin :: new( & mut tokio:: io:: stderr( ) )
188
- . poll_write( cx, & buf[ range. clone( ) ] ) ) ;
189
- let written = match written {
190
- Ok ( e) => e,
191
- Err ( e) => return Poll :: Ready ( Err ( e) ) ,
192
- } ;
193
- if range. start + written >= range. end {
194
- let end = range. end ;
195
- this. state = ComponentStdioWriterState :: File ;
196
- return Poll :: Ready ( Ok ( end) ) ;
197
- } else {
198
- this. state =
199
- ComponentStdioWriterState :: Follow ( ( range. start + written) ..range. end ) ;
200
- } ;
207
+ return Poll :: Ready ( Ok ( written) ) ;
201
208
}
209
+ ComponentStdioWriterInner :: Forward {
210
+ async_file,
211
+ state,
212
+ follow,
213
+ ..
214
+ } => match & state {
215
+ ComponentStdioWriterState :: File => {
216
+ let written =
217
+ futures:: ready!( std:: pin:: Pin :: new( async_file) . poll_write( cx, buf) ) ;
218
+ let written = match written {
219
+ Ok ( w) => w,
220
+ Err ( e) => return Poll :: Ready ( Err ( e) ) ,
221
+ } ;
222
+ if * follow {
223
+ * state = ComponentStdioWriterState :: Follow ( 0 ..written) ;
224
+ } else {
225
+ return Poll :: Ready ( Ok ( written) ) ;
226
+ }
227
+ }
228
+ ComponentStdioWriterState :: Follow ( range) => {
229
+ let written = futures:: ready!( std:: pin:: Pin :: new( & mut tokio:: io:: stderr( ) )
230
+ . poll_write( cx, & buf[ range. clone( ) ] ) ) ;
231
+ let written = match written {
232
+ Ok ( w) => w,
233
+ Err ( e) => return Poll :: Ready ( Err ( e) ) ,
234
+ } ;
235
+ if range. start + written >= range. end {
236
+ let end = range. end ;
237
+ * state = ComponentStdioWriterState :: File ;
238
+ return Poll :: Ready ( Ok ( end) ) ;
239
+ } else {
240
+ * state = ComponentStdioWriterState :: Follow (
241
+ ( range. start + written) ..range. end ,
242
+ ) ;
243
+ } ;
244
+ }
245
+ } ,
202
246
}
203
247
}
204
248
}
@@ -208,13 +252,19 @@ impl AsyncWrite for ComponentStdioWriter {
208
252
cx : & mut std:: task:: Context < ' _ > ,
209
253
) -> Poll < std:: result:: Result < ( ) , std:: io:: Error > > {
210
254
let this = self . get_mut ( ) ;
211
- match this. state {
212
- ComponentStdioWriterState :: File => {
213
- std:: pin:: Pin :: new ( & mut this. async_file ) . poll_flush ( cx)
214
- }
215
- ComponentStdioWriterState :: Follow ( _) => {
255
+
256
+ match & mut this. inner {
257
+ ComponentStdioWriterInner :: Inherit => {
216
258
std:: pin:: Pin :: new ( & mut tokio:: io:: stderr ( ) ) . poll_flush ( cx)
217
259
}
260
+ ComponentStdioWriterInner :: Forward {
261
+ async_file, state, ..
262
+ } => match state {
263
+ ComponentStdioWriterState :: File => std:: pin:: Pin :: new ( async_file) . poll_flush ( cx) ,
264
+ ComponentStdioWriterState :: Follow ( _) => {
265
+ std:: pin:: Pin :: new ( & mut tokio:: io:: stderr ( ) ) . poll_flush ( cx)
266
+ }
267
+ } ,
218
268
}
219
269
}
220
270
@@ -223,32 +273,57 @@ impl AsyncWrite for ComponentStdioWriter {
223
273
cx : & mut std:: task:: Context < ' _ > ,
224
274
) -> Poll < std:: result:: Result < ( ) , std:: io:: Error > > {
225
275
let this = self . get_mut ( ) ;
226
- match this. state {
227
- ComponentStdioWriterState :: File => {
228
- std:: pin:: Pin :: new ( & mut this. async_file ) . poll_shutdown ( cx)
229
- }
230
- ComponentStdioWriterState :: Follow ( _) => {
276
+
277
+ match & mut this. inner {
278
+ ComponentStdioWriterInner :: Inherit => {
231
279
std:: pin:: Pin :: new ( & mut tokio:: io:: stderr ( ) ) . poll_flush ( cx)
232
280
}
281
+ ComponentStdioWriterInner :: Forward {
282
+ async_file, state, ..
283
+ } => match state {
284
+ ComponentStdioWriterState :: File => std:: pin:: Pin :: new ( async_file) . poll_shutdown ( cx) ,
285
+ ComponentStdioWriterState :: Follow ( _) => {
286
+ std:: pin:: Pin :: new ( & mut tokio:: io:: stderr ( ) ) . poll_flush ( cx)
287
+ }
288
+ } ,
233
289
}
234
290
}
235
291
}
236
292
237
293
impl std:: io:: Write for ComponentStdioWriter {
238
294
fn write ( & mut self , buf : & [ u8 ] ) -> std:: io:: Result < usize > {
239
- let written = self . sync_file . write ( buf) ?;
240
- if self . follow {
241
- std:: io:: stderr ( ) . write_all ( & buf[ ..written] ) ?;
295
+ spin_telemetry:: log:: app_log_to_tracing_event ( buf) ;
296
+
297
+ match & mut self . inner {
298
+ ComponentStdioWriterInner :: Inherit => {
299
+ std:: io:: stderr ( ) . write_all ( buf) ?;
300
+ Ok ( buf. len ( ) )
301
+ }
302
+ ComponentStdioWriterInner :: Forward {
303
+ sync_file, follow, ..
304
+ } => {
305
+ let written = sync_file. write ( buf) ?;
306
+ if * follow {
307
+ std:: io:: stderr ( ) . write_all ( & buf[ ..written] ) ?;
308
+ }
309
+ Ok ( written)
310
+ }
242
311
}
243
- Ok ( written)
244
312
}
245
313
246
314
fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
247
- self . sync_file . flush ( ) ?;
248
- if self . follow {
249
- std:: io:: stderr ( ) . flush ( ) ?;
315
+ match & mut self . inner {
316
+ ComponentStdioWriterInner :: Inherit => std:: io:: stderr ( ) . flush ( ) ,
317
+ ComponentStdioWriterInner :: Forward {
318
+ sync_file, follow, ..
319
+ } => {
320
+ sync_file. flush ( ) ?;
321
+ if * follow {
322
+ std:: io:: stderr ( ) . flush ( ) ?;
323
+ }
324
+ Ok ( ( ) )
325
+ }
250
326
}
251
- Ok ( ( ) )
252
327
}
253
328
}
254
329
0 commit comments