@percy/logger 1.0.0-beta.7 → 1.0.0-beta.73

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/README.md CHANGED
@@ -1,58 +1,93 @@
1
1
  # @percy/logger
2
2
 
3
- Common [winston](https://github.com/winstonjs/winston) logger used throughout the Percy CLI.
3
+ Common logger used throughout the Percy CLI and SDKs.
4
+
5
+ - [Usage](#usage)
6
+ - [`logger()`](#loggerdebug)
7
+ - [`logger.loglevel()`](#loggerloglevel)
8
+ - [`logger.format()`](#loggerformat)
9
+ - [`logger.query()`](#loggerquery)
4
10
 
5
11
  ## Usage
6
12
 
7
13
  ``` js
8
- import log from '@percy/logger'
14
+ import logger from '@percy/logger'
15
+
16
+ const log = logger('foobar')
9
17
 
10
18
  log.info('info message')
11
19
  log.error('error message')
12
20
  log.warn('warning message')
13
21
  log.debug('debug message')
22
+ log.deprecated('deprecation message')
23
+ ```
24
+
25
+ ### `logger([debug])`
26
+
27
+ Creates a group of logging functions that will be associated with the provided `debug` label. When
28
+ debug logging is enabled, this label is printed with the `[percy:*]` label and can be filtered via
29
+ the `PERCY_DEBUG` environment variable.
30
+
31
+ ``` js
32
+ PERCY_DEBUG="one:*,*:a,-*:b"
33
+
34
+ logger.loglevel('debug')
35
+
36
+ logger('one').debug('test')
37
+ logger('one:a').debug('test')
38
+ logger('one:b').debug('test')
39
+ logger('one:c').debug('test')
40
+ logger('two').debug('test')
41
+ logger('two:a').debug('test')
42
+
43
+ // only logs from the matching debug string are printed
44
+ //=> [percy:one] test
45
+ //=> [percy:one:a] test
46
+ //=> [percy:one:c] test
47
+ //=> [percy:two:a] test
14
48
  ```
15
49
 
16
- ### `#loglevel([level][, flags])`
50
+ ### `logger.loglevel([level][, flags])`
17
51
 
18
- Sets or retrieves the log level of the console transport. If the second argument is provided,
19
- `level` is treated as a fallback when all logging flags are `false`. When no arguments are provided,
20
- the method will return the current log level of the console transport.
52
+ Sets or retrieves the log level of the shared logger. If the second argument is provided, `level` is
53
+ treated as a fallback when all logging flags are `false`. When no arguments are provided, the method
54
+ will return the current log level of the shared logger.
21
55
 
22
56
  ``` js
23
- log.loglevel('info', { verbose: true })
24
- log.loglevel() === 'debug'
57
+ logger.loglevel('info', { verbose: true })
58
+ logger.loglevel() === 'debug'
25
59
 
26
- log.loglevel('info', { quiet: true })
27
- log.loglevel() === 'warn'
60
+ logger.loglevel('info', { quiet: true })
61
+ logger.loglevel() === 'warn'
28
62
 
29
- log.loglevel('info', { silent: true })
30
- log.loglevel() === 'silent'
63
+ logger.loglevel('info', { silent: true })
64
+ logget.loglevel() === 'silent'
31
65
 
32
- log.loglevel('info')
33
- log.loglevel() === 'info'
66
+ logger.loglevel('info')
67
+ logger.loglevel() === 'info'
34
68
  ```
35
69
 
36
- ### `#error(errorOrMessage)`
70
+ ### `logger.format(message, debug[, level])`
37
71
 
38
- Patched `#error()` method that handles `Error` instance's and similar error objects. When
39
- `#loglevel()` is equal to `debug`, the `Error` instance's stack trace is logged.
72
+ Returns a formatted `message` depending on the provided level and logger's own log level. When
73
+ debugging, the `debug` label is added to the prepended `[percy:*]` label.
40
74
 
41
75
  ``` js
42
- log.loglevel('debug')
43
- log.error(new Error('example'))
44
- // [percy] Error: example
45
- // at example:2:10
46
- // ...
76
+ logger.format('foobar', 'test')
77
+ //=> [percy] foobar
78
+
79
+ logger.loglevel('debug')
80
+ logger.format('foobar', 'test', warn')
81
+ //=> [percy:test] foobar (yellow for warnings)
47
82
  ```
48
83
 
49
- ### `#query(options)`
84
+ ### `logger.query(filter)`
50
85
 
51
- Patched `#query()` method that is promisified and allows a `filter` function option.
86
+ Returns an array of logs matching the provided filter function.
52
87
 
53
88
  ``` js
54
- let logs = await log.query({
55
- filter: log => true
56
- // ...other query options (see winston docs)
89
+ let logs = logger.query(log => {
90
+ return log.level === 'debug' &&
91
+ log.message.match(/foobar/)
57
92
  })
58
93
  ```
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.PercyBrowserLogger = void 0;
7
+
8
+ var _util = require("./util");
9
+
10
+ var _logger = _interopRequireDefault(require("./logger"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ class PercyBrowserLogger extends _logger.default {
15
+ write(level, message) {
16
+ let out = ['warn', 'error'].includes(level) ? level : 'log';
17
+ let colors = [];
18
+ message = message.replace(_util.ANSI_REG, (_, ansi) => {
19
+ colors.push(`color:${_util.ANSI_COLORS[ansi] || 'inherit'}`);
20
+ return '%c';
21
+ });
22
+ console[out](message, ...colors);
23
+ }
24
+
25
+ progress() {
26
+ console.error('The log.progress() method is not supported in browsers');
27
+ }
28
+
29
+ }
30
+
31
+ exports.PercyBrowserLogger = PercyBrowserLogger;
32
+ var _default = PercyBrowserLogger;
33
+ exports.default = _default;
package/dist/bundle.js ADDED
@@ -0,0 +1,481 @@
1
+ (function() {
2
+ (function (exports) {
3
+ 'use strict';
4
+
5
+ const process = (typeof globalThis !== "undefined" && globalThis.process) || {};
6
+ process.env = process.env || {};
7
+ process.env.__PERCY_BROWSERIFIED__ = true;
8
+
9
+ function getAugmentedNamespace(n) {
10
+ if (n.__esModule) return n;
11
+ var a = Object.defineProperty({}, '__esModule', {value: true});
12
+ Object.keys(n).forEach(function (k) {
13
+ var d = Object.getOwnPropertyDescriptor(n, k);
14
+ Object.defineProperty(a, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () {
17
+ return n[k];
18
+ }
19
+ });
20
+ });
21
+ return a;
22
+ }
23
+
24
+ const {
25
+ assign,
26
+ entries
27
+ } = Object; // matches ansi escape sequences
28
+
29
+ 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
30
+
31
+ const ANSI_COLORS = {
32
+ '91m': 'red',
33
+ '32m': 'green',
34
+ '93m': 'yellow',
35
+ '34m': 'blue',
36
+ '95m': 'magenta',
37
+ '90m': 'grey'
38
+ }; // colorize each line of a string using an ansi escape sequence
39
+
40
+ const LINE_REG = /^.*$/gm;
41
+
42
+ function colorize(code, str) {
43
+ return str.replace(LINE_REG, line => `\u001b[${code}${line}\u001b[39m`);
44
+ } // map ansi colors to bound colorize functions
45
+
46
+
47
+ const colors = entries(ANSI_COLORS).reduce((colors, _ref) => {
48
+ let [code, name] = _ref;
49
+ return assign(colors, {
50
+ [name]: colorize.bind(null, code)
51
+ });
52
+ }, {});
53
+
54
+ function _defineProperty(obj, key, value) {
55
+ if (key in obj) {
56
+ Object.defineProperty(obj, key, {
57
+ value: value,
58
+ enumerable: true,
59
+ configurable: true,
60
+ writable: true
61
+ });
62
+ } else {
63
+ obj[key] = value;
64
+ }
65
+
66
+ return obj;
67
+ }
68
+
69
+ const URL_REGEXP = /\bhttps?:\/\/[^\s/$.?#].[^\s]*\b/i;
70
+ const LOG_LEVELS = {
71
+ debug: 0,
72
+ info: 1,
73
+ warn: 2,
74
+ error: 3
75
+ }; // A PercyLogger instance retains logs in-memory for quick lookups while also writing log
76
+ // messages to stdout and stderr depending on the log level and debug string.
77
+
78
+ class PercyLogger {
79
+ // default log level
80
+ // namespace regular expressions used to determine which debug logs to write
81
+ // in-memory store for logs and meta info
82
+ // track deprecations to limit noisy logging
83
+ // static vars can be overriden for testing
84
+ // Handles setting env var values and returns a singleton
85
+ constructor() {
86
+ _defineProperty(this, "level", 'info');
87
+
88
+ _defineProperty(this, "namespaces", {
89
+ include: [/^.*?$/],
90
+ exclude: []
91
+ });
92
+
93
+ _defineProperty(this, "messages", new Set());
94
+
95
+ _defineProperty(this, "deprecations", new Set());
96
+
97
+ let {
98
+ instance = this
99
+ } = this.constructor;
100
+
101
+ if (process.env.PERCY_DEBUG) {
102
+ instance.debug(process.env.PERCY_DEBUG);
103
+ } else if (process.env.PERCY_LOGLEVEL) {
104
+ instance.loglevel(process.env.PERCY_LOGLEVEL);
105
+ }
106
+
107
+ this.constructor.instance = instance;
108
+ return instance;
109
+ } // Change log level at any time or return the current log level
110
+
111
+
112
+ loglevel(level) {
113
+ if (level) this.level = level;
114
+ return this.level;
115
+ } // Change namespaces by generating an array of namespace regular expressions from a
116
+ // comma separated debug string
117
+
118
+
119
+ debug(namespaces) {
120
+ if (this.namespaces.string === namespaces) return;
121
+ this.namespaces.string = namespaces;
122
+ namespaces = namespaces.split(/[\s,]+/).filter(Boolean);
123
+ if (!namespaces.length) return this.namespaces;
124
+ this.loglevel('debug');
125
+ this.namespaces = namespaces.reduce((namespaces, ns) => {
126
+ ns = ns.replace(/:?\*/g, m => m[0] === ':' ? ':?.*?' : '.*?');
127
+
128
+ if (ns[0] === '-') {
129
+ namespaces.exclude.push(new RegExp('^' + ns.substr(1) + '$'));
130
+ } else {
131
+ namespaces.include.push(new RegExp('^' + ns + '$'));
132
+ }
133
+
134
+ return namespaces;
135
+ }, {
136
+ string: namespaces,
137
+ include: [],
138
+ exclude: []
139
+ });
140
+ } // Creates a new log group and returns level specific functions for logging
141
+
142
+
143
+ group(name) {
144
+ return Object.keys(LOG_LEVELS).reduce((group, level) => Object.assign(group, {
145
+ [level]: this.log.bind(this, name, level)
146
+ }), {
147
+ deprecated: this.deprecated.bind(this, name),
148
+ shouldLog: this.shouldLog.bind(this, name),
149
+ progress: this.progress.bind(this, name),
150
+ format: this.format.bind(this, name),
151
+ loglevel: this.loglevel.bind(this),
152
+ stdout: this.constructor.stdout,
153
+ stderr: this.constructor.stderr
154
+ });
155
+ } // Query for a set of logs by filtering the in-memory store
156
+
157
+
158
+ query(filter) {
159
+ return Array.from(this.messages).filter(filter);
160
+ } // Formats messages before they are logged to stdio
161
+
162
+
163
+ format(debug, level, message, elapsed) {
164
+ let label = 'percy';
165
+ let suffix = '';
166
+
167
+ if (arguments.length === 1) {
168
+ // format(message)
169
+ [debug, message] = [null, debug];
170
+ } else if (arguments.length === 2) {
171
+ // format(debug, message)
172
+ [level, message] = [null, level];
173
+ }
174
+
175
+ if (this.level === 'debug') {
176
+ // include debug info in the label
177
+ if (debug) label += `:${debug}`; // include elapsed time since last log
178
+
179
+ if (elapsed != null) {
180
+ suffix = ' ' + colors.grey(`(${elapsed}ms)`);
181
+ }
182
+ }
183
+
184
+ label = colors.magenta(label);
185
+
186
+ if (level === 'error') {
187
+ // red errors
188
+ message = colors.red(message);
189
+ } else if (level === 'warn') {
190
+ // yellow warnings
191
+ message = colors.yellow(message);
192
+ } else if (level === 'info' || level === 'debug') {
193
+ // blue info and debug URLs
194
+ message = message.replace(URL_REGEXP, colors.blue('$&'));
195
+ }
196
+
197
+ return `[${label}] ${message}${suffix}`;
198
+ } // Replaces the current line with a log message
199
+
200
+
201
+ progress(debug, message, persist) {
202
+ if (!this.shouldLog(debug, 'info')) return;
203
+ let {
204
+ stdout
205
+ } = this.constructor;
206
+
207
+ if (stdout.isTTY || !this._progress) {
208
+ message && (message = this.format(debug, message));
209
+ if (stdout.isTTY) stdout.cursorTo(0);else message && (message = message + '\n');
210
+ if (message) stdout.write(message);
211
+ if (stdout.isTTY) stdout.clearLine(1);
212
+ }
213
+
214
+ this._progress = !!message && {
215
+ message,
216
+ persist
217
+ };
218
+ } // Returns true or false if the level and debug group can write messages to stdio
219
+
220
+
221
+ shouldLog(debug, level) {
222
+ 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));
223
+ } // Ensures that deprecation messages are not logged more than once
224
+
225
+
226
+ deprecated(debug, message, meta) {
227
+ if (this.deprecations.has(message)) return;
228
+ this.deprecations.add(message);
229
+ this.log(debug, 'warn', `Warning: ${message}`, meta);
230
+ } // Returns true if a socket is present and ready
231
+
232
+
233
+ get isRemote() {
234
+ var _this$socket;
235
+
236
+ return ((_this$socket = this.socket) === null || _this$socket === void 0 ? void 0 : _this$socket.readyState) === 1;
237
+ } // Generic log method accepts a debug group, log level, log message, and optional meta
238
+ // information to store with the message and other info
239
+
240
+
241
+ log(debug, level, message) {
242
+ let meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
243
+ // message might be an error object
244
+ let isError = typeof message !== 'string' && (level === 'error' || level === 'debug');
245
+ let error = isError && message; // if remote, send logs there
246
+
247
+ if (this.isRemote) {
248
+ // serialize error messages
249
+ message = isError && 'stack' in error ? {
250
+ message: error.message,
251
+ stack: error.stack
252
+ } : message;
253
+ return this.socket.send(JSON.stringify({
254
+ log: [debug, level, message, {
255
+ remote: true,
256
+ ...meta
257
+ }]
258
+ }));
259
+ } // ensure the message is a string
260
+
261
+
262
+ message = isError && message.stack || message.message || message.toString(); // timestamp each log
263
+
264
+ let timestamp = Date.now();
265
+ let entry = {
266
+ debug,
267
+ level,
268
+ message,
269
+ meta,
270
+ timestamp
271
+ };
272
+ this.messages.add(entry); // maybe write the message to stdio
273
+
274
+ if (this.shouldLog(debug, level)) {
275
+ let elapsed = timestamp - (this.lastlog || timestamp);
276
+ if (isError && this.level !== 'debug') message = error.toString();
277
+ this.write(level, this.format(debug, error ? 'error' : level, message, elapsed));
278
+ this.lastlog = timestamp;
279
+ }
280
+ } // Writes a message to stdio based on the loglevel
281
+
282
+
283
+ write(level, message) {
284
+ var _this$_progress;
285
+
286
+ let {
287
+ stdout,
288
+ stderr
289
+ } = this.constructor;
290
+ let progress = stdout.isTTY && this._progress;
291
+
292
+ if (progress) {
293
+ stdout.cursorTo(0);
294
+ stdout.clearLine();
295
+ }
296
+
297
+ (level === 'info' ? stdout : stderr).write(message + '\n');
298
+ if (!((_this$_progress = this._progress) !== null && _this$_progress !== void 0 && _this$_progress.persist)) delete this._progress;else if (progress) stdout.write(progress.message);
299
+ } // Opens a socket logging connection
300
+
301
+
302
+ connect(socket) {
303
+ // send logging environment info
304
+ let PERCY_DEBUG = process.env.PERCY_DEBUG;
305
+ let PERCY_LOGLEVEL = process.env.PERCY_LOGLEVEL || this.loglevel();
306
+ socket.send(JSON.stringify({
307
+ env: {
308
+ PERCY_DEBUG,
309
+ PERCY_LOGLEVEL
310
+ }
311
+ })); // attach remote logging handler
312
+
313
+ socket.onmessage = _ref => {
314
+ let {
315
+ data
316
+ } = _ref;
317
+ let {
318
+ log,
319
+ logAll
320
+ } = JSON.parse(data);
321
+ if (logAll) logAll.forEach(e => this.messages.add(e));
322
+ if (log) this.log(...log);
323
+ }; // return a cleanup function
324
+
325
+
326
+ return () => {
327
+ socket.onmessage = null;
328
+ };
329
+ } // Connects to a remote logger
330
+
331
+
332
+ async remote(createSocket) {
333
+ let timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
334
+ if (this.isRemote) return; // if not already connected, wait until the timeout
335
+
336
+ let err = await new Promise(resolve => {
337
+ let done = event => {
338
+ if (timeoutid == null) return;
339
+ timeoutid = clearTimeout(timeoutid);
340
+ if (this.socket) this.socket.onopen = this.socket.onerror = null;
341
+ resolve((event === null || event === void 0 ? void 0 : event.error) || (event === null || event === void 0 ? void 0 : event.type) === 'error' && 'Error: Socket connection failed');
342
+ };
343
+
344
+ let timeoutid = setTimeout(done, timeout, {
345
+ error: 'Error: Socket connection timed out'
346
+ });
347
+ Promise.resolve().then(async () => {
348
+ this.socket = await createSocket();
349
+ if (this.isRemote) return done();
350
+ this.socket.onopen = this.socket.onerror = done;
351
+ }).catch(error => done({
352
+ error
353
+ }));
354
+ }); // there was an error connecting, will fallback to normal logging
355
+
356
+ if (err) {
357
+ this.log('logger', 'debug', 'Unable to connect to remote logger');
358
+ this.log('logger', 'debug', err);
359
+ return;
360
+ } // send any messages already logged in this environment
361
+
362
+
363
+ if (this.messages.size) {
364
+ this.socket.send(JSON.stringify({
365
+ logAll: Array.from(this.messages).map(entry => ({ ...entry,
366
+ meta: {
367
+ remote: true,
368
+ ...entry.meta
369
+ }
370
+ }))
371
+ }));
372
+ } // attach an incoming message handler
373
+
374
+
375
+ this.socket.onmessage = _ref2 => {
376
+ let {
377
+ data
378
+ } = _ref2;
379
+ let {
380
+ env
381
+ } = JSON.parse(data); // update local environment info
382
+
383
+ if (env) Object.assign(process.env, env);
384
+ }; // return a cleanup function
385
+
386
+
387
+ return () => {
388
+ this.socket.onmessage = null;
389
+ this.socket = null;
390
+ };
391
+ }
392
+
393
+ }
394
+
395
+ _defineProperty(PercyLogger, "stdout", process.stdout);
396
+
397
+ _defineProperty(PercyLogger, "stderr", process.stderr);
398
+
399
+ var logger$1 = /*#__PURE__*/Object.freeze({
400
+ __proto__: null,
401
+ PercyLogger: PercyLogger,
402
+ 'default': PercyLogger
403
+ });
404
+
405
+ class PercyBrowserLogger extends PercyLogger {
406
+ write(level, message) {
407
+ let out = ['warn', 'error'].includes(level) ? level : 'log';
408
+ let colors = [];
409
+ message = message.replace(ANSI_REG, (_, ansi) => {
410
+ colors.push(`color:${ANSI_COLORS[ansi] || 'inherit'}`);
411
+ return '%c';
412
+ });
413
+ console[out](message, ...colors);
414
+ }
415
+
416
+ progress() {
417
+ console.error('The log.progress() method is not supported in browsers');
418
+ }
419
+
420
+ }
421
+
422
+ var browser = /*#__PURE__*/Object.freeze({
423
+ __proto__: null,
424
+ PercyBrowserLogger: PercyBrowserLogger,
425
+ 'default': PercyBrowserLogger
426
+ });
427
+
428
+ var require$$0 = /*@__PURE__*/getAugmentedNamespace(browser);
429
+
430
+ var require$$1 = /*@__PURE__*/getAugmentedNamespace(logger$1);
431
+
432
+ const {
433
+ default: Logger
434
+ } = process.env.__PERCY_BROWSERIFIED__ ? require$$0 : require$$1;
435
+
436
+ function logger(name) {
437
+ return new Logger().group(name);
438
+ }
439
+
440
+ Object.assign(logger, {
441
+ format: function () {
442
+ return new Logger().format(...arguments);
443
+ },
444
+ query: function () {
445
+ return new Logger().query(...arguments);
446
+ },
447
+ connect: function () {
448
+ return new Logger().connect(...arguments);
449
+ },
450
+ remote: function () {
451
+ return new Logger().remote(...arguments);
452
+ },
453
+ loglevel: function () {
454
+ return new Logger().loglevel(...arguments);
455
+ }
456
+ });
457
+ Object.defineProperties(logger, {
458
+ Logger: {
459
+ get: () => Logger
460
+ },
461
+ stdout: {
462
+ get: () => Logger.stdout
463
+ },
464
+ stderr: {
465
+ get: () => Logger.stderr
466
+ }
467
+ });
468
+ var src = logger;
469
+
470
+ exports["default"] = src;
471
+
472
+ Object.defineProperty(exports, '__esModule', { value: true });
473
+
474
+ })(this.PercyLogger = this.PercyLogger || {});
475
+ }).call(window);
476
+
477
+ if (typeof define === "function" && define.amd) {
478
+ define([], () => window.PercyLogger);
479
+ } else if (typeof module === "object" && module.exports) {
480
+ module.exports = window.PercyLogger;
481
+ }
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ const {
4
+ default: Logger
5
+ } = process.env.__PERCY_BROWSERIFIED__ ? require('./browser') : require('./logger');
6
+
7
+ function logger(name) {
8
+ return new Logger().group(name);
9
+ }
10
+
11
+ Object.assign(logger, {
12
+ format: (...args) => new Logger().format(...args),
13
+ query: (...args) => new Logger().query(...args),
14
+ connect: (...args) => new Logger().connect(...args),
15
+ remote: (...args) => new Logger().remote(...args),
16
+ loglevel: (...args) => new Logger().loglevel(...args)
17
+ });
18
+ Object.defineProperties(logger, {
19
+ Logger: {
20
+ get: () => Logger
21
+ },
22
+ stdout: {
23
+ get: () => Logger.stdout
24
+ },
25
+ stderr: {
26
+ get: () => Logger.stderr
27
+ }
28
+ });
29
+ module.exports = logger;