Skip to content

Commit

Permalink
fix: 1702 and integrate custom exception handling
Browse files Browse the repository at this point in the history
Integrate custom exception handling and additionally integrate exception
handling for nested / cascaded exceptions

Closes: hyperledger-cacti#1702
Signed-off-by: Michael Courtin <michael.courtin@accenture.com>
  • Loading branch information
m-courtin committed Jan 12, 2022
1 parent 8dd17e9 commit 9664449
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 30 deletions.
97 changes: 69 additions & 28 deletions packages/cactus-common/src/main/typescript/logging/log-helper.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { RuntimeError } from "run-time-error";

export class LogHelper {
public static getExceptionStack(exception: unknown): string {
// handle unknown exception input
const defaultStack = "NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION";
const invalidStack = "INVALID_STACK_INFORMATION";
let stack = defaultStack;
let exceptionHandled = false;

// 1st need to check that exception is not null or undefined before trying to access the wanted stack information
if (exception && typeof exception === "object") {
// 2nd need to check if a stack property is available
if (Object.hasOwnProperty.call(exception, "stack")) {
// 3rd check if the stack property is already of type string
if (typeof (exception as Record<string, unknown>).stack === "string") {
stack = (exception as { stack: string }).stack;
} else {
// need to stringify stack information first
try {
stack = JSON.stringify((exception as { stack: unknown }).stack);
} catch (error) {
// stringify failed -> maybe due to cyclic dependency stack etc.
stack = invalidStack;
if (exception) {
if (exception instanceof RuntimeError) {
// handling RuntimeError stack inclusive nested / cascaded stacks
stack = this.safeJsonStringify(exception);
exceptionHandled = true;
}

if (!exceptionHandled && typeof exception === "object") {
// 2nd need to check if a stack property is available
if (Object.hasOwnProperty.call(exception, "stack")) {
// 3rd check if the stack property is already of type string
if (
typeof (exception as Record<string, unknown>).stack === "string"
) {
stack = (exception as { stack: string }).stack;
} else {
// need to stringify stack information first
stack = this.safeJsonStringify(
(exception as { stack: unknown }).stack,
invalidStack,
);
}
}
}
Expand All @@ -29,30 +40,60 @@ export class LogHelper {
public static getExceptionMessage(exception: unknown): string {
// handle unknown exception input
const defaultMessage = "NO_MESSAGE_INCLUDED_IN_EXCEPTION";
const invalidException = "INVALID_EXCEPTION";
const invalidMessage = "INVALID_EXCEPTION_MESSAGE";
const customExceptionPrefix = "A CUSTOM EXCEPTION WAS THROWN: ";
let message = defaultMessage;
let exceptionHandled = false;

// 1st need to check that exception is not null or undefined before trying to access the wanted message information
if (exception && typeof exception === "object") {
// 2nd need to check if a message property is available
if (Object.hasOwnProperty.call(exception, "message")) {
// 3rd check if the message property is already of type string
if (
typeof (exception as Record<string, unknown>).message === "string"
) {
message = (exception as { message: string }).message;
} else {
// need to stringify message information first
try {
message = JSON.stringify(
if (exception) {
if (typeof exception === "object") {
// 2nd need to check if a message property is available
if (Object.hasOwnProperty.call(exception, "message")) {
// 3rd check if the message property is already of type string
if (
typeof (exception as Record<string, unknown>).message === "string"
) {
message = (exception as { message: string }).message;
} else {
// need to stringify message information first
message = this.safeJsonStringify(
(exception as { message: unknown }).message,
invalidMessage,
);
} catch (error) {
// stringify failed -> maybe due to invalid message content etc.
message = invalidMessage;
}
exceptionHandled = true;
}
}

// handling of custom exceptions
if (!exceptionHandled) {
// check if thrown custom exception is a string type only -> directly use it as exception message
if (typeof exception === "string") {
message = exception;
} else {
// custom exception is of a different type -> need to stringify it
message =
customExceptionPrefix &&
this.safeJsonStringify(exception, invalidException);
}
}
}
return message;
}

private static safeJsonStringify(
input: unknown,
catchMessage = "INVALID_INPUT",
): string {
let message = "";

try {
message = JSON.stringify(input);
} catch (error) {
// stringify failed maybe due to cyclic dependency
message = catchMessage;
}
return message;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import "jest-extended";
import { RuntimeError } from "run-time-error";
import { LogHelper } from "../../../../main/typescript/logging/log-helper";

describe("log-helper tests", () => {
const no_message_available = "NO_MESSAGE_INCLUDED_IN_EXCEPTION";
const no_stack_available = "NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION";
const errorMessage = "Oops";
const errorNumber = 2468;

describe("exception stack-tests", () => {
it("gets the stack information from a regular Error object", () => {
Expand Down Expand Up @@ -167,8 +169,9 @@ describe("log-helper tests", () => {
expect(message).toBe(expectedResult);
});

it("gets no exception message information as the faked Error object is not containing any message information and therefore returning NO_MESSAGE_INCLUDED_IN_EXCEPTION", () => {
const expectedResult = no_message_available;
it("gets no exception message information as the faked Error object is not containing any message information and therefore tries to stringify the whole exception", () => {
const expectedResult =
'{"stack":"This is a fake error object without message information"}';
let message = no_message_available;

const fakeErrorWithoutMessage = {
Expand Down Expand Up @@ -217,4 +220,82 @@ describe("log-helper tests", () => {
expect(message).toBe(expectedResult);
});
});

describe("handling of custom exceptions", () => {
it("handles a thrown string", () => {
const expectedErrorMessage = errorMessage;
const expectedStack = no_stack_available;
let message = no_message_available;
let stack = no_stack_available;

try {
throw errorMessage;
} catch (error) {
message = LogHelper.getExceptionMessage(error);
stack = LogHelper.getExceptionStack(error);
}

// check message + stack
expect(message).toBe(expectedErrorMessage);
expect(stack).toBe(expectedStack);
});

it("handles a thrown number", () => {
const expectedErrorMessage = `${errorNumber}`;
const expectedStack = no_stack_available;
let message = no_message_available;
let stack = no_stack_available;

try {
throw errorNumber;
} catch (error) {
message = LogHelper.getExceptionMessage(error);
stack = LogHelper.getExceptionStack(error);
}

// check message + stack
expect(message).toBe(expectedErrorMessage);
expect(stack).toBe(expectedStack);
});

it("handles an arbitrary exception", () => {
const expectedErrorMessage = '{"error":"Oops"}';
const expectedStack = no_stack_available;
let message = no_message_available;
let stack = no_stack_available;
const arbitraryException = { error: errorMessage };

try {
throw arbitraryException;
} catch (error) {
message = LogHelper.getExceptionMessage(error);
stack = LogHelper.getExceptionStack(error);
}

// check message + stack
expect(message).toBe(expectedErrorMessage);
expect(stack).toBe(expectedStack);
});

it("handles nested exceptions", () => {
const expectedErrorMessage = "RTE3";
const expectedStackPart = "RTE1";
let message = no_message_available;
let stack = no_stack_available;
const rtE1 = new RuntimeError("RTE1");
const rtE2 = new RuntimeError("RTE2", rtE1);
const rtE3 = new RuntimeError("RTE3", rtE2);

try {
throw rtE3;
} catch (error) {
message = LogHelper.getExceptionMessage(error);
stack = LogHelper.getExceptionStack(error);
}

// check message + stack
expect(message).toBe(expectedErrorMessage);
expect(stack).toContain(expectedStackPart);
});
});
});

0 comments on commit 9664449

Please sign in to comment.