Skip to content

Commit e9eccce

Browse files
feat(Logs): add worker logs
1 parent 851e925 commit e9eccce

2 files changed

Lines changed: 72 additions & 13 deletions

File tree

app/components/ContainerLogs.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@ function getContainerName(hostname: string): string {
66
return `specify7-test-panel-${hostname}-1`;
77
}
88

9-
export function ContainerLogs({ deployment }: { deployment: Deployment }) {
10-
if (!deployment.hostname) {
9+
export function ContainerLogs({
10+
deployment,
11+
containerName: customContainerName
12+
}: {
13+
deployment: Deployment;
14+
containerName?: string;
15+
}) {
16+
if (!deployment.hostname && !customContainerName) {
1117
return <div>Error: No hostname available for this deployment</div>;
1218
}
13-
const containerName = getContainerName(deployment.hostname);
19+
const containerName = customContainerName || getContainerName(deployment.hostname!);
1420
const [logs, setLogs] = React.useState<string>('');
1521
const [loading, setLoading] = React.useState<boolean>(true);
1622
const [error, setError] = React.useState<string | null>(null);

app/components/DeploymentOptions.tsx

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ function getContainerName(hostname: string): string {
1919
// Match the docker naming scheme for deployment containers
2020
return `specify7-test-panel-${hostname}-1`;
2121
}
22+
23+
function getWorkerContainerName(hostname: string): string {
24+
// Match the docker naming scheme for worker containers
25+
return `specify7-test-panel-${hostname}-worker-1`;
26+
}
27+
2228
export function DeploymentOptions({
2329
deployment,
2430
schemaVersions,
@@ -48,25 +54,33 @@ export function DeploymentOptions({
4854

4955
const [listUsers, setListUsers] = React.useState(false);
5056
const [showLogs, setShowLogs] = React.useState(false);
57+
const [showWorkerLogs, setShowWorkerLogs] = React.useState(false);
5158
const [downloading, setDownloading] = React.useState(false);
59+
const [downloadingWorker, setDownloadingWorker] = React.useState(false);
5260

53-
const handleDownloadLogs = async () => {
61+
const handleDownloadLogs = async (containerType: 'main' | 'worker' = 'main') => {
5462
if (!deployment.hostname) return;
5563

56-
setDownloading(true);
64+
const isWorker = containerType === 'worker';
65+
const setDownloadingState = isWorker ? setDownloadingWorker : setDownloading;
66+
67+
setDownloadingState(true);
5768
try {
58-
const containerName = getContainerName(deployment.hostname);
69+
const containerName = isWorker
70+
? getWorkerContainerName(deployment.hostname)
71+
: getContainerName(deployment.hostname);
72+
5973
const response = await fetch(`/api/logs/${encodeURIComponent(containerName)}`);
60-
if (!response.ok) throw new Error('Failed to fetch logs for download');
74+
if (!response.ok) throw new Error(`Failed to fetch ${isWorker ? 'worker ' : ''}logs for download`);
6175

6276
const logsText = await response.text();
6377

6478
if (!logsText || logsText.trim() === '') {
65-
alert('No logs available for this container');
79+
alert(`No ${isWorker ? 'worker ' : ''}logs available for this container`);
6680
return;
6781
}
6882

69-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
83+
const timestamp = new Date().toISOString().slice(0, 16).replace(/[T:]/g, '-');
7084
const filename = `${containerName}-logs-${timestamp}.txt`;
7185

7286
const blob = new Blob([logsText], { type: 'text/plain' });
@@ -80,10 +94,10 @@ export function DeploymentOptions({
8094
document.body.removeChild(link);
8195
URL.revokeObjectURL(url);
8296
} catch (error) {
83-
console.error('Failed to download logs:', error);
84-
alert('Failed to download logs. Please try again.');
97+
console.error(`Failed to download ${isWorker ? 'worker ' : ''}logs:`, error);
98+
alert(`Failed to download ${isWorker ? 'worker ' : ''}logs. Please try again.`);
8599
} finally {
86-
setDownloading(false);
100+
setDownloadingState(false);
87101
}
88102
};
89103

@@ -109,7 +123,7 @@ export function DeploymentOptions({
109123
buttons={
110124
<>
111125
<button
112-
onClick={handleDownloadLogs}
126+
onClick={() => handleDownloadLogs('main')}
113127
disabled={downloading}
114128
className={infoButtonClassName}
115129
>
@@ -131,6 +145,38 @@ export function DeploymentOptions({
131145
<ContainerLogs deployment={deployment} />
132146
</ModalDialog>
133147
)}
148+
{showWorkerLogs && (
149+
<ModalDialog
150+
title="Worker Container Logs"
151+
onClose={(): void => setShowWorkerLogs(false)}
152+
buttons={
153+
<>
154+
<button
155+
onClick={() => handleDownloadLogs('worker')}
156+
disabled={downloadingWorker}
157+
className={infoButtonClassName}
158+
>
159+
{icons.download}
160+
<span className="ml-2">
161+
{downloadingWorker ? 'Downloading...' : 'Download Worker Logs'}
162+
</span>
163+
</button>
164+
<button
165+
className={infoButtonClassName}
166+
type="button"
167+
onClick={(): void => setShowWorkerLogs(false)}
168+
>
169+
Close
170+
</button>
171+
</>
172+
}
173+
>
174+
<ContainerLogs
175+
deployment={deployment}
176+
containerName={deployment.hostname ? getWorkerContainerName(deployment.hostname) : undefined}
177+
/>
178+
</ModalDialog>
179+
)}
134180
<ModalDialog
135181
buttons={
136182
<>
@@ -157,6 +203,13 @@ export function DeploymentOptions({
157203
>
158204
{localization.viewLogs ?? "View Logs"}
159205
</button>
206+
<button
207+
className={infoButtonClassName}
208+
type="button"
209+
onClick={(): void => setShowWorkerLogs(true)}
210+
>
211+
View Worker Logs
212+
</button>
160213
<button
161214
className={infoButtonClassName}
162215
type="button"

0 commit comments

Comments
 (0)