diff --git a/README.md b/README.md index 2b2cb02..86512f5 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ instance.interceptors.request.use((request) => { | `prefixText` | string \| `false` | `'Axios'` | `false` => no prefix, otherwise, customize the prefix wanted. | | `dateFormat` | [dateformat](https://github.com/felixge/node-dateformat) \| `false` | `false` | `false` => no timestamp, otherwise, customize its format | | `logger` | function | `console.log` | Allows users to customize the logger function to be used. e.g. Winston's `logger.info` could be leveraged, like this: `logger.info.bind(this)` | +| `traceId` | string \| `false` | `false` | Allows users to add a string to be used as a traceable id between request/response (or a sequence of them). | ## CONTRIBUTE diff --git a/src/common/__test__/config.spec.js b/src/common/__test__/config.spec.js index ba5dda8..25159f5 100644 --- a/src/common/__test__/config.spec.js +++ b/src/common/__test__/config.spec.js @@ -15,6 +15,7 @@ test('Default globalConfig properties should be equal to default values', () => dateFormat: false, prefixText: DEFAULT_PREFIX, headers: false, + traceId: false, }); }); @@ -37,6 +38,7 @@ test('setGlobalConfig should set config. getGlobalConfig should return globalCon dateFormat: false, prefixText: DEFAULT_PREFIX, headers: false, + traceId: false, }); }); @@ -64,5 +66,6 @@ test('assembleBuildConfig should return merged with globalConfig object.', () => dateFormat: 'hh:mm:ss', prefixText: DEFAULT_PREFIX, headers: false, + traceId: false, }); }); diff --git a/src/common/__test__/string-builder.spec.js b/src/common/__test__/string-builder.spec.js index cbf329f..810f97b 100644 --- a/src/common/__test__/string-builder.spec.js +++ b/src/common/__test__/string-builder.spec.js @@ -1,5 +1,6 @@ import StringBuilder from '../string-builder'; import { getGlobalConfig } from '../config'; +import chalk from 'chalk'; test('makeLogTypeWithPrefix should add prefix text', () => { const sb = new StringBuilder(getGlobalConfig()); @@ -85,3 +86,16 @@ test('makeData should not stringify data if configured not to', () => { const result = sb.makeData(a).build(); expect(result).toEqual(''); }); + +test('makeTraceId should add a string if configured', () => { + const testId = '1324567890987654321'; + const config = { + ...getGlobalConfig(), + traceId: testId, + }; + + const sb = new StringBuilder(config); + const result = sb.makeTraceId().build(); + + expect(result).toContain(`[${chalk.blue(testId)}]`); +}); diff --git a/src/common/config.ts b/src/common/config.ts index 54aa650..9c5a225 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -11,6 +11,7 @@ let globalConfig: Required = { prefixText: 'Axios', dateFormat: false, headers: false, + traceId: false, }; function getGlobalConfig() { diff --git a/src/common/string-builder.ts b/src/common/string-builder.ts index 7a92288..1931c6a 100644 --- a/src/common/string-builder.ts +++ b/src/common/string-builder.ts @@ -4,18 +4,20 @@ import chalk from 'chalk'; class StringBuilder { private config: GlobalLogConfig; + private lineHeader: Array; private printQueue: Array; private filteredHeaderList: Array; constructor(config: GlobalLogConfig) { this.config = config; + this.lineHeader = []; this.printQueue = []; this.filteredHeaderList = ['common', 'delete', 'get', 'head', 'post', 'put', 'patch', 'content-type', 'content-length', 'vary', 'date', 'connection', 'content-security-policy']; } makeLogTypeWithPrefix(logType: string) { const prefix = this.config.prefixText === false ? `[${logType}]` : `[${this.config.prefixText || 'Axios'}][${logType}]`; - this.printQueue.push(chalk.green(prefix)); + this.lineHeader.push(chalk.green(prefix)); return this; } @@ -24,7 +26,14 @@ class StringBuilder { if (this.config.dateFormat !== false) { // @ts-ignore const dateFormat = dateformat(date, this.config.dateFormat || 'isoDateTime'); - this.printQueue.push(dateFormat); + this.lineHeader.push(`[${dateFormat}]`); + } + return this; + } + + makeTraceId() { + if (this.config.traceId !== false) { + this.lineHeader.push(`[${chalk.blue(this.config.traceId)}]`); } return this; } @@ -76,8 +85,12 @@ class StringBuilder { return this; } + buildLineHeader() { + return this.lineHeader.join(''); + } + build() { - return this.printQueue.join(' '); + return this.buildLineHeader() ? `${this.buildLineHeader()} ` + this.printQueue.join(' ') : this.printQueue.join(' '); } /** diff --git a/src/common/types.ts b/src/common/types.ts index 42c8e9e..95a5fb8 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -12,18 +12,21 @@ export interface GlobalLogConfig extends CommonConfig { url?: boolean, status?: boolean, statusText?: boolean, + traceId?: string | boolean, } export interface RequestLogConfig extends CommonConfig { data?: boolean, method?: boolean, url?: boolean, + traceId?: string | boolean, } export interface ResponseLogConfig extends CommonConfig { data?: boolean, status?: boolean, statusText?: boolean, + traceId?: string | boolean, } export interface ErrorLogConfig extends CommonConfig { diff --git a/src/logger/__test__/request.spec.js b/src/logger/__test__/request.spec.js index 72299c3..7276fd8 100644 --- a/src/logger/__test__/request.spec.js +++ b/src/logger/__test__/request.spec.js @@ -1,4 +1,5 @@ import { requestLogger, setGlobalConfig } from '../../index'; +import chalk from 'chalk'; const printLog = jest.fn(() => {}); @@ -91,3 +92,32 @@ test('if baseUrl is taken into consideration', () => { expect(printLog).toHaveBeenCalled(); expect(printLog).toBeCalledWith(expect.stringContaining(axiosRequestConfig.url)); }); + +test('test a full request', () => { + const globalConfig = { + prefixText: 'TEST', + dateFormat: 'dd-mm-yyyy HH:MM:ss.l', + status: true, + method: true, + headers: true, + data: true, + traceId: '1324567890987654321', + }; + + setGlobalConfig(globalConfig); + requestLogger({ ...axiosRequestConfig, baseURL: 'https://github.com/', url: '/hg-pyun' }); + expect(printLog).toHaveBeenCalled(); + expect(printLog).toBeCalledWith(expect.stringContaining('[TEST]')); + expect(printLog).toBeCalledWith(expect.stringContaining('[Request]')); + expect(printLog).toBeCalledWith(expect.stringContaining(`[${chalk.blue('1324567890987654321')}]`)); + expect(printLog).toBeCalledWith( + expect.stringMatching( + new RegExp( + /\[[01]?\d-(([0-2]?\d)|([3][01]))-((199\d)|([2-9]\d{3})) ([0-1]\d|[2][0-3]):([0-5]\d):([0-5]\d).(\d{0,3})\]/ + ) + ) + ); + expect(printLog).toBeCalledWith(expect.stringContaining(axiosRequestConfig.method)); + expect(printLog).toBeCalledWith(expect.stringContaining(axiosRequestConfig.url)); + expect(printLog).toBeCalledWith(expect.stringContaining(JSON.stringify(axiosRequestConfig.data))); +}); diff --git a/src/logger/__test__/response.spec.js b/src/logger/__test__/response.spec.js index 54ecd61..8da5471 100644 --- a/src/logger/__test__/response.spec.js +++ b/src/logger/__test__/response.spec.js @@ -1,4 +1,5 @@ import { responseLogger, setGlobalConfig } from '../../index'; +import chalk from 'chalk'; const printLog = jest.fn(() => {}); @@ -104,3 +105,40 @@ test('if baseUrl is taken into consideration', () => { expect(printLog).toHaveBeenCalled(); expect(printLog).toBeCalledWith(expect.stringContaining(axiosResponse.config.url)); }); + +test('test a full response', () => { + const globalConfig = { + prefixText: 'TEST', + dateFormat: 'dd-mm-yyyy HH:MM:ss.l', + status: true, + method: true, + headers: true, + data: true, + traceId: '1324567890987654321', + }; + + const { + status, + statusText, + data, + config: { url, method }, + } = axiosResponse; + + setGlobalConfig(globalConfig); + responseLogger(axiosResponse); + expect(printLog).toHaveBeenCalled(); + expect(printLog).toBeCalledWith(expect.stringContaining('[TEST]')); + expect(printLog).toBeCalledWith(expect.stringContaining('[Response]')); + expect(printLog).toBeCalledWith(expect.stringContaining(`[${chalk.blue('1324567890987654321')}]`)); + expect(printLog).toBeCalledWith( + expect.stringMatching( + new RegExp( + /\[[01]?\d-(([0-2]?\d)|([3][01]))-((199\d)|([2-9]\d{3})) ([0-1]\d|[2][0-3]):([0-5]\d):([0-5]\d).(\d{0,3})\]/ + ) + ) + ); + expect(printLog).toBeCalledWith(expect.stringContaining(method)); + expect(printLog).toBeCalledWith(expect.stringContaining(url)); + expect(printLog).toBeCalledWith(expect.stringContaining(`${status}:${statusText}`)); + expect(printLog).toBeCalledWith(expect.stringContaining(data)); +}); diff --git a/src/logger/error.ts b/src/logger/error.ts index 4cb9bcb..274d089 100644 --- a/src/logger/error.ts +++ b/src/logger/error.ts @@ -24,6 +24,7 @@ function errorLoggerWithoutPromise(error: AxiosError, config: ErrorLogConfig = { const log = stringBuilder .makeLogTypeWithPrefix('Error') .makeDateFormat(new Date()) + .makeTraceId() .makeMethod(method) .makeUrl(url, baseURL) .makeParams(params) diff --git a/src/logger/request.ts b/src/logger/request.ts index 33646e2..26dea80 100644 --- a/src/logger/request.ts +++ b/src/logger/request.ts @@ -12,6 +12,7 @@ function requestLogger(request: AxiosRequestConfig, config: RequestLogConfig = { const log = stringBuilder .makeLogTypeWithPrefix('Request') .makeDateFormat(new Date()) + .makeTraceId() .makeMethod(method) .makeUrl(url, baseURL) .makeParams(params) diff --git a/src/logger/response.ts b/src/logger/response.ts index 0ff56ef..4f311bd 100644 --- a/src/logger/response.ts +++ b/src/logger/response.ts @@ -12,6 +12,7 @@ function responseLogger(response: AxiosResponse, config: ResponseLogConfig = {}) const log = stringBuilder .makeLogTypeWithPrefix('Response') .makeDateFormat(new Date()) + .makeTraceId() .makeMethod(method) .makeUrl(url, baseURL) .makeParams(params)