Skip to content

Commit bef3ee3

Browse files
authored
feat!: Improve logging and add logLevel (#427)
BREAKING CHANGE: The `logging` configuration option has now been replaced with `logLevel`. This allows for more fine-grain control over what should be logged as well as silencing logs altogether.
1 parent 36fd9f8 commit bef3ee3

File tree

17 files changed

+128
-90
lines changed

17 files changed

+128
-90
lines changed

docs/configuration.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ polly.configure({
2323

2424
[config.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/packages/@pollyjs/core/src/defaults/config.js ':include :type=code')
2525

26-
## logging
26+
## logLevel
2727

28-
_Type_: `Boolean`
29-
_Default_: `false`
28+
_Type_: `'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'`
29+
_Default_: `'warn'`
3030

31-
Logs requests and their responses to the console grouped by the recording name.
31+
Set the log level for the polly instance.
3232

3333
**Example**
3434

3535
```js
3636
polly.configure({
37-
logging: true
37+
logLevel: 'info'
3838
});
3939
```
4040

@@ -110,8 +110,8 @@ polly.configure({
110110

111111
## expiryStrategy
112112

113-
_Type_: `String`
114-
_Default_: `warn`
113+
_Type_: `'warn' | 'error' | 'record'`
114+
_Default_: `'warn'`
115115

116116
The strategy for what should occur when Polly tries to use an expired recording in `replay` mode. Can be one of the following:
117117

@@ -432,7 +432,7 @@ a GUID for the request.
432432

433433
```js
434434
// Retrieve our model
435-
let model = await fetch('/models/1').then(res => res.json());
435+
let model = await fetch('/models/1').then((res) => res.json());
436436

437437
// Modify the model
438438
model.foo = 'bar';
@@ -441,7 +441,7 @@ a GUID for the request.
441441
await fetch('/models/1', { method: 'POST', body: JSON.stringify(model) });
442442

443443
// Get our updated model
444-
model = await fetch('/models/1').then(res => res.json());
444+
model = await fetch('/models/1').then((res) => res.json());
445445

446446
// Assert that our change persisted
447447
expect(model.foo).to.equal('bar');

docs/quick-start.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ import LocalStoragePersister from '@pollyjs/persister-local-storage';
108108
Polly.register(FetchAdapter);
109109
Polly.register(LocalStoragePersister);
110110

111-
describe('Simple Example', function() {
112-
it('fetches a post', async function() {
111+
describe('Simple Example', function () {
112+
it('fetches a post', async function () {
113113
/*
114114
Create a new polly instance.
115115
@@ -119,7 +119,7 @@ describe('Simple Example', function() {
119119
const polly = new Polly('Simple Example', {
120120
adapters: ['fetch'], // Hook into `fetch`
121121
persister: 'local-storage', // Read/write to/from local-storage
122-
logging: true // Log requests to console
122+
logLevel: 'info' // Log requests to console
123123
});
124124

125125
const response = await fetch(
@@ -253,8 +253,8 @@ Lets take a look at how we can modify our previous test case to test against a
253253
post that does not exist.
254254

255255
```js
256-
describe('Simple Client-Side Server Example', function() {
257-
it('fetches an unknown post', async function() {
256+
describe('Simple Client-Side Server Example', function () {
257+
it('fetches an unknown post', async function () {
258258
/*
259259
Create a new polly instance.
260260
@@ -264,7 +264,7 @@ describe('Simple Client-Side Server Example', function() {
264264
const polly = new Polly('Simple Client-Side Server Example', {
265265
adapters: ['fetch'], // Hook into `fetch`
266266
persister: 'local-storage', // Read/write to/from local-storage
267-
logging: true // Log requests to console
267+
logLevel: 'info' // Log requests to console
268268
});
269269
const { server } = polly;
270270

docs/server/request.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ The recording the request should be recorded under.
9191

9292
Get a header with a given name.
9393

94-
| Param | Type | Description |
95-
| ----------- | ---------------- | ---------------------- |
96-
| name | `String` | The name of the header |
97-
| **Returns** | `String | Array` | The header value |
94+
| Param | Type | Description |
95+
| ----------- | -------- | ---------------------- | ---------------- |
96+
| name | `String` | The name of the header |
97+
| **Returns** | `String | Array` | The header value |
9898

9999
**Example**
100100

@@ -107,11 +107,11 @@ req.getHeader('Content-Type'); // → application/json
107107
Set a header with a given name. If the value is `null` or `undefined`, the header will be
108108
removed.
109109

110-
| Param | Type | Description |
111-
| ----------- | ------------------------- | ------------------------ |
112-
| name | `String` | The name of the header |
113-
| value | `String | Array` | The value for the header |
114-
| **Returns** | [Request](server/request) | The current request |
110+
| Param | Type | Description |
111+
| ----------- | ------------------------- | ---------------------- | ------------------------ |
112+
| name | `String` | The name of the header |
113+
| value | `String | Array` | The value for the header |
114+
| **Returns** | [Request](server/request) | The current request |
115115

116116
**Example**
117117

@@ -276,5 +276,5 @@ req.configure({ recordFailedRequests: true });
276276

277277
req.configure({ timing: Timing.relative(3.0) });
278278

279-
req.configure({ logging: true });
279+
req.configure({ logLevel: 'info' });
280280
```

docs/server/route-handler.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ called in the order they were declared.
2727
```js
2828
server
2929
.get('/session')
30-
.on('request', req => {
30+
.on('request', (req) => {
3131
req.headers['X-AUTH'] = '<ACCESS_TOKEN>';
3232
req.query.email = 'test@app.com';
3333
})
@@ -60,7 +60,7 @@ called in the order they were declared.
6060
```js
6161
server
6262
.get('/session')
63-
.once('request', req => {
63+
.once('request', (req) => {
6464
req.headers['X-AUTH'] = '<ACCESS_TOKEN>';
6565
req.query.email = 'test@app.com';
6666
})
@@ -292,7 +292,7 @@ server.any('/session').configure({ recordFailedRequests: true });
292292

293293
server.get('/users/:id').configure({ timing: Timing.relative(3.0) });
294294

295-
server.get('/users/1').configure({ logging: true });
295+
server.get('/users/1').configure({ logLevel: 'info' });
296296
```
297297

298298
### recordingName

packages/@pollyjs/adapter-fetch/src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default class FetchAdapter extends Adapter {
3232
const { context } = this.options;
3333

3434
if (isNode) {
35-
console.warn(
35+
this.polly.logger.log.warn(
3636
'[Polly] [adapter:fetch] Using the fetch adapter in Node has been deprecated. Please use the node-http adapter instead.'
3737
);
3838
}

packages/@pollyjs/adapter/src/index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,21 @@ export default class Adapter {
4747
if (!this.isConnected) {
4848
this.onConnect();
4949
this.isConnected = true;
50+
51+
this.polly.logger.log.debug(
52+
`Connected to ${this.constructor.id} adapter.`
53+
);
5054
}
5155
}
5256

5357
disconnect() {
5458
if (this.isConnected) {
5559
this.onDisconnect();
5660
this.isConnected = false;
61+
62+
this.polly.logger.log.debug(
63+
`Disconnected from ${this.constructor.id} adapter.`
64+
);
5765
}
5866
}
5967

@@ -149,7 +157,7 @@ export default class Adapter {
149157
pollyRequest.action = ACTIONS.RECORD;
150158

151159
if ('navigator' in global && !navigator.onLine) {
152-
console.warn(
160+
pollyRequest.log.warn(
153161
'[Polly] Recording may fail because the browser is offline.\n' +
154162
`${stringifyRequest(pollyRequest)}`
155163
);
@@ -189,7 +197,7 @@ export default class Adapter {
189197
break;
190198
// log a warning and continue if expiryStrategy is "warn".
191199
case EXPIRY_STRATEGIES.WARN:
192-
console.warn(`[Polly] ${message}`);
200+
pollyRequest.log.warn(`[Polly] ${message}`);
193201
break;
194202
// throw an error if we encounter an unsupported expiryStrategy.
195203
default:

packages/@pollyjs/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"fast-json-stable-stringify": "^2.1.0",
5050
"is-absolute-url": "^3.0.3",
5151
"lodash-es": "^4.17.21",
52+
"loglevel": "^1.8.0",
5253
"route-recognizer": "^0.3.4",
5354
"slugify": "^1.6.3"
5455
},
Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ACTIONS } from '@pollyjs/utils';
2+
import logLevel from 'loglevel';
23

34
const FORMATTED_ACTIONS = {
45
[ACTIONS.RECORD]: 'Recorded',
@@ -10,72 +11,50 @@ const FORMATTED_ACTIONS = {
1011
export default class Logger {
1112
constructor(polly) {
1213
this.polly = polly;
13-
this.groupName = null;
14+
this.log = logLevel.getLogger(`@pollyjs/core:${this.polly.recordingName}`);
15+
16+
this.log.setLevel(polly.config.logLevel);
1417
}
1518

1619
connect() {
1720
this._middleware = this.polly.server
1821
.any()
19-
.on('error', (...args) => this.logError(...args))
20-
.on('response', (...args) => this.logRequest(...args));
22+
.on('error', (...args) => this.logRequestError(...args))
23+
.on('request', (...args) => this.logRequest(...args))
24+
.on('response', (...args) => this.logRequestResponse(...args));
2125
}
2226

2327
disconnect() {
24-
this.groupEnd();
2528
this._middleware.off('error');
2629
this._middleware.off('response');
2730
}
2831

29-
groupStart(groupName) {
30-
// If the provided groupName is different, end the current group so a new one
31-
// can be started.
32-
if (this.groupName && this.groupName !== groupName) {
33-
this.groupEnd();
34-
this.groupName = null;
35-
}
36-
37-
// Create a new console group for the provided groupName if one
38-
// doesn't already exist.
39-
if (!this.groupName) {
40-
this.groupName = groupName;
41-
console.group(this.groupName);
42-
}
43-
}
44-
45-
groupEnd() {
46-
if (this.groupName) {
47-
console.groupEnd();
48-
}
49-
}
50-
5132
logRequest(request) {
52-
if (request.config.logging) {
53-
this.groupStart(request.recordingName);
33+
const { log } = request;
5434

55-
console.groupCollapsed(
56-
`${FORMATTED_ACTIONS[request.action]}${request.method} ${
57-
request.url
58-
} ${request.response.statusCode}${request.responseTime}ms`
59-
);
60-
console.log('Request:', request);
61-
console.log('Response:', request.response);
62-
console.log('Identifiers:', request.identifiers);
63-
console.groupEnd();
64-
}
35+
log.debug(`Request: ${request.method} ${request.url}`, { request });
6536
}
6637

67-
logError(request, error) {
68-
this.groupStart(request.recordingName);
38+
logRequestResponse(request) {
39+
const { log } = request;
40+
const debug = log.getLevel() <= log.levels.DEBUG;
6941

70-
console.group(`Errored ➞ ${request.method} ${request.url}`);
71-
console.error(error);
72-
console.log('Request:', request);
42+
log.info(
43+
`Response: ${FORMATTED_ACTIONS[request.action]}${request.method} ${
44+
request.url
45+
} ${request.response.statusCode}${request.responseTime}ms`,
46+
...(debug ? [{ request, response: request.response }] : [])
47+
);
48+
}
7349

74-
if (request.didRespond) {
75-
console.log('Response:', request.response);
76-
}
50+
logRequestError(request, error) {
51+
const { log } = request;
52+
const debug = log.getLevel() <= log.levels.DEBUG;
7753

78-
console.log('Identifiers:', request.identifiers);
79-
console.groupEnd();
54+
log.error(
55+
`Errored ➞ ${request.method} ${request.url}`,
56+
error,
57+
...(debug ? [{ request }] : [])
58+
);
8059
}
8160
}

packages/@pollyjs/core/src/-private/request.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import md5 from 'blueimp-md5';
22
import stringify from 'fast-json-stable-stringify';
33
import isAbsoluteUrl from 'is-absolute-url';
44
import { URL, assert, timestamp } from '@pollyjs/utils';
5+
import logLevel from 'loglevel';
56

67
import NormalizeRequest from '../utils/normalize-request';
78
import parseUrl from '../utils/parse-url';
@@ -138,6 +139,20 @@ export default class PollyRequest extends HTTPBase {
138139
return this[ROUTE].shouldIntercept();
139140
}
140141

142+
get log() {
143+
if (this.id) {
144+
const log = logLevel.getLogger(
145+
`@pollyjs/core:${this.recordingName}:${this.id}`
146+
);
147+
148+
log.setLevel(this.config.logLevel);
149+
150+
return log;
151+
} else {
152+
return this[POLLY].logger.log;
153+
}
154+
}
155+
141156
on(eventName, listener) {
142157
this[EVENT_EMITTER].on(eventName, listener);
143158

@@ -275,5 +290,12 @@ export default class PollyRequest extends HTTPBase {
275290
(r) => r.id === this.id && r.recordingId === this.recordingId
276291
).length
277292
: 0;
293+
294+
this.log.debug('Request Identified:', {
295+
id: this.id,
296+
order: this.order,
297+
identifiers: this.identifiers,
298+
request: this
299+
});
278300
}
279301
}

packages/@pollyjs/core/src/defaults/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MODES, EXPIRY_STRATEGIES } from '@pollyjs/utils';
2+
import logLevel from 'loglevel';
23

34
import Timing from '../utils/timing';
45

@@ -14,7 +15,7 @@ export default {
1415
disableSortingHarEntries: false
1516
},
1617

17-
logging: false,
18+
logLevel: logLevel.levels.WARN,
1819
flushRequestsOnStop: false,
1920

2021
recordIfMissing: true,

0 commit comments

Comments
 (0)