diff --git a/packages/logger/package.json b/packages/logger/package.json index ad3640eb4..d7b4bfc4b 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -5,9 +5,12 @@ "build": "tsc -p tsconfig.build.json && cp ./out/packages/logger/src/* ./out && rm -rf out/packages && ../../node_modules/.bin/webpack --config ./webpack.config.js", "postinstall": "if [ ! -d out ];then npm run build; fi" }, - "version": "1.0.3", + "version": "1.1.0", "main": "out/main.js", "types": "out/index.d.ts", "author": "Coder", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@google-cloud/logging": "^4.5.2" + } } diff --git a/packages/logger/src/extender.test.ts b/packages/logger/src/extender.test.ts new file mode 100644 index 000000000..cae17196f --- /dev/null +++ b/packages/logger/src/extender.test.ts @@ -0,0 +1,12 @@ +import { field, logger } from "./logger"; +import { createStackdriverExtender } from "./extender"; + +describe("Extender", () => { + it("should add stackdriver extender", () => { + logger.extend(createStackdriverExtender("coder-dev-1", "logging-package-tests")); + }); + + it("should log", async () => { + logger.debug("Bananas!", field("frog", { hi: "wow" })); + }); +}); diff --git a/packages/logger/src/extender.ts b/packages/logger/src/extender.ts new file mode 100644 index 000000000..b46c735d5 --- /dev/null +++ b/packages/logger/src/extender.ts @@ -0,0 +1,63 @@ +import * as gcl from "@google-cloud/logging"; +import { Extender, logger, field } from "./logger"; + +export const createStackdriverExtender = (projectId: string, logId: string): Extender => { + enum GcpLogSeverity { + DEFAULT = 0, + DEBUG = 100, + INFO = 200, + NOTICE = 300, + WARNING = 400, + ERROR = 500, + CRITICAL = 600, + ALERT = 700, + EMERGENCY = 800, + } + + const logging = new gcl.Logging({ + autoRetry: true, + projectId, + }); + + const log = logging.log(logId); + const convertSeverity = (severity: "trace" | "info" | "warn" | "debug" | "error"): GcpLogSeverity => { + switch (severity) { + case "trace": + case "debug": + return GcpLogSeverity.DEBUG; + case "info": + return GcpLogSeverity.INFO; + case "error": + return GcpLogSeverity.ERROR; + case "warn": + return GcpLogSeverity.WARNING; + } + }; + + return (options): void => { + const severity = convertSeverity(options.type); + // tslint:disable-next-line:no-any + const metadata = {} as any; + if (options.fields) { + options.fields.forEach((f) => { + if (!f) { + return; + } + metadata[f.identifier] = f.value; + }); + } + + const entry = log.entry({ + // tslint:disable-next-line:no-any + severity: severity as any, + }, { + ...metadata, + message: options.message, + }); + + log.write(entry).catch((ex) => { + logger.named("GCP").error("Failed to log", field("error", ex)); + }); + }; + +}; diff --git a/packages/logger/src/logger.ts b/packages/logger/src/logger.ts index cfd19a7b2..70e15affd 100644 --- a/packages/logger/src/logger.ts +++ b/packages/logger/src/logger.ts @@ -59,6 +59,14 @@ export const field = (name: string, value: T): Field => { return new Field(name, value); }; +export type Extender = (msg: { + message: string, + level: Level, + type: "trace" | "info" | "warn" | "debug" | "error", + fields?: FieldArray, + section?: string, +}) => void; + /** * This formats & builds text for logging. * It should only be used to build one log item at a time since it stores the @@ -221,6 +229,7 @@ export class Logger { private _formatter: Formatter, private readonly name?: string, private readonly defaultFields?: FieldArray, + private readonly extenders: Extender[] = [], ) { if (name) { this.nameColor = this.hashStringToColor(name); @@ -248,6 +257,10 @@ export class Logger { this.muted = true; } + public extend(extender: Extender): void { + this.extenders.push(extender); + } + /** * Outputs information. */ @@ -328,7 +341,7 @@ export class Logger { * Each name is deterministically generated a color. */ public named(name: string, ...fields: FieldArray): Logger { - const l = new Logger(this._formatter, name, fields); + const l = new Logger(this._formatter, name, fields, this.extenders); if (this.muted) { l.mute(); } @@ -393,6 +406,16 @@ export class Logger { console.log(...this._formatter.flush()); } // tslint:enable no-console + + this.extenders.forEach((extender) => { + extender({ + section: this.name, + fields: options.fields, + level: options.level, + message: options.message as string, + type: options.type, + }); + }); } /**