Skip to content

Commit f3f2418

Browse files
lpcoxCopilot
andauthored
refactor: extract runWithSignalHandling helper (#2495)
Deduplicate the identical signal-handler + readline loop from streamFromContainer and tailFile into a shared runWithSignalHandling helper. Closes #2481 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 88b5e5f commit f3f2418

1 file changed

Lines changed: 32 additions & 46 deletions

File tree

src/logs/log-streamer.ts

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)