Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/nasty-dots-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fluidframework/telemetry-utils": minor
"__section": legacy
---
Wrapped errors preserved as `cause` property

`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`).
Comment thread
TommyBrosman marked this conversation as resolved.
7 changes: 7 additions & 0 deletions packages/utils/telemetry-utils/src/errorLogging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ export function wrapError<T extends LoggingError>(

const newError = newErrorFn(message);

newError.cause = innerError;

if (stack !== undefined) {
overwriteStack(newError, stack);
}
Expand Down Expand Up @@ -394,6 +396,8 @@ export class LoggingError
extends Error
implements ILoggingError, Omit<IFluidErrorBase, "errorType">
{
public cause?: unknown;

private _errorInstanceId = uuid();
public get errorInstanceId(): string {
return this._errorInstanceId;
Expand All @@ -418,6 +422,9 @@ export class LoggingError
// Don't log this list itself, or the private _errorInstanceId
omitPropsFromLogging.add("omitPropsFromLogging");
omitPropsFromLogging.add("_errorInstanceId");
// Nor log the unknown cause property, which could be anything.
// Core elements of "cause" are already promoted by wrapError.
omitPropsFromLogging.add("cause");

if (props) {
this.addTelemetryProperties(props);
Expand Down
14 changes: 14 additions & 0 deletions packages/utils/telemetry-utils/src/fluidErrorBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ export interface IFluidErrorBase extends Error {
*/
readonly errorInstanceId: string;

/**
* When present, inner error that caused this error.
*
* @remarks
* 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.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
*
* @privateRemarks
* The "cause" field is added in ES2022. Using it even without that built-in support, is still helpful.
* TODO: remove this declaration (use `Error.cause` property) when targeting ES2022 lib or later.
*/
cause?: unknown;

/**
* Get the telemetry properties stashed on this error for logging.
*/
Expand Down
13 changes: 12 additions & 1 deletion packages/utils/telemetry-utils/src/test/errorLogging.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -983,14 +983,25 @@ const createTestError = (
});

describe("wrapError", () => {
it("Copy message, stack, and props", () => {
it("Copy `message`, `stack`, and `props`", () => {
const innerError = new LoggingError("hello", { someProp: 123 });
innerError.stack = "extra special stack";
const newError = wrapError(innerError, createTestError);
assert.equal(newError.message, innerError.message, "messages should match");
assert.equal(newError.stack, innerError.stack, "stacks should match");
assert.equal(newError.getTelemetryProperties().someProp, 123, "Props should be preserved");
});
it("retains inner error as `cause` but does not promote it", () => {
const innerError = new LoggingError("hello", { someProp: 123 });
innerError.stack = "extra special stack";
const newError = wrapError(innerError, createTestError);
assert.strictEqual(newError.cause, innerError, "`cause` should be innerError");
assert.equal(
newError.getTelemetryProperties().cause,
undefined,
"`cause` should NOT be a telemetry property",
);
});
it("Include matching errorInstanceId and innerErrorInstanceId in telemetry props", () => {
const innerError = new LoggingError("hello");
const newError = wrapError(innerError, createTestError);
Expand Down
Loading