diff --git a/src/common/logger-transports/index.ts b/src/common/logger-transports/index.ts new file mode 100644 index 0000000000..6c7f04647b --- /dev/null +++ b/src/common/logger-transports/index.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * 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 * from "./sentry"; diff --git a/src/common/logger-transports/sentry.ts b/src/common/logger-transports/sentry.ts new file mode 100644 index 0000000000..bfcb7c4584 --- /dev/null +++ b/src/common/logger-transports/sentry.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * 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. + */ + +import Transport from "winston-transport"; +import { LEVEL } from "triple-beam"; +import { Severity } from "@sentry/browser"; +import * as Sentry from "@sentry/electron"; + +const SENTRY_LEVELS_MAP = { + silly: Severity.Debug, + verbose: Severity.Debug, + debug: Severity.Debug, + info: Severity.Info, + warn: Severity.Warning, + error: Severity.Error, +}; +const WINSTON_CMP: Record> = { + silly: new Set(["silly", "verbose", "debug", "info", "warn", "error"]), + verbose: new Set(["verbose", "debug", "info", "warn", "error"]), + debug: new Set(["debug", "info", "warn", "error"]), + info: new Set(["info", "warn", "error"]), + warn: new Set(["warn", "error"]), + error: new Set(["error"]), +}; + +export type WinstonLevel = keyof typeof SENTRY_LEVELS_MAP; + +export class SentryTransport extends Transport { + logLevels: Set; + + constructor(minWinstonLevel: WinstonLevel) { + super(); + + this.logLevels = WINSTON_CMP[minWinstonLevel]; + } + + log(info: any, next: () => void) { + setImmediate(() => { + this.emit("logged", info); + }); + + const { message, level: _, tags, user, ...extra } = info; + const winstonLevel: WinstonLevel = info[LEVEL]; + const level = SENTRY_LEVELS_MAP[winstonLevel]; + + try { + if (this.logLevels.has(winstonLevel)) { + Sentry.captureMessage(message, { + level, + tags, + extra, + }); + } + } finally { + next(); + } + } +} diff --git a/src/common/logger.ts b/src/common/logger.ts index 57f12ce4e9..723d43f70c 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -20,62 +20,64 @@ */ import { app, ipcMain, remote } from "electron"; -import winston from "winston"; +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 { SentryTransport } from "./logger-transports"; +const logLevel = process.env.LOG_LEVEL + ? process.env.LOG_LEVEL + : isDebugging + ? "debug" + : "info"; -const logLevel = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : isDebugging ? "debug" : "info"; -let consoleOptions: winston.transports.ConsoleTransportOptions; +const transports: Transport[] = [ + new SentryTransport("error") +]; if (ipcMain) { - consoleOptions = { - handleExceptions: false, - level: logLevel, - format: winston.format.combine( - winston.format.colorize({ level: true, message: false}), - winston.format.padLevels(), - winston.format.ms(), - consoleFormat({ - showMeta: true, - inspectOptions: { - depth: 4, - colors: true, - maxArrayLength: 10, - breakLength: 120, - compact: Infinity, - }, - }) - ) - }; + 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, + }, + }), + ), + }) + ); } else { - consoleOptions = { - handleExceptions: false, - level: logLevel, - format: winston.format.combine( - winston.format.colorize({ level: true, message: false}), - ) - }; + transports.push(new BrowserConsole()); } -const fileOptions: winston.transports.FileTransportOptions = { - handleExceptions: false, - level: logLevel, - filename: "lens.log", - dirname: (app ?? remote?.app)?.getPath("logs"), - maxsize: 16 * 1024, - maxFiles: 16, - tailable: true, -}; -const logger = winston.createLogger({ - format: winston.format.combine( - winston.format.simple() - ), - transports: [ - ipcMain ? new winston.transports.Console(consoleOptions) : new BrowserConsole(), - ...(isTestEnv ? [] : [new winston.transports.File(fileOptions)]), - ], -}); +if (!isTestEnv) { + transports.push( + new winston.transports.File({ + handleExceptions: false, + level: logLevel, + filename: "lens.log", + dirname: (app ?? remote?.app)?.getPath("logs"), + maxsize: 16 * 1024, + maxFiles: 16, + tailable: true, + }) + ); +} -export default logger; +export default winston.createLogger({ + format: format.simple(), + transports, +});