Skip to content

Commit e5bb348

Browse files
jason-haanthony-murphy-agent
authored andcommitted
feat(client-telemetry-utils): Retain inner error as cause when wrapping errors (microsoft#25485)
but do not log [`cause` property](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) itself.
1 parent f378f60 commit e5bb348

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

.changeset/nasty-dots-dress.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@fluidframework/telemetry-utils": minor
3+
"__section": legacy
4+
---
5+
Wrapped errors preserved as `cause` property
6+
7+
`IFluidErrorBase` (internal basis for FluidFramework client errors) declares `cause` property matching ES2022 lib (whether targeted or not). When an error is wrapped, `cause` will be set to the originating error (which may or may not itself be an `Error`).

packages/utils/telemetry-utils/src/errorLogging.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ export function wrapError<T extends LoggingError>(
242242

243243
const newError = newErrorFn(message);
244244

245+
newError.cause = innerError;
246+
245247
if (stack !== undefined) {
246248
overwriteStack(newError, stack);
247249
}
@@ -394,6 +396,8 @@ export class LoggingError
394396
extends Error
395397
implements ILoggingError, Omit<IFluidErrorBase, "errorType">
396398
{
399+
public cause?: unknown;
400+
397401
private _errorInstanceId = uuid();
398402
public get errorInstanceId(): string {
399403
return this._errorInstanceId;
@@ -418,6 +422,9 @@ export class LoggingError
418422
// Don't log this list itself, or the private _errorInstanceId
419423
omitPropsFromLogging.add("omitPropsFromLogging");
420424
omitPropsFromLogging.add("_errorInstanceId");
425+
// Nor log the unknown cause property, which could be anything.
426+
// Core elements of "cause" are already promoted by wrapError.
427+
omitPropsFromLogging.add("cause");
421428

422429
if (props) {
423430
this.addTelemetryProperties(props);

packages/utils/telemetry-utils/src/fluidErrorBase.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ export interface IFluidErrorBase extends Error {
6060
*/
6161
readonly errorInstanceId: string;
6262

63+
/**
64+
* When present, inner error that caused this error.
65+
*
66+
* @remarks
67+
* This will often be an {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error|Error}, but could be any type.
68+
*
69+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
70+
*
71+
* @privateRemarks
72+
* The "cause" field is added in ES2022. Using it even without that built-in support, is still helpful.
73+
* TODO: remove this declaration (use `Error.cause` property) when targeting ES2022 lib or later.
74+
*/
75+
cause?: unknown;
76+
6377
/**
6478
* Get the telemetry properties stashed on this error for logging.
6579
*/

packages/utils/telemetry-utils/src/test/errorLogging.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,14 +983,25 @@ const createTestError = (
983983
});
984984

985985
describe("wrapError", () => {
986-
it("Copy message, stack, and props", () => {
986+
it("Copy `message`, `stack`, and `props`", () => {
987987
const innerError = new LoggingError("hello", { someProp: 123 });
988988
innerError.stack = "extra special stack";
989989
const newError = wrapError(innerError, createTestError);
990990
assert.equal(newError.message, innerError.message, "messages should match");
991991
assert.equal(newError.stack, innerError.stack, "stacks should match");
992992
assert.equal(newError.getTelemetryProperties().someProp, 123, "Props should be preserved");
993993
});
994+
it("retains inner error as `cause` but does not promote it", () => {
995+
const innerError = new LoggingError("hello", { someProp: 123 });
996+
innerError.stack = "extra special stack";
997+
const newError = wrapError(innerError, createTestError);
998+
assert.strictEqual(newError.cause, innerError, "`cause` should be innerError");
999+
assert.equal(
1000+
newError.getTelemetryProperties().cause,
1001+
undefined,
1002+
"`cause` should NOT be a telemetry property",
1003+
);
1004+
});
9941005
it("Include matching errorInstanceId and innerErrorInstanceId in telemetry props", () => {
9951006
const innerError = new LoggingError("hello");
9961007
const newError = wrapError(innerError, createTestError);

0 commit comments

Comments
 (0)