UNPKG

bit-bin

Version:

<a href="https://opensource.org/licenses/Apache-2.0"><img alt="apache" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a> <a href="https://github.com/teambit/bit/blob/master/CONTRIBUTING.md"><img alt="prs" src="https://img.shields.io/b

430 lines (341 loc) 12.6 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getFormat = getFormat; exports.default = exports.printWarning = exports.createExtensionLogger = exports.baseFileTransportOpts = void 0; function _bluebird() { const data = require("bluebird"); _bluebird = function () { return data; }; return data; } function _defineProperty2() { const data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); _defineProperty2 = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _yn() { const data = _interopRequireDefault(require("yn")); _yn = function () { return data; }; return data; } function _serializeError() { const data = require("serialize-error"); _serializeError = function () { return data; }; return data; } function _stringFormat() { const data = _interopRequireDefault(require("string-format")); _stringFormat = function () { return data; }; return data; } function _winston() { const data = _interopRequireDefault(require("winston")); _winston = function () { return data; }; return data; } function path() { const data = _interopRequireWildcard(require("path")); path = function () { return data; }; return data; } function _constants() { const data = require("../constants"); _constants = function () { return data; }; return data; } function _analytics() { const data = require("../analytics/analytics"); _analytics = function () { return data; }; return data; } function _globalConfig() { const data = require("../api/consumer/lib/global-config"); _globalConfig = function () { return data; }; return data; } function _defaultErrorHandler() { const data = _interopRequireDefault(require("../cli/default-error-handler")); _defaultErrorHandler = function () { return data; }; return data; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // Store the extensionsLoggers to prevent create more than one logger for the same extension // in case the extension developer use api.logger more than once const extensionsLoggers = new Map(); const jsonFormat = (0, _yn().default)((0, _globalConfig().getSync)(_constants().CFG_LOG_JSON_FORMAT), { default: false }); const logLevel = (0, _globalConfig().getSync)(_constants().CFG_LOG_LEVEL) || 'debug'; const baseFileTransportOpts = { filename: _constants().DEBUG_LOG, format: jsonFormat ? _winston().default.format.combine(_winston().default.format.timestamp(), _winston().default.format.json()) : getFormat(), level: logLevel, maxsize: 10 * 1024 * 1024, // 10MB maxFiles: 10, // If true, log files will be rolled based on maxsize and maxfiles, but in ascending order. // The filename will always have the most recent log lines. The larger the appended number, the older the log file tailable: true }; exports.baseFileTransportOpts = baseFileTransportOpts; function getFormat() { return _winston().default.format.combine(_winston().default.format.metadata(), _winston().default.format.colorize(), _winston().default.format.timestamp(), _winston().default.format.splat(), // does nothing? _winston().default.format.errors({ stack: true }), _winston().default.format.prettyPrint({ depth: 3, colorize: true }), // does nothing? _winston().default.format.printf(info => customPrint(info))); function customPrint(info) { const getMetadata = () => { if (!Object.keys(info.metadata).length) return ''; try { return JSON.stringify(info.metadata, null, 2); } catch (err) { return `logger error: logging failed to stringify the metadata Json. (error: ${err.message})`; } }; return `${info.timestamp} ${info.level}: ${info.message} ${getMetadata()}`; } } const exceptionsFileTransportOpts = Object.assign({}, baseFileTransportOpts, { filename: path().join(_constants().GLOBAL_LOGS, 'exceptions.log') }); class BitLogger { /** * being set on command-registrar, once the flags are parsed. here, it's a workaround to have * it set before the command-registrar is loaded. at this stage we don't know for sure the "-j" * is actually "json". that's why this variable is overridden once the command-registrar is up. */ constructor(logger) { (0, _defineProperty2().default)(this, "logger", void 0); (0, _defineProperty2().default)(this, "shouldWriteToConsole", !process.argv.includes('--json') && !process.argv.includes('-j')); this.logger = logger; logger.on('error', err => { // eslint-disable-next-line no-console console.log('got an error from the logger', err); }); } silly(...args) { // @ts-ignore this.logger.silly(...args); } debug(...args) { // @ts-ignore this.logger.debug(...args); } warn(...args) { // @ts-ignore this.logger.warn(...args); } info(...args) { // @ts-ignore this.logger.info(...args); } error(...args) { // @ts-ignore this.logger.error(...args); } console(msg, level = 'info', color) { let actualMessage = msg; if (msg instanceof Error) { const { message } = (0, _defaultErrorHandler().default)(msg); actualMessage = message; } if (!this.shouldWriteToConsole) { this[level](actualMessage); return; } if (color) { try { // @ts-ignore actualMessage = _chalk().default.keyword(color)(actualMessage); } catch (e) { this.silly('a wrong color provided to logger.console method'); } } _winston().default.loggers.get('consoleOnly')[level](actualMessage); } exitAfterFlush(code = 0, commandName) { var _this = this; return (0, _bluebird().coroutine)(function* () { yield _analytics().Analytics.sendData(); let level; let msg; if (code === 0) { level = 'info'; msg = `[*] the command ${commandName} has been completed successfully`; } else { level = 'error'; msg = `[*] the command ${commandName} has been terminated with an error code ${code}`; } _this.logger[level](msg); yield waitForLogger(); process.exit(code); })(); } debugAndAddBreadCrumb(category, message, data, extraData) { this.addToLoggerAndToBreadCrumb('debug', category, message, data, extraData); } warnAndAddBreadCrumb(category, message, data, extraData) { this.addToLoggerAndToBreadCrumb('warn', category, message, data, extraData); } errorAndAddBreadCrumb(category, message, data, extraData) { this.addToLoggerAndToBreadCrumb('error', category, message, data, extraData); } addToLoggerAndToBreadCrumb(level, category, message, data, extraData) { if (!category) throw new TypeError('addToLoggerAndToBreadCrumb, category is missing'); if (!message) throw new TypeError('addToLoggerAndToBreadCrumb, message is missing'); const messageWithData = data ? (0, _stringFormat().default)(message, data) : message; this.logger[level](`${category}, ${messageWithData}`, extraData); addBreadCrumb(category, message, data, extraData); } } const winstonLogger = _winston().default.createLogger({ transports: [new (_winston().default.transports.File)(baseFileTransportOpts)], exceptionHandlers: [new (_winston().default.transports.File)(exceptionsFileTransportOpts)], exitOnError: false }); const logger = new BitLogger(winstonLogger); /** * Create a logger instance for extension * The extension name will be added as label so it will appear in the begining of each log line * The logger is cached for each extension so there is no problem to use getLogger few times for the same extension * @param {string} extensionName */ const createExtensionLogger = extensionName => { // Getting logger from cache const existingLogger = extensionsLoggers.get(extensionName); if (existingLogger) { return existingLogger; } const extensionFileTransportOpts = Object.assign({}, baseFileTransportOpts, { filename: path().join(_constants().GLOBAL_LOGS, 'extensions.log'), label: extensionName }); const extLogger = _winston().default.createLogger({ transports: [new (_winston().default.transports.File)(extensionFileTransportOpts)], exceptionHandlers: [new (_winston().default.transports.File)(extensionFileTransportOpts)], exitOnError: false }); extensionsLoggers.set(extensionName, extLogger); return extLogger; }; exports.createExtensionLogger = createExtensionLogger; const printWarning = msg => { const cfgNoWarnings = (0, _globalConfig().getSync)(_constants().CFG_NO_WARNINGS); if (cfgNoWarnings !== 'true') { // eslint-disable-next-line no-console console.log(_chalk().default.yellow(`Warning: ${msg}`)); } }; /** * @credit dpraul from https://github.com/winstonjs/winston/issues/1250 * it solves an issue when exiting the code explicitly and the log file is not written * * there are still two issues though. * 1. sometimes, an error is thrown "write after end". can be reproduced by running the * performance e2e-test on 3,000 components, 100 dependencies, on export. * 2. sometimes, it doesn't write all messages to the log. can be reproduced by the same method as * above, but even with 300 components and 10 dependencies. * * if you try to fix these issues, please make sure that after your fix, the following are working: * 1. the two cases should work. * 2. when error is thrown, it exists successfully with the correct error-code. (the standard * e2e-tests cover this multiple times). * 3. the ssh is working. (not covered by the e2e-tests). run a simple export to an ssh and make * sure it doesn't hang. * * for the record, the following was working for #1 and #2 but not for #3. * ``` * const loggerDone = new Promise(resolve => logger.logger.on(code ? 'finish' : 'close', resolve)); * if (code) logger.logger.end(); * ``` */ exports.printWarning = printWarning; function waitForLogger() { return _waitForLogger.apply(this, arguments); } function _waitForLogger() { _waitForLogger = (0, _bluebird().coroutine)(function* () { const loggerDone = new Promise(resolve => logger.logger.on('finish', resolve)); logger.logger.end(); return loggerDone; }); return _waitForLogger.apply(this, arguments); } function addBreadCrumb(category, message, data = {}, extraData) { const hashedData = {}; Object.keys(data).forEach(key => hashedData[key] = _analytics().Analytics.hashData(data[key])); const messageWithHashedData = (0, _stringFormat().default)(message, hashedData); extraData = extraData instanceof Error ? (0, _serializeError().serializeError)(extraData) : extraData; _analytics().Analytics.addBreadCrumb(category, messageWithHashedData, extraData); } /** * prefix BIT_LOG to the command, provides the ability to log into the console. * two options are available here: * 1) use the level. e.g. `BIT_LOG=error bit import`. * 2) use the message prefix, e.g. `BIT_LOG=ssh bit import`. * 3) use multiple message prefixes, e.g. `BIT_LOG=ssh,env bit import`. */ if (process.env.BIT_LOG) { const levels = ['error', 'warn', 'info', 'verbose', 'debug', 'silly']; const isLevel = levels.includes(process.env.BIT_LOG); const prefixes = process.env.BIT_LOG.split(','); const filterPrefix = _winston().default.format(info => { if (isLevel) return info; if (prefixes.some(prefix => info.message.startsWith(prefix))) return info; return false; }); logger.logger.add(new (_winston().default.transports.Console)({ level: isLevel ? process.env.BIT_LOG : 'silly', format: _winston().default.format.combine(filterPrefix(), _winston().default.format.printf(info => info.message)) })); } /** * useful when in the middle of the process, Bit needs to print to the console. * it's better than using `console.log` because, this way, it's possible to turn it on/off */ _winston().default.loggers.add('consoleOnly', { format: _winston().default.format.combine(_winston().default.format.printf(info => info.message)), transports: [new (_winston().default.transports.Console)({ level: 'silly' })] }); var _default = logger; exports.default = _default;