diff --git a/config/config.schema.yaml b/config/config.schema.yaml index 3690ce400fe96ab135d96032d0fb347e54b29780..9774f033a3eb8219e0f6d8ccd98718d8d90b1aad 100644 --- a/config/config.schema.yaml +++ b/config/config.schema.yaml @@ -54,6 +54,10 @@ properties: enum: ["error", "warn", "info", "verbose", "silly"] maxFiles: type: "number" + maxSize: + type: ["number", "string"] + datePattern: + type: "string" enabled: type: "array" items: diff --git a/package-lock.json b/package-lock.json index 69d02c9c2db5300e4e96b0892e09dee5f47742d2..888163e58b1d9a428b9da538c765543013636966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1213,6 +1213,11 @@ "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=" }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -1987,6 +1992,14 @@ "object-assign": "^4.0.1" } }, + "file-stream-rotator": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.2.1.tgz", + "integrity": "sha1-DW/qGpp6uiWofP0xtuJp5E6PCvI=", + "requires": { + "moment": "^2.11.2" + } + }, "fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -5557,6 +5570,35 @@ } } }, + "winston-compat": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.1.4.tgz", + "integrity": "sha512-mMEfFsSm6GmkFF+f4/0UJtG4N1vSaczGmXLVJYmS/+u2zUaIPcw2ZRuwUg2TvVBjswgiraN+vNnAG8z4fRUZ4w==", + "requires": { + "cycle": "~1.0.3", + "logform": "^1.6.0", + "triple-beam": "^1.2.0" + } + }, + "winston-daily-rotate-file": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-3.3.0.tgz", + "integrity": "sha512-Q+lU5XjHYF7FY33dSGjeY4bdIeK8Pyf/JTKYJxDzetPh3QxT/tS4i30QwCxr99V5s8K4YSV+eJ6UnBwqC3RlOg==", + "requires": { + "file-stream-rotator": "^0.2.1", + "semver": "^5.5.0", + "triple-beam": "^1.3.0", + "winston-compat": "^0.1.4", + "winston-transport": "^4.2.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + } + } + }, "winston-transport": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.2.0.tgz", diff --git a/package.json b/package.json index 8428484fbb0acd74081357ed9820a0b9bf19d891..b8a8553eb17a8e00974a1d73b220900e71828ccb 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,8 @@ "sqlite3": "^4.0.0", "tslint": "^4.4.2", "typescript": "2.3.4", - "winston": "^3.0.0" + "winston": "^3.0.0", + "winston-daily-rotate-file": "^3.3.0" }, "devDependencies": { "@types/chai": "^3.4.35", diff --git a/src/config.ts b/src/config.ts index 710b26b61bcffef75fa00ad4450f8ece887f4aac..3744ef4408d45447cb1678c5e83532c37be951c5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -49,6 +49,8 @@ export class LoggingFile { public file: string; public level: string = "info"; public maxFiles: number = -1; - public enabled: string[]; - public disabled: string[]; + public maxSize: string|number = "50m"; + public datePattern: string = "YYYY-MM-DD"; + public enabled: string[] = []; + public disabled: string[] = []; } diff --git a/src/log.ts b/src/log.ts index 2774fe0a049041179e1f194e88d217802f076825..29cad87683dc8f7e2daa297f4a19fbe6c3d4ef2a 100644 --- a/src/log.ts +++ b/src/log.ts @@ -1,33 +1,26 @@ -import { createLogger, Logger, format, transports } from "winston"; -import { DiscordBridgeConfigLogging } from "./config"; +import { createLogger, Logger, format, transports } from "winston"; +import { DiscordBridgeConfigLogging, LoggingFile} from "./config"; import { inspect } from "util"; import * as moment from "moment"; +import "winston-daily-rotate-file"; -const formatFunc = format.printf(info => { +const FORMAT_FUNC = format.printf((info) => { return `${info.timestamp} [${info.module}] ${info.level}: ${info.message}`; }); export class Log { - private static config: DiscordBridgeConfigLogging; - private static logger: Logger = null; - - constructor(private module: string) { - - } - - static get level() { + public static get level() { return this.logger.level; } - static set level(level) { + public static set level(level) { this.logger.level = level; } public static ConfigureBridge(config: DiscordBridgeConfigLogging) { - Log.config = config; - //Log.logger = createLogger({ - - //}); + // Merge defaults. + Log.config = Object.assign(new DiscordBridgeConfigLogging(), config); + Log.setupLogger(); } public static ForceSilent() { @@ -35,47 +28,74 @@ export class Log { Log.logger.silent = true; } - static _now() { + private static config: DiscordBridgeConfigLogging; + private static logger: Logger = null; + + private static now() { return moment().format(Log.config.lineDateFormat); } - - private log(level: string, msg: any[]) { - if (Log.logger === null) { - // We've not configured the logger yet, so create a basic one. - Log.config = new DiscordBridgeConfigLogging(); - Log.logger = createLogger({ - format: format.combine( - format.timestamp({ - format: Log._now, - }), - format.colorize(), - formatFunc, - ), - transports: [new transports.Console({ - level: "info" - })] - }); + private static setupLogger() { + if (Log.logger) { + Log.logger.close(); } - const msgStr = msg.map((item) => { - return typeof(item) === "string" ? item : inspect(item); - }).join(" "); - - Log.logger.log(level, msgStr, {module: this.module}); + const tsports: transports.StreamTransportInstance[] = Log.config.files.map((file) => + Log.setupFileTransport(file), + ); + tsports.push(new transports.Console({ + level: Log.config.console, + })); + Log.logger = createLogger({ + format: format.combine( + format.timestamp({ + format: Log.now, + }), + format.colorize(), + FORMAT_FUNC, + ), + transports: tsports, + }); + } + private static setupFileTransport(config: LoggingFile): transports.FileTransportInstance { + config = Object.assign(new LoggingFile(), config); + const filterOutMods = format((info, opts) => { + if (config.disabled.includes(info.module) && + config.enabled.length > 0 && + !config.enabled.includes(info.module) + ) { + return false; + } + return info; + }); + + const opts = { + filename: config.file, + maxFiles: config.maxFiles, + maxSize: config.maxSize, + datePattern: config.datePattern, + level: config.level, + format: format.combine( + filterOutMods(), + FORMAT_FUNC, + ), + }; + + return new (transports as any).DailyRotateFile(opts); } + public warning = this.warn; + + constructor(private module: string) { } + public error(...msg: any[]) { this.log("error", msg); } - public warn(...msg: any[]) { this.log("warn", msg); } - public warning = this.warn; - public info(...msg: any[]) { this.log("info", msg); } @@ -88,4 +108,16 @@ export class Log { this.log("silly", msg); } -} \ No newline at end of file + private log(level: string, msg: any[]) { + if (Log.logger === null) { + // We've not configured the logger yet, so create a basic one. + Log.config = new DiscordBridgeConfigLogging(); + Log.setupLogger(); + } + const msgStr = msg.map((item) => { + return typeof(item) === "string" ? item : inspect(item); + }).join(" "); + + Log.logger.log(level, msgStr, {module: this.module}); + } +}