1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Renderer file logging through IPC

Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
This commit is contained in:
Sami Tiilikainen 2023-03-06 10:47:04 +02:00
parent 47796228d0
commit d3cc345cde
19 changed files with 666 additions and 11 deletions

View File

@ -3,20 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { createLogger, format } from "winston";
import type { Logger } from "./logger"; import type { Logger } from "./logger";
import { loggerTransportInjectionToken } from "./logger/transports"; import winstonLoggerInjectable from "./winston-logger.injectable";
const loggerInjectable = getInjectable({ const loggerInjectable = getInjectable({
id: "logger", id: "logger",
instantiate: (di): Logger => { instantiate: (di): Logger => {
const baseLogger = createLogger({ const baseLogger = di.inject(winstonLoggerInjectable);
format: format.combine(
format.splat(),
format.simple(),
),
transports: di.injectMany(loggerTransportInjectionToken),
});
return { return {
debug: (message, ...data) => baseLogger.debug(message, ...data), debug: (message, ...data) => baseLogger.debug(message, ...data),

View File

@ -0,0 +1,20 @@
import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
export type IpcFileLogObject = {
fileId: string;
entry: {
level: string;
message: string;
internalMessage: string;
};
};
export type IpcFileLoggerChannel = MessageChannel<IpcFileLogObject>;
export const ipcFileLoggerChannel: IpcFileLoggerChannel = {
id: "ipc-file-logger-channel",
};
export const closeIpcFileLoggerChannel: MessageChannel<string> = {
id: "close-ipc-file-logger-channel",
};

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { createLogger, format } from "winston";
import { loggerTransportInjectionToken } from "./logger/transports";
const winstonLoggerInjectable = getInjectable({
id: "winston-logger",
instantiate: (di) =>
createLogger({
format: format.combine(format.splat(), format.simple()),
transports: di.injectMany(loggerTransportInjectionToken),
}),
});
export default winstonLoggerInjectable;

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
import {
closeIpcFileLoggerChannel,
} from "../../common/logger/ipc-file-logger-channel";
const closeIpcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
id: "close-ipc-file-logging",
channel: closeIpcFileLoggerChannel,
handler: (di) => (fileId) =>
di
.inject(ipcFileLoggerInjectable)
.close(fileId),
});
export default closeIpcFileLoggingListenerInjectable;

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { transports } from "winston";
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
import IpcFileLogger from "./ipc-file-logger";
const ipcFileLoggerInjectable = getInjectable({
id: "ipc-file-logger",
instantiate: (di) =>
new IpcFileLogger(
{
dirname: di.inject(directoryForLogsInjectable),
maxsize: 1024 * 1024,
maxFiles: 2,
tailable: true,
},
(options: transports.FileTransportOptions) => new transports.File(options)
),
});
export default ipcFileLoggerInjectable;

View File

@ -0,0 +1,179 @@
import IpcFileLogger from "./ipc-file-logger";
describe("ipc file logger in main", () => {
let logMock: jest.Mock;
let closeMock: jest.Mock;
let createFileTransportMock: jest.Mock;
let logger: IpcFileLogger;
beforeEach(() => {
logMock = jest.fn();
closeMock = jest.fn();
createFileTransportMock = jest.fn(() => ({
log: logMock,
close: closeMock,
}));
logger = new IpcFileLogger(
{
dirname: "some-logs-dir",
maxFiles: 1,
tailable: true,
},
createFileTransportMock
);
});
it("creates a transport for new log file", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
expect(createFileTransportMock).toHaveBeenCalledWith({
dirname: "some-logs-dir",
filename: "lens-some-log-file.log",
maxFiles: 1,
tailable: true,
});
});
it("uses existing transport for log file", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
expect(createFileTransportMock).toHaveBeenCalledTimes(1);
expect(createFileTransportMock).toHaveBeenCalledWith({
dirname: "some-logs-dir",
filename: "lens-some-log-file.log",
maxFiles: 1,
tailable: true,
});
});
it("creates separate transport for each log file", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.log({
fileId: "some-other-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.log({
fileId: "some-yet-another-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
expect(createFileTransportMock).toHaveBeenCalledTimes(3);
expect(createFileTransportMock).toHaveBeenCalledWith({
dirname: "some-logs-dir",
filename: "lens-some-log-file.log",
maxFiles: 1,
tailable: true,
});
expect(createFileTransportMock).toHaveBeenCalledWith({
dirname: "some-logs-dir",
filename: "lens-some-other-log-file.log",
maxFiles: 1,
tailable: true,
});
expect(createFileTransportMock).toHaveBeenCalledWith({
dirname: "some-logs-dir",
filename: "lens-some-yet-another-log-file.log",
maxFiles: 1,
tailable: true,
});
});
it("logs using file transport", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "some-log-message" },
});
expect(logMock.mock.calls[0][0]).toEqual({
level: "irrelevant",
message: "some-log-message",
});
});
it("logs to correct files", () => {
const someLogMock = jest.fn();
const someOthertLogMock = jest.fn();
createFileTransportMock.mockImplementation((options) => {
if (options.filename === "lens-some-log-file.log") {
return { log: someLogMock };
}
if (options.filename === "lens-some-other-log-file.log") {
return { log: someOthertLogMock };
}
return null;
});
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "some-log-message" },
});
logger.log({
fileId: "some-other-log-file",
entry: { level: "irrelevant", message: "some-other-log-message" },
});
expect(someLogMock).toHaveBeenCalledTimes(1);
expect(someLogMock.mock.calls[0][0]).toEqual({
level: "irrelevant",
message: "some-log-message",
});
expect(someOthertLogMock).toHaveBeenCalledTimes(1);
expect(someOthertLogMock.mock.calls[0][0]).toEqual({
level: "irrelevant",
message: "some-other-log-message",
});
});
it("closes transport (to ensure no file handles are left open)", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.close("some-log-file");
expect(closeMock).toHaveBeenCalled();
});
it("creates a new transport once needed after closing previous", () => {
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
logger.close("some-log-file");
logger.log({
fileId: "some-log-file",
entry: { level: "irrelevant", message: "irrelevant" },
});
expect(createFileTransportMock).toHaveBeenCalledTimes(2);
expect(logMock).toHaveBeenCalledTimes(2);
});
});

View File

@ -0,0 +1,51 @@
import type { LogEntry, transports } from "winston";
type IpcFileLoggerOptions = Omit<transports.FileTransportOptions, "filename">;
class IpcFileLogger {
private fileTransports = new Map<string, transports.FileTransportInstance>();
constructor(
private options: IpcFileLoggerOptions,
private createNewFileTransport: (
options: transports.FileTransportOptions
) => transports.FileTransportInstance
) {}
log({ fileId, entry }: { fileId: string; entry: LogEntry }) {
const transport = this.ensureTransportForFile(fileId);
transport?.log?.(entry, () => {});
}
close(fileId: string) {
const transport = this.fileTransports.get(fileId);
if (transport) {
transport.close?.();
this.fileTransports.delete(fileId);
}
}
closeAll() {
[...this.fileTransports.keys()].forEach((fileId) => {
this.close(fileId);
});
}
private ensureTransportForFile(fileId: string) {
if (this.fileTransports.has(fileId)) {
return this.fileTransports.get(fileId);
}
const fileTransport = this.createNewFileTransport({
...this.options,
filename: `lens-${fileId}.log`,
});
this.fileTransports.set(fileId, fileTransport);
return fileTransport;
}
}
export default IpcFileLogger;

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
import {
ipcFileLoggerChannel,
IpcFileLogObject,
} from "../../common/logger/ipc-file-logger-channel";
import { MESSAGE } from "triple-beam";
/**
* Winston uses symbol property for the actual message.
*
* For that to get through IPC, use the internalMessage property instead
*/
export function deserializeLogFromIpc(ipcFileLogObject: IpcFileLogObject) {
const { internalMessage, ...standardEntry } = ipcFileLogObject.entry;
return {
...ipcFileLogObject,
entry: {
...standardEntry,
[MESSAGE]: internalMessage,
},
};
}
const ipcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
id: "ipc-file-logging",
channel: ipcFileLoggerChannel,
handler: (di) => (ipcFileLogObject) =>
di
.inject(ipcFileLoggerInjectable)
.log(deserializeLogFromIpc(ipcFileLogObject)),
});
export default ipcFileLoggingListenerInjectable;

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { MESSAGE } from "triple-beam";
import { deserializeLogFromIpc } from "./ipc-logging-listener.injectable";
describe("Ipc log deserialization", () => {
it("fills in the unique symbol message property Winston transports use internally", () => {
const logObject = {
fileId: "irrelevant",
entry: {
level: "irrelevant",
message: "some public message",
internalMessage: "some internal message",
someProperty: "irrelevant",
},
};
expect(deserializeLogFromIpc(logObject)).toEqual({
entry: {
level: "irrelevant",
message: "some public message",
[MESSAGE]: "some internal message",
someProperty: "irrelevant",
},
fileId: "irrelevant",
});
});
});

View File

@ -12,6 +12,7 @@ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.
import loadExtensionsInjectable from "../../load-extensions.injectable"; import loadExtensionsInjectable from "../../load-extensions.injectable";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable"; import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable";
import closeRendererLogFileInjectable from "../../../logger/close-renderer-log-file.injectable";
const initClusterFrameInjectable = getInjectable({ const initClusterFrameInjectable = getInjectable({
id: "init-cluster-frame", id: "init-cluster-frame",
@ -29,6 +30,7 @@ const initClusterFrameInjectable = getInjectable({
emitAppEvent: di.inject(emitAppEventInjectable), emitAppEvent: di.inject(emitAppEventInjectable),
logger: di.inject(loggerInjectable), logger: di.inject(loggerInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable),
closeFileLogging: di.inject(closeRendererLogFileInjectable),
}); });
}, },
}); });

View File

@ -2,6 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { once } from "lodash";
import type { Cluster } from "../../../../common/cluster/cluster"; import type { Cluster } from "../../../../common/cluster/cluster";
import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry";
import type { ShowNotification } from "../../../components/notifications"; import type { ShowNotification } from "../../../components/notifications";
@ -18,6 +19,7 @@ interface Dependencies {
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
logger: Logger; logger: Logger;
showErrorNotification: ShowNotification; showErrorNotification: ShowNotification;
closeFileLogging: () => void;
} }
const logPrefix = "[CLUSTER-FRAME]:"; const logPrefix = "[CLUSTER-FRAME]:";
@ -30,6 +32,7 @@ export const initClusterFrame = ({
emitAppEvent, emitAppEvent,
logger, logger,
showErrorNotification, showErrorNotification,
closeFileLogging,
}: Dependencies) => }: Dependencies) =>
async (unmountRoot: () => void) => { async (unmountRoot: () => void) => {
// TODO: Make catalogEntityRegistry already initialized when passed as dependency // TODO: Make catalogEntityRegistry already initialized when passed as dependency
@ -73,11 +76,15 @@ export const initClusterFrame = ({
}); });
}); });
window.onbeforeunload = () => { const onCloseFrame = once(() => {
logger.info( logger.info(
`${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`, `${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`,
); );
unmountRoot(); unmountRoot();
}; closeFileLogging();
});
window.addEventListener("beforeunload", onCloseFrame);
window.addEventListener("pagehide", onCloseFrame);
}; };

View File

@ -13,6 +13,7 @@ import loggerInjectable from "../../../common/logger.injectable";
import { delay } from "@k8slens/utilities"; import { delay } from "@k8slens/utilities";
import { broadcastMessage } from "../../../common/ipc"; import { broadcastMessage } from "../../../common/ipc";
import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling"; import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling";
import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable";
const initRootFrameInjectable = getInjectable({ const initRootFrameInjectable = getInjectable({
id: "init-root-frame", id: "init-root-frame",
@ -24,6 +25,7 @@ const initRootFrameInjectable = getInjectable({
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable); const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const closeRendererLogFile = di.inject(closeRendererLogFileInjectable);
return async (unmountRoot: () => void) => { return async (unmountRoot: () => void) => {
catalogEntityRegistry.init(); catalogEntityRegistry.init();
@ -60,6 +62,7 @@ const initRootFrameInjectable = getInjectable({
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
logger.info("[ROOT-FRAME]: Unload app"); logger.info("[ROOT-FRAME]: Unload app");
closeRendererLogFile();
unmountRoot(); unmountRoot();
}); });
}; };

View File

@ -0,0 +1,49 @@
import winstonLoggerInjectable from "../../common/winston-logger.injectable";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import closeRendererLogFileInjectable from "./close-renderer-log-file.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import type winston from "winston";
import { SendMessageToChannel, sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
import ipcLogTransportInjectable from "./ipc-transport.injectable";
import type IpcLogTransport from "./ipc-transport";
describe("close renderer file logging", () => {
let di: DiContainer;
let sendIpcMock: SendMessageToChannel;
let winstonMock: winston.Logger;
let ipcTransportMock: IpcLogTransport;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: false });
sendIpcMock = jest.fn();
winstonMock = {
remove: jest.fn(),
} as any as winston.Logger;
ipcTransportMock = { name: "ipc-renderer-transport" } as IpcLogTransport;
di.override(winstonLoggerInjectable, () => winstonMock);
di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
di.override(rendererLogFileIdInjectable, () => "some-log-id");
di.override(ipcLogTransportInjectable, () => ipcTransportMock);
});
it("sends the ipc close message with correct log id", () => {
const closeLog = di.inject(closeRendererLogFileInjectable);
closeLog();
expect(sendIpcMock).toHaveBeenCalledWith(
{ id: "close-ipc-file-logger-channel" },
"some-log-id"
);
});
it("removes the transport to prevent further logging to closed file", () => {
const closeLog = di.inject(closeRendererLogFileInjectable);
closeLog();
expect(winstonMock.remove).toHaveBeenCalledWith({
name: "ipc-renderer-transport",
});
});
});

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import winstonLoggerInjectable from "../../common/winston-logger.injectable";
import { closeIpcFileLoggerChannel } from "../../common/logger/ipc-file-logger-channel";
import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
import ipcLogTransportInjectable from "./ipc-transport.injectable";
const closeRendererLogFileInjectable = getInjectable({
id: "close-renderer-log-file",
instantiate: (di) => {
const winstonLogger = di.inject(winstonLoggerInjectable);
const ipcLogTransport = di.inject(ipcLogTransportInjectable);
const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
const fileId = di.inject(rendererLogFileIdInjectable);
return () => {
messageToChannel(closeIpcFileLoggerChannel, fileId);
winstonLogger.remove(ipcLogTransport);
};
},
});
export default closeRendererLogFileInjectable;

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { loggerTransportInjectionToken } from "../../common/logger/transports";
import type winston from "winston";
import { MESSAGE } from "triple-beam";
import IpcLogTransport from "./ipc-transport";
import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
import {
closeIpcFileLoggerChannel,
ipcFileLoggerChannel,
IpcFileLogObject,
} from "../../common/logger/ipc-file-logger-channel";
import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
/**
* Winston uses symbol property for the actual message.
*
* For that to get through IPC, use the internalMessage property instead
*/
function serializeLogForIpc(
fileId: string,
entry: winston.LogEntry
): IpcFileLogObject {
return {
fileId,
entry: {
level: entry.level,
message: entry.message,
internalMessage: Object.getOwnPropertyDescriptor(entry, MESSAGE)?.value,
},
};
}
const ipcLogTransportInjectable = getInjectable({
id: "renderer-file-logger-transport",
instantiate: (di) => {
const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
const fileId = di.inject(rendererLogFileIdInjectable);
return new IpcLogTransport({
sendIpcLogMessage: (entry) =>
messageToChannel(
ipcFileLoggerChannel,
serializeLogForIpc(fileId, entry)
),
closeIpcLogging: () =>
messageToChannel(closeIpcFileLoggerChannel, fileId),
handleExceptions: false,
level: "info",
});
},
injectionToken: loggerTransportInjectionToken,
});
export default ipcLogTransportInjectable;

View File

@ -0,0 +1,42 @@
import type { DiContainer } from "@ogre-tools/injectable";
import { SendMessageToChannel, sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
import ipcLogTransportInjectable from "./ipc-transport.injectable";
import { MESSAGE } from "triple-beam";
describe("renderer log transport through ipc", () => {
let di: DiContainer;
let sendIpcMock: SendMessageToChannel;
beforeEach(() => {
sendIpcMock = jest.fn();
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
di.override(rendererLogFileIdInjectable, () => "some-log-id");
});
it("send serialized ipc messages on log", () => {
const logTransport = di.inject(ipcLogTransportInjectable);
logTransport.log(
{
level: "info",
message: "some log text",
[MESSAGE]: "actual winston log text",
},
() => {}
);
expect(sendIpcMock).toHaveBeenCalledWith(
{ id: "ipc-file-logger-channel" },
{
entry: {
level: "info",
message: "some log text",
internalMessage: "actual winston log text",
},
fileId: "some-log-id",
}
);
});
});

View File

@ -0,0 +1,35 @@
import type { LogEntry } from "winston";
import TransportStream, { TransportStreamOptions } from "winston-transport";
interface IpcLogTransportOptions extends TransportStreamOptions {
sendIpcLogMessage: (entry: LogEntry) => void;
closeIpcLogging: () => void;
}
class IpcLogTransport extends TransportStream {
sendIpcLogMessage: (entry: LogEntry) => void;
closeIpcLogging: () => void;
name = "ipc-renderer-transport";
constructor(options: IpcLogTransportOptions) {
const { sendIpcLogMessage, closeIpcLogging, ...winstonOptions } = options;
super(winstonOptions);
this.sendIpcLogMessage = sendIpcLogMessage;
this.closeIpcLogging = closeIpcLogging;
}
log(logEntry: LogEntry, next: () => void) {
setImmediate(() => {
this.emit("logged", logEntry);
});
this.sendIpcLogMessage(logEntry);
next();
}
close() {
this.closeIpcLogging();
}
}
export default IpcLogTransport;

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
import { getClusterIdFromHost } from "../utils";
const rendererLogFileIdInjectable = getInjectable({
id: "renderer-log-file-id",
instantiate: (di) => {
let frameId: string;
const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable);
if (currentlyInClusterFrame) {
const { host } = di.inject(windowLocationInjectable);
const clusterId = getClusterIdFromHost(host);
frameId = clusterId ? `cluster-${clusterId}` : "cluster";
} else {
frameId = "main";
}
return `renderer-${frameId}`;
},
});
export default rendererLogFileIdInjectable;

View File

@ -0,0 +1,27 @@
import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
describe("renderer log file id", () => {
it("clearly names log for renderer main frame", () => {
const di = getDiForUnitTesting({ doGeneralOverrides: false });
di.override(currentlyInClusterFrameInjectable, () => false);
const mainFileId = di.inject(rendererLogFileIdInjectable);
expect(mainFileId).toBe("renderer-main");
});
it("includes cluster id in renderer log file names", () => {
const di = getDiForUnitTesting({ doGeneralOverrides: false });
di.override(currentlyInClusterFrameInjectable, () => true);
di.override(windowLocationInjectable, () => ({
host: "some-cluster.lens.app",
port: "irrelevant",
}));
const clusterFileId = di.inject(rendererLogFileIdInjectable);
expect(clusterFileId).toBe("renderer-cluster-some-cluster");
});
});