lib/util/logging.ts (119 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as winston from "winston";
let logDir = path.resolve(os.homedir(), "oav_output");
let currentLogFile: string;
/*
* Provides current time in custom format that will be used in naming log files. Example:
* '20140820_151113'
* @return {string} Current time in a custom string format
*/
function getTimeStamp(): string {
// We pad each value so that sorted directory listings show the files in chronological order
function pad(num: number): string {
return num < 10 ? "0" + num.toString() : num.toString();
}
const now = new Date();
return (
pad(now.getFullYear()) +
pad(now.getMonth() + 1) +
pad(now.getDate()) +
"_" +
pad(now.getHours()) +
pad(now.getMinutes()) +
pad(now.getSeconds())
);
}
const customLogLevels = {
off: 0,
json: 1,
error: 2,
warn: 3,
info: 4,
verbose: 5,
debug: 6,
silly: 7,
};
export type ILogger = winston.Logger & {
consoleLogLevel: unknown;
filepath: unknown;
directory: unknown;
};
export const log = winston.createLogger({
transports: [
new winston.transports.Console({
level: "warn",
format: winston.format.combine(winston.format.colorize(), winston.format.prettyPrint()),
}),
],
levels: customLogLevels,
}) as ILogger;
Object.defineProperties(log, {
consoleLogLevel: {
enumerable: true,
get() {
const transport = (this as ILogger).transports.find(
(t) => t instanceof winston.transports.Console
);
return transport !== undefined ? transport.level : undefined;
},
set(level) {
if (!level) {
level = "warn";
}
const validLevels = Object.keys(customLogLevels);
if (!validLevels.some((item) => item === level)) {
throw new Error(
`The logging level provided is "${level}". Valid values are: "${validLevels}".`
);
}
const transport = (this as ILogger).transports.find(
(t) => t instanceof winston.transports.Console
);
if (transport !== undefined) {
transport.level = level;
}
},
},
directory: {
enumerable: true,
get(): string {
return logDir;
},
set(logDirectory: string): void {
if (!logDirectory || (logDirectory && typeof logDirectory.valueOf() !== "string")) {
throw new Error('logDirectory cannot be null or undefined and must be of type "string".');
}
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory);
}
logDir = logDirectory;
},
},
filepath: {
enumerable: true,
get(): string {
if (!currentLogFile) {
const filename = `validate_log_${getTimeStamp()}.log`;
currentLogFile = path.join(this.directory, filename);
}
return currentLogFile;
},
set(logFilePath: string): void {
const self = this as ILogger;
if (!logFilePath || (logFilePath && typeof logFilePath.valueOf() !== "string")) {
throw new Error(
"filepath cannot be null or undefined and must be of type string. It must be " +
"an absolute file path."
);
}
currentLogFile = logFilePath;
self.directory = path.dirname(logFilePath);
if (!self.transports.some((t) => t instanceof winston.transports.File)) {
self.add(
new winston.transports.File({
level: "silly",
format: winston.format.prettyPrint(),
silent: false,
filename: logFilePath,
})
);
}
},
},
});