diff --git a/package.json b/package.json index f289862c68..20a95b5fe7 100644 --- a/package.json +++ b/package.json @@ -304,6 +304,7 @@ "@types/tar": "^4.0.5", "@types/tcp-port-used": "^1.0.0", "@types/tempy": "^0.3.0", + "@types/triple-beam": "^1.3.2", "@types/url-parse": "^1.4.3", "@types/uuid": "^8.3.0", "@types/webdriverio": "^4.13.0", diff --git a/src/common/sentry.ts b/src/common/sentry.ts index da0e749994..06bef321d1 100644 --- a/src/common/sentry.ts +++ b/src/common/sentry.ts @@ -23,7 +23,7 @@ import { CaptureConsole, Dedupe, Offline } from "@sentry/integrations"; import * as Sentry from "@sentry/electron"; import { sentryDsn, isProduction } from "./vars"; import { UserStore } from "./user-store"; -import logger from "../main/logger"; +import { inspect } from "util"; /** * "Translate" 'browser' to 'main' as Lens developer more familiar with the term 'main' @@ -51,10 +51,13 @@ export function SentryInit() { return event; } - logger.info(`🔒 [SENTRY-BEFORE-SEND-HOOK]: allowErrorReporting: ${allowErrorReporting}. Sentry event is caught but not sent to server.`); - logger.info("🔒 [SENTRY-BEFORE-SEND-HOOK]: === START OF SENTRY EVENT ==="); - logger.info(event); - logger.info("🔒 [SENTRY-BEFORE-SEND-HOOK]: === END OF SENTRY EVENT ==="); + /** + * Directly write to stdout so that no other integrations capture this and create an infinite loop + */ + process.stdout.write(`🔒 [SENTRY-BEFORE-SEND-HOOK]: allowErrorReporting: ${allowErrorReporting}. Sentry event is caught but not sent to server.`); + process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === START OF SENTRY EVENT ==="); + process.stdout.write(inspect(event, false, null, true)); + process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === END OF SENTRY EVENT ==="); // if return null, the event won't be sent // ref https://github.com/getsentry/sentry-javascript/issues/2039 @@ -64,12 +67,11 @@ export function SentryInit() { integrations: [ new CaptureConsole({ levels: ["error"] }), new Dedupe(), - new Offline() + new Offline(), ], initialScope: { tags: { - - "process": processName + "process": processName, } }, environment: isProduction ? "production" : "development", diff --git a/src/main/logger.ts b/src/main/logger.ts index 89a0ede7f0..ffa695ac2e 100644 --- a/src/main/logger.ts +++ b/src/main/logger.ts @@ -21,31 +21,93 @@ import { app, remote } from "electron"; import winston from "winston"; +import Transport from "winston-transport"; import { isDebugging, isTestEnv } from "../common/vars"; +import { LEVEL } from "triple-beam"; +import { Severity } from "@sentry/browser"; +import * as Sentry from "@sentry/electron"; -const logLevel = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : isDebugging ? "debug" : "info"; -const consoleOptions: winston.transports.ConsoleTransportOptions = { - handleExceptions: false, - level: logLevel, +const SENTRY_LEVELS_MAP = { + silly: Severity.Debug, + verbose: Severity.Debug, + debug: Severity.Debug, + info: Severity.Info, + warn: Severity.Warning, + error: Severity.Error, }; -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 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"]), }; -const logger = winston.createLogger({ + +type WinstonLevel = keyof typeof SENTRY_LEVELS_MAP; + +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(); + } + } +} + +interface CreateLoggerOpts extends winston.LoggerOptions { + transports?: Transport[]; +} + +const logLevel = process.env.LOG_LEVEL || (isDebugging ? "debug" : "info"); + +const loggerOpts: CreateLoggerOpts = { format: winston.format.combine( winston.format.colorize(), winston.format.simple(), ), transports: [ - new winston.transports.Console(consoleOptions), - ...(isTestEnv ? [] : [new winston.transports.File(fileOptions)]), + new SentryTransport("error"), + new winston.transports.Console({ + handleExceptions: false, + level: logLevel, + }), ], -}); +}; -export default logger; +if (!isTestEnv) { + loggerOpts.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 winston.createLogger(loggerOpts); diff --git a/yarn.lock b/yarn.lock index dc2c28ce88..f6d70866db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2010,6 +2010,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== +"@types/triple-beam@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" + integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== + "@types/trusted-types@*": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.4.tgz#922d092c84a776a59acb0bd6785fd82b59b9bad5"