UNPKG

@zowe/imperative

Version:
377 lines 16.4 kB
"use strict"; /* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Copyright Contributors to the Zowe Project. * */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Logger = void 0; const util_1 = require("util"); const ImperativeError_1 = require("../../error/src/ImperativeError"); const StackTrace = require("stack-trace"); const path = require("path"); const TextUtils_1 = require("../../utilities/src/TextUtils"); const io_1 = require("../../io"); const LoggerManager_1 = require("./LoggerManager"); const log4js = require("log4js"); const Console_1 = require("../../console/src/Console"); const censor_1 = require("../../censor"); /** * Note(Kelosky): it seems from the log4js doc that you only get a single * instance of log4js per category. To reconfigure, you should "shutdown" logger. */ class Logger { /** * Get accessibility to logging service to invoke log calls, e.g * Logger.getLogger.info("important log info goes here"); * @param {string} category - category of logger to obtain * @return {Logger} - instance of logger set to our app's category */ static getLoggerCategory(category) { if (category === Logger.DEFAULT_CONSOLE_NAME) { return new Logger(new Console_1.Console(), Logger.DEFAULT_CONSOLE_NAME); } else { return new Logger(log4js.getLogger(category), category); } } /** * Get accessibility to logging service to invoke log calls, e.g * Logger.getLogger.info("important log info goes here"); * @return {Logger} - instance of logger set to our app's category */ static getImperativeLogger() { return Logger.getLoggerCategory(Logger.DEFAULT_IMPERATIVE_NAME); } /** * Get log4js instance directed at our app's category. * @return {Logger} - instance of logger set to our app's category */ static getAppLogger() { return Logger.getLoggerCategory(Logger.DEFAULT_APP_NAME); } static setLogInMemory(status, maxQueueSize) { LoggerManager_1.LoggerManager.instance.logInMemory = status; if (maxQueueSize != null) { LoggerManager_1.LoggerManager.instance.maxQueueSize = maxQueueSize; } } /** * Write all messages that was stored in memory to the input file. * @param {string} file - destination file name */ static writeInMemoryMessages(file) { LoggerManager_1.LoggerManager.instance.dumpQueuedMessages(file); } /** * Test if the input level is a valid value for Log4js. * @param {string} testLevel - input level to be tested * @returns {boolean} - status if the input level is valid */ static isValidLevel(testLevel) { let status = false; if (testLevel != null && Logger.DEFAULT_VALID_LOG_LEVELS.indexOf(testLevel.toUpperCase()) > -1) { status = true; } return status; } /** * Return an instance to the console logger which applies TextUtils invoked * through this Logger class. * * Note(Kelosky): this is not the same as obtaining a new Console() directly, * since we can make use of the internationalization and other routines * within this Logger class via this implementation. * * @return {Logger} - instance of logger set to our app's category */ static getConsoleLogger() { return Logger.getLoggerCategory(Logger.DEFAULT_CONSOLE_NAME); } /** * Get an instance of logging and adjust for config if config is present; * otherwise, use defaults. * @param {IConfigLogging} loggingConfig [description] * @return {[type]} [description] */ static initLogger(loggingConfig) { if (loggingConfig == null) { throw new ImperativeError_1.ImperativeError({ msg: "Input logging config document is required" }); } if (loggingConfig.log4jsConfig == null) { throw new ImperativeError_1.ImperativeError({ msg: "Input logging config is incomplete, does not contain log4jsConfig" }); } if (loggingConfig.log4jsConfig.appenders == null) { throw new ImperativeError_1.ImperativeError({ msg: "Input logging config is incomplete, does not contain log4jsConfig.appenders" }); } let logger; try { for (const appenderName of Object.keys(loggingConfig.log4jsConfig.appenders)) { const appender = loggingConfig.log4jsConfig.appenders[appenderName]; if (appender.type === "file" || appender.type === "fileSync") { io_1.IO.createDirsSyncFromFilePath(appender.filename); } } log4js.configure(loggingConfig.log4jsConfig); logger = log4js.getLogger(); logger.level = "debug"; LoggerManager_1.LoggerManager.instance.isLoggerInit = true; return new Logger(logger); } catch (err) { const cons = new Console_1.Console(); cons.error("Couldn't make desired logger: %s", (0, util_1.inspect)(err)); return new Logger(cons); } } constructor(mJsLogger, category) { this.mJsLogger = mJsLogger; this.category = category; if (LoggerManager_1.LoggerManager.instance.isLoggerInit && LoggerManager_1.LoggerManager.instance.QueuedMessages.length > 0) { LoggerManager_1.LoggerManager.instance.QueuedMessages.slice().reverse().forEach((value) => { if (this.category === value.category) { mJsLogger[value.method](value.message); LoggerManager_1.LoggerManager.instance.QueuedMessages.splice(LoggerManager_1.LoggerManager.instance.QueuedMessages.indexOf(value), 1); } }); } this.initStatus = LoggerManager_1.LoggerManager.instance.isLoggerInit; } // TODO: Can we find trace info for TypeScript to have e.g. [ERROR] Jobs.ts : 43 - Error encountered /** * Log a message at the "trace" level * Example: 'Entering cheese testing' * @param message - printf style template string, or a plain string message * @param args - printf style args * @returns {any} */ trace(message, ...args) { const finalMessage = TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.trace(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "trace", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message at the "debug" level * Example: 'Got cheese' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ debug(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.debug(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "debug", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message at the "info" level * Example: 'Cheese is Gouda' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ info(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.info(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "info", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message at the "warn" level * Example: 'Cheese is quite smelly.' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ warn(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.warn(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "warn", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message at the "error" level * Example: 'Cheese is too ripe!' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ error(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.error(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "error", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message at the "fatal" level * Example: 'Cheese was breeding ground for listeria.' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ fatal(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.fatal(this.getCallerFileAndLineTag() + finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "fatal", this.getCallerFileAndLineTag() + finalMessage); } return finalMessage; } /** * Log a message without CallerFileAndLineTag * Example: 'Cheese that is plain' * @param message - printf or mustache style template string, or a plain string message * @param args - printf or mustache style args * @returns {any} */ simple(message, ...args) { const finalMessage = censor_1.Censor.censorRawData(TextUtils_1.TextUtils.formatMessage.apply(this, [message].concat(args)), this.category); if (LoggerManager_1.LoggerManager.instance.isLoggerInit || this.category === Logger.DEFAULT_CONSOLE_NAME) { this.logService.info(finalMessage); } else { LoggerManager_1.LoggerManager.instance.queueMessage(this.category, "info", finalMessage); } return finalMessage; } /** * Log an Imperative error, including any optional fields if present * @param {ImperativeError} err - the error to log */ logError(err) { this.debug("Stack at time of error logging: %s", new Error().stack); if (!(err.details.additionalDetails == null)) { this.error(err.details.additionalDetails); } if (!(err.stack == null)) { this.error(err.stack); } if (!(err.details.causeErrors == null) && !(err.details.causeErrors.length == null) && err.details.causeErrors.length > 0) { for (const cause of err.details.causeErrors) { this.error("Cause error:\n%s", (0, util_1.inspect)(cause)); } } this.error(err.message); } /** * translate a message if possible * @param message - original message to translate, possibly with printf or {{obj}} style template * @param args - varargs to use to translate / format * @returns {string} translated or replaced result */ // public translate(message: string, ...args: any[]): string { // let result: string; // let translationError: Error; // try { // result = i18n.__.apply(global, [message].concat(args)); // } catch (e) { // result = undefined; // translationError = e; // } // if (isNullOrUndefined(result)) { // if (translationError) { // this.logService.warn("Error while translating!\n%s", inspect(translationError)); // } // result = TextUtils.formatMessage(message, ...args); // } // return result; // } /** * Obtain .js file name and line number which issued the log message. * NOTE(Kelosky): Consensus seems to be that this may produce a lot of overhead * by creating an Error and obtaining stack information for EVERY log message * that is issued. * * There are also packages available to obtain the appropriate line number. * * Perhaps when a package pops up that gives the appropriate .ts line number * and file name, we'll remove usage of this method. * @returns {string} - file and line number */ getCallerFileAndLineTag() { try { const frame = StackTrace.parse(new Error()); let callerStackIndex = 1; while (!frame[callerStackIndex].getFileName() || frame[callerStackIndex].getFileName().indexOf(path.basename(__filename)) >= 0) { // go up the stack until we're outside of the Zowe Logger file callerStackIndex += 1; } const filename = path.basename(frame[callerStackIndex].getFileName()); const lineNumber = frame[callerStackIndex].getLineNumber(); return (0, util_1.format)("[%s:%s] ", filename, lineNumber); } catch (e) { return "[<unknown>] "; } } /** * Allow for programmatic adjustments to the logger * @param {string} level - new level to set */ set level(level) { this.logService.level = level; } /** * Get current level setting * @return {string} - level of current log setting */ get level() { return this.logService.level.toString(); } /** * Get underlying logger service * * This function also check to see if log4js is configured since the last time it * was called. If yes, then update the logger with to leverage the new configuration. */ get logService() { if (this.initStatus !== LoggerManager_1.LoggerManager.instance.isLoggerInit) { const newLogger = Logger.getLoggerCategory(this.category); this.mJsLogger = newLogger.mJsLogger; this.initStatus = newLogger.initStatus; } return this.mJsLogger; } /** * Set underlying logger service */ set logService(service) { this.mJsLogger = service; } } exports.Logger = Logger; Logger.DEFAULT_IMPERATIVE_NAME = "imperative"; Logger.DEFAULT_APP_NAME = "app"; Logger.DEFAULT_CONSOLE_NAME = "console"; Logger.DEFAULT_VALID_LOG_LEVELS = ["ALL", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "MARK", "OFF"]; //# sourceMappingURL=Logger.js.map