logging.js
3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import npmlog from 'npmlog';
import _ from 'lodash';
import { unleakString } from './util';
import moment from 'moment';
// levels that are available from `npmlog`
const NPM_LEVELS = ['silly', 'verbose', 'debug', 'info', 'http', 'warn', 'error'];
const MAX_LOG_RECORDS_COUNT = 3000;
const PREFIX_TIMESTAMP_FORMAT = 'HH-mm-ss:SSS';
// mock log object used in testing mode
let mockLog = {};
for (let level of NPM_LEVELS) {
mockLog[level] = () => {};
}
function patchLogger (logger) {
if (!logger.debug) {
logger.addLevel('debug', 1000, { fg: 'blue', bg: 'black' }, 'dbug');
}
}
function _getLogger () {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = parseInt(process.env._TESTING, 10) === 1;
const forceLogMode = parseInt(process.env._FORCE_LOGS, 10) === 1;
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const usingGlobalLog = !!global._global_npmlog;
let logger;
if (testingMode && !forceLogMode) {
// in testing mode, use a mock logger object that we can query
logger = mockLog;
} else {
// otherwise, either use the global, or a new `npmlog` object
logger = global._global_npmlog || npmlog;
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
}
patchLogger(logger);
return [logger, usingGlobalLog];
}
function getActualPrefix (prefix, logTimestamp = false) {
let actualPrefix = _.isFunction(prefix) ? prefix() : prefix;
if (logTimestamp) {
actualPrefix = `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}] ${actualPrefix}`;
}
return actualPrefix;
}
function getLogger (prefix = null) {
let [logger, usingGlobalLog] = _getLogger();
// wrap the logger so that we can catch and modify any logging
let wrappedLogger = {unwrap: () => logger};
// allow access to the level of the underlying logger
Object.defineProperty(wrappedLogger, 'level', {
get: () => { return logger.level; },
set: (newValue) => { logger.level = newValue; },
enumerable: true,
configurable: true
});
const logTimestamp = parseInt(process.env._LOG_TIMESTAMP, 10) === 1;
// add all the levels from `npmlog`, and map to the underlying logger
for (const level of NPM_LEVELS) {
wrappedLogger[level] = function (...args) {
const actualPrefix = getActualPrefix(prefix, logTimestamp);
for (const arg of args) {
const out = (_.isError(arg) && arg.stack) ? arg.stack : `${arg}`;
for (const line of out.split('\n')) {
logger[level](actualPrefix, unleakString(line));
}
}
};
}
// add method to log an error, and throw it, for convenience
wrappedLogger.errorAndThrow = function (err) {
this.error(err);
// make sure we have an `Error` object. Wrap if necessary
throw (_.isError(err) ? err : new Error(unleakString(err)));
};
if (!usingGlobalLog) {
// if we're not using a global log specified from some top-level package,
// set the log level to a default of verbose. Otherwise, let the top-level
// package set the log level
wrappedLogger.level = 'verbose';
}
wrappedLogger.levels = NPM_LEVELS;
return wrappedLogger;
}
// export a default logger with no prefix
const log = getLogger();
export { log, patchLogger, getLogger };
export default log;