a60b7be55d
- F1: cover the hook's riskiest path — a following stream that ends with an unwritten tail fragment then resumes (tail:0 + nano-since), asserting the fragment is dropped, resume params are correct, and the boundary line is deduped to one; plus MAX_LOG_LINES head-trim and buffer reset on resourceId/lineCount change. - F2: clear the error banner on a SUCCESSFUL reconnect (via a new onOpen signal on StreamLogsFn), not only when new lines arrive — an idle-but-healthy reconnect no longer leaves a stuck 'unable to stream' banner. - F4: update the stale comment in the React logs view registration (the React logs migration is now complete). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
import { useCurrentStateAndParams } from '@uirouter/react';
|
|
|
|
import { useContainer } from '@/react/docker/containers/queries/useContainer';
|
|
import {
|
|
streamContainerLogs,
|
|
StreamLogsParams,
|
|
} from '@/react/docker/containers/containers.service';
|
|
import { ContainerDetailsViewModel } from '@/docker/models/containerDetails';
|
|
import { trimContainerName } from '@/docker/filters/utils';
|
|
|
|
import { InformationPanel } from '@@/InformationPanel';
|
|
import { TextTip } from '@@/Tip/TextTip';
|
|
import { Link } from '@@/Link';
|
|
import { LogViewer } from '@@/LogViewer';
|
|
|
|
export function LogView() {
|
|
const {
|
|
params: { endpointId: environmentId, id: containerId, nodeName },
|
|
} = useCurrentStateAndParams();
|
|
|
|
const containerQuery = useContainer(
|
|
{ environmentId, containerId, nodeName },
|
|
{
|
|
select: (c) => new ContainerDetailsViewModel(c),
|
|
}
|
|
);
|
|
if (!containerQuery.data || containerQuery.isLoading) {
|
|
return null;
|
|
}
|
|
|
|
const container = containerQuery.data;
|
|
|
|
const logsEnabled =
|
|
container.HostConfig?.LogConfig?.Type && // if a portion of the object path doesn't exist, logging is likely disabled
|
|
container.HostConfig.LogConfig.Type !== 'none'; // if type === none logging is disabled
|
|
|
|
if (!logsEnabled) {
|
|
return <LogsDisabledInfoPanel />;
|
|
}
|
|
|
|
// A TTY container emits a single raw stream; a non-TTY container multiplexes
|
|
// stdout/stderr with 8-byte frame headers the stream processor must demux.
|
|
const hasFrameHeaders = !container.Config?.Tty;
|
|
const resourceName = trimContainerName(container.Name || 'container');
|
|
|
|
// Bind the environment/container so the viewer only knows the generic
|
|
// "open a log stream" contract (StreamLogsFn), not Docker proxy specifics.
|
|
function streamLogs(
|
|
params: StreamLogsParams,
|
|
onChunk: (bytes: Uint8Array) => void,
|
|
signal: AbortSignal,
|
|
onOpen?: () => void
|
|
) {
|
|
return streamContainerLogs(
|
|
environmentId,
|
|
containerId,
|
|
params,
|
|
onChunk,
|
|
signal,
|
|
onOpen
|
|
);
|
|
}
|
|
|
|
return (
|
|
<LogViewer
|
|
streamLogs={streamLogs}
|
|
resourceId={containerId}
|
|
hasFrameHeaders={hasFrameHeaders}
|
|
resourceName={resourceName}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function LogsDisabledInfoPanel() {
|
|
const {
|
|
params: { id: containerId, nodeName },
|
|
} = useCurrentStateAndParams();
|
|
|
|
return (
|
|
<div className="row">
|
|
<div className="col-sm-12">
|
|
<InformationPanel>
|
|
<TextTip color="blue">
|
|
Logging is disabled for this container. If you want to re-enable
|
|
logging, please{' '}
|
|
<Link
|
|
to="docker.containers.new"
|
|
params={{ from: containerId, nodeName }}
|
|
data-cy="redeploy-container-link"
|
|
>
|
|
redeploy your container
|
|
</Link>{' '}
|
|
and select a logging driver in the "Command & logging"
|
|
panel.
|
|
</TextTip>
|
|
</InformationPanel>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|