mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add winston formatting support for error causes (#6576)
* Add winston formatting support for error causes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix failing to run built version - Finally make logger fully injectable - Simplify startMainApplication to only have runMany(Sync) invocations to fix time of use bugs related to logger Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove legacy type enforced ipc to fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
a4d0451dec
commit
8dd1d1a8fa
@ -290,7 +290,6 @@
|
|||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"win-ca": "^3.5.0",
|
"win-ca": "^3.5.0",
|
||||||
"winston": "^3.8.2",
|
"winston": "^3.8.2",
|
||||||
"winston-console-format": "^1.0.8",
|
|
||||||
"winston-transport-browserconsole": "^1.0.5",
|
"winston-transport-browserconsole": "^1.0.5",
|
||||||
"ws": "^8.11.0",
|
"ws": "^8.11.0",
|
||||||
"xterm-link-provider": "^1.3.1"
|
"xterm-link-provider": "^1.3.1"
|
||||||
@ -366,6 +365,7 @@
|
|||||||
"@typescript-eslint/parser": "^5.43.0",
|
"@typescript-eslint/parser": "^5.43.0",
|
||||||
"adr": "^1.4.3",
|
"adr": "^1.4.3",
|
||||||
"ansi_up": "^5.1.0",
|
"ansi_up": "^5.1.0",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"circular-dependency-plugin": "^5.2.2",
|
"circular-dependency-plugin": "^5.2.2",
|
||||||
"cli-progress": "^3.11.2",
|
"cli-progress": "^3.11.2",
|
||||||
|
|||||||
13
src/common/app-paths/directory-for-logs.injectable.ts
Normal file
13
src/common/app-paths/directory-for-logs.injectable.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 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 appPathsInjectable from "./app-paths.injectable";
|
||||||
|
|
||||||
|
const directoryForLogsInjectable = getInjectable({
|
||||||
|
id: "directory-for-logs",
|
||||||
|
instantiate: (di) => di.inject(appPathsInjectable).logs,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default directoryForLogsInjectable;
|
||||||
@ -1,131 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { EventEmitter } from "events";
|
|
||||||
import { onCorrect, onceCorrect } from "../type-enforced-ipc";
|
|
||||||
|
|
||||||
describe("type enforced ipc tests", () => {
|
|
||||||
describe("onCorrect tests", () => {
|
|
||||||
it("should call the handler if the args are valid", () => {
|
|
||||||
let called = false;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called = true;
|
|
||||||
const verifier = (args: unknown[]): args is [] => true;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not call the handler if the args are not valid", () => {
|
|
||||||
let called = false;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called = true;
|
|
||||||
const verifier = (args: unknown[]): args is [] => false;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the handler twice if the args are valid on two emits", () => {
|
|
||||||
let called = 0;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called += 1;
|
|
||||||
const verifier = (args: unknown[]): args is [] => true;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the handler twice if the args are [valid, invalid, valid]", () => {
|
|
||||||
let called = 0;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called += 1;
|
|
||||||
const results = [true, false, true];
|
|
||||||
const verifier = (args: unknown[]): args is [] => results.pop() ?? false;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
source.emit(channel);
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("onceCorrect tests", () => {
|
|
||||||
it("should call the handler if the args are valid", () => {
|
|
||||||
let called = false;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called = true;
|
|
||||||
const verifier = (args: unknown[]): args is [] => true;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onceCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not call the handler if the args are not valid", () => {
|
|
||||||
let called = false;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called = true;
|
|
||||||
const verifier = (args: unknown[]): args is [] => false;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onceCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the handler only once even if args are valid multiple times", () => {
|
|
||||||
let called = 0;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = () => called += 1;
|
|
||||||
const verifier = (args: unknown[]): args is [] => true;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onceCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel);
|
|
||||||
source.emit(channel);
|
|
||||||
expect(called).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the handler on only the first valid set of args", () => {
|
|
||||||
let called = "";
|
|
||||||
let verifierCalled = 0;
|
|
||||||
const source = new EventEmitter();
|
|
||||||
const listener = (info: any, arg: string) => called = arg;
|
|
||||||
const verifier = (args: unknown[]): args is [string] => (++verifierCalled) % 3 === 0;
|
|
||||||
const channel = "foobar";
|
|
||||||
|
|
||||||
onceCorrect({ source, listener, verifier, channel });
|
|
||||||
|
|
||||||
source.emit(channel, {}, "a");
|
|
||||||
source.emit(channel, {}, "b");
|
|
||||||
source.emit(channel, {}, "c");
|
|
||||||
source.emit(channel, {}, "d");
|
|
||||||
source.emit(channel, {}, "e");
|
|
||||||
source.emit(channel, {}, "f");
|
|
||||||
source.emit(channel, {}, "g");
|
|
||||||
source.emit(channel, {}, "h");
|
|
||||||
source.emit(channel, {}, "i");
|
|
||||||
expect(called).toBe("c");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -5,4 +5,3 @@
|
|||||||
|
|
||||||
export * from "./ipc";
|
export * from "./ipc";
|
||||||
export * from "./invalid-kubeconfig";
|
export * from "./invalid-kubeconfig";
|
||||||
export * from "./type-enforced-ipc";
|
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { EventEmitter } from "events";
|
|
||||||
import { ipcMain } from "electron";
|
|
||||||
import logger from "../../main/logger";
|
|
||||||
import type { Disposer } from "../utils";
|
|
||||||
import { ipcMainHandle } from "./ipc";
|
|
||||||
|
|
||||||
export type ListenerEvent<EM extends EventEmitter> = Parameters<Parameters<EM["on"]>[1]>[0];
|
|
||||||
export type ListVerifier<T extends any[]> = (args: unknown[]) => args is T;
|
|
||||||
export type Rest<T> = T extends [any, ...infer R] ? R : [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to `source` that waits for the first IPC message with the correct
|
|
||||||
* argument data is sent.
|
|
||||||
* @param channel The channel to be listened on
|
|
||||||
* @param listener The function for the channel to be called if the args of the correct type
|
|
||||||
* @param verifier The function to be called to verify that the args are the correct type
|
|
||||||
*/
|
|
||||||
export function onceCorrect<
|
|
||||||
IPC extends EventEmitter,
|
|
||||||
Listener extends (event: ListenerEvent<IPC>, ...args: any[]) => any,
|
|
||||||
>({
|
|
||||||
source,
|
|
||||||
channel,
|
|
||||||
listener,
|
|
||||||
verifier,
|
|
||||||
}: {
|
|
||||||
source: IPC;
|
|
||||||
channel: string;
|
|
||||||
listener: Listener;
|
|
||||||
verifier: ListVerifier<Rest<Parameters<Listener>>>;
|
|
||||||
}): void {
|
|
||||||
function wrappedListener(event: ListenerEvent<IPC>, ...args: unknown[]): void {
|
|
||||||
if (verifier(args)) {
|
|
||||||
source.removeListener(channel, wrappedListener); // remove immediately
|
|
||||||
|
|
||||||
(async () => (listener(event, ...args)))() // might return a promise, or throw, or reject
|
|
||||||
.catch((error: any) => logger.error("[IPC]: channel once handler threw error", { channel, error }));
|
|
||||||
} else {
|
|
||||||
logger.error("[IPC]: channel was emitted with invalid data", { channel, args });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
source.on(channel, wrappedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to `source` that checks to verify the arguments before calling the handler.
|
|
||||||
* @param channel The channel to be listened on
|
|
||||||
* @param listener The function for the channel to be called if the args of the correct type
|
|
||||||
* @param verifier The function to be called to verify that the args are the correct type
|
|
||||||
*/
|
|
||||||
export function onCorrect<
|
|
||||||
IPC extends EventEmitter,
|
|
||||||
Listener extends (event: ListenerEvent<IPC>, ...args: any[]) => any,
|
|
||||||
>({
|
|
||||||
source,
|
|
||||||
channel,
|
|
||||||
listener,
|
|
||||||
verifier,
|
|
||||||
}: {
|
|
||||||
source: IPC;
|
|
||||||
channel: string;
|
|
||||||
listener: Listener;
|
|
||||||
verifier: ListVerifier<Rest<Parameters<Listener>>>;
|
|
||||||
}): Disposer {
|
|
||||||
function wrappedListener(event: ListenerEvent<IPC>, ...args: unknown[]) {
|
|
||||||
if (verifier(args)) {
|
|
||||||
(async () => (listener(event, ...args)))() // might return a promise, or throw, or reject
|
|
||||||
.catch(error => logger.error("[IPC]: channel on handler threw error", { channel, error }));
|
|
||||||
} else {
|
|
||||||
logger.error("[IPC]: channel was emitted with invalid data", { channel, args });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
source.on(channel, wrappedListener);
|
|
||||||
|
|
||||||
return () => source.off(channel, wrappedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleCorrect<
|
|
||||||
Handler extends (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any,
|
|
||||||
>({
|
|
||||||
channel,
|
|
||||||
handler,
|
|
||||||
verifier,
|
|
||||||
}: {
|
|
||||||
channel: string;
|
|
||||||
handler: Handler;
|
|
||||||
verifier: ListVerifier<Rest<Parameters<Handler>>>;
|
|
||||||
}): Disposer {
|
|
||||||
function wrappedHandler(event: Electron.IpcMainInvokeEvent, ...args: unknown[]): ReturnType<Handler> {
|
|
||||||
if (verifier(args)) {
|
|
||||||
return handler(event, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TypeError(`Invalid args for invoke on channel: ${channel}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcMainHandle(channel, wrappedHandler);
|
|
||||||
|
|
||||||
return () => ipcMain.removeHandler(channel);
|
|
||||||
}
|
|
||||||
@ -3,12 +3,19 @@
|
|||||||
* 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 logger from "./logger";
|
import { loggerTransportInjectionToken } from "./logger/transports";
|
||||||
|
|
||||||
const loggerInjectable = getInjectable({
|
const loggerInjectable = getInjectable({
|
||||||
id: "logger",
|
id: "logger",
|
||||||
instantiate: (): Logger => logger,
|
instantiate: (di): Logger => createLogger({
|
||||||
|
format: format.combine(
|
||||||
|
format.splat(),
|
||||||
|
format.simple(),
|
||||||
|
),
|
||||||
|
transports: di.injectMany(loggerTransportInjectionToken),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default loggerInjectable;
|
export default loggerInjectable;
|
||||||
|
|||||||
@ -3,12 +3,8 @@
|
|||||||
* 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 { app, ipcMain } from "electron";
|
import { asLegacyGlobalForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
import winston, { format } from "winston";
|
import loggerInjectable from "./logger.injectable";
|
||||||
import type Transport from "winston-transport";
|
|
||||||
import { consoleFormat } from "winston-console-format";
|
|
||||||
import { isDebugging, isTestEnv } from "./vars";
|
|
||||||
import BrowserConsole from "winston-transport-browserconsole";
|
|
||||||
|
|
||||||
export interface Logger {
|
export interface Logger {
|
||||||
info: (message: string, ...args: any) => void;
|
info: (message: string, ...args: any) => void;
|
||||||
@ -18,64 +14,7 @@ export interface Logger {
|
|||||||
silly: (message: string, ...args: any) => void;
|
silly: (message: string, ...args: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const logLevel = process.env.LOG_LEVEL
|
/**
|
||||||
? process.env.LOG_LEVEL
|
* @deprecated use `di.inject(loggerInjectable)` instead
|
||||||
: isDebugging
|
|
||||||
? "debug"
|
|
||||||
: isTestEnv
|
|
||||||
? "error"
|
|
||||||
: "info";
|
|
||||||
|
|
||||||
const transports: Transport[] = [];
|
|
||||||
|
|
||||||
if (ipcMain) {
|
|
||||||
transports.push(
|
|
||||||
new winston.transports.Console({
|
|
||||||
handleExceptions: false,
|
|
||||||
level: logLevel,
|
|
||||||
format: format.combine(
|
|
||||||
format.colorize({ level: true, message: false }),
|
|
||||||
format.padLevels(),
|
|
||||||
format.ms(),
|
|
||||||
consoleFormat({
|
|
||||||
showMeta: true,
|
|
||||||
inspectOptions: {
|
|
||||||
depth: 4,
|
|
||||||
colors: true,
|
|
||||||
maxArrayLength: 10,
|
|
||||||
breakLength: 120,
|
|
||||||
compact: Infinity,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isTestEnv) {
|
|
||||||
transports.push(
|
|
||||||
new winston.transports.File({
|
|
||||||
handleExceptions: false,
|
|
||||||
level: "debug",
|
|
||||||
filename: "lens.log",
|
|
||||||
/**
|
|
||||||
* SAFTEY: the `ipcMain` check above should mean that this is only
|
|
||||||
* called in the main process
|
|
||||||
*/
|
*/
|
||||||
dirname: app.getPath("logs"),
|
export default asLegacyGlobalForExtensionApi(loggerInjectable);
|
||||||
maxsize: 1024 * 1024,
|
|
||||||
maxFiles: 16,
|
|
||||||
tailable: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
transports.push(new BrowserConsole());
|
|
||||||
}
|
|
||||||
|
|
||||||
export default winston.createLogger({
|
|
||||||
format: format.combine(
|
|
||||||
format.splat(),
|
|
||||||
format.simple(),
|
|
||||||
),
|
|
||||||
transports,
|
|
||||||
}) as Logger;
|
|
||||||
|
|||||||
11
src/common/logger/transports.ts
Normal file
11
src/common/logger/transports.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import type TransportStream from "winston-transport";
|
||||||
|
|
||||||
|
export const loggerTransportInjectionToken = getInjectionToken<TransportStream>({
|
||||||
|
id: "logger-transport",
|
||||||
|
});
|
||||||
@ -12,8 +12,8 @@ import { pathNames } from "../../common/app-paths/app-path-names";
|
|||||||
import { fromPairs, map } from "lodash/fp";
|
import { fromPairs, map } from "lodash/fp";
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||||
import { beforeElectronIsReadyInjectionToken } from "../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
|
||||||
import appNameInjectable from "../../common/vars/app-name.injectable";
|
import appNameInjectable from "../../common/vars/app-name.injectable";
|
||||||
|
import { appPathsRunnablePhaseInjectionToken } from "../start-main-application/runnable-tokens/phases";
|
||||||
|
|
||||||
const setupAppPathsInjectable = getInjectable({
|
const setupAppPathsInjectable = getInjectable({
|
||||||
id: "setup-app-paths",
|
id: "setup-app-paths",
|
||||||
@ -51,7 +51,7 @@ const setupAppPathsInjectable = getInjectable({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
injectionToken: appPathsRunnablePhaseInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupAppPathsInjectable;
|
export default setupAppPathsInjectable;
|
||||||
|
|||||||
@ -111,14 +111,12 @@ export class ContextHandler implements ClusterContextHandler {
|
|||||||
const potentialServices = await Promise.allSettled(
|
const potentialServices = await Promise.allSettled(
|
||||||
providers.map(provider => provider.getPrometheusService(apiClient)),
|
providers.map(provider => provider.getPrometheusService(apiClient)),
|
||||||
);
|
);
|
||||||
const errors: any[] = [];
|
const errors = [];
|
||||||
|
|
||||||
for (const res of potentialServices) {
|
for (const res of potentialServices) {
|
||||||
switch (res.status) {
|
switch (res.status) {
|
||||||
case "rejected":
|
case "rejected":
|
||||||
if (res.reason) {
|
errors.push(res.reason);
|
||||||
errors.push(String(res.reason));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "fulfilled":
|
case "fulfilled":
|
||||||
@ -128,7 +126,7 @@ export class ContextHandler implements ClusterContextHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Object.assign(new Error("No Prometheus service found"), { cause: errors });
|
throw new Error("No Prometheus service found", { cause: errors });
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveAuthProxyUrl(): Promise<string> {
|
async resolveAuthProxyUrl(): Promise<string> {
|
||||||
|
|||||||
@ -11,15 +11,10 @@ import * as LensExtensionsCommonApi from "../extensions/common-api";
|
|||||||
import * as LensExtensionsMainApi from "../extensions/main-api";
|
import * as LensExtensionsMainApi from "../extensions/main-api";
|
||||||
import { getDi } from "./getDi";
|
import { getDi } from "./getDi";
|
||||||
import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable";
|
import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable";
|
||||||
import shouldStartHiddenInjectable from "./electron-app/features/should-start-hidden.injectable";
|
|
||||||
|
|
||||||
const di = getDi();
|
const di = getDi();
|
||||||
|
|
||||||
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
|
void di.inject(startMainApplicationInjectable);
|
||||||
|
|
||||||
const startApplication = di.inject(startMainApplicationInjectable);
|
|
||||||
|
|
||||||
void startApplication(!shouldStartHidden);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports for virtual package "@k8slens/extensions" for main-process.
|
* Exports for virtual package "@k8slens/extensions" for main-process.
|
||||||
|
|||||||
184
src/main/logger/console-format.ts
Normal file
184
src/main/logger/console-format.ts
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { LEVEL, MESSAGE, SPLAT } from "triple-beam";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import type { InspectOptions } from "util";
|
||||||
|
import { inspect } from "util";
|
||||||
|
|
||||||
|
// The following license was copied from https://github.com/duccio/winston-console-format/blob/master/LICENSE
|
||||||
|
// This was modified to support formatting causes
|
||||||
|
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2015 Eugeny Dementev
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ConsoleFormatOptions {
|
||||||
|
showMeta?: boolean;
|
||||||
|
metaStrip?: string[];
|
||||||
|
inspectOptions?: InspectOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransformableInfo {
|
||||||
|
level: string;
|
||||||
|
message: string;
|
||||||
|
[key: string | symbol]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConsoleFormat {
|
||||||
|
private static readonly reSpaces = /^\s+/;
|
||||||
|
private static readonly reSpacesOrEmpty = /^(\s*)/;
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
private static readonly reColor = /\x1B\[\d+m/;
|
||||||
|
private static readonly defaultStrip = [LEVEL, MESSAGE, SPLAT, "level", "message", "ms", "stack"];
|
||||||
|
private static readonly chars = {
|
||||||
|
singleLine: "▪",
|
||||||
|
startLine: "┏",
|
||||||
|
line: "┃",
|
||||||
|
endLine: "┗",
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly showMeta: boolean;
|
||||||
|
private readonly metaStrip: string[];
|
||||||
|
private readonly inspectOptions: InspectOptions;
|
||||||
|
|
||||||
|
public constructor(opts?: ConsoleFormatOptions) {
|
||||||
|
this.showMeta = opts?.showMeta ?? true;
|
||||||
|
this.metaStrip = opts?.metaStrip ?? [];
|
||||||
|
this.inspectOptions = opts?.inspectOptions ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLines(value: unknown): string[] {
|
||||||
|
return inspect(value, this.inspectOptions).split("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private message(info: TransformableInfo, chr: string, color: string): string {
|
||||||
|
const message = info.message.replace(
|
||||||
|
ConsoleFormat.reSpacesOrEmpty,
|
||||||
|
`$1${color}${chalk.dim(chr)}${chalk.reset(" ")}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return `${info.level}:${message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private pad(message?: string): string {
|
||||||
|
return message?.match(ConsoleFormat.reSpaces)?.[0] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ms(info: TransformableInfo): string {
|
||||||
|
if (info.ms) {
|
||||||
|
return chalk.italic(chalk.dim(` ${info.ms}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private stack(info: TransformableInfo): string[] {
|
||||||
|
const messages: string[] = [];
|
||||||
|
|
||||||
|
if (info.stack) {
|
||||||
|
const error = new Error();
|
||||||
|
|
||||||
|
error.stack = info.stack;
|
||||||
|
messages.push(...this.getLines(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cause(source: unknown): string[] {
|
||||||
|
const messages: string[] = [];
|
||||||
|
|
||||||
|
if (source instanceof Error && source.cause) {
|
||||||
|
messages.push(`Cause: ${source.cause}`);
|
||||||
|
messages.push(...this.getLines(source.cause).map(l => ` ${l}`));
|
||||||
|
messages.push(...this._cause(source.cause));
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private cause(info: TransformableInfo): string[] {
|
||||||
|
const splats = info[SPLAT];
|
||||||
|
|
||||||
|
if (Array.isArray(splats)) {
|
||||||
|
return splats.flatMap(splat => this._cause(splat));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private meta(info: TransformableInfo): string[] {
|
||||||
|
const messages: string[] = [];
|
||||||
|
const stripped = { ...info };
|
||||||
|
|
||||||
|
ConsoleFormat.defaultStrip.forEach((e) => delete stripped[e]);
|
||||||
|
this.metaStrip?.forEach((e) => delete stripped[e]);
|
||||||
|
|
||||||
|
if (Object.keys(stripped).length > 0) {
|
||||||
|
messages.push(...this.getLines(stripped));
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getColor(info: TransformableInfo): string {
|
||||||
|
return info.level.match(ConsoleFormat.reColor)?.[0] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private write(info: TransformableInfo, messages: string[], color: string): void {
|
||||||
|
const pad = this.pad(info.message);
|
||||||
|
|
||||||
|
messages.forEach((line, index, arr) => {
|
||||||
|
const lineNumber = chalk.dim(`[${(index + 1).toString().padStart(arr.length.toString().length, " ")}]`);
|
||||||
|
let chr = ConsoleFormat.chars.line;
|
||||||
|
|
||||||
|
if (index === arr.length - 1) {
|
||||||
|
chr = ConsoleFormat.chars.endLine;
|
||||||
|
}
|
||||||
|
info[MESSAGE] += `\n${chalk.dim(info.level)}:${pad}${color}${chalk.dim(chr)}${chalk.reset(" ")}`;
|
||||||
|
info[MESSAGE] += `${lineNumber} ${line}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public transform(info: TransformableInfo): TransformableInfo {
|
||||||
|
const messages: string[] = [];
|
||||||
|
|
||||||
|
if (this.showMeta) {
|
||||||
|
messages.push(...this.stack(info));
|
||||||
|
messages.push(...this.meta(info));
|
||||||
|
messages.push(...this.cause(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
const color = this.getColor(info);
|
||||||
|
|
||||||
|
info[MESSAGE] = this.message(info, ConsoleFormat.chars[messages.length > 0 ? "startLine" : "singleLine"], color);
|
||||||
|
info[MESSAGE] += this.ms(info);
|
||||||
|
|
||||||
|
this.write(info, messages, color);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/logger/console-transport.injectable.ts
Normal file
35
src/main/logger/console-transport.injectable.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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 { format, transports } from "winston";
|
||||||
|
import { ConsoleFormat } from "./console-format";
|
||||||
|
import { loggerTransportInjectionToken } from "../../common/logger/transports";
|
||||||
|
import logLevelInjectable from "./level.injectable";
|
||||||
|
|
||||||
|
const consoleLoggerTransportInjectable = getInjectable({
|
||||||
|
id: "console-logger-transport",
|
||||||
|
instantiate: (di) => new transports.Console({
|
||||||
|
handleExceptions: false,
|
||||||
|
level: di.inject(logLevelInjectable),
|
||||||
|
format: format.combine(
|
||||||
|
format.colorize({ level: true, message: false }),
|
||||||
|
format.padLevels(),
|
||||||
|
format.ms(),
|
||||||
|
new ConsoleFormat({
|
||||||
|
showMeta: true,
|
||||||
|
inspectOptions: {
|
||||||
|
depth: 4,
|
||||||
|
colors: true,
|
||||||
|
maxArrayLength: 10,
|
||||||
|
breakLength: 120,
|
||||||
|
compact: Infinity,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
injectionToken: loggerTransportInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default consoleLoggerTransportInjectable;
|
||||||
28
src/main/logger/file-transport.injectable.ts
Normal file
28
src/main/logger/file-transport.injectable.ts
Normal 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 { transports } from "winston";
|
||||||
|
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
|
||||||
|
import { loggerTransportInjectionToken } from "../../common/logger/transports";
|
||||||
|
|
||||||
|
const fileLoggerTranportInjectable = getInjectable({
|
||||||
|
id: "file-logger-tranport",
|
||||||
|
instantiate: (di) => new transports.File({
|
||||||
|
handleExceptions: false,
|
||||||
|
level: "debug",
|
||||||
|
filename: "lens.log",
|
||||||
|
/**
|
||||||
|
* SAFTEY: the `ipcMain` check above should mean that this is only
|
||||||
|
* called in the main process
|
||||||
|
*/
|
||||||
|
dirname: di.inject(directoryForLogsInjectable),
|
||||||
|
maxsize: 1024 * 1024,
|
||||||
|
maxFiles: 16,
|
||||||
|
tailable: true,
|
||||||
|
}),
|
||||||
|
injectionToken: loggerTransportInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default fileLoggerTranportInjectable;
|
||||||
9
src/main/logger/level.global-override-for-injectable.ts
Normal file
9
src/main/logger/level.global-override-for-injectable.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getGlobalOverride } from "../../common/test-utils/get-global-override";
|
||||||
|
import logLevelInjectable from "./level.injectable";
|
||||||
|
|
||||||
|
export default getGlobalOverride(logLevelInjectable, () => "error");
|
||||||
30
src/main/logger/level.injectable.ts
Normal file
30
src/main/logger/level.injectable.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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 isDebuggingInjectable from "../../common/vars/is-debugging.injectable";
|
||||||
|
|
||||||
|
export type LogLevel = "silly" | "debug" | "info" | "warn" | "error";
|
||||||
|
|
||||||
|
const logLevelInjectable = getInjectable({
|
||||||
|
id: "log-level",
|
||||||
|
instantiate: (di): LogLevel => {
|
||||||
|
const isDebugging = di.inject(isDebuggingInjectable);
|
||||||
|
const baseLevel = process.env.LOG_LEVEL?.toLowerCase();
|
||||||
|
|
||||||
|
switch (baseLevel) {
|
||||||
|
case "silly":
|
||||||
|
case "debug":
|
||||||
|
case "info":
|
||||||
|
case "warn":
|
||||||
|
case "error":
|
||||||
|
return baseLevel;
|
||||||
|
default:
|
||||||
|
return isDebugging ? "debug" : "info";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default logLevelInjectable;
|
||||||
@ -28,15 +28,18 @@ const loadMetricsFor = (getMetrics: GetMetrics) => async (promQueries: string[],
|
|||||||
try {
|
try {
|
||||||
return await getMetrics(cluster, prometheusPath, { query, ...queryParams });
|
return await getMetrics(cluster, prometheusPath, { query, ...queryParams });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRequestError(error)) {
|
if (
|
||||||
if (lastAttempt || (error.statusCode && error.statusCode >= 400 && error.statusCode < 500)) {
|
!isRequestError(error)
|
||||||
|
|| lastAttempt
|
||||||
|
|| (
|
||||||
|
!lastAttempt && (
|
||||||
|
typeof error.statusCode === "number" &&
|
||||||
|
400 <= error.statusCode && error.statusCode < 500
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
throw new Error("Metrics not available", { cause: error });
|
throw new Error("Metrics not available", { cause: error });
|
||||||
}
|
}
|
||||||
} else if (error instanceof Error) {
|
|
||||||
throw new Error("Metrics not available", { cause: error });
|
|
||||||
} else {
|
|
||||||
throw new Error("Metrics not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
|
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/main/start-main-application/runnable-tokens/phases.ts
Normal file
28
src/main/start-main-application/runnable-tokens/phases.ts
Normal 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 { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import type { Runnable } from "../../../common/runnable/run-many-for";
|
||||||
|
import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These tokens are here so that the importing of their respective dependencies
|
||||||
|
* can be delayed until all of them are ready
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This runnable token should only have the app paths init so that it can be run by itself
|
||||||
|
*/
|
||||||
|
export const appPathsRunnablePhaseInjectionToken = getInjectionToken<RunnableSync>({
|
||||||
|
id: "app-paths-runnable-phase",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const showLoadingRunnablePhaseInjectionToken = getInjectionToken<Runnable>({
|
||||||
|
id: "show-loading-runnable-phase",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const showInitialWindowRunnablePhaseInjectionToken = getInjectionToken<Runnable>({
|
||||||
|
id: "show-initial-window-runnable-phase",
|
||||||
|
});
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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 shouldStartHiddenInjectable from "../../electron-app/features/should-start-hidden.injectable";
|
||||||
|
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
|
||||||
|
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
|
||||||
|
import createFirstApplicationWindowInjectable from "../lens-window/application-window/create-first-application-window.injectable";
|
||||||
|
import splashWindowInjectable from "../lens-window/splash-window/splash-window.injectable";
|
||||||
|
import { showInitialWindowRunnablePhaseInjectionToken } from "../runnable-tokens/phases";
|
||||||
|
|
||||||
|
const getDeepLinkUrl = (commandLineArguments: string[]) => (
|
||||||
|
commandLineArguments
|
||||||
|
.map(arg => arg.toLowerCase())
|
||||||
|
.find(arg => arg.startsWith("lens://"))
|
||||||
|
);
|
||||||
|
|
||||||
|
const showInitialWindowInjectable = getInjectable({
|
||||||
|
id: "show-initial-window",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
|
||||||
|
const shouldStartWindow = !shouldStartHidden;
|
||||||
|
const createFirstApplicationWindow = di.inject(createFirstApplicationWindowInjectable);
|
||||||
|
const splashWindow = di.inject(splashWindowInjectable);
|
||||||
|
const openDeepLink = di.inject(openDeepLinkInjectable);
|
||||||
|
const commandLineArguments = di.inject(commandLineArgumentsInjectable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: "show-initial-window",
|
||||||
|
run: async () => {
|
||||||
|
if (shouldStartWindow) {
|
||||||
|
const deepLinkUrl = getDeepLinkUrl(commandLineArguments);
|
||||||
|
|
||||||
|
if (deepLinkUrl) {
|
||||||
|
await openDeepLink(deepLinkUrl);
|
||||||
|
} else {
|
||||||
|
const applicationWindow = createFirstApplicationWindow();
|
||||||
|
|
||||||
|
await applicationWindow.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
splashWindow.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: showInitialWindowRunnablePhaseInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showInitialWindowInjectable;
|
||||||
@ -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 shouldStartHiddenInjectable from "../../electron-app/features/should-start-hidden.injectable";
|
||||||
|
import splashWindowInjectable from "../lens-window/splash-window/splash-window.injectable";
|
||||||
|
import { showLoadingRunnablePhaseInjectionToken } from "../runnable-tokens/phases";
|
||||||
|
|
||||||
|
const showLoadingInjectable = getInjectable({
|
||||||
|
id: "show-loading",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
|
||||||
|
const shouldShowLoadingWindow = !shouldStartHidden;
|
||||||
|
const splashWindow = di.inject(splashWindowInjectable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: "show-loading",
|
||||||
|
run: async () => {
|
||||||
|
if (shouldShowLoadingWindow) {
|
||||||
|
await splashWindow.start();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: showLoadingRunnablePhaseInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showLoadingInjectable;
|
||||||
@ -10,13 +10,8 @@ import { beforeElectronIsReadyInjectionToken } from "./runnable-tokens/before-el
|
|||||||
import { beforeApplicationIsLoadingInjectionToken } from "./runnable-tokens/before-application-is-loading-injection-token";
|
import { beforeApplicationIsLoadingInjectionToken } from "./runnable-tokens/before-application-is-loading-injection-token";
|
||||||
import { onLoadOfApplicationInjectionToken } from "./runnable-tokens/on-load-of-application-injection-token";
|
import { onLoadOfApplicationInjectionToken } from "./runnable-tokens/on-load-of-application-injection-token";
|
||||||
import { afterApplicationIsLoadedInjectionToken } from "./runnable-tokens/after-application-is-loaded-injection-token";
|
import { afterApplicationIsLoadedInjectionToken } from "./runnable-tokens/after-application-is-loaded-injection-token";
|
||||||
import splashWindowInjectable from "./lens-window/splash-window/splash-window.injectable";
|
|
||||||
import openDeepLinkInjectable from "../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { find, map, startsWith, toLower } from "lodash/fp";
|
|
||||||
import commandLineArgumentsInjectable from "../utils/command-line-arguments.injectable";
|
|
||||||
import waitForElectronToBeReadyInjectable from "../electron-app/features/wait-for-electron-to-be-ready.injectable";
|
import waitForElectronToBeReadyInjectable from "../electron-app/features/wait-for-electron-to-be-ready.injectable";
|
||||||
import createFirstApplicationWindowInjectable from "./lens-window/application-window/create-first-application-window.injectable";
|
import { appPathsRunnablePhaseInjectionToken, showInitialWindowRunnablePhaseInjectionToken, showLoadingRunnablePhaseInjectionToken } from "./runnable-tokens/phases";
|
||||||
|
|
||||||
const startMainApplicationInjectable = getInjectable({
|
const startMainApplicationInjectable = getInjectable({
|
||||||
id: "start-main-application",
|
id: "start-main-application",
|
||||||
@ -25,53 +20,29 @@ const startMainApplicationInjectable = getInjectable({
|
|||||||
const runMany = runManyFor(di);
|
const runMany = runManyFor(di);
|
||||||
const runManySync = runManySyncFor(di);
|
const runManySync = runManySyncFor(di);
|
||||||
const waitForElectronToBeReady = di.inject(waitForElectronToBeReadyInjectable);
|
const waitForElectronToBeReady = di.inject(waitForElectronToBeReadyInjectable);
|
||||||
const createFirstApplicationWindow = di.inject(createFirstApplicationWindowInjectable);
|
|
||||||
const splashWindow = di.inject(splashWindowInjectable);
|
|
||||||
const openDeepLink = di.inject(openDeepLinkInjectable);
|
|
||||||
const commandLineArguments = di.inject(commandLineArgumentsInjectable);
|
|
||||||
|
|
||||||
|
const appPathsRunnablePhase = runManySync(appPathsRunnablePhaseInjectionToken);
|
||||||
const beforeElectronIsReady = runManySync(beforeElectronIsReadyInjectionToken);
|
const beforeElectronIsReady = runManySync(beforeElectronIsReadyInjectionToken);
|
||||||
const beforeApplicationIsLoading = runMany(beforeApplicationIsLoadingInjectionToken);
|
const beforeApplicationIsLoading = runMany(beforeApplicationIsLoadingInjectionToken);
|
||||||
|
const showLoadingRunnablePhase = runMany(showLoadingRunnablePhaseInjectionToken);
|
||||||
const onLoadOfApplication = runMany(onLoadOfApplicationInjectionToken);
|
const onLoadOfApplication = runMany(onLoadOfApplicationInjectionToken);
|
||||||
|
const showInitialWindowRunnablePhase = runMany(showInitialWindowRunnablePhaseInjectionToken);
|
||||||
const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken);
|
const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken);
|
||||||
|
|
||||||
return (shouldStartWindow: boolean) => {
|
|
||||||
// Stuff happening before application is ready needs to be synchronous because of
|
// Stuff happening before application is ready needs to be synchronous because of
|
||||||
// https://github.com/electron/electron/issues/21370
|
// https://github.com/electron/electron/issues/21370
|
||||||
|
appPathsRunnablePhase();
|
||||||
beforeElectronIsReady();
|
beforeElectronIsReady();
|
||||||
|
|
||||||
return (async () => {
|
return (async () => {
|
||||||
await waitForElectronToBeReady();
|
await waitForElectronToBeReady();
|
||||||
|
|
||||||
await beforeApplicationIsLoading();
|
await beforeApplicationIsLoading();
|
||||||
|
await showLoadingRunnablePhase();
|
||||||
if (shouldStartWindow) {
|
|
||||||
await splashWindow.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
await onLoadOfApplication();
|
await onLoadOfApplication();
|
||||||
|
await showInitialWindowRunnablePhase();
|
||||||
if (shouldStartWindow) {
|
|
||||||
const deepLinkUrl = getDeepLinkUrl(commandLineArguments);
|
|
||||||
|
|
||||||
if (deepLinkUrl) {
|
|
||||||
await openDeepLink(deepLinkUrl);
|
|
||||||
} else {
|
|
||||||
const applicationWindow = createFirstApplicationWindow();
|
|
||||||
|
|
||||||
await applicationWindow.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
splashWindow.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
await afterApplicationIsLoaded();
|
await afterApplicationIsLoaded();
|
||||||
})();
|
})();
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getDeepLinkUrl = (commandLineArguments: string[]) =>
|
|
||||||
pipeline(commandLineArguments, map(toLower), find(startsWith("lens://")));
|
|
||||||
|
|
||||||
export default startMainApplicationInjectable;
|
export default startMainApplicationInjectable;
|
||||||
|
|||||||
@ -68,6 +68,7 @@ import applicationMenuItemCompositeInjectable from "../../../features/applicatio
|
|||||||
import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths";
|
import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths";
|
||||||
import { discoverFor } from "./discovery-of-html-elements";
|
import { discoverFor } from "./discovery-of-html-elements";
|
||||||
import { findComposite } from "../../../common/utils/composite/find-composite/find-composite";
|
import { findComposite } from "../../../common/utils/composite/find-composite/find-composite";
|
||||||
|
import shouldStartHiddenInjectable from "../../../main/electron-app/features/should-start-hidden.injectable";
|
||||||
|
|
||||||
type Callback = (di: DiContainer) => void | Promise<void>;
|
type Callback = (di: DiContainer) => void | Promise<void>;
|
||||||
|
|
||||||
@ -313,11 +314,9 @@ export const getApplicationBuilder = () => {
|
|||||||
.map(toWindowWithHelpersFor(windowHelpers)),
|
.map(toWindowWithHelpersFor(windowHelpers)),
|
||||||
|
|
||||||
get: (id) => {
|
get: (id) => {
|
||||||
const applicationWindows = builder.applicationWindow.getAll();
|
const applicationWindow = builder.applicationWindow
|
||||||
|
.getAll()
|
||||||
const applicationWindow = applicationWindows.find(
|
.find((window) => window.id === id);
|
||||||
(window) => window.id === id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!applicationWindow) {
|
if (!applicationWindow) {
|
||||||
throw new Error(`Tried to get application window with ID "${id}" but it was not found.`);
|
throw new Error(`Tried to get application window with ID "${id}" but it was not found.`);
|
||||||
@ -327,9 +326,7 @@ export const getApplicationBuilder = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
create: (id) => {
|
create: (id) => {
|
||||||
const createApplicationWindow = mainDi.inject(
|
const createApplicationWindow = mainDi.inject(createApplicationWindowInjectable);
|
||||||
createApplicationWindowInjectable,
|
|
||||||
);
|
|
||||||
|
|
||||||
createApplicationWindow(id);
|
createApplicationWindow(id);
|
||||||
|
|
||||||
@ -656,11 +653,8 @@ export const getApplicationBuilder = () => {
|
|||||||
await callback(mainDi);
|
await callback(mainDi);
|
||||||
}
|
}
|
||||||
|
|
||||||
const startMainApplication = mainDi.inject(
|
mainDi.override(shouldStartHiddenInjectable, () => true);
|
||||||
startMainApplicationInjectable,
|
await mainDi.inject(startMainApplicationInjectable);
|
||||||
);
|
|
||||||
|
|
||||||
await startMainApplication(false);
|
|
||||||
|
|
||||||
applicationHasStarted = true;
|
applicationHasStarted = true;
|
||||||
},
|
},
|
||||||
@ -672,21 +666,15 @@ export const getApplicationBuilder = () => {
|
|||||||
await callback(mainDi);
|
await callback(mainDi);
|
||||||
}
|
}
|
||||||
|
|
||||||
const startMainApplication = mainDi.inject(
|
mainDi.override(shouldStartHiddenInjectable, () => false);
|
||||||
startMainApplicationInjectable,
|
await mainDi.inject(startMainApplicationInjectable);
|
||||||
);
|
|
||||||
|
|
||||||
await startMainApplication(true);
|
|
||||||
|
|
||||||
applicationHasStarted = true;
|
applicationHasStarted = true;
|
||||||
|
|
||||||
const applicationWindow = builder.applicationWindow.get(
|
return builder
|
||||||
"first-application-window",
|
.applicationWindow
|
||||||
);
|
.get("first-application-window")
|
||||||
|
.rendered;
|
||||||
assert(applicationWindow);
|
|
||||||
|
|
||||||
return applicationWindow.rendered;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
select: {
|
select: {
|
||||||
|
|||||||
@ -5,10 +5,9 @@
|
|||||||
|
|
||||||
import type { IpcRendererEvent } from "electron";
|
import type { IpcRendererEvent } from "electron";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import { onCorrect } from "../../common/ipc";
|
|
||||||
import { Notifications } from "../components/notifications";
|
import { Notifications } from "../components/notifications";
|
||||||
import { defaultHotbarCells } from "../../common/hotbars/types";
|
import { defaultHotbarCells } from "../../common/hotbars/types";
|
||||||
import { type ListNamespaceForbiddenArgs, clusterListNamespaceForbiddenChannel, isListNamespaceForbiddenArgs } from "../../common/ipc/cluster";
|
import { type ListNamespaceForbiddenArgs, clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster";
|
||||||
import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar";
|
import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar";
|
||||||
|
|
||||||
function HotbarTooManyItemsHandler(): void {
|
function HotbarTooManyItemsHandler(): void {
|
||||||
@ -23,15 +22,6 @@ interface Dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const registerIpcListeners = ({ listNamespacesForbiddenHandler }: Dependencies) => () => {
|
export const registerIpcListeners = ({ listNamespacesForbiddenHandler }: Dependencies) => () => {
|
||||||
onCorrect({
|
ipcRenderer.on(clusterListNamespaceForbiddenChannel, listNamespacesForbiddenHandler);
|
||||||
source: ipcRenderer,
|
ipcRenderer.on(hotbarTooManyItemsChannel, HotbarTooManyItemsHandler);
|
||||||
channel: clusterListNamespaceForbiddenChannel,
|
};
|
||||||
listener: listNamespacesForbiddenHandler,
|
|
||||||
verifier: isListNamespaceForbiddenArgs,
|
|
||||||
});
|
|
||||||
onCorrect({
|
|
||||||
source: ipcRenderer,
|
|
||||||
channel: hotbarTooManyItemsChannel,
|
|
||||||
listener: HotbarTooManyItemsHandler,
|
|
||||||
verifier: (args: unknown[]): args is [] => args.length === 0,
|
|
||||||
});};
|
|
||||||
|
|||||||
@ -107,7 +107,7 @@ export class KubeWatchApi {
|
|||||||
unsubscribe.push(store.subscribe({ onLoadFailure, abortController: childController }));
|
unsubscribe.push(store.subscribe({ onLoadFailure, abortController: childController }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error instanceof DOMException)) {
|
if (!(error instanceof DOMException)) {
|
||||||
this.log(Object.assign(new Error("Loading stores has failed"), { cause: error }), {
|
this.log(new Error("Loading stores has failed", { cause: error }), {
|
||||||
meta: { store, namespaces },
|
meta: { store, namespaces },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/renderer/logger/browser-transport.injectable.ts
Normal file
15
src/renderer/logger/browser-transport.injectable.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 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 BrowserConsole from "winston-transport-browserconsole";
|
||||||
|
import { loggerTransportInjectionToken } from "../../common/logger/transports";
|
||||||
|
|
||||||
|
const browserLoggerTransportInjectable = getInjectable({
|
||||||
|
id: "browser-logger-transport",
|
||||||
|
instantiate: () => new BrowserConsole(),
|
||||||
|
injectionToken: loggerTransportInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default browserLoggerTransportInjectable;
|
||||||
@ -7,30 +7,10 @@ import React from "react";
|
|||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import * as proto from "../../../common/protocol-handler";
|
import * as proto from "../../../common/protocol-handler";
|
||||||
import Url from "url-parse";
|
import Url from "url-parse";
|
||||||
import { onCorrect } from "../../../common/ipc";
|
|
||||||
import type { LensProtocolRouterDependencies } from "../../../common/protocol-handler";
|
import type { LensProtocolRouterDependencies } from "../../../common/protocol-handler";
|
||||||
import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../../common/protocol-handler";
|
import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../../common/protocol-handler";
|
||||||
import { Notifications } from "../../components/notifications";
|
import { Notifications } from "../../components/notifications";
|
||||||
|
|
||||||
function verifyIpcArgs(args: unknown[]): args is [string, RouteAttempt] {
|
|
||||||
if (args.length !== 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof args[0] !== "string") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (args[1]) {
|
|
||||||
case RouteAttempt.MATCHED:
|
|
||||||
case RouteAttempt.MISSING:
|
|
||||||
case RouteAttempt.MISSING_EXTENSION:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies extends LensProtocolRouterDependencies {}
|
interface Dependencies extends LensProtocolRouterDependencies {}
|
||||||
|
|
||||||
export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
||||||
@ -42,11 +22,7 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
|||||||
* This function is needed to be called early on in the renderers lifetime.
|
* This function is needed to be called early on in the renderers lifetime.
|
||||||
*/
|
*/
|
||||||
public init(): void {
|
public init(): void {
|
||||||
onCorrect({
|
ipcRenderer.on(proto.ProtocolHandlerInternal, (event, rawUrl: string, mainAttemptResult: RouteAttempt) => {
|
||||||
channel: proto.ProtocolHandlerInternal,
|
|
||||||
source: ipcRenderer,
|
|
||||||
verifier: verifyIpcArgs,
|
|
||||||
listener: (event, rawUrl, mainAttemptResult) => {
|
|
||||||
const rendererAttempt = this._routeToInternal(new Url(rawUrl, true));
|
const rendererAttempt = this._routeToInternal(new Url(rawUrl, true));
|
||||||
|
|
||||||
if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) {
|
if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) {
|
||||||
@ -58,13 +34,8 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
|||||||
</p>
|
</p>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
onCorrect({
|
ipcRenderer.on(proto.ProtocolHandlerExtension, async (event, rawUrl: string, mainAttemptResult: RouteAttempt) => {
|
||||||
channel: proto.ProtocolHandlerExtension,
|
|
||||||
source: ipcRenderer,
|
|
||||||
verifier: verifyIpcArgs,
|
|
||||||
listener: async (event, rawUrl, mainAttemptResult) => {
|
|
||||||
const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true));
|
const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true));
|
||||||
|
|
||||||
switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) {
|
switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) {
|
||||||
@ -87,12 +58,8 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
|||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
onCorrect({
|
ipcRenderer.on(ProtocolHandlerInvalid, (event, error: string, rawUrl: string) => {
|
||||||
channel: ProtocolHandlerInvalid,
|
|
||||||
source: ipcRenderer,
|
|
||||||
listener: (event, error, rawUrl) => {
|
|
||||||
Notifications.error((
|
Notifications.error((
|
||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
@ -107,10 +74,6 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
|
|||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
},
|
|
||||||
verifier: (args): args is [string, string] => {
|
|
||||||
return args.length === 2 && typeof args[0] === "string";
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
import Webpack from "webpack";
|
import Webpack from "webpack";
|
||||||
import WebpackDevServer from "webpack-dev-server";
|
import WebpackDevServer from "webpack-dev-server";
|
||||||
import { webpackLensRenderer } from "./renderer";
|
import { webpackLensRenderer } from "./renderer";
|
||||||
import logger from "../src/common/logger";
|
|
||||||
import { buildDir, webpackDevServerPort } from "./vars";
|
import { buildDir, webpackDevServerPort } from "./vars";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,6 +41,6 @@ const server = new WebpackDevServer({
|
|||||||
},
|
},
|
||||||
}, compiler);
|
}, compiler);
|
||||||
|
|
||||||
logger.info(`[WEBPACK-DEV-SERVER]: created with options`, server.options);
|
console.info(`[WEBPACK-DEV-SERVER]: created with options`, server.options);
|
||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
|
|||||||
13
yarn.lock
13
yarn.lock
@ -4118,7 +4118,7 @@ colors@1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||||
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
|
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
|
||||||
|
|
||||||
colors@^1.3.3, colors@^1.4.0:
|
colors@^1.3.3:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||||
@ -8462,7 +8462,7 @@ lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
logform@^2.2.0, logform@^2.3.2, logform@^2.4.0:
|
logform@^2.3.2, logform@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe"
|
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe"
|
||||||
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==
|
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==
|
||||||
@ -12851,15 +12851,6 @@ win-ca@^3.5.0:
|
|||||||
node-forge "^1.2.1"
|
node-forge "^1.2.1"
|
||||||
split "^1.0.1"
|
split "^1.0.1"
|
||||||
|
|
||||||
winston-console-format@^1.0.8:
|
|
||||||
version "1.0.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/winston-console-format/-/winston-console-format-1.0.8.tgz#591adc8e9567c3397a3fa2e29e596d56e48db840"
|
|
||||||
integrity sha512-dq7t/E0D0QRi4XIOwu6HM1+5e//WPqylH88GVjKEhQVrzGFg34MCz+G7pMJcXFBen9C0kBsu5GYgbYsE2LDwKw==
|
|
||||||
dependencies:
|
|
||||||
colors "^1.4.0"
|
|
||||||
logform "^2.2.0"
|
|
||||||
triple-beam "^1.3.0"
|
|
||||||
|
|
||||||
winston-transport-browserconsole@^1.0.5:
|
winston-transport-browserconsole@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/winston-transport-browserconsole/-/winston-transport-browserconsole-1.0.5.tgz#8ef1bc32da5fb0a66604f2b8b6f127ed725108c9"
|
resolved "https://registry.yarnpkg.com/winston-transport-browserconsole/-/winston-transport-browserconsole-1.0.5.tgz#8ef1bc32da5fb0a66604f2b8b6f127ed725108c9"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user