From 8dd1d1a8faef9901b05a736b2065a8fa13adb695 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 17 Nov 2022 04:56:16 -0800 Subject: [PATCH] Add winston formatting support for error causes (#6576) * Add winston formatting support for error causes Signed-off-by: Sebastian Malton * 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 * Remove legacy type enforced ipc to fix tests Signed-off-by: Sebastian Malton * Fix type error Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- package.json | 2 +- .../directory-for-logs.injectable.ts | 13 ++ .../ipc/__tests__/type-enforced-ipc.test.ts | 131 ------------- src/common/ipc/index.ts | 1 - src/common/ipc/type-enforced-ipc.ts | 107 ---------- src/common/logger.injectable.ts | 11 +- src/common/logger.ts | 73 +------ src/common/logger/transports.ts | 11 ++ .../app-paths/setup-app-paths.injectable.ts | 4 +- src/main/context-handler/context-handler.ts | 8 +- src/main/index.ts | 7 +- src/main/logger/console-format.ts | 184 ++++++++++++++++++ .../logger/console-transport.injectable.ts | 35 ++++ src/main/logger/file-transport.injectable.ts | 28 +++ .../level.global-override-for-injectable.ts | 9 + src/main/logger/level.injectable.ts | 30 +++ .../metrics/add-metrics-route.injectable.ts | 17 +- .../runnable-tokens/phases.ts | 28 +++ .../show-initial-window.injectable.ts | 51 +++++ .../runnables/show-loading.injectable.ts | 29 +++ .../start-main-application.injectable.ts | 61 ++---- .../test-utils/get-application-builder.tsx | 38 ++-- src/renderer/ipc/register-listeners.tsx | 18 +- src/renderer/kube-watch-api/kube-watch-api.ts | 2 +- .../logger/browser-transport.injectable.ts | 15 ++ .../lens-protocol-router-renderer.tsx | 121 ++++-------- webpack/dev-server.ts | 3 +- yarn.lock | 13 +- 28 files changed, 544 insertions(+), 506 deletions(-) create mode 100644 src/common/app-paths/directory-for-logs.injectable.ts delete mode 100644 src/common/ipc/__tests__/type-enforced-ipc.test.ts delete mode 100644 src/common/ipc/type-enforced-ipc.ts create mode 100644 src/common/logger/transports.ts create mode 100644 src/main/logger/console-format.ts create mode 100644 src/main/logger/console-transport.injectable.ts create mode 100644 src/main/logger/file-transport.injectable.ts create mode 100644 src/main/logger/level.global-override-for-injectable.ts create mode 100644 src/main/logger/level.injectable.ts create mode 100644 src/main/start-main-application/runnable-tokens/phases.ts create mode 100644 src/main/start-main-application/runnables/show-initial-window.injectable.ts create mode 100644 src/main/start-main-application/runnables/show-loading.injectable.ts create mode 100644 src/renderer/logger/browser-transport.injectable.ts diff --git a/package.json b/package.json index b071ed224c..d6976382a6 100644 --- a/package.json +++ b/package.json @@ -290,7 +290,6 @@ "uuid": "^8.3.2", "win-ca": "^3.5.0", "winston": "^3.8.2", - "winston-console-format": "^1.0.8", "winston-transport-browserconsole": "^1.0.5", "ws": "^8.11.0", "xterm-link-provider": "^1.3.1" @@ -366,6 +365,7 @@ "@typescript-eslint/parser": "^5.43.0", "adr": "^1.4.3", "ansi_up": "^5.1.0", + "chalk": "^4.1.2", "chart.js": "^2.9.4", "circular-dependency-plugin": "^5.2.2", "cli-progress": "^3.11.2", diff --git a/src/common/app-paths/directory-for-logs.injectable.ts b/src/common/app-paths/directory-for-logs.injectable.ts new file mode 100644 index 0000000000..e9abc35c44 --- /dev/null +++ b/src/common/app-paths/directory-for-logs.injectable.ts @@ -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; diff --git a/src/common/ipc/__tests__/type-enforced-ipc.test.ts b/src/common/ipc/__tests__/type-enforced-ipc.test.ts deleted file mode 100644 index bd456257cb..0000000000 --- a/src/common/ipc/__tests__/type-enforced-ipc.test.ts +++ /dev/null @@ -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"); - }); - }); -}); diff --git a/src/common/ipc/index.ts b/src/common/ipc/index.ts index bb60ce4f6c..aa2a538560 100644 --- a/src/common/ipc/index.ts +++ b/src/common/ipc/index.ts @@ -5,4 +5,3 @@ export * from "./ipc"; export * from "./invalid-kubeconfig"; -export * from "./type-enforced-ipc"; diff --git a/src/common/ipc/type-enforced-ipc.ts b/src/common/ipc/type-enforced-ipc.ts deleted file mode 100644 index d37dce2f85..0000000000 --- a/src/common/ipc/type-enforced-ipc.ts +++ /dev/null @@ -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 = Parameters[1]>[0]; -export type ListVerifier = (args: unknown[]) => args is T; -export type Rest = 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, ...args: any[]) => any, ->({ - source, - channel, - listener, - verifier, -}: { - source: IPC; - channel: string; - listener: Listener; - verifier: ListVerifier>>; -}): void { - function wrappedListener(event: ListenerEvent, ...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, ...args: any[]) => any, ->({ - source, - channel, - listener, - verifier, -}: { - source: IPC; - channel: string; - listener: Listener; - verifier: ListVerifier>>; -}): Disposer { - function wrappedListener(event: ListenerEvent, ...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>>; -}): Disposer { - function wrappedHandler(event: Electron.IpcMainInvokeEvent, ...args: unknown[]): ReturnType { - if (verifier(args)) { - return handler(event, ...args); - } - - throw new TypeError(`Invalid args for invoke on channel: ${channel}`); - } - - ipcMainHandle(channel, wrappedHandler); - - return () => ipcMain.removeHandler(channel); -} diff --git a/src/common/logger.injectable.ts b/src/common/logger.injectable.ts index 55e2f26b2a..e1a085f199 100644 --- a/src/common/logger.injectable.ts +++ b/src/common/logger.injectable.ts @@ -3,12 +3,19 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import { createLogger, format } from "winston"; import type { Logger } from "./logger"; -import logger from "./logger"; +import { loggerTransportInjectionToken } from "./logger/transports"; const loggerInjectable = getInjectable({ id: "logger", - instantiate: (): Logger => logger, + instantiate: (di): Logger => createLogger({ + format: format.combine( + format.splat(), + format.simple(), + ), + transports: di.injectMany(loggerTransportInjectionToken), + }), }); export default loggerInjectable; diff --git a/src/common/logger.ts b/src/common/logger.ts index 231729cc44..948404a6b9 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -3,12 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { app, ipcMain } from "electron"; -import winston, { format } from "winston"; -import type Transport from "winston-transport"; -import { consoleFormat } from "winston-console-format"; -import { isDebugging, isTestEnv } from "./vars"; -import BrowserConsole from "winston-transport-browserconsole"; +import { asLegacyGlobalForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; +import loggerInjectable from "./logger.injectable"; export interface Logger { info: (message: string, ...args: any) => void; @@ -18,64 +14,7 @@ export interface Logger { silly: (message: string, ...args: any) => void; } -const logLevel = process.env.LOG_LEVEL - ? process.env.LOG_LEVEL - : 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"), - 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; +/** + * @deprecated use `di.inject(loggerInjectable)` instead + */ +export default asLegacyGlobalForExtensionApi(loggerInjectable); diff --git a/src/common/logger/transports.ts b/src/common/logger/transports.ts new file mode 100644 index 0000000000..1407eb91b8 --- /dev/null +++ b/src/common/logger/transports.ts @@ -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({ + id: "logger-transport", +}); diff --git a/src/main/app-paths/setup-app-paths.injectable.ts b/src/main/app-paths/setup-app-paths.injectable.ts index 54bc0ea2ba..c48b35fba9 100644 --- a/src/main/app-paths/setup-app-paths.injectable.ts +++ b/src/main/app-paths/setup-app-paths.injectable.ts @@ -12,8 +12,8 @@ import { pathNames } from "../../common/app-paths/app-path-names"; import { fromPairs, map } from "lodash/fp"; import { pipeline } from "@ogre-tools/fp"; 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 { appPathsRunnablePhaseInjectionToken } from "../start-main-application/runnable-tokens/phases"; const setupAppPathsInjectable = getInjectable({ id: "setup-app-paths", @@ -51,7 +51,7 @@ const setupAppPathsInjectable = getInjectable({ }; }, - injectionToken: beforeElectronIsReadyInjectionToken, + injectionToken: appPathsRunnablePhaseInjectionToken, }); export default setupAppPathsInjectable; diff --git a/src/main/context-handler/context-handler.ts b/src/main/context-handler/context-handler.ts index 563c7afecc..63a4cfd0b1 100644 --- a/src/main/context-handler/context-handler.ts +++ b/src/main/context-handler/context-handler.ts @@ -111,14 +111,12 @@ export class ContextHandler implements ClusterContextHandler { const potentialServices = await Promise.allSettled( providers.map(provider => provider.getPrometheusService(apiClient)), ); - const errors: any[] = []; + const errors = []; for (const res of potentialServices) { switch (res.status) { case "rejected": - if (res.reason) { - errors.push(String(res.reason)); - } + errors.push(res.reason); break; 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 { diff --git a/src/main/index.ts b/src/main/index.ts index 870ca7490c..b44341324f 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -11,15 +11,10 @@ import * as LensExtensionsCommonApi from "../extensions/common-api"; import * as LensExtensionsMainApi from "../extensions/main-api"; import { getDi } from "./getDi"; import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable"; -import shouldStartHiddenInjectable from "./electron-app/features/should-start-hidden.injectable"; const di = getDi(); -const shouldStartHidden = di.inject(shouldStartHiddenInjectable); - -const startApplication = di.inject(startMainApplicationInjectable); - -void startApplication(!shouldStartHidden); +void di.inject(startMainApplicationInjectable); /** * Exports for virtual package "@k8slens/extensions" for main-process. diff --git a/src/main/logger/console-format.ts b/src/main/logger/console-format.ts new file mode 100644 index 0000000000..909b244aa0 --- /dev/null +++ b/src/main/logger/console-format.ts @@ -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; + } +} diff --git a/src/main/logger/console-transport.injectable.ts b/src/main/logger/console-transport.injectable.ts new file mode 100644 index 0000000000..8200eafe2a --- /dev/null +++ b/src/main/logger/console-transport.injectable.ts @@ -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; diff --git a/src/main/logger/file-transport.injectable.ts b/src/main/logger/file-transport.injectable.ts new file mode 100644 index 0000000000..2f1eab5c0c --- /dev/null +++ b/src/main/logger/file-transport.injectable.ts @@ -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; diff --git a/src/main/logger/level.global-override-for-injectable.ts b/src/main/logger/level.global-override-for-injectable.ts new file mode 100644 index 0000000000..ac29c9ee1a --- /dev/null +++ b/src/main/logger/level.global-override-for-injectable.ts @@ -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"); diff --git a/src/main/logger/level.injectable.ts b/src/main/logger/level.injectable.ts new file mode 100644 index 0000000000..7ee1b9d587 --- /dev/null +++ b/src/main/logger/level.injectable.ts @@ -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; diff --git a/src/main/routes/metrics/add-metrics-route.injectable.ts b/src/main/routes/metrics/add-metrics-route.injectable.ts index e55fc8bec8..a11d406737 100644 --- a/src/main/routes/metrics/add-metrics-route.injectable.ts +++ b/src/main/routes/metrics/add-metrics-route.injectable.ts @@ -28,14 +28,17 @@ const loadMetricsFor = (getMetrics: GetMetrics) => async (promQueries: string[], try { return await getMetrics(cluster, prometheusPath, { query, ...queryParams }); } catch (error) { - if (isRequestError(error)) { - if (lastAttempt || (error.statusCode && error.statusCode >= 400 && error.statusCode < 500)) { - throw new Error("Metrics not available", { cause: error }); - } - } else if (error instanceof Error) { + if ( + !isRequestError(error) + || lastAttempt + || ( + !lastAttempt && ( + typeof error.statusCode === "number" && + 400 <= error.statusCode && error.statusCode < 500 + ) + ) + ) { 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 diff --git a/src/main/start-main-application/runnable-tokens/phases.ts b/src/main/start-main-application/runnable-tokens/phases.ts new file mode 100644 index 0000000000..0b61ae88f2 --- /dev/null +++ b/src/main/start-main-application/runnable-tokens/phases.ts @@ -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({ + id: "app-paths-runnable-phase", +}); + +export const showLoadingRunnablePhaseInjectionToken = getInjectionToken({ + id: "show-loading-runnable-phase", +}); + +export const showInitialWindowRunnablePhaseInjectionToken = getInjectionToken({ + id: "show-initial-window-runnable-phase", +}); diff --git a/src/main/start-main-application/runnables/show-initial-window.injectable.ts b/src/main/start-main-application/runnables/show-initial-window.injectable.ts new file mode 100644 index 0000000000..0ce02bfa85 --- /dev/null +++ b/src/main/start-main-application/runnables/show-initial-window.injectable.ts @@ -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; diff --git a/src/main/start-main-application/runnables/show-loading.injectable.ts b/src/main/start-main-application/runnables/show-loading.injectable.ts new file mode 100644 index 0000000000..2305d4cf3b --- /dev/null +++ b/src/main/start-main-application/runnables/show-loading.injectable.ts @@ -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; diff --git a/src/main/start-main-application/start-main-application.injectable.ts b/src/main/start-main-application/start-main-application.injectable.ts index fcd1851575..ff52bd0f9d 100644 --- a/src/main/start-main-application/start-main-application.injectable.ts +++ b/src/main/start-main-application/start-main-application.injectable.ts @@ -10,13 +10,8 @@ import { beforeElectronIsReadyInjectionToken } from "./runnable-tokens/before-el import { beforeApplicationIsLoadingInjectionToken } from "./runnable-tokens/before-application-is-loading-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 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 createFirstApplicationWindowInjectable from "./lens-window/application-window/create-first-application-window.injectable"; +import { appPathsRunnablePhaseInjectionToken, showInitialWindowRunnablePhaseInjectionToken, showLoadingRunnablePhaseInjectionToken } from "./runnable-tokens/phases"; const startMainApplicationInjectable = getInjectable({ id: "start-main-application", @@ -25,53 +20,29 @@ const startMainApplicationInjectable = getInjectable({ const runMany = runManyFor(di); const runManySync = runManySyncFor(di); 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 beforeApplicationIsLoading = runMany(beforeApplicationIsLoadingInjectionToken); + const showLoadingRunnablePhase = runMany(showLoadingRunnablePhaseInjectionToken); const onLoadOfApplication = runMany(onLoadOfApplicationInjectionToken); + const showInitialWindowRunnablePhase = runMany(showInitialWindowRunnablePhaseInjectionToken); const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken); - return (shouldStartWindow: boolean) => { - // Stuff happening before application is ready needs to be synchronous because of - // https://github.com/electron/electron/issues/21370 - beforeElectronIsReady(); + // Stuff happening before application is ready needs to be synchronous because of + // https://github.com/electron/electron/issues/21370 + appPathsRunnablePhase(); + beforeElectronIsReady(); - return (async () => { - await waitForElectronToBeReady(); - - await beforeApplicationIsLoading(); - - if (shouldStartWindow) { - await splashWindow.start(); - } - - await onLoadOfApplication(); - - if (shouldStartWindow) { - const deepLinkUrl = getDeepLinkUrl(commandLineArguments); - - if (deepLinkUrl) { - await openDeepLink(deepLinkUrl); - } else { - const applicationWindow = createFirstApplicationWindow(); - - await applicationWindow.start(); - } - - splashWindow.close(); - } - - await afterApplicationIsLoaded(); - })(); - }; + return (async () => { + await waitForElectronToBeReady(); + await beforeApplicationIsLoading(); + await showLoadingRunnablePhase(); + await onLoadOfApplication(); + await showInitialWindowRunnablePhase(); + await afterApplicationIsLoaded(); + })(); }, }); -const getDeepLinkUrl = (commandLineArguments: string[]) => - pipeline(commandLineArguments, map(toLower), find(startsWith("lens://"))); - export default startMainApplicationInjectable; diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx index 7d57093f18..575ea7b508 100644 --- a/src/renderer/components/test-utils/get-application-builder.tsx +++ b/src/renderer/components/test-utils/get-application-builder.tsx @@ -68,6 +68,7 @@ import applicationMenuItemCompositeInjectable from "../../../features/applicatio import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths"; import { discoverFor } from "./discovery-of-html-elements"; 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; @@ -313,11 +314,9 @@ export const getApplicationBuilder = () => { .map(toWindowWithHelpersFor(windowHelpers)), get: (id) => { - const applicationWindows = builder.applicationWindow.getAll(); - - const applicationWindow = applicationWindows.find( - (window) => window.id === id, - ); + const applicationWindow = builder.applicationWindow + .getAll() + .find((window) => window.id === id); if (!applicationWindow) { 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) => { - const createApplicationWindow = mainDi.inject( - createApplicationWindowInjectable, - ); + const createApplicationWindow = mainDi.inject(createApplicationWindowInjectable); createApplicationWindow(id); @@ -656,11 +653,8 @@ export const getApplicationBuilder = () => { await callback(mainDi); } - const startMainApplication = mainDi.inject( - startMainApplicationInjectable, - ); - - await startMainApplication(false); + mainDi.override(shouldStartHiddenInjectable, () => true); + await mainDi.inject(startMainApplicationInjectable); applicationHasStarted = true; }, @@ -672,21 +666,15 @@ export const getApplicationBuilder = () => { await callback(mainDi); } - const startMainApplication = mainDi.inject( - startMainApplicationInjectable, - ); - - await startMainApplication(true); + mainDi.override(shouldStartHiddenInjectable, () => false); + await mainDi.inject(startMainApplicationInjectable); applicationHasStarted = true; - const applicationWindow = builder.applicationWindow.get( - "first-application-window", - ); - - assert(applicationWindow); - - return applicationWindow.rendered; + return builder + .applicationWindow + .get("first-application-window") + .rendered; }, select: { diff --git a/src/renderer/ipc/register-listeners.tsx b/src/renderer/ipc/register-listeners.tsx index 4d2ed9f5c8..c7b447c334 100644 --- a/src/renderer/ipc/register-listeners.tsx +++ b/src/renderer/ipc/register-listeners.tsx @@ -5,10 +5,9 @@ import type { IpcRendererEvent } from "electron"; import { ipcRenderer } from "electron"; -import { onCorrect } from "../../common/ipc"; import { Notifications } from "../components/notifications"; 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"; function HotbarTooManyItemsHandler(): void { @@ -23,15 +22,6 @@ interface Dependencies { } export const registerIpcListeners = ({ listNamespacesForbiddenHandler }: Dependencies) => () => { - onCorrect({ - source: ipcRenderer, - channel: clusterListNamespaceForbiddenChannel, - listener: listNamespacesForbiddenHandler, - verifier: isListNamespaceForbiddenArgs, - }); - onCorrect({ - source: ipcRenderer, - channel: hotbarTooManyItemsChannel, - listener: HotbarTooManyItemsHandler, - verifier: (args: unknown[]): args is [] => args.length === 0, - });}; + ipcRenderer.on(clusterListNamespaceForbiddenChannel, listNamespacesForbiddenHandler); + ipcRenderer.on(hotbarTooManyItemsChannel, HotbarTooManyItemsHandler); +}; diff --git a/src/renderer/kube-watch-api/kube-watch-api.ts b/src/renderer/kube-watch-api/kube-watch-api.ts index 95c0442fcb..c3eca74d29 100644 --- a/src/renderer/kube-watch-api/kube-watch-api.ts +++ b/src/renderer/kube-watch-api/kube-watch-api.ts @@ -107,7 +107,7 @@ export class KubeWatchApi { unsubscribe.push(store.subscribe({ onLoadFailure, abortController: childController })); } catch (error) { 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 }, }); } diff --git a/src/renderer/logger/browser-transport.injectable.ts b/src/renderer/logger/browser-transport.injectable.ts new file mode 100644 index 0000000000..5a38c5a396 --- /dev/null +++ b/src/renderer/logger/browser-transport.injectable.ts @@ -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; diff --git a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx index 8015678361..0875037134 100644 --- a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx +++ b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx @@ -7,30 +7,10 @@ import React from "react"; import { ipcRenderer } from "electron"; import * as proto from "../../../common/protocol-handler"; import Url from "url-parse"; -import { onCorrect } from "../../../common/ipc"; import type { LensProtocolRouterDependencies } from "../../../common/protocol-handler"; import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../../common/protocol-handler"; 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 {} export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { @@ -42,75 +22,58 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { * This function is needed to be called early on in the renderers lifetime. */ public init(): void { - onCorrect({ - channel: proto.ProtocolHandlerInternal, - source: ipcRenderer, - verifier: verifyIpcArgs, - listener: (event, rawUrl, mainAttemptResult) => { - const rendererAttempt = this._routeToInternal(new Url(rawUrl, true)); + ipcRenderer.on(proto.ProtocolHandlerInternal, (event, rawUrl: string, mainAttemptResult: RouteAttempt) => { + const rendererAttempt = this._routeToInternal(new Url(rawUrl, true)); - if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) { + if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) { + Notifications.shortInfo(( +

+ {"Unknown action "} + {rawUrl} + {". Are you on the latest version?"} +

+ )); + } + }); + ipcRenderer.on(proto.ProtocolHandlerExtension, async (event, rawUrl: string, mainAttemptResult: RouteAttempt) => { + const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true)); + + switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) { + case RouteAttempt.MISSING: Notifications.shortInfo((

{"Unknown action "} {rawUrl} - {". Are you on the latest version?"} + {". Are you on the latest version of the extension?"}

)); - } - }, - }); - onCorrect({ - channel: proto.ProtocolHandlerExtension, - source: ipcRenderer, - verifier: verifyIpcArgs, - listener: async (event, rawUrl, mainAttemptResult) => { - const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true)); - - switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) { - case RouteAttempt.MISSING: - Notifications.shortInfo(( -

- {"Unknown action "} - {rawUrl} - {". Are you on the latest version of the extension?"} -

- )); - break; - case RouteAttempt.MISSING_EXTENSION: - Notifications.shortInfo(( -

- {"Missing extension for action "} - {rawUrl} - {". Not able to find extension in our known list. Try installing it manually."} -

- )); - break; - } - }, - }); - onCorrect({ - channel: ProtocolHandlerInvalid, - source: ipcRenderer, - listener: (event, error, rawUrl) => { - Notifications.error(( - <> + break; + case RouteAttempt.MISSING_EXTENSION: + Notifications.shortInfo((

- {"Failed to route "} + {"Missing extension for action "} {rawUrl} - . + {". Not able to find extension in our known list. Try installing it manually."}

-

- Error: - {" "} - {error} -

- - )); - }, - verifier: (args): args is [string, string] => { - return args.length === 2 && typeof args[0] === "string"; - }, + )); + break; + } + }); + ipcRenderer.on(ProtocolHandlerInvalid, (event, error: string, rawUrl: string) => { + Notifications.error(( + <> +

+ {"Failed to route "} + {rawUrl} + . +

+

+ Error: + {" "} + {error} +

+ + )); }); } } diff --git a/webpack/dev-server.ts b/webpack/dev-server.ts index d4d62ddb0c..68fd72bb35 100644 --- a/webpack/dev-server.ts +++ b/webpack/dev-server.ts @@ -6,7 +6,6 @@ import Webpack from "webpack"; import WebpackDevServer from "webpack-dev-server"; import { webpackLensRenderer } from "./renderer"; -import logger from "../src/common/logger"; import { buildDir, webpackDevServerPort } from "./vars"; /** @@ -42,6 +41,6 @@ const server = new WebpackDevServer({ }, }, compiler); -logger.info(`[WEBPACK-DEV-SERVER]: created with options`, server.options); +console.info(`[WEBPACK-DEV-SERVER]: created with options`, server.options); server.start(); diff --git a/yarn.lock b/yarn.lock index 749d6a726d..ac1cc0a8e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4118,7 +4118,7 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.3.3, colors@^1.4.0: +colors@^1.3.3: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 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" 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" resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== @@ -12851,15 +12851,6 @@ win-ca@^3.5.0: node-forge "^1.2.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: version "1.0.5" resolved "https://registry.yarnpkg.com/winston-transport-browserconsole/-/winston-transport-browserconsole-1.0.5.tgz#8ef1bc32da5fb0a66604f2b8b6f127ed725108c9"