Skip to content

Commit

Permalink
Merge pull request #211 from Half-Shot/hs/error-async
Browse files Browse the repository at this point in the history
Improve stack traces thrown from client functions
  • Loading branch information
turt2live authored May 29, 2022
2 parents 6851613 + 5986a09 commit 61735cf
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 111 deletions.
58 changes: 30 additions & 28 deletions src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let lastRequestId = 0;
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
export function doHttpRequest(baseUrl: string, method: "GET"|"POST"|"PUT"|"DELETE", endpoint: string, qs = null, body = null, headers = {}, timeout = 60000, raw = false, contentType = "application/json", noEncoding = false): Promise<any> {
export async function doHttpRequest(baseUrl: string, method: "GET"|"POST"|"PUT"|"DELETE", endpoint: string, qs = null, body = null, headers = {}, timeout = 60000, raw = false, contentType = "application/json", noEncoding = false): Promise<any> {
if (!endpoint.startsWith('/')) {
endpoint = '/' + endpoint;
}
Expand Down Expand Up @@ -62,52 +62,54 @@ export function doHttpRequest(baseUrl: string, method: "GET"|"POST"|"PUT"|"DELET
}
}

return new Promise((resolve, reject) => {
getRequestFn()(params, (err, response, resBody) => {
const {response, resBody} = await new Promise<{response: any, resBody: any}>((resolve, reject) => {
getRequestFn()(params, (err, res, rBody) => {
if (err) {
LogService.error("MatrixHttpClient", "(REQ-" + requestId + ")", err);
reject(err);
return;
}

if (typeof (resBody) === 'string') {
if (typeof (rBody) === 'string') {
try {
resBody = JSON.parse(resBody);
rBody = JSON.parse(rBody);
} catch (e) {
}
}

if (typeof (response.body) === 'string') {
if (typeof (res.body) === 'string') {
try {
response.body = JSON.parse(response.body);
res.body = JSON.parse(res.body);
} catch (e) {
}
}

const respIsBuffer = (response.body instanceof Buffer);
resolve({response: res, resBody: rBody});
});
});

// Check for errors.
const errBody = response.body || resBody;
if (errBody && 'errcode' in errBody) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(errBody);
LogService.error("MatrixHttpClient (REQ-" + requestId + ")", redactedBody);
reject(new MatrixError(errBody, response.statusCode));
return;
}
const respIsBuffer = (response.body instanceof Buffer);

// Don't log the body unless we're in debug mode. They can be large.
if (LogService.level.includes(LogLevel.TRACE)) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService.trace("MatrixHttpClient (REQ-" + requestId + " RESP-H" + response.statusCode + ")", redactedBody);
}
// Check for errors.
const errBody = response.body || resBody;
if (typeof (errBody) === "object" && 'errcode' in errBody) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(errBody);
LogService.error("MatrixHttpClient (REQ-" + requestId + ")", redactedBody);
throw new MatrixError(errBody, response.statusCode);
}

if (response.statusCode < 200 || response.statusCode >= 300) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService.error("MatrixHttpClient (REQ-" + requestId + ")", redactedBody);
reject(response);
} else resolve(raw ? response : resBody);
});
});
// Don't log the body unless we're in debug mode. They can be large.
if (LogService.level.includes(LogLevel.TRACE)) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService.trace("MatrixHttpClient (REQ-" + requestId + " RESP-H" + response.statusCode + ")", redactedBody);
}

if (response.statusCode < 200 || response.statusCode >= 300) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService.error("MatrixHttpClient (REQ-" + requestId + ")", redactedBody);
throw response;
}
return raw ? response : resBody;
}

export function redactObjectForLogging(input: any): any {
Expand Down
119 changes: 37 additions & 82 deletions src/metrics/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,25 @@ import { IdentityClientCallContext, IntentCallContext, MatrixClientCallContext }
* @category Metrics
*/
export function timedMatrixClientFunctionCall() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
return function (_target: unknown, functionName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const metrics = this.metrics;

const context = metrics.assignUniqueContextId(<MatrixClientCallContext>{
functionName: propertyKey,
descriptor.value = async function (...args) {
const context = this.metrics.assignUniqueContextId(<MatrixClientCallContext>{
functionName,
client: this,
});
metrics.start(METRIC_MATRIX_CLIENT_FUNCTION_CALL, context);

let result;
let exception;

this.metrics.start(METRIC_MATRIX_CLIENT_FUNCTION_CALL, context);
try {
result = originalMethod.apply(this, args);
const result = await originalMethod.apply(this, args);
this.metrics.increment(METRIC_MATRIX_CLIENT_SUCCESSFUL_FUNCTION_CALL, context, 1);
return result;
} catch (e) {
exception = e;
result = Promise.reject(e);
this.metrics.increment(METRIC_MATRIX_CLIENT_FAILED_FUNCTION_CALL, context, 1);
throw e;
} finally {
this.metrics.end(METRIC_MATRIX_CLIENT_FUNCTION_CALL, context);
}

let promise = result;
if (!(result instanceof Promise) && result !== null && result !== undefined) {
promise = Promise.resolve(result);
}

promise
.then(() => metrics.increment(METRIC_MATRIX_CLIENT_SUCCESSFUL_FUNCTION_CALL, context, 1))
.catch(() => metrics.increment(METRIC_MATRIX_CLIENT_FAILED_FUNCTION_CALL, context, 1))
.finally(() => metrics.end(METRIC_MATRIX_CLIENT_FUNCTION_CALL, context));

if (exception) throw exception;
return result;
}
};
};
}

Expand All @@ -58,39 +43,24 @@ export function timedMatrixClientFunctionCall() {
* @category Metrics
*/
export function timedIdentityClientFunctionCall() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
return function (_target: unknown, functionName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const metrics = this.metrics;

const context = metrics.assignUniqueContextId(<IdentityClientCallContext>{
functionName: propertyKey,
descriptor.value = async function (...args: any[]) {
const context = this.metrics.assignUniqueContextId(<IdentityClientCallContext>{
functionName,
client: this,
});
metrics.start(METRIC_IDENTITY_CLIENT_FUNCTION_CALL, context);

let result;
let exception;

this.metrics.start(METRIC_IDENTITY_CLIENT_FUNCTION_CALL, context);
try {
result = originalMethod.apply(this, args);
const result = await originalMethod.apply(this, args);
this.metrics.increment(METRIC_IDENTITY_CLIENT_SUCCESSFUL_FUNCTION_CALL, context, 1);
return result;
} catch (e) {
exception = e;
result = Promise.reject(e);
this.metrics.increment(METRIC_IDENTITY_CLIENT_FAILED_FUNCTION_CALL, context, 1);
throw e;
} finally {
this.metrics.end(METRIC_IDENTITY_CLIENT_FUNCTION_CALL, context);
}

let promise = result;
if (!(result instanceof Promise) && result !== null && result !== undefined) {
promise = Promise.resolve(result);
}

promise
.then(() => metrics.increment(METRIC_IDENTITY_CLIENT_SUCCESSFUL_FUNCTION_CALL, context, 1))
.catch(() => metrics.increment(METRIC_IDENTITY_CLIENT_FAILED_FUNCTION_CALL, context, 1))
.finally(() => metrics.end(METRIC_IDENTITY_CLIENT_FUNCTION_CALL, context));

if (exception) throw exception;
return result;
}
};
}
Expand All @@ -100,40 +70,25 @@ export function timedIdentityClientFunctionCall() {
* @category Metrics
*/
export function timedIntentFunctionCall() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
return function (_target: unknown, functionName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const metrics = this.metrics;

const context = metrics.assignUniqueContextId(<IntentCallContext>{
functionName: propertyKey,
descriptor.value = async function (...args: any[]) {
const context = this.metrics.assignUniqueContextId(<IntentCallContext>{
functionName,
client: this.client,
intent: this,
});
metrics.start(METRIC_INTENT_FUNCTION_CALL, context);

let result;
let exception;

this.metrics.start(METRIC_INTENT_FUNCTION_CALL, context);
try {
result = originalMethod.apply(this, args);
const result = await originalMethod.apply(this, args);
this.metrics.increment(METRIC_INTENT_SUCCESSFUL_FUNCTION_CALL, context, 1);
return result;
} catch (e) {
exception = e;
result = Promise.reject(e);
}

let promise = result;
if (!(result instanceof Promise) && result !== null && result !== undefined) {
promise = Promise.resolve(result);
this.metrics.increment(METRIC_INTENT_FAILED_FUNCTION_CALL, context, 1);
throw e;
} finally {
this.metrics.end(METRIC_INTENT_FUNCTION_CALL, context);
}

promise
.then(() => metrics.increment(METRIC_INTENT_SUCCESSFUL_FUNCTION_CALL, context, 1))
.catch(() => metrics.increment(METRIC_INTENT_FAILED_FUNCTION_CALL, context, 1))
.finally(() => metrics.end(METRIC_INTENT_FUNCTION_CALL, context));

if (exception) throw exception;
return result;
}
};
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2015",
"target": "es2020",
"noImplicitAny": false,
"sourceMap": false,
"outDir": "./lib",
Expand Down

0 comments on commit 61735cf

Please sign in to comment.