@@ -49,35 +49,26 @@ export async function streamLogs(options: StreamOptions): Promise<void> {
4949}
5050
5151/**
52- * Streams logs from a running Docker container
52+ * Runs a child process with signal-handler cleanup, streaming its stdout
53+ * through readline and processing each line.
54+ *
55+ * Both streamFromContainer and tailFile shared this identical block;
56+ * extracting it eliminates the duplication and ensures signal-handler
57+ * lifecycle changes only need to happen in one place.
5358 */
54- async function streamFromContainer (
55- containerName : string ,
56- follow : boolean ,
59+ async function runWithSignalHandling (
60+ proc : execa . ExecaChildProcess ,
5761 formatter : LogFormatter ,
5862 parse : boolean ,
5963 withPid : boolean
6064) : Promise < void > {
61- logger . debug ( `Streaming logs from container: ${ containerName } ` ) ;
62-
63- // Use docker exec to read logs from within the container
64- const args = follow
65- ? [ 'exec' , containerName , 'tail' , '-f' , '/var/log/squid/access.log' ]
66- : [ 'exec' , containerName , 'cat' , '/var/log/squid/access.log' ] ;
67-
68- const proc = execa ( 'docker' , args , {
69- reject : false ,
70- } ) ;
71-
72- // Setup cleanup on process exit
7365 const cleanup = ( ) => {
7466 proc . kill ( 'SIGTERM' ) ;
7567 } ;
7668 process . on ( 'SIGINT' , cleanup ) ;
7769 process . on ( 'SIGTERM' , cleanup ) ;
7870
7971 try {
80- // Process stdout line-by-line
8172 if ( proc . stdout ) {
8273 const rl = readline . createInterface ( {
8374 input : proc . stdout ,
@@ -91,7 +82,6 @@ async function streamFromContainer(
9182
9283 await proc ;
9384 } catch ( error ) {
94- // Ignore SIGTERM errors (normal termination)
9585 if ( error instanceof Error && 'signal' in error && error . signal === 'SIGTERM' ) {
9686 return ;
9787 }
@@ -102,6 +92,29 @@ async function streamFromContainer(
10292 }
10393}
10494
95+ /**
96+ * Streams logs from a running Docker container
97+ */
98+ async function streamFromContainer (
99+ containerName : string ,
100+ follow : boolean ,
101+ formatter : LogFormatter ,
102+ parse : boolean ,
103+ withPid : boolean
104+ ) : Promise < void > {
105+ logger . debug ( `Streaming logs from container: ${ containerName } ` ) ;
106+
107+ const args = follow
108+ ? [ 'exec' , containerName , 'tail' , '-f' , '/var/log/squid/access.log' ]
109+ : [ 'exec' , containerName , 'cat' , '/var/log/squid/access.log' ] ;
110+
111+ const proc = execa ( 'docker' , args , {
112+ reject : false ,
113+ } ) ;
114+
115+ await runWithSignalHandling ( proc , formatter , parse , withPid ) ;
116+ }
117+
105118/**
106119 * Streams logs from a preserved log file
107120 */
@@ -161,34 +174,7 @@ async function tailFile(
161174 reject : false ,
162175 } ) ;
163176
164- const cleanup = ( ) => {
165- proc . kill ( 'SIGTERM' ) ;
166- } ;
167- process . on ( 'SIGINT' , cleanup ) ;
168- process . on ( 'SIGTERM' , cleanup ) ;
169-
170- try {
171- if ( proc . stdout ) {
172- const rl = readline . createInterface ( {
173- input : proc . stdout ,
174- crlfDelay : Infinity ,
175- } ) ;
176-
177- for await ( const line of rl ) {
178- processLine ( line , formatter , parse , withPid ) ;
179- }
180- }
181-
182- await proc ;
183- } catch ( error ) {
184- if ( error instanceof Error && 'signal' in error && error . signal === 'SIGTERM' ) {
185- return ;
186- }
187- throw error ;
188- } finally {
189- process . off ( 'SIGINT' , cleanup ) ;
190- process . off ( 'SIGTERM' , cleanup ) ;
191- }
177+ await runWithSignalHandling ( proc , formatter , parse , withPid ) ;
192178}
193179
194180/**
0 commit comments