UNPKG

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