Skip to content

Commit e991e27

Browse files
author
Jonathan Wenger
committed
Updates to match logger with other SDK languages formats.
1 parent 23a68a8 commit e991e27

File tree

3 files changed

+119
-117
lines changed

3 files changed

+119
-117
lines changed

lib/AvaTaxClient.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ import { createBasicAuthHeader } from './utils/basic_auth';
2020
import { withTimeout } from './utils/withTimeout';
2121
import * as Models from './models/index';
2222
import * as Enums from './enums/index';
23-
import Logger, { LogLevel, LogOptions, RequestLogger } from './utils/logger';
23+
import Logger, { LogLevel, LogOptions } from './utils/logger';
24+
import LogObject from './utils/logObject';
2425

25-
class AvalaraError extends Error {
26+
export class AvalaraError extends Error {
2627
code: string;
2728
target: string;
2829
details: string
2930
}
3031

31-
interface HttpOptions {
32+
export interface HttpOptions {
3233
method: string;
3334
headers: NodeJS.Dict<string>;
3435
body: string | null;
@@ -123,34 +124,40 @@ export default class AvaTaxClient {
123124
if (this.customHttpAgent) {
124125
options.agent = this.customHttpAgent;
125126
}
126-
const startTime = Date.now();
127-
// Instantiate a logger for this request, use the correlation id passed in by the consumer if it exists,
128-
// Otherwise requestLogger will generate a UUID and append it to the request header 'x-correlation-id'
129-
const requestLogger = new RequestLogger(this.logger, options.headers);
130-
const urlAndMethodMessage = `REST ${options.method && options.method.toUpperCase()} call to ${url}`;
131-
requestLogger.info(`Initiate ${urlAndMethodMessage}`);
132-
requestLogger.info(`Request Headers for ${urlAndMethodMessage}: ${JSON.stringify(options.headers)}`);
133-
requestLogger.debug(`Request Body for ${urlAndMethodMessage}: ${JSON.stringify(options.body)}`);
127+
const logObject = new LogObject(this.logger.logRequestAndResponseInfo);
128+
logObject.populateRequestInfo(url, options, payload);
134129
return withTimeout(this.timeout, fetch(url, options)).then((res: Response) => {
135-
const contentType = res.headers.get('content-type');
130+
logObject.populateElapsedTime();
131+
const contentType = res.headers.get('content-type');
136132
const contentLength = res.headers.get('content-length');
137-
requestLogger.info(`Response Headers for ${urlAndMethodMessage}: ${JSON.stringify([...res.headers])}`);
138-
requestLogger.info(`Elapsed time for ${urlAndMethodMessage}: ${Date.now() - startTime} ms`);
139133

140134
if (contentType === 'application/vnd.ms-excel' || contentType === 'text/csv') {
135+
res.text().then((txt: string) => {
136+
logObject.populateResponseInfo(res, txt);
137+
}).catch((error) => {
138+
let ex = new AvalaraError('The server returned the response is in an unexpected format');
139+
ex.code = 'FormatException';
140+
ex.details = error;
141+
logObject.populateErrorInfo(res, ex);
142+
throw ex;
143+
}).finally(() => {
144+
this.createLogEntry(logObject);
145+
});
141146
return res;
142147
}
143148

144149
if (contentType && contentType.includes('application/json')) {
145150
if ((contentLength === 0 && Math.trunc(res.status / 100) === 2) || res.status === 204){
151+
logObject.populateResponseInfo(res, null);
152+
this.createLogEntry(logObject);
146153
return null;
147154
}
148155
}
149156
return res.json().catch((error) => {
150157
let ex = new AvalaraError('The server returned the response is in an unexpected format');
151158
ex.code = 'FormatException';
152159
ex.details = error;
153-
requestLogger.error(`Error occurred parsing json response for ${urlAndMethodMessage}: ${JSON.stringify(ex)}`)
160+
logObject.populateErrorInfo(res, ex);
154161
throw ex;
155162
}).then(json => {
156163
// handle error
@@ -159,13 +166,14 @@ export default class AvaTaxClient {
159166
ex.code = json.error.code;
160167
ex.target = json.error.target;
161168
ex.details = json.error.details;
162-
requestLogger.error(`Error occurred parsing json response for ${urlAndMethodMessage}: ${JSON.stringify(ex)}`)
169+
logObject.populateErrorInfo(res, ex);
163170
throw ex;
164171
} else {
165-
requestLogger.debug(`Response Body for ${urlAndMethodMessage}: ${JSON.stringify(json)}`);
166-
requestLogger.info(`Completed ${urlAndMethodMessage}`);
172+
logObject.populateResponseInfo(res, json);
167173
return json;
168174
}
175+
}).finally(() => {
176+
this.createLogEntry(logObject);
169177
});
170178
});
171179
}
@@ -191,6 +199,14 @@ export default class AvaTaxClient {
191199
return this.baseUrl + url;
192200
}
193201

202+
createLogEntry(logObject: LogObject) {
203+
if (logObject.getStatusCode() <= 299) {
204+
this.logger.info(logObject.toString());
205+
} else {
206+
this.logger.error(logObject.toString());
207+
}
208+
}
209+
194210
/**
195211
* Reset this account's license key
196212
* Resets the existing license key for this account to a new key.

lib/utils/logObject.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { AvalaraError, HttpOptions } from "../AvaTaxClient";
2+
3+
export default class LogObject {
4+
private logRequestAndResponseInfo: boolean = false;
5+
private httpMethod: string;
6+
private correlationId: string;
7+
private requestDetails: string;
8+
private responseDetails: string;
9+
private requestURI: string;
10+
private totalExecutionTime: number;
11+
private statusCode: number;
12+
private timestamp: string;
13+
private errorInfo: AvalaraError;
14+
private startTime: number;
15+
16+
constructor(logRequestAndResponseInfo: boolean) {
17+
this.logRequestAndResponseInfo = logRequestAndResponseInfo;
18+
}
19+
20+
public populateRequestInfo(uri: string, options: HttpOptions, payload: any) {
21+
this.timestamp = new Date().toUTCString();
22+
this.startTime = Date.now();
23+
this.httpMethod = options.method;
24+
this.requestURI = uri;
25+
if (payload && this.logRequestAndResponseInfo) {
26+
this.requestDetails = payload;
27+
}
28+
}
29+
30+
public populateResponseInfo(response: Response, json: any) {
31+
this.populateCorrelationId(response.headers);
32+
this.populateStatusCode(response);
33+
if (json && this.logRequestAndResponseInfo) {
34+
this.responseDetails = json;
35+
}
36+
}
37+
38+
public populateErrorInfo(response: Response, errorInfo: AvalaraError) {
39+
this.populateCorrelationId(response.headers);
40+
this.populateStatusCode(response);
41+
if (errorInfo) {
42+
this.errorInfo = errorInfo;
43+
}
44+
}
45+
46+
public populateElapsedTime() {
47+
this.totalExecutionTime = Date.now() - this.startTime;
48+
}
49+
50+
public getStatusCode(): number {
51+
return this.statusCode;
52+
}
53+
54+
public toString(): string {
55+
const {
56+
httpMethod, correlationId, requestDetails, responseDetails, requestURI,
57+
totalExecutionTime, statusCode, timestamp, errorInfo
58+
} = this;
59+
return JSON.stringify({
60+
httpMethod,
61+
correlationId,
62+
requestDetails,
63+
responseDetails,
64+
requestURI,
65+
totalExecutionTime,
66+
statusCode,
67+
timestamp,
68+
errorInfo
69+
});
70+
}
71+
72+
private populateStatusCode(response: Response) {
73+
this.statusCode = response.status;
74+
}
75+
76+
private populateCorrelationId(headers: Headers) {
77+
if(headers.get('x-correlation-id')) {
78+
this.correlationId = headers.get('x-correlation-id');
79+
}
80+
}
81+
}

lib/utils/logger.ts

Lines changed: 4 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,20 @@ export enum LogLevel {
2323
export interface LogOptions {
2424
logEnabled: boolean;
2525
logLevel?: LogLevel;
26-
logHttpInfo?: boolean;
26+
logRequestAndResponseInfo?: boolean;
2727
logger?: BaseLogger;
2828
}
2929

3030
export default class Logger implements BaseLogger {
3131
private logEnabled: boolean;
3232
private logLevel: LogLevel;
3333
private logger: BaseLogger;
34-
public logHttpInfo: boolean;
34+
public logRequestAndResponseInfo: boolean;
3535

36-
constructor({ logEnabled, logLevel, logHttpInfo, logger }: LogOptions) {
36+
constructor({ logEnabled, logLevel, logRequestAndResponseInfo, logger }: LogOptions) {
3737
this.logEnabled = logEnabled;
3838
this.logLevel = logLevel;
39-
this.logHttpInfo = logHttpInfo;
39+
this.logRequestAndResponseInfo = logRequestAndResponseInfo;
4040
if (logger) {
4141
if (this.isValidCustomLogger(logger)) {
4242
this.logger = logger;
@@ -47,28 +47,24 @@ export default class Logger implements BaseLogger {
4747
}
4848

4949
public debug(message: string): void {
50-
message = this.prefixLog(LogLevel.Debug, message);
5150
if (this.logLevel >= LogLevel.Debug && this.logEnabled) {
5251
this.logger ? this.logger.debug(message) : console.debug(message);
5352
}
5453
}
5554

5655
public info(message: string): void {
57-
message = this.prefixLog(LogLevel.Info, message);
5856
if (this.logLevel >= LogLevel.Info && this.logEnabled) {
5957
this.logger ? this.logger.info(message) : console.info(message);
6058
}
6159
}
6260

6361
public warn(message: string): void {
64-
message = this.prefixLog(LogLevel.Warn, message);
6562
if (this.logLevel >= LogLevel.Warn && this.logEnabled) {
6663
this.logger ? this.logger.warn(message) : console.warn(message);
6764
}
6865
}
6966

7067
public error(message: string): void {
71-
message = this.prefixLog(LogLevel.Error, message);
7268
if (this.logLevel >= LogLevel.Error && this.logEnabled) {
7369
this.logger ? this.logger.error(message) : console.error(message);
7470
}
@@ -101,95 +97,4 @@ export default class Logger implements BaseLogger {
10197
typeof logger.log === "function"
10298
);
10399
}
104-
105-
private prefixLog(logLevel: LogLevel, message: string) : string {
106-
const currentDateTime = new Date();
107-
// If message is already prefixed, return message as is.
108-
if (message.indexOf('[Avatax]') >= 0) {
109-
return message;
110-
}
111-
switch (logLevel) {
112-
case LogLevel.Debug:
113-
return `[Avatax] | ${currentDateTime} | DEBUG | ${message}`;
114-
case LogLevel.Info:
115-
return `[Avatax] | ${currentDateTime} | INFO | ${message}`;
116-
case LogLevel.Warn:
117-
return `[Avatax] | ${currentDateTime} | WARN | ${message}`;
118-
case LogLevel.Error:
119-
return `[Avatax] | ${currentDateTime} | ERROR | ${message}`;
120-
}
121-
}
122-
123-
}
124-
125-
export class RequestLogger implements BaseLogger {
126-
private requestId: string;
127-
private logger: Logger;
128-
constructor(logger: Logger, headers: NodeJS.Dict<string>) {
129-
if (headers['x-correlation-id']) {
130-
this.requestId = headers['x-correlation-id'];
131-
} else {
132-
this.requestId = headers['x-correlation-id'] = randomUUID();
133-
}
134-
this.logger = logger;
135-
}
136-
137-
public debug(message: string): void {
138-
if (this.logger.logHttpInfo) {
139-
message = this.prefixLog(LogLevel.Debug, message);
140-
this.logger.debug(message);
141-
}
142-
}
143-
144-
public info(message: string): void {
145-
if (this.logger.logHttpInfo) {
146-
message = this.prefixLog(LogLevel.Info, message);
147-
this.logger.info(message);
148-
}
149-
}
150-
151-
public warn(message: string): void {
152-
if (this.logger.logHttpInfo) {
153-
message = this.prefixLog(LogLevel.Warn, message);
154-
this.logger.warn(message);
155-
}
156-
}
157-
158-
public error(message: string): void {
159-
if (this.logger.logHttpInfo) {
160-
message = this.prefixLog(LogLevel.Error, message);
161-
this.logger.error(message);
162-
}
163-
}
164-
165-
public log(logLevel: LogLevel, message: string) {
166-
switch (logLevel) {
167-
case LogLevel.Debug:
168-
this.debug(message);
169-
break;
170-
case LogLevel.Info:
171-
this.info(message);
172-
break;
173-
case LogLevel.Warn:
174-
this.warn(message);
175-
break;
176-
case LogLevel.Error:
177-
this.error(message);
178-
break;
179-
}
180-
}
181-
182-
private prefixLog(logLevel: LogLevel, message: string) : string {
183-
const currentDateTime = new Date();
184-
switch (logLevel) {
185-
case LogLevel.Debug:
186-
return `[Avatax] | [request-id: ${this.requestId}] | ${currentDateTime} | DEBUG | ${message}`;
187-
case LogLevel.Info:
188-
return `[Avatax] | [request-id: ${this.requestId}] | ${currentDateTime} | INFO | ${message}`;
189-
case LogLevel.Warn:
190-
return `[Avatax] | [request-id: ${this.requestId}] | ${currentDateTime} | WARN | ${message}`;
191-
case LogLevel.Error:
192-
return `[Avatax] | [request-id: ${this.requestId}] | ${currentDateTime} | ERROR | ${message}`;
193-
}
194-
}
195100
}

0 commit comments

Comments
 (0)