@ovotech/axios-logger
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -15,21 +15,14 @@ import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
} | ||
export interface WithLogger extends AxiosRequestConfig, AxiosLogger { | ||
} | ||
export interface WithLoggerAxiosResponse<T = any, TConfig extends WithLogger = WithLogger> extends AxiosResponse<T> { | ||
config: TConfig; | ||
} | ||
export interface WithLoggerAxiosError<TConfig extends WithLogger = WithLogger> extends AxiosError { | ||
config: TConfig; | ||
response?: WithLoggerAxiosResponse<any, TConfig>; | ||
} | ||
export declare const startHeader = "X-Request-Started"; | ||
export declare const redactHeader = "X-Redact-Log"; | ||
export declare const redactPaths: (paths: string[], object: any) => any; | ||
export declare const getMeta: (response: AxiosResponse<any>) => LogMeta; | ||
export declare const axiosLogger: <TConfig extends WithLogger = WithLogger>(log: (level: "error" | "info", meta: LogMeta, config: TConfig) => void) => { | ||
export declare const axiosLogger: (log: (level: "error" | "info", meta: LogMeta, config: AxiosRequestConfig) => void) => { | ||
request: { | ||
onFulfilled: (config: TConfig) => TConfig; | ||
onFulfilled: (config: AxiosRequestConfig) => AxiosRequestConfig; | ||
}; | ||
response: { | ||
onFulfilled: (response: WithLoggerAxiosResponse<any, TConfig>) => WithLoggerAxiosResponse<any, TConfig>; | ||
onRejected: (error: WithLoggerAxiosError<TConfig>) => Promise<never>; | ||
onFulfilled: (response: AxiosResponse<any>) => AxiosResponse<any>; | ||
onRejected: (error: AxiosError<any>) => Promise<never>; | ||
}; | ||
}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.startHeader = 'X-Request-Started'; | ||
exports.redactHeader = 'X-Redact-Log'; | ||
const redactPath = (path, obj) => { | ||
@@ -14,8 +16,8 @@ const [current, ...rest] = path; | ||
exports.getMeta = (response) => { | ||
const { redact, url: uri, data, params, method, requestStartedAt } = response.config; | ||
const { url: uri, data, params, method, headers } = response.config; | ||
const { data: responseBody, status } = response; | ||
const requestBody = data === undefined ? undefined : JSON.parse(data); | ||
const responseTime = requestStartedAt ? new Date().getTime() - requestStartedAt.getTime() : undefined; | ||
const responseTime = headers[exports.startHeader] ? new Date().getTime() - headers[exports.startHeader].getTime() : undefined; | ||
const meta = { uri, method, requestBody, responseBody, params, status, responseTime }; | ||
return exports.redactPaths(redact || defaultRedact, meta); | ||
return exports.redactPaths(headers[exports.redactHeader] ? headers[exports.redactHeader].split(',').map((path) => path.trim()) : defaultRedact, meta); | ||
}; | ||
@@ -26,3 +28,6 @@ exports.axiosLogger = (log) => ({ | ||
...config, | ||
requestStartedAt: config.requestStartedAt || new Date(), | ||
headers: { | ||
...config.headers, | ||
[exports.startHeader]: config.headers[exports.startHeader] || new Date(), | ||
}, | ||
}), | ||
@@ -29,0 +34,0 @@ }, |
{ | ||
"name": "@ovotech/axios-logger", | ||
"description": "Log responses with sanitization", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"main": "dist/index.js", | ||
@@ -23,3 +23,3 @@ "source": "src/index.ts", | ||
"@types/node": "^11.11.4", | ||
"axios": "^0.18.0", | ||
"axios": "^0.19.0", | ||
"jest": "^24.5.0", | ||
@@ -34,3 +34,3 @@ "prettier": "^1.16.4", | ||
}, | ||
"gitHead": "72d7ad2099b362786b576b69797bbe020fbd36e7" | ||
"gitHead": "7ef4350cc06ee55b43b122ff7843fff1e2d37d52" | ||
} |
@@ -12,3 +12,3 @@ # Axios Logger Interceptor | ||
```typescript | ||
import { axiosLogger } from '@ovotech/axios-logger'; | ||
import { axiosLogger, redactHeader } from '@ovotech/axios-logger'; | ||
import axios from 'axios'; | ||
@@ -27,3 +27,3 @@ | ||
const body = { user: { cards: [{ id: '111' }, { id: '222' }] } }; | ||
api.post('/update/path', body, { redact: ['requestBody.user.cards.*.id'] }); | ||
api.post('/update/path', body, { headers: { [redactHeader]: 'requestBody.user.cards.*.id' } }); | ||
``` | ||
@@ -53,3 +53,3 @@ | ||
By default `uri`, `params`, `requestBody` and `responseBody` will be "redacted", since they can contain personally identifiable information. You can control that with the `redact` property. Its a list of dot delimited field paths to be redacted. Can contain wildcard `*` path to target all array items. | ||
By default `uri`, `params`, `requestBody` and `responseBody` will be "redacted", since they can contain personally identifiable information. You can control that with the `redactHeader`. Its a comma separated list of dot delimited field paths to be redacted. Can contain wildcard `*` path to target all array items. | ||
@@ -59,3 +59,3 @@ For example to redact some fields. | ||
```typescript | ||
api.post('/update/path', body, { redact: ['requestBody.id', 'responseBody.user'] }); | ||
api.post('/update/path', body, { headers: { [redactHeader]: 'requestBody.id, responseBody.user' }); | ||
``` | ||
@@ -69,10 +69,2 @@ | ||
### TypeScript | ||
Since axios types strictly define available properties for axios configs, if you want to use `redact` you'll need to take advantage of the `WithLogger` type: | ||
```typescript | ||
const api = axios.create({ redact: ['requestBody'] } as WithLogger); | ||
``` | ||
### Granular logging | ||
@@ -83,3 +75,3 @@ | ||
```typescript | ||
import { axiosLogger, WithLogger } from '@ovotech/axios-logger'; | ||
import { axiosLogger } from '@ovotech/axios-logger'; | ||
import axios from 'axios'; | ||
@@ -86,0 +78,0 @@ |
@@ -18,13 +18,5 @@ import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
export interface WithLogger extends AxiosRequestConfig, AxiosLogger {} | ||
export const startHeader = 'X-Request-Started'; | ||
export const redactHeader = 'X-Redact-Log'; | ||
export interface WithLoggerAxiosResponse<T = any, TConfig extends WithLogger = WithLogger> extends AxiosResponse<T> { | ||
config: TConfig; | ||
} | ||
export interface WithLoggerAxiosError<TConfig extends WithLogger = WithLogger> extends AxiosError { | ||
config: TConfig; | ||
response?: WithLoggerAxiosResponse<any, TConfig>; | ||
} | ||
const redactPath = (path: string[], obj: any): any => { | ||
@@ -46,26 +38,30 @@ const [current, ...rest] = path; | ||
export const getMeta = (response: AxiosResponse): LogMeta => { | ||
const { redact, url: uri, data, params, method, requestStartedAt }: WithLogger = response.config; | ||
const { url: uri, data, params, method, headers } = response.config; | ||
const { data: responseBody, status } = response; | ||
const requestBody = data === undefined ? undefined : JSON.parse(data); | ||
const responseTime = requestStartedAt ? new Date().getTime() - requestStartedAt.getTime() : undefined; | ||
const responseTime = headers[startHeader] ? new Date().getTime() - headers[startHeader].getTime() : undefined; | ||
const meta = { uri, method, requestBody, responseBody, params, status, responseTime }; | ||
return redactPaths(redact || defaultRedact, meta); | ||
return redactPaths( | ||
headers[redactHeader] ? headers[redactHeader].split(',').map((path: string) => path.trim()) : defaultRedact, | ||
meta, | ||
); | ||
}; | ||
export const axiosLogger = <TConfig extends WithLogger = WithLogger>( | ||
log: (level: 'info' | 'error', meta: LogMeta, config: TConfig) => void, | ||
) => ({ | ||
export const axiosLogger = (log: (level: 'info' | 'error', meta: LogMeta, config: AxiosRequestConfig) => void) => ({ | ||
request: { | ||
onFulfilled: (config: TConfig): TConfig => ({ | ||
onFulfilled: (config: AxiosRequestConfig): AxiosRequestConfig => ({ | ||
...config, | ||
requestStartedAt: config.requestStartedAt || new Date(), | ||
headers: { | ||
...config.headers, | ||
[startHeader]: config.headers[startHeader] || new Date(), | ||
}, | ||
}), | ||
}, | ||
response: { | ||
onFulfilled: (response: WithLoggerAxiosResponse<any, TConfig>) => { | ||
onFulfilled: (response: AxiosResponse) => { | ||
log('info', getMeta(response), response.config); | ||
return response; | ||
}, | ||
onRejected: (error: WithLoggerAxiosError<TConfig>) => { | ||
onRejected: (error: AxiosError) => { | ||
if (error.response) { | ||
@@ -72,0 +68,0 @@ const { responseBody, ...meta } = getMeta(error.response); |
import axios from 'axios'; | ||
import * as nock from 'nock'; | ||
import { axiosLogger, WithLogger } from '../src'; | ||
import { axiosLogger, redactHeader } from '../src'; | ||
interface ConfigWithLogger extends WithLogger { | ||
test?: string; | ||
} | ||
const log = jest.fn(); | ||
const instance = axios.create({ baseURL: 'http://api.example.com' }); | ||
const logger = axiosLogger<ConfigWithLogger>((level, meta, cfg) => { | ||
log(level, meta); | ||
cfg.test = '123'; | ||
}); | ||
const logger = axiosLogger((level, meta) => log(level, meta)); | ||
@@ -20,5 +13,3 @@ instance.interceptors.request.use(logger.request.onFulfilled); | ||
describe('Integration test', () => { | ||
beforeEach(() => { | ||
log.mockClear(); | ||
}); | ||
beforeEach(() => log.mockClear()); | ||
@@ -67,4 +58,4 @@ it('Test logger response', async () => { | ||
await instance.post('/users/13', body, { | ||
redact: ['requestBody.cards.*.id', 'responseBody.cards.*.name'], | ||
} as WithLogger); | ||
headers: { [redactHeader]: 'requestBody.cards.*.id,responseBody.cards.*.name' }, | ||
}); | ||
@@ -96,4 +87,4 @@ expect(log).toHaveBeenCalledWith('info', { | ||
instance.patch('/users/13', body, { | ||
redact: ['requestBody.cards.*.id', 'responseBody.cards.*.name'], | ||
} as WithLogger), | ||
headers: { [redactHeader]: 'requestBody.cards.*.id, responseBody.cards.*.name' }, | ||
}), | ||
).rejects.toHaveProperty('response', expect.objectContaining({ status: 404 })); | ||
@@ -100,0 +91,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
15967
226
113