mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add winston formatting support for error causes
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
86ac417cab
commit
cd8a6f8dcc
@ -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"
|
||||
|
||||
184
src/common/logger-formaters/console-format.ts
Normal file
184
src/common/logger-formaters/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 colors from "colors/safe";
|
||||
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}${colors.dim(chr)}${colors.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 colors.italic(colors.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 = colors.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${colors.dim(info.level)}:${pad}${color}${colors.dim(chr)}${colors.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;
|
||||
}
|
||||
}
|
||||
@ -6,9 +6,9 @@
|
||||
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 { ConsoleFormat } from "./logger-formaters/console-format";
|
||||
|
||||
export interface Logger {
|
||||
info: (message: string, ...args: any) => void;
|
||||
@ -37,7 +37,7 @@ if (ipcMain) {
|
||||
format.colorize({ level: true, message: false }),
|
||||
format.padLevels(),
|
||||
format.ms(),
|
||||
consoleFormat({
|
||||
new ConsoleFormat({
|
||||
showMeta: true,
|
||||
inspectOptions: {
|
||||
depth: 4,
|
||||
|
||||
@ -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<string> {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 },
|
||||
});
|
||||
}
|
||||
|
||||
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"
|
||||
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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user