forked from hyperledger-cacti/cacti
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: provide generic exception handling functionality
Introduce a log helper with static functions to get exception message / exception stack whatever is thrown or provided. Closes: hyperledger-cacti#1702 Signed-off-by: Michael Courtin <michael.courtin@accenture.com>
- Loading branch information
Showing
4 changed files
with
286 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
packages/cactus-common/src/main/typescript/logging/log-helper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
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; | ||
|
||
// 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; | ||
} | ||
} | ||
} | ||
} | ||
return stack; | ||
} | ||
|
||
public static getExceptionMessage(exception: unknown): string { | ||
// handle unknown exception input | ||
const defaultMessage = "NO_MESSAGE_INCLUDED_IN_EXCEPTION"; | ||
const invalidMessage = "INVALID_EXCEPTION_MESSAGE"; | ||
let message = defaultMessage; | ||
|
||
// 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( | ||
(exception as { message: unknown }).message, | ||
); | ||
} catch (error) { | ||
// stringify failed -> maybe due to invalid message content etc. | ||
message = invalidMessage; | ||
} | ||
} | ||
} | ||
} | ||
return message; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
220 changes: 220 additions & 0 deletions
220
packages/cactus-common/src/test/typescript/unit/logging/log-helper.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
import "jest-extended"; | ||
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"; | ||
|
||
describe("exception stack-tests", () => { | ||
it("gets the stack information from a regular Error object", () => { | ||
let expectedResult: string | undefined = ""; | ||
let stack = no_stack_available; | ||
|
||
try { | ||
const testError = new Error(errorMessage); | ||
expectedResult = testError.stack; | ||
throw testError; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
|
||
it("gets stack information from a faked Error object which is containing stack information as string type", () => { | ||
const expectedResult = "Faked stack"; | ||
let stack = no_stack_available; | ||
|
||
const fakeErrorWithStack = { | ||
message: | ||
"This is a fake error object with string-type stack information", | ||
stack: expectedResult, | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithStack; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
|
||
it("gets stack information from a faked Error object which is containing stack information as number type and therefore need to be stringified", () => { | ||
const expectedResult = "123456"; | ||
let stack = no_stack_available; | ||
|
||
const fakeErrorWithStack = { | ||
message: | ||
"This is a fake error object with number-type stack information", | ||
stack: 123456, | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithStack; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
|
||
it("gets no stack information as the faked Error object is not containing any stack information", () => { | ||
const expectedResult = no_stack_available; | ||
let stack = no_stack_available; | ||
|
||
const fakeErrorWithoutStack = { | ||
message: "This is a fake error object without stack information", | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithoutStack; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
|
||
it("handles throwing null successfully and returns NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION string", () => { | ||
const expectedResult = no_stack_available; | ||
let stack = no_stack_available; | ||
|
||
const fakeError = null; | ||
|
||
try { | ||
throw fakeError; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
|
||
it("handles throwing undefined successfully and returns NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION string", () => { | ||
const expectedResult = no_stack_available; | ||
let stack = no_stack_available; | ||
|
||
const fakeError = undefined; | ||
|
||
try { | ||
throw fakeError; | ||
} catch (error) { | ||
stack = LogHelper.getExceptionStack(error); | ||
} | ||
|
||
// check stack | ||
expect(stack).toBe(expectedResult); | ||
}); | ||
}); | ||
|
||
describe("exception message-tests", () => { | ||
it("gets the exception message from a regular Error object", () => { | ||
const expectedResult = errorMessage; | ||
let message = no_message_available; | ||
|
||
try { | ||
const testError = new Error(errorMessage); | ||
throw testError; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
expect(message).toBe(expectedResult); | ||
}); | ||
|
||
it("gets the exception message from a faked Error object which is containing message as string type", () => { | ||
const expectedResult = errorMessage; | ||
let message = no_message_available; | ||
|
||
const fakeErrorWithMessage = { | ||
message: errorMessage, | ||
stack: expectedResult, | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithMessage; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
expect(message).toBe(expectedResult); | ||
}); | ||
|
||
it("gets exception message from a faked Error object which is containing message information as number type and therefore need to be stringified", () => { | ||
const expectedResult = "123456"; | ||
let message = no_message_available; | ||
|
||
const fakeErrorWithNumberMessage = { | ||
message: 123456, | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithNumberMessage; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
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; | ||
let message = no_message_available; | ||
|
||
const fakeErrorWithoutMessage = { | ||
stack: "This is a fake error object without message information", | ||
}; | ||
|
||
try { | ||
throw fakeErrorWithoutMessage; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
expect(message).toBe(expectedResult); | ||
}); | ||
|
||
it("handles throwing null successfully and returning NO_MESSAGE_INCLUDED_IN_EXCEPTION string", () => { | ||
const expectedResult = no_message_available; | ||
let message = no_message_available; | ||
|
||
const fakeError = null; | ||
|
||
try { | ||
throw fakeError; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
expect(message).toBe(expectedResult); | ||
}); | ||
|
||
it("handles throwing undefined successfully and returning NO_MESSAGE_INCLUDED_IN_EXCEPTION string", () => { | ||
const expectedResult = no_message_available; | ||
let message = no_message_available; | ||
|
||
const fakeError = undefined; | ||
|
||
try { | ||
throw fakeError; | ||
} catch (error) { | ||
message = LogHelper.getExceptionMessage(error); | ||
} | ||
|
||
// check message | ||
expect(message).toBe(expectedResult); | ||
}); | ||
}); | ||
}); |