@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/index.js DELETED
@@ -1,99 +0,0 @@
1
- const os = require('os');
2
- const path = require('path');
3
- const { createLogger, transports, format } = require('winston');
4
- const colors = require('colors/safe');
5
-
6
- // A very basic url regexp only used for highlighting URLs blue in the CLI.
7
- const URL_REGEXP = /\b(https?:\/\/)[\w-]+(\.[\w-]+)*(:\d+)?(\/\S*)?\b/gi;
8
- const TEMP_DIR = path.join(os.tmpdir(), 'percy');
9
-
10
- // Custom logging formatter to color log levels, insert a megenta label, and
11
- // highlight URLs logged to the transport
12
- function formatter({ label, level, message }) {
13
- label = colors.magenta(label);
14
-
15
- if (level === 'error') {
16
- message = colors.red(message);
17
- } else if (level === 'warn') {
18
- message = colors.yellow(message);
19
- } else if (level === 'info') {
20
- // highlight urls blue
21
- message = message.replace(URL_REGEXP, colors.blue('$&'));
22
- }
23
-
24
- return `[${label}] ${message}`;
25
- }
26
-
27
- // Global logger
28
- const logger = createLogger({
29
- transports: [
30
- // console transport logs errors by default
31
- new transports.Console({
32
- level: 'error',
33
- stderrLevels: ['error'],
34
- format: format.combine(
35
- format.label({ label: 'percy' }),
36
- format.printf(formatter)
37
- )
38
- }),
39
- // file transport logs everything
40
- new transports.File({
41
- level: 'debug',
42
- filename: path.join(TEMP_DIR, `percy.${Date.now()}.log`),
43
- format: format.combine(
44
- format.timestamp(),
45
- format.json()
46
- )
47
- })
48
- ]
49
- });
50
-
51
- // Attach the formatter so external logs can utilize it
52
- logger.formatter = message => formatter(
53
- typeof message === 'string' ? { label: 'percy', message } : message
54
- );
55
-
56
- // Method to change the global logger's console log level. The second argument
57
- // can be used to set the appropriate level based on boolean flags and fallback
58
- // to the provided log level.
59
- logger.loglevel = function loglevel(level, flags = {}) {
60
- if (!level) return this.transports[0].level;
61
-
62
- if (flags.verbose) level = 'debug';
63
- else if (flags.quiet) level = 'warn';
64
- else if (flags.silent) level = 'silent';
65
-
66
- this.transports[0].level = level;
67
- };
68
-
69
- // Patch the error method to handle error objects. Winston accepts objects with
70
- // messages and meta as an argument but will fail to log real error instances.
71
- logger.error = function(message) {
72
- // libraries may also throw errors which are not technically Error instances
73
- if (typeof message === 'object') {
74
- // get the stack trace for debug (no ternary to always fallback to string)
75
- message = (this.loglevel() === 'debug' && message.stack) || message.toString();
76
- }
77
-
78
- // return super.error(message)
79
- return this.constructor.prototype.error.call(this, message);
80
- };
81
-
82
- // Patch the query method to return a promise, query the file transport only,
83
- // and allow filtering logs with a filter function.
84
- logger.query = function(options = {}) {
85
- return new Promise((resolve, reject) => {
86
- // the query method mutates options to normalize and set defaults, so the
87
- // same is done here to set more desirable defaults
88
- options.limit = options.limit || Infinity;
89
- options.order = options.order || 'asc';
90
-
91
- this.transports[1].query(options, (err, logs) => {
92
- if (err) return reject(err);
93
- if (options.filter) logs = logs.filter(options.filter);
94
- resolve(logs);
95
- });
96
- });
97
- };
98
-
99
- module.exports = logger;
package/test/.eslintrc DELETED
@@ -1,2 +0,0 @@
1
- env:
2
- mocha: true
package/test/helper.js DELETED
@@ -1,52 +0,0 @@
1
- import stripAnsi from 'strip-ansi';
2
-
3
- const og = {
4
- out: process.stdout.write,
5
- err: process.stderr.write
6
- };
7
-
8
- function format(chunk, { ansi = false } = {}) {
9
- // strip ansi and normalize line endings
10
- return (ansi ? chunk : stripAnsi(chunk)).replace('\r\n', '\n');
11
- }
12
-
13
- function tryFinally(fn, cb) {
14
- let done = (r, e) => {
15
- if ((cb(), e)) throw e;
16
- return r;
17
- };
18
-
19
- let r, e;
20
- try { r = fn(); } catch (err) { e = err; }
21
-
22
- if (typeof r?.then === 'function') {
23
- return r.then(done, e => done(null, e));
24
- } else {
25
- return done(r, e);
26
- }
27
- }
28
-
29
- const stdio = {
30
- 1: [],
31
- 2: [],
32
-
33
- capture(fn, options) {
34
- stdio.flush();
35
- process.stdout.write = chunk => stdio[1].push(format(chunk, options));
36
- process.stderr.write = chunk => stdio[2].push(format(chunk, options));
37
- return fn ? tryFinally(fn, stdio.restore) : null;
38
- },
39
-
40
- restore() {
41
- process.stdout.write = og.out;
42
- process.stderr.write = og.err;
43
- },
44
-
45
- flush() {
46
- let output = [null, stdio[1], stdio[2]];
47
- stdio[1] = []; stdio[2] = [];
48
- return output;
49
- }
50
- };
51
-
52
- export default stdio;
@@ -1,134 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import expect from 'expect';
4
- import colors from 'colors/safe';
5
- import stdio from './helper';
6
- import log from '..';
7
-
8
- describe('logger', () => {
9
- const label = colors.magenta('percy');
10
-
11
- beforeEach(() => {
12
- let { dirname, filename } = log.transports[1];
13
- let logfile = path.join(dirname, filename);
14
- fs.writeFileSync(logfile, '');
15
- log.loglevel('debug');
16
- });
17
-
18
- afterEach(() => {
19
- log.loglevel('error');
20
- });
21
-
22
- it('formats the message with a percy label', () => {
23
- stdio.capture(() => log.debug('test'), { ansi: true });
24
- expect(stdio[1]).toEqual([`[${label}] test\n`]);
25
- });
26
-
27
- it('formats errors red', () => {
28
- stdio.capture(() => log.error('error'), { ansi: true });
29
- expect(stdio[2]).toEqual([`[${label}] ${colors.red('error')}\n`]);
30
- });
31
-
32
- it('formats warnings yellow', () => {
33
- stdio.capture(() => log.warn('warning'), { ansi: true });
34
- expect(stdio[1]).toEqual([`[${label}] ${colors.yellow('warning')}\n`]);
35
- });
36
-
37
- it('formats info URLs blue', () => {
38
- let url = 'https://localhost:3000/foobar/baz.png';
39
- stdio.capture(() => log.info(`url = ${url}`), { ansi: true });
40
- expect(stdio[1]).toEqual([`[${label}] url = ${colors.blue(url)}\n`]);
41
- });
42
-
43
- describe('#formatter()', () => {
44
- it('returns a string formatted as a percy log', () => {
45
- expect(log.formatter('testing')).toEqual(`[${label}] testing`);
46
- });
47
-
48
- it('accepts a logger meta object', () => {
49
- expect(log.formatter({ label: 'test', message: 'foobar' }))
50
- .toEqual(`[${colors.magenta('test')}] foobar`);
51
- });
52
- });
53
-
54
- describe('#loglevel()', () => {
55
- it('sets the first transport log level', () => {
56
- expect(log.transports[0].level).toBe('debug');
57
- log.loglevel('info');
58
- expect(log.transports[0].level).toBe('info');
59
- });
60
-
61
- it('returns the first transport log level without args', () => {
62
- expect(log.loglevel()).toBe(log.transports[0].level);
63
- });
64
-
65
- it('sets the log level to debug with a verbose flag', () => {
66
- log.loglevel('info', { verbose: true });
67
- expect(log.loglevel()).toBe('debug');
68
- });
69
-
70
- it('sets the log level to warn with a quiet flag', () => {
71
- log.loglevel('info', { quiet: true });
72
- expect(log.loglevel()).toBe('warn');
73
- });
74
-
75
- it('sets the log level to silent with a silent flag', () => {
76
- log.loglevel('info', { silent: true });
77
- expect(log.loglevel()).toBe('silent');
78
- });
79
- });
80
-
81
- describe('#error()', () => {
82
- it('is patched to log error instance strings', () => {
83
- log.loglevel('error');
84
- stdio.capture(() => log.error(new Error('message')));
85
- expect(stdio[2]).toEqual(['[percy] Error: message\n']);
86
- });
87
-
88
- it('logs the error instance stack trace in debug', () => {
89
- let err = new Error('message');
90
- stdio.capture(() => log.error(err));
91
- expect(stdio[2]).toEqual([`[percy] ${err.stack}\n`]);
92
- });
93
-
94
- it('falls back if there is no error instance stack in debug', () => {
95
- let err = { toString: () => 'error' };
96
- stdio.capture(() => log.error(err));
97
- expect(stdio[2]).toEqual(['[percy] error\n']);
98
- });
99
- });
100
-
101
- describe('#query()', () => {
102
- beforeEach(() => {
103
- stdio.capture(() => {
104
- log.info('foo', { foo: true });
105
- log.info('bar', { bar: true });
106
- });
107
- });
108
-
109
- it('is promisified', async () => {
110
- await expect(log.query()).resolves.toEqual([
111
- expect.objectContaining({ message: 'foo', foo: true }),
112
- expect.objectContaining({ message: 'bar', bar: true })
113
- ]);
114
-
115
- await expect(log.query({
116
- get level() { throw new Error('test'); }
117
- })).rejects.toThrow('test');
118
- });
119
-
120
- it('can filter logs', async () => {
121
- await expect(log.query({
122
- filter: log => log.foo
123
- })).resolves.toEqual([
124
- expect.objectContaining({ message: 'foo', foo: true })
125
- ]);
126
-
127
- await expect(log.query({
128
- filter: log => log.bar
129
- })).resolves.toEqual([
130
- expect.objectContaining({ message: 'bar', bar: true })
131
- ]);
132
- });
133
- });
134
- });