@percy/logger 1.0.0-beta.8 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/logger.js ADDED
@@ -0,0 +1,321 @@
1
+ import { colors } from './utils.js';
2
+ const URL_REGEXP = /\bhttps?:\/\/[^\s/$.?#].[^\s]*\b/i;
3
+ const LOG_LEVELS = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3
8
+ }; // A PercyLogger instance retains logs in-memory for quick lookups while also writing log
9
+ // messages to stdout and stderr depending on the log level and debug string.
10
+
11
+ export class PercyLogger {
12
+ // default log level
13
+ level = 'info'; // namespace regular expressions used to determine which debug logs to write
14
+
15
+ namespaces = {
16
+ include: [/^.*?$/],
17
+ exclude: []
18
+ }; // in-memory store for logs and meta info
19
+
20
+ messages = new Set(); // track deprecations to limit noisy logging
21
+
22
+ deprecations = new Set(); // static vars can be overriden for testing
23
+
24
+ static stdout = process.stdout;
25
+ static stderr = process.stderr; // Handles setting env var values and returns a singleton
26
+
27
+ constructor() {
28
+ let {
29
+ instance = this
30
+ } = this.constructor;
31
+
32
+ if (process.env.PERCY_DEBUG) {
33
+ instance.debug(process.env.PERCY_DEBUG);
34
+ } else if (process.env.PERCY_LOGLEVEL) {
35
+ instance.loglevel(process.env.PERCY_LOGLEVEL);
36
+ }
37
+
38
+ this.constructor.instance = instance;
39
+ return instance;
40
+ } // Change log level at any time or return the current log level
41
+
42
+
43
+ loglevel(level) {
44
+ if (level) this.level = level;
45
+ return this.level;
46
+ } // Change namespaces by generating an array of namespace regular expressions from a
47
+ // comma separated debug string
48
+
49
+
50
+ debug(namespaces) {
51
+ if (this.namespaces.string === namespaces) return;
52
+ this.namespaces.string = namespaces;
53
+ namespaces = namespaces.split(/[\s,]+/).filter(Boolean);
54
+ if (!namespaces.length) return this.namespaces;
55
+ this.loglevel('debug');
56
+ this.namespaces = namespaces.reduce((namespaces, ns) => {
57
+ ns = ns.replace(/:?\*/g, m => m[0] === ':' ? ':?.*?' : '.*?');
58
+
59
+ if (ns[0] === '-') {
60
+ namespaces.exclude.push(new RegExp('^' + ns.substr(1) + '$'));
61
+ } else {
62
+ namespaces.include.push(new RegExp('^' + ns + '$'));
63
+ }
64
+
65
+ return namespaces;
66
+ }, {
67
+ string: namespaces,
68
+ include: [],
69
+ exclude: []
70
+ });
71
+ } // Creates a new log group and returns level specific functions for logging
72
+
73
+
74
+ group(name) {
75
+ return Object.keys(LOG_LEVELS).reduce((group, level) => Object.assign(group, {
76
+ [level]: this.log.bind(this, name, level)
77
+ }), {
78
+ deprecated: this.deprecated.bind(this, name),
79
+ shouldLog: this.shouldLog.bind(this, name),
80
+ progress: this.progress.bind(this, name),
81
+ format: this.format.bind(this, name),
82
+ loglevel: this.loglevel.bind(this),
83
+ stdout: this.constructor.stdout,
84
+ stderr: this.constructor.stderr
85
+ });
86
+ } // Query for a set of logs by filtering the in-memory store
87
+
88
+
89
+ query(filter) {
90
+ return Array.from(this.messages).filter(filter);
91
+ } // Formats messages before they are logged to stdio
92
+
93
+
94
+ format(debug, level, message, elapsed) {
95
+ let label = 'percy';
96
+ let suffix = '';
97
+
98
+ if (arguments.length === 1) {
99
+ // format(message)
100
+ [debug, message] = [null, debug];
101
+ } else if (arguments.length === 2) {
102
+ // format(debug, message)
103
+ [level, message] = [null, level];
104
+ }
105
+
106
+ if (this.level === 'debug') {
107
+ // include debug info in the label
108
+ if (debug) label += `:${debug}`; // include elapsed time since last log
109
+
110
+ if (elapsed != null) {
111
+ suffix = ' ' + colors.grey(`(${elapsed}ms)`);
112
+ }
113
+ }
114
+
115
+ label = colors.magenta(label);
116
+
117
+ if (level === 'error') {
118
+ // red errors
119
+ message = colors.red(message);
120
+ } else if (level === 'warn') {
121
+ // yellow warnings
122
+ message = colors.yellow(message);
123
+ } else if (level === 'info' || level === 'debug') {
124
+ // blue info and debug URLs
125
+ message = message.replace(URL_REGEXP, colors.blue('$&'));
126
+ }
127
+
128
+ return `[${label}] ${message}${suffix}`;
129
+ } // Replaces the current line with a log message
130
+
131
+
132
+ progress(debug, message, persist) {
133
+ if (!this.shouldLog(debug, 'info')) return;
134
+ let {
135
+ stdout
136
+ } = this.constructor;
137
+
138
+ if (stdout.isTTY || !this._progress) {
139
+ message && (message = this.format(debug, message));
140
+ if (stdout.isTTY) stdout.cursorTo(0);else message && (message = message + '\n');
141
+ if (message) stdout.write(message);
142
+ if (stdout.isTTY) stdout.clearLine(1);
143
+ }
144
+
145
+ this._progress = !!message && {
146
+ message,
147
+ persist
148
+ };
149
+ } // Returns true or false if the level and debug group can write messages to stdio
150
+
151
+
152
+ shouldLog(debug, level) {
153
+ return LOG_LEVELS[level] != null && LOG_LEVELS[level] >= LOG_LEVELS[this.level] && !this.namespaces.exclude.some(ns => ns.test(debug)) && this.namespaces.include.some(ns => ns.test(debug));
154
+ } // Ensures that deprecation messages are not logged more than once
155
+
156
+
157
+ deprecated(debug, message, meta) {
158
+ if (this.deprecations.has(message)) return;
159
+ this.deprecations.add(message);
160
+ this.log(debug, 'warn', `Warning: ${message}`, meta);
161
+ } // Returns true if a socket is present and ready
162
+
163
+
164
+ get isRemote() {
165
+ var _this$socket;
166
+
167
+ return ((_this$socket = this.socket) === null || _this$socket === void 0 ? void 0 : _this$socket.readyState) === 1;
168
+ } // Generic log method accepts a debug group, log level, log message, and optional meta
169
+ // information to store with the message and other info
170
+
171
+
172
+ log(debug, level, message, meta = {}) {
173
+ // message might be an error object
174
+ let isError = typeof message !== 'string' && (level === 'error' || level === 'debug');
175
+ let error = isError && message; // if remote, send logs there
176
+
177
+ if (this.isRemote) {
178
+ // serialize error messages
179
+ message = isError && 'stack' in error ? {
180
+ message: error.message,
181
+ stack: error.stack
182
+ } : message;
183
+ return this.socket.send(JSON.stringify({
184
+ log: [debug, level, message, {
185
+ remote: true,
186
+ ...meta
187
+ }]
188
+ }));
189
+ } // ensure the message is a string
190
+
191
+
192
+ message = isError && message.stack || message.message || message.toString(); // timestamp each log
193
+
194
+ let timestamp = Date.now();
195
+ let entry = {
196
+ debug,
197
+ level,
198
+ message,
199
+ meta,
200
+ timestamp
201
+ };
202
+ this.messages.add(entry); // maybe write the message to stdio
203
+
204
+ if (this.shouldLog(debug, level)) {
205
+ let elapsed = timestamp - (this.lastlog || timestamp);
206
+ if (isError && this.level !== 'debug') message = error.toString();
207
+ this.write(level, this.format(debug, error ? 'error' : level, message, elapsed));
208
+ this.lastlog = timestamp;
209
+ }
210
+ } // Writes a message to stdio based on the loglevel
211
+
212
+
213
+ write(level, message) {
214
+ var _this$_progress;
215
+
216
+ let {
217
+ stdout,
218
+ stderr
219
+ } = this.constructor;
220
+ let progress = stdout.isTTY && this._progress;
221
+
222
+ if (progress) {
223
+ stdout.cursorTo(0);
224
+ stdout.clearLine();
225
+ }
226
+
227
+ (level === 'info' ? stdout : stderr).write(message + '\n');
228
+ if (!((_this$_progress = this._progress) !== null && _this$_progress !== void 0 && _this$_progress.persist)) delete this._progress;else if (progress) stdout.write(progress.message);
229
+ } // Opens a socket logging connection
230
+
231
+
232
+ connect(socket) {
233
+ // send logging environment info
234
+ let PERCY_DEBUG = process.env.PERCY_DEBUG;
235
+ let PERCY_LOGLEVEL = process.env.PERCY_LOGLEVEL || this.loglevel();
236
+ socket.send(JSON.stringify({
237
+ env: {
238
+ PERCY_DEBUG,
239
+ PERCY_LOGLEVEL
240
+ }
241
+ })); // attach remote logging handler
242
+
243
+ socket.onmessage = ({
244
+ data
245
+ }) => {
246
+ let {
247
+ log,
248
+ logAll
249
+ } = JSON.parse(data);
250
+ if (logAll) logAll.forEach(e => this.messages.add(e));
251
+ if (log) this.log(...log);
252
+ }; // return a cleanup function
253
+
254
+
255
+ return () => {
256
+ socket.onmessage = null;
257
+ };
258
+ } // Connects to a remote logger
259
+
260
+
261
+ async remote(createSocket, timeout = 1000) {
262
+ if (this.isRemote) return; // if not already connected, wait until the timeout
263
+
264
+ let err = await new Promise(resolve => {
265
+ let done = event => {
266
+ if (timeoutid == null) return;
267
+ timeoutid = clearTimeout(timeoutid);
268
+ if (this.socket) this.socket.onopen = this.socket.onerror = null;
269
+ resolve((event === null || event === void 0 ? void 0 : event.error) || (event === null || event === void 0 ? void 0 : event.type) === 'error' && 'Error: Socket connection failed');
270
+ };
271
+
272
+ let timeoutid = setTimeout(done, timeout, {
273
+ error: 'Error: Socket connection timed out'
274
+ });
275
+ Promise.resolve().then(async () => {
276
+ this.socket = await createSocket();
277
+ if (this.isRemote) return done();
278
+ this.socket.onopen = this.socket.onerror = done;
279
+ }).catch(error => done({
280
+ error
281
+ }));
282
+ }); // there was an error connecting, will fallback to normal logging
283
+
284
+ if (err) {
285
+ this.log('logger', 'debug', 'Unable to connect to remote logger');
286
+ this.log('logger', 'debug', err);
287
+ return;
288
+ } // send any messages already logged in this environment
289
+
290
+
291
+ if (this.messages.size) {
292
+ this.socket.send(JSON.stringify({
293
+ logAll: Array.from(this.messages).map(entry => ({ ...entry,
294
+ meta: {
295
+ remote: true,
296
+ ...entry.meta
297
+ }
298
+ }))
299
+ }));
300
+ } // attach an incoming message handler
301
+
302
+
303
+ this.socket.onmessage = ({
304
+ data
305
+ }) => {
306
+ let {
307
+ env
308
+ } = JSON.parse(data); // update local environment info
309
+
310
+ if (env) Object.assign(process.env, env);
311
+ }; // return a cleanup function
312
+
313
+
314
+ return () => {
315
+ this.socket.onmessage = null;
316
+ this.socket = null;
317
+ };
318
+ }
319
+
320
+ }
321
+ export default PercyLogger;
package/dist/utils.js ADDED
@@ -0,0 +1,28 @@
1
+ const {
2
+ assign,
3
+ entries
4
+ } = Object; // matches ansi escape sequences
5
+
6
+ export const ANSI_REG = new RegExp('[\\u001B\\u009B][[\\]()#;?]*((?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)' + '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g'); // color names by ansi escape code
7
+
8
+ export const ANSI_COLORS = {
9
+ '91m': 'red',
10
+ '32m': 'green',
11
+ '93m': 'yellow',
12
+ '34m': 'blue',
13
+ '95m': 'magenta',
14
+ '90m': 'grey'
15
+ }; // colorize each line of a string using an ansi escape sequence
16
+
17
+ const LINE_REG = /^.*$/gm;
18
+
19
+ function colorize(code, str) {
20
+ return str.replace(LINE_REG, line => `\u001b[${code}${line}\u001b[39m`);
21
+ } // map ansi colors to bound colorize functions
22
+
23
+
24
+ export const colors = entries(ANSI_COLORS).reduce((colors, [code, name]) => {
25
+ return assign(colors, {
26
+ [name]: colorize.bind(null, code)
27
+ });
28
+ }, {});
package/package.json CHANGED
@@ -1,25 +1,48 @@
1
1
  {
2
2
  "name": "@percy/logger",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
- "main": "index.js",
6
- "scripts": {
7
- "lint": "eslint --ignore-path ../../.gitignore .",
8
- "test": "cross-env NODE_ENV=test mocha",
9
- "test:coverage": "nyc yarn test"
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/percy/cli",
8
+ "directory": "packages/logger"
10
9
  },
11
10
  "publishConfig": {
12
11
  "access": "public"
13
12
  },
14
- "mocha": {
15
- "require": "../../scripts/babel-register"
13
+ "engines": {
14
+ "node": ">=14"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "test/helpers.js",
19
+ "test/client.js"
20
+ ],
21
+ "main": "./dist/index.js",
22
+ "browser": "./dist/bundle.js",
23
+ "type": "module",
24
+ "exports": {
25
+ ".": "./dist/index.js",
26
+ "./utils": "./dist/utils.js",
27
+ "./test/helpers": "./test/helpers.js",
28
+ "./test/client": "./test/client.js"
16
29
  },
17
- "dependencies": {
18
- "colors": "^1.4.0",
19
- "winston": "^3.2.1"
30
+ "imports": {
31
+ "#logger": {
32
+ "node": "./dist/logger.js",
33
+ "default": "./dist/browser.js"
34
+ }
35
+ },
36
+ "scripts": {
37
+ "build": "node ../../scripts/build",
38
+ "lint": "eslint --ignore-path ../../.gitignore .",
39
+ "test": "node ../../scripts/test",
40
+ "test:coverage": "yarn test --coverage"
20
41
  },
21
- "devDependencies": {
22
- "strip-ansi": "^6.0.0"
42
+ "rollup": {
43
+ "output": {
44
+ "name": "PercyLogger"
45
+ }
23
46
  },
24
- "gitHead": "6015850e7c20c130d625fcb327b10d7513b35707"
47
+ "gitHead": "38917e6027299d6cd86008e2ccd005d90bbf89c0"
25
48
  }
package/test/client.js ADDED
@@ -0,0 +1,173 @@
1
+ (function() {
2
+ this.PercyLogger = this.PercyLogger || {};
3
+ this.PercyLogger.TestHelpers = (function (logger) {
4
+ 'use strict';
5
+
6
+ const process = (typeof globalThis !== "undefined" && globalThis.process) || {};
7
+ process.env = process.env || {};
8
+ process.env.__PERCY_BROWSERIFIED__ = true;
9
+
10
+ globalThis.process = globalThis.process || process;
11
+
12
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
+
14
+ var logger__default = /*#__PURE__*/_interopDefaultLegacy(logger);
15
+
16
+ const {
17
+ assign,
18
+ entries
19
+ } = Object; // matches ansi escape sequences
20
+
21
+ const ANSI_REG = new RegExp('[\\u001B\\u009B][[\\]()#;?]*((?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)' + '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g'); // color names by ansi escape code
22
+
23
+ const ANSI_COLORS = {
24
+ '91m': 'red',
25
+ '32m': 'green',
26
+ '93m': 'yellow',
27
+ '34m': 'blue',
28
+ '95m': 'magenta',
29
+ '90m': 'grey'
30
+ }; // colorize each line of a string using an ansi escape sequence
31
+
32
+ const LINE_REG = /^.*$/gm;
33
+
34
+ function colorize(code, str) {
35
+ return str.replace(LINE_REG, line => `\u001b[${code}${line}\u001b[39m`);
36
+ } // map ansi colors to bound colorize functions
37
+
38
+
39
+ entries(ANSI_COLORS).reduce((colors, _ref) => {
40
+ let [code, name] = _ref;
41
+ return assign(colors, {
42
+ [name]: colorize.bind(null, code)
43
+ });
44
+ }, {});
45
+
46
+ const ELAPSED_REG = /\s\S*?\(\d+ms\)\S*/;
47
+ const NEWLINE_REG = /\r\n/g;
48
+ const LASTLINE_REG = /\n$/;
49
+
50
+ function sanitizeLog(str) {
51
+ let {
52
+ ansi,
53
+ elapsed
54
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
55
+ // normalize line endings
56
+ str = str.replace(NEWLINE_REG, '\n'); // strip ansi colors
57
+
58
+ if (!ansi) str = str.replace(ANSI_REG, ''); // strip elapsed time
59
+
60
+ if (!elapsed) str = str.replace(ELAPSED_REG, ''); // strip trailing line endings
61
+
62
+ return str.replace(LASTLINE_REG, '');
63
+ }
64
+
65
+ function spy(object, method, func) {
66
+ if (object[method].restore) object[method].restore();
67
+ let spy = Object.assign(function spy() {
68
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
69
+ args[_key] = arguments[_key];
70
+ }
71
+
72
+ spy.calls.push(args);
73
+ if (func) return func.apply(this, args);
74
+ }, {
75
+ restore: () => object[method] = spy.originalValue,
76
+ reset: () => (spy.calls.length = 0) || spy,
77
+ originalValue: object[method],
78
+ calls: []
79
+ });
80
+ object[method] = spy;
81
+ return spy;
82
+ }
83
+
84
+ const {
85
+ Logger,
86
+ loglevel
87
+ } = logger__default["default"];
88
+ const helpers = {
89
+ stdout: [],
90
+ stderr: [],
91
+ loglevel,
92
+
93
+ async mock() {
94
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
95
+ helpers.reset();
96
+
97
+ if (process.env.__PERCY_BROWSERIFIED__) {
98
+ spy(Logger.prototype, 'write', function (lvl, msg) {
99
+ let stdio = lvl === 'info' ? 'stdout' : 'stderr';
100
+ helpers[stdio].push(sanitizeLog(msg, options));
101
+ return this.write.originalValue.call(this, lvl, msg);
102
+ });
103
+ spy(console, 'log');
104
+ spy(console, 'warn');
105
+ spy(console, 'error');
106
+ } else {
107
+ let {
108
+ Writable
109
+ } = await import('stream');
110
+
111
+ for (let stdio of ['stdout', 'stderr']) {
112
+ Logger[stdio] = Object.assign(new Writable(), {
113
+ columns: options.isTTY ? 100 : null,
114
+ isTTY: options.isTTY,
115
+
116
+ cursorTo() {},
117
+
118
+ clearLine() {},
119
+
120
+ _write(chunk, encoding, callback) {
121
+ helpers[stdio].push(sanitizeLog(chunk.toString(), options));
122
+ callback();
123
+ }
124
+
125
+ });
126
+ }
127
+ }
128
+ },
129
+
130
+ reset(soft) {
131
+ if (soft) loglevel('info');else delete Logger.instance;
132
+ helpers.stdout.length = 0;
133
+ helpers.stderr.length = 0;
134
+
135
+ if (console.log.reset) {
136
+ console.log.reset();
137
+ console.warn.reset();
138
+ console.error.reset();
139
+ }
140
+ },
141
+
142
+ dump() {
143
+ let msgs = Array.from(Logger.instance && Logger.instance.messages || []);
144
+ if (!msgs.length) return;
145
+
146
+ let log = m => process.env.__PERCY_BROWSERIFIED__ ? console.log.and ? console.log.and.originalFn(m) : console.log(m) : process.stderr.write(`${m}\n`);
147
+
148
+ logger__default["default"].loglevel('debug');
149
+ log(logger__default["default"].format('testing', 'warn', '--- DUMPING LOGS ---'));
150
+ msgs.reduce((last, _ref) => {
151
+ let {
152
+ debug,
153
+ level,
154
+ message,
155
+ timestamp
156
+ } = _ref;
157
+ log(logger__default["default"].format(debug, level, message, timestamp - last));
158
+ return timestamp;
159
+ }, msgs[0].timestamp);
160
+ }
161
+
162
+ };
163
+
164
+ return helpers;
165
+
166
+ })(PercyLogger);
167
+ }).call(window);
168
+
169
+ if (typeof define === "function" && define.amd) {
170
+ define([], () => window.PercyLogger.TestHelpers);
171
+ } else if (typeof module === "object" && module.exports) {
172
+ module.exports = window.PercyLogger.TestHelpers;
173
+ }
@@ -0,0 +1,111 @@
1
+ import logger from '@percy/logger';
2
+ import { ANSI_REG } from '@percy/logger/utils';
3
+
4
+ const ELAPSED_REG = /\s\S*?\(\d+ms\)\S*/;
5
+ const NEWLINE_REG = /\r\n/g;
6
+ const LASTLINE_REG = /\n$/;
7
+
8
+ function sanitizeLog(str, { ansi, elapsed } = {}) {
9
+ // normalize line endings
10
+ str = str.replace(NEWLINE_REG, '\n');
11
+ // strip ansi colors
12
+ if (!ansi) str = str.replace(ANSI_REG, '');
13
+ // strip elapsed time
14
+ if (!elapsed) str = str.replace(ELAPSED_REG, '');
15
+ // strip trailing line endings
16
+ return str.replace(LASTLINE_REG, '');
17
+ }
18
+
19
+ function spy(object, method, func) {
20
+ if (object[method].restore) object[method].restore();
21
+
22
+ let spy = Object.assign(function spy(...args) {
23
+ spy.calls.push(args);
24
+ if (func) return func.apply(this, args);
25
+ }, {
26
+ restore: () => (object[method] = spy.originalValue),
27
+ reset: () => (spy.calls.length = 0) || spy,
28
+ originalValue: object[method],
29
+ calls: []
30
+ });
31
+
32
+ object[method] = spy;
33
+ return spy;
34
+ }
35
+
36
+ const {
37
+ Logger,
38
+ loglevel
39
+ } = logger;
40
+
41
+ const helpers = {
42
+ stdout: [],
43
+ stderr: [],
44
+ loglevel,
45
+
46
+ async mock(options = {}) {
47
+ helpers.reset();
48
+
49
+ if (process.env.__PERCY_BROWSERIFIED__) {
50
+ spy(Logger.prototype, 'write', function(lvl, msg) {
51
+ let stdio = lvl === 'info' ? 'stdout' : 'stderr';
52
+ helpers[stdio].push(sanitizeLog(msg, options));
53
+ return this.write.originalValue.call(this, lvl, msg);
54
+ });
55
+
56
+ spy(console, 'log');
57
+ spy(console, 'warn');
58
+ spy(console, 'error');
59
+ } else {
60
+ let { Writable } = await import('stream');
61
+
62
+ for (let stdio of ['stdout', 'stderr']) {
63
+ Logger[stdio] = Object.assign(new Writable(), {
64
+ columns: options.isTTY ? 100 : null,
65
+ isTTY: options.isTTY,
66
+ cursorTo() {},
67
+ clearLine() {},
68
+
69
+ _write(chunk, encoding, callback) {
70
+ helpers[stdio].push(sanitizeLog(chunk.toString(), options));
71
+ callback();
72
+ }
73
+ });
74
+ }
75
+ }
76
+ },
77
+
78
+ reset(soft) {
79
+ if (soft) loglevel('info');
80
+ else delete Logger.instance;
81
+
82
+ helpers.stdout.length = 0;
83
+ helpers.stderr.length = 0;
84
+
85
+ if (console.log.reset) {
86
+ console.log.reset();
87
+ console.warn.reset();
88
+ console.error.reset();
89
+ }
90
+ },
91
+
92
+ dump() {
93
+ let msgs = Array.from((Logger.instance && Logger.instance.messages) || []);
94
+ if (!msgs.length) return;
95
+
96
+ let log = m => process.env.__PERCY_BROWSERIFIED__ ? (
97
+ console.log.and ? console.log.and.originalFn(m) : console.log(m)
98
+ ) : process.stderr.write(`${m}\n`);
99
+
100
+ logger.loglevel('debug');
101
+ log(logger.format('testing', 'warn', '--- DUMPING LOGS ---'));
102
+
103
+ msgs.reduce((last, { debug, level, message, timestamp }) => {
104
+ log(logger.format(debug, level, message, timestamp - last));
105
+ return timestamp;
106
+ }, msgs[0].timestamp);
107
+ }
108
+ };
109
+
110
+ export { helpers as logger };
111
+ export default helpers;