UNPKG

6.48 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const stream_1 = require("stream");
4const util = require("util");
5const colors_1 = require("./colors");
6const format_1 = require("./format");
7const utils_1 = require("./utils");
8exports.LOGGER_LEVELS = Object.freeze({
9 DEBUG: 10,
10 INFO: 20,
11 WARN: 30,
12 ERROR: 40,
13});
14exports.LOGGER_LEVEL_NAMES = new Map([
15 [exports.LOGGER_LEVELS.DEBUG, 'DEBUG'],
16 [exports.LOGGER_LEVELS.INFO, 'INFO'],
17 [exports.LOGGER_LEVELS.WARN, 'WARN'],
18 [exports.LOGGER_LEVELS.ERROR, 'ERROR'],
19]);
20function getLoggerLevelName(level) {
21 if (level) {
22 const levelName = exports.LOGGER_LEVEL_NAMES.get(level);
23 if (levelName) {
24 return levelName;
25 }
26 }
27}
28exports.getLoggerLevelName = getLoggerLevelName;
29function getLoggerLevelColor(colors, level) {
30 const levelName = getLoggerLevelName(level);
31 if (levelName) {
32 return colors.log[levelName];
33 }
34}
35exports.getLoggerLevelColor = getLoggerLevelColor;
36class StreamHandler {
37 constructor({ stream, filter, formatter }) {
38 this.stream = stream;
39 this.filter = filter;
40 this.formatter = formatter;
41 }
42 clone(opts) {
43 const { stream, filter, formatter } = this;
44 return new StreamHandler({ stream, filter, formatter, ...opts });
45 }
46 handle(record) {
47 if (this.filter && !this.filter(record)) {
48 return;
49 }
50 const msg = this.formatter && record.format !== false ? this.formatter(record) : record.msg;
51 this.stream.write(utils_1.enforceLF(msg));
52 }
53}
54exports.StreamHandler = StreamHandler;
55const stdoutLogRecordFilter = (record) => !record.level || record.level === exports.LOGGER_LEVELS.INFO;
56const stderrLogRecordFilter = (record) => !!record.level && record.level !== exports.LOGGER_LEVELS.INFO;
57exports.DEFAULT_LOGGER_HANDLERS = new Set([
58 new StreamHandler({ stream: process.stdout, filter: stdoutLogRecordFilter }),
59 new StreamHandler({ stream: process.stderr, filter: stderrLogRecordFilter }),
60]);
61class Logger {
62 constructor({ level = exports.LOGGER_LEVELS.INFO, handlers } = {}) {
63 this.level = level;
64 this.handlers = handlers ? handlers : Logger.cloneHandlers(exports.DEFAULT_LOGGER_HANDLERS);
65 }
66 static cloneHandlers(handlers) {
67 return new Set([...handlers].map(handler => handler.clone()));
68 }
69 /**
70 * Clone this logger, optionally overriding logger options.
71 *
72 * @param opts Logger options to override from this logger.
73 */
74 clone(opts = {}) {
75 const { level, handlers } = this;
76 return new Logger({ level, handlers: Logger.cloneHandlers(handlers), ...opts });
77 }
78 /**
79 * Log a message as-is.
80 *
81 * @param msg The string to log.
82 */
83 msg(msg) {
84 this.log(this.createRecord(msg));
85 }
86 /**
87 * Log a message using the `debug` logger level.
88 *
89 * @param msg The string to log.
90 */
91 debug(msg) {
92 this.log(this.createRecord(msg, exports.LOGGER_LEVELS.DEBUG));
93 }
94 /**
95 * Log a message using the `info` logger level.
96 *
97 * @param msg The string to log.
98 */
99 info(msg) {
100 this.log(this.createRecord(msg, exports.LOGGER_LEVELS.INFO));
101 }
102 /**
103 * Log a message using the `warn` logger level.
104 *
105 * @param msg The string to log.
106 */
107 warn(msg) {
108 this.log(this.createRecord(msg, exports.LOGGER_LEVELS.WARN));
109 }
110 /**
111 * Log a message using the `error` logger level.
112 *
113 * @param msg The string to log.
114 */
115 error(msg) {
116 this.log(this.createRecord(msg, exports.LOGGER_LEVELS.ERROR));
117 }
118 createRecord(msg, level, format) {
119 return {
120 // If the logger is used to quickly print something, let's pretty-print
121 // it into a string.
122 msg: util.format(msg),
123 level,
124 logger: this,
125 format,
126 };
127 }
128 /**
129 * Log newlines using a logger output found via `level`.
130 *
131 * @param num The number of newlines to log.
132 * @param level The logger level. If omitted, the default output is used.
133 */
134 nl(num = 1, level) {
135 this.log({ ...this.createRecord('\n'.repeat(num), level), format: false });
136 }
137 /**
138 * Log a record using a logger output found via `level`.
139 */
140 log(record) {
141 if (typeof record.level === 'number' && this.level > record.level) {
142 return;
143 }
144 for (const handler of this.handlers) {
145 handler.handle(record);
146 }
147 }
148 createWriteStream(level, format) {
149 const self = this;
150 return new class extends stream_1.Writable {
151 _write(chunk, encoding, callback) {
152 self.log(self.createRecord(chunk.toString(), level, format));
153 callback();
154 }
155 }();
156 }
157}
158exports.Logger = Logger;
159function createTaggedFormatter({ colors = colors_1.NO_COLORS, prefix = '', titleize, wrap } = {}) {
160 return ({ msg, level, format }) => {
161 if (format === false) {
162 return msg;
163 }
164 const { strong, weak } = colors;
165 const [firstLine, ...lines] = msg.split('\n');
166 const levelName = getLoggerLevelName(level);
167 const levelColor = getLoggerLevelColor(colors, level);
168 const tag = ((typeof prefix === 'function' ? prefix() : prefix) +
169 (levelName ? `${weak('[')}\x1b[40m${strong(levelColor ? levelColor(levelName) : levelName)}\x1b[49m${weak(']')}` : ''));
170 const title = titleize && lines.length > 0 ? `${strong(levelColor ? levelColor(firstLine) : firstLine)}\n` : firstLine;
171 const indentation = tag ? format_1.stringWidth(tag) + 1 : 0;
172 const pulledLines = utils_1.dropWhile(lines, l => l === '');
173 return ((tag ? `${tag} ` : '') +
174 (wrap
175 ? format_1.wordWrap([title, ...pulledLines].join('\n'), { indentation, ...(typeof wrap === 'object' ? wrap : {}) })
176 : [title, ...pulledLines.map(l => l ? ' '.repeat(indentation) + l : '')].join('\n')));
177 };
178}
179exports.createTaggedFormatter = createTaggedFormatter;
180function createPrefixedFormatter(prefix) {
181 return ({ msg, format }) => {
182 if (format === false) {
183 return msg;
184 }
185 return `${typeof prefix === 'function' ? prefix() : prefix} ${msg}`;
186 };
187}
188exports.createPrefixedFormatter = createPrefixedFormatter;