/** * @license * Copyright 2019-2020 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import _ = require('lodash'); import { EOL as eol } from 'os'; import { StreamLogger } from 'resin-stream-logger'; import { getChalk } from './lazy'; enum Level { BUILD = 'build', INFO = 'info', DEBUG = 'debug', SUCCESS = 'success', WARN = 'warn', ERROR = 'error', LOGS = 'logs', LIVEPUSH = 'livepush', } interface LoggerAdapter { debug: (msg: string) => void; error: (msg: string) => void; info: (msg: string) => void; log: (msg: string) => void; warn: (msg: string) => void; } /** * General purpose logger class with support for log streams and colours. * Call `Logger.getLogger()` to retrieve a global shared instance of this * class. The `new Logger()` pattern is not recommended because it may lead * to Node printing "MaxListenersExceededWarning" warning messages to the * console. */ class Logger { public static readonly Level = Level; // `Logger.command` is currently set in `preparser.ts` public static command: string; // CLI cmd, e.g. 'push', 'env add', ... public streams: { build: NodeJS.ReadWriteStream; info: NodeJS.ReadWriteStream; debug: NodeJS.ReadWriteStream; success: NodeJS.ReadWriteStream; warn: NodeJS.ReadWriteStream; error: NodeJS.ReadWriteStream; logs: NodeJS.ReadWriteStream; livepush: NodeJS.ReadWriteStream; }; public formatMessage: (name: string, message: string) => string; protected deferredLogMessages: Array<[string, Level]>; protected adapter: LoggerAdapter; protected constructor() { const logger = new StreamLogger(); const chalk = getChalk(); logger.addPrefix('build', chalk.blue('[Build]')); logger.addPrefix('info', chalk.cyan('[Info]')); logger.addPrefix('debug', chalk.magenta('[Debug]')); logger.addPrefix('success', chalk.green('[Success]')); logger.addPrefix('warn', chalk.yellow('[Warn]')); logger.addPrefix('error', chalk.red('[Error]')); logger.addPrefix('logs', chalk.green('[Logs]')); logger.addPrefix('live', chalk.yellow('[Live]')); this.streams = { build: logger.createLogStream('build'), info: logger.createLogStream('info'), debug: logger.createLogStream('debug'), success: logger.createLogStream('success'), warn: logger.createLogStream('warn'), error: logger.createLogStream('error'), logs: logger.createLogStream('logs'), livepush: logger.createLogStream('live'), }; _.forEach(this.streams, function (stream, key) { if (key !== 'debug') { stream.pipe(process.stdout); } else if (process.env.DEBUG) { stream.pipe(process.stderr); } }); this.formatMessage = logger.formatWithPrefix.bind(logger); this.deferredLogMessages = []; this.adapter = { debug: (msg: string) => this.logDebug(msg), error: (msg: string) => this.logError(msg), info: (msg: string) => this.logInfo(msg), log: (msg: string) => this.logLogs(msg), warn: (msg: string) => this.logWarn(msg), }; } protected static logger: Logger; /** Retrieve a global shared instance of this class */ public static getLogger() { if (!this.logger) { this.logger = new Logger(); } return this.logger; } public logInfo(msg: string) { return this.streams.info.write(msg + eol); } public logDebug(msg: string) { return this.streams.debug.write(msg + eol); } public logSuccess(msg: string) { return this.streams.success.write(msg + eol); } public logWarn(msg: string) { return this.streams.warn.write(msg + eol); } public logError(msg: string) { return this.streams.error.write(msg + eol); } public logBuild(msg: string) { return this.streams.build.write(msg + eol); } public logLogs(msg: string) { return this.streams.logs.write(msg + eol); } public logLivepush(msg: string) { return this.streams.livepush.write(msg + eol); } /** * Log a message for output later, ignore duplicates. */ public deferredLog(msg: string, level: Level) { if (!this.deferredLogMessages.find((entry) => entry[0] === msg)) { this.deferredLogMessages.push([msg, level]); } } /** Output any messages that have been queued for deferred output */ public outputDeferredMessages() { this.deferredLogMessages.forEach((m) => { this.streams[m[1]].write(m[0] + eol); }); this.deferredLogMessages = []; } public getAdapter(): LoggerAdapter { return this.adapter; } } export = Logger;