mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'preload-logs-on-scroll' into log-controls-more-info
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
commit
d3f54bd828
@ -13,7 +13,7 @@ interface Props {
|
|||||||
ready: boolean
|
ready: boolean
|
||||||
tabId: string
|
tabId: string
|
||||||
tabData: IPodLogsData
|
tabData: IPodLogsData
|
||||||
logs: [string, string]
|
logs: string[][]
|
||||||
save: (data: Partial<IPodLogsData>) => void
|
save: (data: Partial<IPodLogsData>) => void
|
||||||
reload: () => void
|
reload: () => void
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ export const PodLogControls = observer((props: Props) => {
|
|||||||
if (!props.ready) return null;
|
if (!props.ready) return null;
|
||||||
const { tabData, tabId, save, reload, logs } = props;
|
const { tabData, tabId, save, reload, logs } = props;
|
||||||
const { selectedContainer, showTimestamps, previous } = tabData;
|
const { selectedContainer, showTimestamps, previous } = tabData;
|
||||||
const timestamps = podLogsStore.getTimestamps(podLogsStore.logs.get(tabId));
|
const since = podLogsStore.getTimestamps(podLogsStore.logs.get(tabId)[0]);
|
||||||
const pod = new Pod(tabData.pod);
|
const pod = new Pod(tabData.pod);
|
||||||
const toggleTimestamps = () => {
|
const toggleTimestamps = () => {
|
||||||
save({ showTimestamps: !showTimestamps });
|
save({ showTimestamps: !showTimestamps });
|
||||||
@ -36,7 +36,7 @@ export const PodLogControls = observer((props: Props) => {
|
|||||||
const downloadLogs = () => {
|
const downloadLogs = () => {
|
||||||
const fileName = selectedContainer ? selectedContainer.name : pod.getName();
|
const fileName = selectedContainer ? selectedContainer.name : pod.getName();
|
||||||
const [oldLogs, newLogs] = logs;
|
const [oldLogs, newLogs] = logs;
|
||||||
downloadFile(fileName + ".log", oldLogs + newLogs, "text/plain");
|
downloadFile(fileName + ".log", [...oldLogs, ...newLogs].join("\n"), "text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
const onContainerChange = (option: SelectOption) => {
|
const onContainerChange = (option: SelectOption) => {
|
||||||
@ -85,10 +85,10 @@ export const PodLogControls = observer((props: Props) => {
|
|||||||
autoConvertOptions={false}
|
autoConvertOptions={false}
|
||||||
/>
|
/>
|
||||||
<div className="time-range">
|
<div className="time-range">
|
||||||
{timestamps && (
|
{since && (
|
||||||
<>
|
<>
|
||||||
<Trans>Since</Trans>{" "}
|
<Trans>Since</Trans>{" "}
|
||||||
<b>{new Date(timestamps[0]).toLocaleString()}</b>
|
<b>{new Date(since[0]).toLocaleString()}</b>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { DockTabStore } from "./dock-tab.store";
|
|||||||
import { dockStore, IDockTab, TabKind } from "./dock.store";
|
import { dockStore, IDockTab, TabKind } from "./dock.store";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { _i18n } from "../../i18n";
|
import { _i18n } from "../../i18n";
|
||||||
import { Notifications } from "../notifications";
|
|
||||||
import { isDevelopment } from "../../../common/vars";
|
import { isDevelopment } from "../../../common/vars";
|
||||||
|
|
||||||
export interface IPodLogsData {
|
export interface IPodLogsData {
|
||||||
@ -18,7 +17,7 @@ export interface IPodLogsData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TabId = string;
|
type TabId = string;
|
||||||
type PodLogs = string;
|
type PodLogLine = string;
|
||||||
|
|
||||||
// Number for log lines to load
|
// Number for log lines to load
|
||||||
export const logRange = isDevelopment ? 100 : 1000;
|
export const logRange = isDevelopment ? 100 : 1000;
|
||||||
@ -31,7 +30,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
this.loadMore(id)
|
this.loadMore(id)
|
||||||
});
|
});
|
||||||
|
|
||||||
@observable logs = observable.map<TabId, PodLogs>();
|
@observable logs = observable.map<TabId, PodLogLine[]>();
|
||||||
@observable newLogSince = observable.map<TabId, string>(); // Timestamp after which all logs are considered to be new
|
@observable newLogSince = observable.map<TabId, string>(); // Timestamp after which all logs are considered to be new
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -64,15 +63,14 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
const logs = await this.loadLogs(tabId, {
|
const logs = await this.loadLogs(tabId, {
|
||||||
tailLines: this.lines + logRange
|
tailLines: this.lines + logRange
|
||||||
});
|
});
|
||||||
if (!this.refresher.isRunning) this.refresher.start();
|
this.refresher.start();
|
||||||
this.logs.set(tabId, logs);
|
this.logs.set(tabId, logs);
|
||||||
} catch ({error}) {
|
} catch ({error}) {
|
||||||
const message = [
|
const message = [
|
||||||
_i18n._(t`Failed to load logs: ${error.message}`),
|
_i18n._(t`Failed to load logs: ${error.message}`),
|
||||||
_i18n._(t`Reason: ${error.reason} (${error.code})`)
|
_i18n._(t`Reason: ${error.reason} (${error.code})`)
|
||||||
].join("\n");
|
];
|
||||||
this.refresher.stop();
|
this.refresher.stop();
|
||||||
Notifications.error(message);
|
|
||||||
this.logs.set(tabId, message);
|
this.logs.set(tabId, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +87,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
sinceTime: this.getLastSinceTime(tabId)
|
sinceTime: this.getLastSinceTime(tabId)
|
||||||
});
|
});
|
||||||
// Add newly received logs to bottom
|
// Add newly received logs to bottom
|
||||||
this.logs.set(tabId, oldLogs + logs);
|
this.logs.set(tabId, [...oldLogs, ...logs]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,11 +103,15 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
const pod = new Pod(data.pod);
|
const pod = new Pod(data.pod);
|
||||||
const namespace = pod.getNs();
|
const namespace = pod.getNs();
|
||||||
const name = pod.getName();
|
const name = pod.getName();
|
||||||
return await podsApi.getLogs({ namespace, name }, {
|
return podsApi.getLogs({ namespace, name }, {
|
||||||
...params,
|
...params,
|
||||||
timestamps: true, // Always setting timestampt to separate old logs from new ones
|
timestamps: true, // Always setting timestampt to separate old logs from new ones
|
||||||
container: selectedContainer.name,
|
container: selectedContainer.name,
|
||||||
previous
|
previous
|
||||||
|
}).then(result => {
|
||||||
|
const logs = [...result.split("\n")]; // Transform them into array
|
||||||
|
logs.pop(); // Remove last empty element
|
||||||
|
return logs;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +133,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
get lines() {
|
get lines() {
|
||||||
const id = dockStore.selectedTabId;
|
const id = dockStore.selectedTabId;
|
||||||
const logs = this.logs.get(id);
|
const logs = this.logs.get(id);
|
||||||
return logs ? logs.split("\n").length : 0;
|
return logs ? logs.length : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,12 +142,10 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
* @param tabId
|
* @param tabId
|
||||||
*/
|
*/
|
||||||
getLastSinceTime(tabId: TabId) {
|
getLastSinceTime(tabId: TabId) {
|
||||||
const timestamps = this.getTimestamps(this.logs.get(tabId));
|
const logs = this.logs.get(tabId);
|
||||||
let stamp = new Date(0);
|
const timestamps = this.getTimestamps(logs[logs.length - 1]);
|
||||||
if (timestamps) {
|
const stamp = new Date(timestamps[0]);
|
||||||
stamp = new Date(timestamps.slice(-1)[0]);
|
|
||||||
stamp.setSeconds(stamp.getSeconds() + 1); // avoid duplicates from last second
|
stamp.setSeconds(stamp.getSeconds() + 1); // avoid duplicates from last second
|
||||||
}
|
|
||||||
return stamp.toISOString();
|
return stamp.toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
* scrolling position
|
* scrolling position
|
||||||
* @param scrollHeight previous scrollHeight position before adding new lines
|
* @param scrollHeight previous scrollHeight position before adding new lines
|
||||||
*/
|
*/
|
||||||
preload = async (scrollHeight: number) => {
|
loadMore = async (scrollHeight: number) => {
|
||||||
if (podLogsStore.lines < logRange) return;
|
if (podLogsStore.lines < logRange) return;
|
||||||
this.preloading = true;
|
this.preloading = true;
|
||||||
await podLogsStore.load(this.tabId).then(() => this.preloading = false);
|
await podLogsStore.load(this.tabId).then(() => this.preloading = false);
|
||||||
@ -93,27 +93,27 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
/**
|
/**
|
||||||
* Computed prop which returns logs with or without timestamps added to each line and
|
* Computed prop which returns logs with or without timestamps added to each line and
|
||||||
* does separation between new and old logs
|
* does separation between new and old logs
|
||||||
* @returns {Array} An array with 2 string items - [oldLogs, newLogs]
|
* @returns {Array} An array with 2 items - [oldLogs, newLogs]
|
||||||
*/
|
*/
|
||||||
@computed
|
@computed
|
||||||
get logs(): [string, string] {
|
get logs() {
|
||||||
if (!podLogsStore.logs.has(this.tabId)) return ["", ""];
|
if (!podLogsStore.logs.has(this.tabId)) return [];
|
||||||
const logs = podLogsStore.logs.get(this.tabId);
|
const logs = podLogsStore.logs.get(this.tabId);
|
||||||
const { getData, removeTimestamps, newLogSince } = podLogsStore;
|
const { getData, removeTimestamps, newLogSince } = podLogsStore;
|
||||||
const { showTimestamps } = getData(this.tabId);
|
const { showTimestamps } = getData(this.tabId);
|
||||||
let oldLogs = logs;
|
let oldLogs: string[] = logs;
|
||||||
let newLogs = "";
|
let newLogs: string[] = [];
|
||||||
if (newLogSince.has(this.tabId)) {
|
if (newLogSince.has(this.tabId)) {
|
||||||
// Finding separator timestamp in logs
|
// Finding separator timestamp in logs
|
||||||
const index = logs.indexOf(newLogSince.get(this.tabId));
|
const index = logs.findIndex(item => item.includes(newLogSince.get(this.tabId)));
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
// Splitting logs to old and new ones
|
// Splitting logs to old and new ones
|
||||||
oldLogs = logs.substring(0, index);
|
oldLogs = logs.slice(0, index);
|
||||||
newLogs = logs.substring(index);
|
newLogs = logs.slice(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!showTimestamps) {
|
if (!showTimestamps) {
|
||||||
return [removeTimestamps(oldLogs), removeTimestamps(newLogs)];
|
return [oldLogs, newLogs].map(logs => logs.map(item => removeTimestamps(item)))
|
||||||
}
|
}
|
||||||
return [oldLogs, newLogs];
|
return [oldLogs, newLogs];
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
const toBottomOffset = 100 * 16; // 100 lines * 16px (height of each line)
|
const toBottomOffset = 100 * 16; // 100 lines * 16px (height of each line)
|
||||||
const { scrollHeight, clientHeight, scrollTop } = logsArea;
|
const { scrollHeight, clientHeight, scrollTop } = logsArea;
|
||||||
if (scrollTop === 0) {
|
if (scrollTop === 0) {
|
||||||
this.preload(scrollHeight);
|
this.loadMore(scrollHeight);
|
||||||
}
|
}
|
||||||
if (scrollHeight - scrollTop > toBottomOffset) {
|
if (scrollHeight - scrollTop > toBottomOffset) {
|
||||||
this.showJumpToBottom = true;
|
this.showJumpToBottom = true;
|
||||||
@ -158,7 +158,7 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
if (!this.ready) {
|
if (!this.ready) {
|
||||||
return <Spinner center/>;
|
return <Spinner center/>;
|
||||||
}
|
}
|
||||||
if (!oldLogs && !newLogs) {
|
if (!oldLogs.length && !newLogs.length) {
|
||||||
return (
|
return (
|
||||||
<div className="flex align-center justify-center">
|
<div className="flex align-center justify-center">
|
||||||
<Trans>There are no logs available for container.</Trans>
|
<Trans>There are no logs available for container.</Trans>
|
||||||
@ -172,11 +172,11 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(this.colorConverter.ansi_to_html(oldLogs))}} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(this.colorConverter.ansi_to_html(oldLogs.join("\n"))) }} />
|
||||||
{newLogs && (
|
{newLogs.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<p className="new-logs-sep" title={_i18n._(t`New logs since opening the dialog`)}/>
|
<p className="new-logs-sep" title={_i18n._(t`New logs since opening the dialog`)}/>
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(this.colorConverter.ansi_to_html(newLogs))}} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(this.colorConverter.ansi_to_html(newLogs.join("\n"))) }} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user