@fluidframework/telemetry-utils 0.50.3 → 0.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { ILoggingError, ITaggedTelemetryPropertyType, ITelemetryProperties } from "@fluidframework/common-definitions";
5
+ import { ILoggingError, ITaggedTelemetryPropertyType, ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
6
6
  import { IFluidErrorBase } from "./fluidErrorBase";
7
7
  /** Inspect the given error for common "safe" props and return them */
8
8
  export declare function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean): {
@@ -16,8 +16,6 @@ export declare const isILoggingError: (x: any) => x is ILoggingError;
16
16
  export interface IFluidErrorAnnotations {
17
17
  /** Telemetry props to log with the error */
18
18
  props?: ITelemetryProperties;
19
- /** fluidErrorCode to mention if error isn't already an IFluidErrorBase */
20
- errorCodeIfNone?: string;
21
19
  }
22
20
  /**
23
21
  * Normalize the given error yielding a valid Fluid Error
@@ -27,6 +25,17 @@ export interface IFluidErrorAnnotations {
27
25
  */
28
26
  export declare function normalizeError(error: unknown, annotations?: IFluidErrorAnnotations): IFluidErrorBase;
29
27
  export declare function generateStack(): string | undefined;
28
+ /**
29
+ * Create a new error, wrapping and caused by the given unknown error.
30
+ * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.
31
+ * The inner error's instance id will also be logged for telemetry analysis.
32
+ * @param innerError - An error from untrusted/unknown origins
33
+ * @param newErrorFn - callback that will create a new error given the original error's message
34
+ * @returns A new error object "wrapping" the given error
35
+ */
36
+ export declare function wrapError<T extends IFluidErrorBase>(innerError: unknown, newErrorFn: (message: string) => T): T;
37
+ /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
38
+ export declare function wrapErrorAndLog<T extends IFluidErrorBase>(innerError: unknown, newErrorFn: (message: string) => T, logger: ITelemetryLogger): T;
30
39
  /**
31
40
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
32
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,4BAA4B,EAC5B,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACH,eAAe,EAGlB,MAAM,kBAAkB,CAAC;AAO1B,sEAAsE;AACtE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO;aAiBhD,MAAM;;;EAkBrC;AAED,6CAA6C;AAC7C,eAAO,MAAM,eAAe,MAAO,GAAG,uBAAwE,CAAC;AAW/G,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAiDD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,WAAW,GAAE,sBAA2B,GACzC,eAAe,CA+BjB;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAWlD;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,4BAA4B,CAExF;AAiCD;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAM,YAAW,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAYlG,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAXzC,QAAQ,CAAC,eAAe,SAAU;IAElC;;;;;OAKG;gBAEC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,oBAAoB,EACX,oBAAoB,GAAE,GAAG,CAAC,MAAM,CAAa;IAYlE;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,oBAAoB;IAIzD;;OAEG;IACI,sBAAsB,IAAI,oBAAoB;CASxD"}
1
+ {"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,4BAA4B,EAC5B,gBAAgB,EAChB,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAEH,eAAe,EAGlB,MAAM,kBAAkB,CAAC;AAO1B,sEAAsE;AACtE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO;aAiBhD,MAAM;;;EAkBrC;AAED,6CAA6C;AAC7C,eAAO,MAAM,eAAe,MAAO,GAAG,uBAAwE,CAAC;AAW/G,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAgDD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,WAAW,GAAE,sBAA2B,GACzC,eAAe,CA+BjB;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAWlD;AAED;;;;;;;GAOG;AACF,wBAAgB,SAAS,CAAC,CAAC,SAAS,eAAe,EAChD,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,GACnC,CAAC,CAsBH;AAED,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACrD,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,EAClC,MAAM,EAAE,gBAAgB,KAa3B;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,4BAA4B,CAExF;AAiCD;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAM,YAAW,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAYlG,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAXzC,QAAQ,CAAC,eAAe,SAAU;IAElC;;;;;OAKG;gBAEC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,oBAAoB,EACX,oBAAoB,GAAE,GAAG,CAAC,MAAM,CAAa;IAYlE;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,oBAAoB;IAIzD;;OAEG;IACI,sBAAsB,IAAI,oBAAoB;CASxD"}
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.LoggingError = exports.isTaggedTelemetryPropertyValue = exports.generateStack = exports.normalizeError = exports.isILoggingError = exports.extractLogSafeErrorProperties = void 0;
7
+ exports.LoggingError = exports.isTaggedTelemetryPropertyValue = exports.wrapErrorAndLog = exports.wrapError = exports.generateStack = exports.normalizeError = exports.isILoggingError = exports.extractLogSafeErrorProperties = void 0;
8
8
  const uuid_1 = require("uuid");
9
9
  const fluidErrorBase_1 = require("./fluidErrorBase");
10
10
  /** @returns true if value is an object but neither null nor an array */
@@ -74,10 +74,10 @@ class SimpleFluidError {
74
74
  }
75
75
  }
76
76
  /** For backwards compatibility with pre-fluidErrorCode valid errors */
77
- function patchWithErrorCode(legacyError, errorCode = "<error predates fluidErrorCode>") {
77
+ function patchWithErrorCode(legacyError) {
78
78
  const patchMe = legacyError;
79
79
  if (patchMe.fluidErrorCode === undefined) {
80
- patchMe.fluidErrorCode = errorCode;
80
+ patchMe.fluidErrorCode = "<error predates fluidErrorCode>";
81
81
  }
82
82
  }
83
83
  /**
@@ -87,10 +87,10 @@ function patchWithErrorCode(legacyError, errorCode = "<error predates fluidError
87
87
  * @param annotations - Annotations to apply to the normalized error
88
88
  */
89
89
  function normalizeError(error, annotations = {}) {
90
- var _a, _b;
90
+ var _a;
91
91
  // Back-compat, while IFluidErrorBase is rolled out
92
92
  if (fluidErrorBase_1.isValidLegacyError(error)) {
93
- patchWithErrorCode(error, annotations.errorCodeIfNone);
93
+ patchWithErrorCode(error);
94
94
  }
95
95
  if (fluidErrorBase_1.isFluidError(error)) {
96
96
  // We can simply add the telemetry props to the error and return it
@@ -101,7 +101,7 @@ function normalizeError(error, annotations = {}) {
101
101
  const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
102
102
  const fluidError = new SimpleFluidError({
103
103
  errorType: "genericError",
104
- fluidErrorCode: (_b = annotations.errorCodeIfNone) !== null && _b !== void 0 ? _b : "none",
104
+ fluidErrorCode: "",
105
105
  message,
106
106
  stack: stack !== null && stack !== void 0 ? stack : generateStack(),
107
107
  });
@@ -127,6 +127,45 @@ function generateStack() {
127
127
  return stack;
128
128
  }
129
129
  exports.generateStack = generateStack;
130
+ /**
131
+ * Create a new error, wrapping and caused by the given unknown error.
132
+ * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.
133
+ * The inner error's instance id will also be logged for telemetry analysis.
134
+ * @param innerError - An error from untrusted/unknown origins
135
+ * @param newErrorFn - callback that will create a new error given the original error's message
136
+ * @returns A new error object "wrapping" the given error
137
+ */
138
+ function wrapError(innerError, newErrorFn) {
139
+ const { message, stack, } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);
140
+ const newError = newErrorFn(message);
141
+ if (stack !== undefined) {
142
+ // supposedly setting stack on an Error can throw.
143
+ try {
144
+ Object.assign(newError, { stack });
145
+ }
146
+ catch (errorSettingStack) {
147
+ newError.addTelemetryProperties({ stack2: stack });
148
+ }
149
+ }
150
+ if (fluidErrorBase_1.hasErrorInstanceId(innerError)) {
151
+ newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });
152
+ }
153
+ return newError;
154
+ }
155
+ exports.wrapError = wrapError;
156
+ /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
157
+ function wrapErrorAndLog(innerError, newErrorFn, logger) {
158
+ const newError = wrapError(innerError, newErrorFn);
159
+ const wrappedByErrorInstanceId = fluidErrorBase_1.hasErrorInstanceId(newError)
160
+ ? newError.errorInstanceId
161
+ : undefined;
162
+ logger.sendTelemetryEvent({
163
+ eventName: "WrapError",
164
+ wrappedByErrorInstanceId,
165
+ }, innerError);
166
+ return newError;
167
+ }
168
+ exports.wrapErrorAndLog = wrapErrorAndLog;
130
169
  /**
131
170
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
132
171
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n /** fluidErrorCode to mention if error isn't already an IFluidErrorBase */\n errorCodeIfNone?: string;\n}\n\n/** Simplest possible implementation of IFluidErrorBase */\nclass SimpleFluidError implements IFluidErrorBase {\n private readonly telemetryProps: ITelemetryProperties = {};\n\n readonly errorType: string;\n readonly fluidErrorCode: string;\n readonly message: string;\n readonly stack?: string;\n readonly name?: string;\n readonly errorInstanceId: string;\n\n constructor(\n errorProps: Omit<IFluidErrorBase,\n \"getTelemetryProperties\" |\n \"addTelemetryProperties\" |\n \"errorInstanceId\">,\n ) {\n this.errorType = errorProps.errorType;\n this.fluidErrorCode = errorProps.fluidErrorCode;\n this.message = errorProps.message;\n this.stack = errorProps.stack;\n this.name = errorProps.name;\n this.errorInstanceId = uuid();\n\n this.addTelemetryProperties(errorProps);\n }\n\n getTelemetryProperties(): ITelemetryProperties {\n return this.telemetryProps;\n }\n\n addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this.telemetryProps, props);\n }\n}\n\n/** For backwards compatibility with pre-fluidErrorCode valid errors */\nfunction patchWithErrorCode(\n legacyError: Omit<IFluidErrorBase, \"fluidErrorCode\">,\n errorCode: string = \"<error predates fluidErrorCode>\",\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { fluidErrorCode?: string } = legacyError as any;\n if (patchMe.fluidErrorCode === undefined) {\n patchMe.fluidErrorCode = errorCode;\n }\n}\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchWithErrorCode(error, annotations.errorCodeIfNone);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: \"genericError\", // Match Container/Driver generic error type\n fluidErrorCode: annotations.errorCodeIfNone ?? \"none\",\n message,\n stack: stack ?? generateStack(),\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nexport function generateStack(): string | undefined {\n // Some browsers will populate stack right away, others require throwing Error\n let stack = new Error(\"<<generated stack>>\").stack;\n if (!stack) {\n try {\n throw new Error(\"<<generated stack>>\");\n } catch (e) {\n stack = e.stack;\n }\n }\n return stack;\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n readonly errorInstanceId = uuid();\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself either\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n hasErrorInstanceId,\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n}\n\n/** Simplest possible implementation of IFluidErrorBase */\nclass SimpleFluidError implements IFluidErrorBase {\n private readonly telemetryProps: ITelemetryProperties = {};\n\n readonly errorType: string;\n readonly fluidErrorCode: string;\n readonly message: string;\n readonly stack?: string;\n readonly name?: string;\n readonly errorInstanceId: string;\n\n constructor(\n errorProps: Omit<IFluidErrorBase,\n \"getTelemetryProperties\" |\n \"addTelemetryProperties\" |\n \"errorInstanceId\">,\n ) {\n this.errorType = errorProps.errorType;\n this.fluidErrorCode = errorProps.fluidErrorCode;\n this.message = errorProps.message;\n this.stack = errorProps.stack;\n this.name = errorProps.name;\n this.errorInstanceId = uuid();\n\n this.addTelemetryProperties(errorProps);\n }\n\n getTelemetryProperties(): ITelemetryProperties {\n return this.telemetryProps;\n }\n\n addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this.telemetryProps, props);\n }\n}\n\n/** For backwards compatibility with pre-fluidErrorCode valid errors */\nfunction patchWithErrorCode(\n legacyError: Omit<IFluidErrorBase, \"fluidErrorCode\">,\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { -readonly [P in \"fluidErrorCode\"]?: IFluidErrorBase[P] } = legacyError as any;\n if (patchMe.fluidErrorCode === undefined) {\n patchMe.fluidErrorCode = \"<error predates fluidErrorCode>\";\n }\n}\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchWithErrorCode(error);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: \"genericError\", // Match Container/Driver generic error type\n fluidErrorCode: \"\",\n message,\n stack: stack ?? generateStack(),\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nexport function generateStack(): string | undefined {\n // Some browsers will populate stack right away, others require throwing Error\n let stack = new Error(\"<<generated stack>>\").stack;\n if (!stack) {\n try {\n throw new Error(\"<<generated stack>>\");\n } catch (e) {\n stack = e.stack;\n }\n }\n return stack;\n}\n\n/**\n * Create a new error, wrapping and caused by the given unknown error.\n * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.\n * The inner error's instance id will also be logged for telemetry analysis.\n * @param innerError - An error from untrusted/unknown origins\n * @param newErrorFn - callback that will create a new error given the original error's message\n * @returns A new error object \"wrapping\" the given error\n */\n export function wrapError<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n): T {\n const {\n message,\n stack,\n } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);\n\n const newError = newErrorFn(message);\n\n if (stack !== undefined) {\n // supposedly setting stack on an Error can throw.\n try {\n Object.assign(newError, { stack });\n } catch (errorSettingStack) {\n newError.addTelemetryProperties({ stack2: stack });\n }\n }\n\n if (hasErrorInstanceId(innerError)) {\n newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });\n }\n\n return newError;\n}\n\n/** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */\nexport function wrapErrorAndLog<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n logger: ITelemetryLogger,\n) {\n const newError = wrapError(innerError, newErrorFn);\n const wrappedByErrorInstanceId = hasErrorInstanceId(newError)\n ? newError.errorInstanceId\n : undefined;\n\n logger.sendTelemetryEvent({\n eventName: \"WrapError\",\n wrappedByErrorInstanceId,\n }, innerError);\n\n return newError;\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n readonly errorInstanceId = uuid();\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself either\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n };\n }\n}\n"]}
@@ -11,8 +11,7 @@ import { TypedEventEmitter, EventEmitterEventType } from "@fluidframework/common
11
11
  */
12
12
  export declare class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {
13
13
  private readonly errorHandler;
14
- constructor(errorHandler?: (eventName: EventEmitterEventType, error: any) => void);
15
- private defaultErrorHandler;
14
+ constructor(errorHandler: (eventName: EventEmitterEventType, error: any) => void);
16
15
  emit(event: EventEmitterEventType, ...args: any[]): boolean;
17
16
  }
18
17
  //# sourceMappingURL=eventEmitterWithErrorHandling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"eventEmitterWithErrorHandling.d.ts","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,qBAAa,6BAA6B,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IACxG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;gBAE1E,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;IAKjF,OAAO,CAAC,mBAAmB;IAWpB,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;CAQrE"}
1
+ {"version":3,"file":"eventEmitterWithErrorHandling.d.ts","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,qBAAa,6BAA6B,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IAC5F,OAAO,CAAC,QAAQ,CAAE,YAAY;gBAAZ,YAAY,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;IAI3F,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;CAQrE"}
@@ -10,16 +10,7 @@ const common_utils_1 = require("@fluidframework/common-utils");
10
10
  class EventEmitterWithErrorHandling extends common_utils_1.TypedEventEmitter {
11
11
  constructor(errorHandler) {
12
12
  super();
13
- this.errorHandler = errorHandler !== null && errorHandler !== void 0 ? errorHandler : this.defaultErrorHandler.bind(this);
14
- }
15
- defaultErrorHandler(event, error) {
16
- // Some listener threw an error, we'll try emitting that error via the error event
17
- // But not if we're already dealing with the error event, in that case just let the error be thrown
18
- if (event === "error") {
19
- throw error;
20
- }
21
- // Note: This will throw if no listeners are registered for the error event
22
- super.emit("error", error);
13
+ this.errorHandler = errorHandler;
23
14
  }
24
15
  emit(event, ...args) {
25
16
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"eventEmitterWithErrorHandling.js","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":";;;AAKA,+DAAwF;AAExF;;;;GAIG;AACH,MAAa,6BAA8D,SAAQ,gCAAyB;IAGxG,YAAY,YAAqE;QAC7E,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,YAAY,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAEO,mBAAmB,CAAC,KAAK,EAAE,KAAK;QACpC,kFAAkF;QAClF,mGAAmG;QACnG,IAAI,KAAK,KAAK,OAAO,EAAE;YACnB,MAAM,KAAK,CAAC;SACf;QAED,2EAA2E;QAC3E,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAEM,IAAI,CAAC,KAA4B,EAAE,GAAG,IAAW;QACpD,IAAI;YACA,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;SACrC;QAAC,OAAO,KAAK,EAAE;YACZ,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;SACf;IACL,CAAC;CACJ;AA3BD,sEA2BC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { IEvent } from \"@fluidframework/common-definitions\";\nimport { TypedEventEmitter, EventEmitterEventType } from \"@fluidframework/common-utils\";\n\n/**\n * Event Emitter helper class\n * Any exceptions thrown by listeners will be caught and raised through \"error\" event.\n * Any exception thrown by \"error\" listeners will propagate to the caller.\n */\nexport class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {\n private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void;\n\n constructor(errorHandler?: (eventName: EventEmitterEventType, error: any) => void) {\n super();\n this.errorHandler = errorHandler ?? this.defaultErrorHandler.bind(this);\n }\n\n private defaultErrorHandler(event, error) {\n // Some listener threw an error, we'll try emitting that error via the error event\n // But not if we're already dealing with the error event, in that case just let the error be thrown\n if (event === \"error\") {\n throw error;\n }\n\n // Note: This will throw if no listeners are registered for the error event\n super.emit(\"error\", error);\n }\n\n public emit(event: EventEmitterEventType, ...args: any[]): boolean {\n try {\n return super.emit(event, ...args);\n } catch (error) {\n this.errorHandler(event, error);\n return true;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"eventEmitterWithErrorHandling.js","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":";;;AAKA,+DAAwF;AAExF;;;;GAIG;AACH,MAAa,6BAA8D,SAAQ,gCAAyB;IACxG,YAA8B,YAAoE;QAC9F,KAAK,EAAE,CAAC;QADkB,iBAAY,GAAZ,YAAY,CAAwD;IAElG,CAAC;IAEM,IAAI,CAAC,KAA4B,EAAE,GAAG,IAAW;QACpD,IAAI;YACA,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;SACrC;QAAC,OAAO,KAAK,EAAE;YACZ,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;SACf;IACL,CAAC;CACJ;AAbD,sEAaC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { IEvent } from \"@fluidframework/common-definitions\";\nimport { TypedEventEmitter, EventEmitterEventType } from \"@fluidframework/common-utils\";\n\n/**\n * Event Emitter helper class\n * Any exceptions thrown by listeners will be caught and raised through \"error\" event.\n * Any exception thrown by \"error\" listeners will propagate to the caller.\n */\nexport class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {\n constructor(private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void) {\n super();\n }\n\n public emit(event: EventEmitterEventType, ...args: any[]): boolean {\n try {\n return super.emit(event, ...args);\n } catch (error) {\n this.errorHandler(event, error);\n return true;\n }\n }\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/telemetry-utils";
8
- export declare const pkgVersion = "0.50.3";
8
+ export declare const pkgVersion = "0.51.2";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/telemetry-utils";
11
- exports.pkgVersion = "0.50.3";
11
+ exports.pkgVersion = "0.51.2";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,iCAAiC,CAAC;AAC5C,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"0.50.3\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,iCAAiC,CAAC;AAC5C,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"0.51.2\";\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { ILoggingError, ITaggedTelemetryPropertyType, ITelemetryProperties } from "@fluidframework/common-definitions";
5
+ import { ILoggingError, ITaggedTelemetryPropertyType, ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
6
6
  import { IFluidErrorBase } from "./fluidErrorBase";
7
7
  /** Inspect the given error for common "safe" props and return them */
8
8
  export declare function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean): {
@@ -16,8 +16,6 @@ export declare const isILoggingError: (x: any) => x is ILoggingError;
16
16
  export interface IFluidErrorAnnotations {
17
17
  /** Telemetry props to log with the error */
18
18
  props?: ITelemetryProperties;
19
- /** fluidErrorCode to mention if error isn't already an IFluidErrorBase */
20
- errorCodeIfNone?: string;
21
19
  }
22
20
  /**
23
21
  * Normalize the given error yielding a valid Fluid Error
@@ -27,6 +25,17 @@ export interface IFluidErrorAnnotations {
27
25
  */
28
26
  export declare function normalizeError(error: unknown, annotations?: IFluidErrorAnnotations): IFluidErrorBase;
29
27
  export declare function generateStack(): string | undefined;
28
+ /**
29
+ * Create a new error, wrapping and caused by the given unknown error.
30
+ * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.
31
+ * The inner error's instance id will also be logged for telemetry analysis.
32
+ * @param innerError - An error from untrusted/unknown origins
33
+ * @param newErrorFn - callback that will create a new error given the original error's message
34
+ * @returns A new error object "wrapping" the given error
35
+ */
36
+ export declare function wrapError<T extends IFluidErrorBase>(innerError: unknown, newErrorFn: (message: string) => T): T;
37
+ /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
38
+ export declare function wrapErrorAndLog<T extends IFluidErrorBase>(innerError: unknown, newErrorFn: (message: string) => T, logger: ITelemetryLogger): T;
30
39
  /**
31
40
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
32
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,4BAA4B,EAC5B,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACH,eAAe,EAGlB,MAAM,kBAAkB,CAAC;AAO1B,sEAAsE;AACtE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO;aAiBhD,MAAM;;;EAkBrC;AAED,6CAA6C;AAC7C,eAAO,MAAM,eAAe,MAAO,GAAG,uBAAwE,CAAC;AAW/G,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAiDD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,WAAW,GAAE,sBAA2B,GACzC,eAAe,CA+BjB;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAWlD;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,4BAA4B,CAExF;AAiCD;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAM,YAAW,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAYlG,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAXzC,QAAQ,CAAC,eAAe,SAAU;IAElC;;;;;OAKG;gBAEC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,oBAAoB,EACX,oBAAoB,GAAE,GAAG,CAAC,MAAM,CAAa;IAYlE;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,oBAAoB;IAIzD;;OAEG;IACI,sBAAsB,IAAI,oBAAoB;CASxD"}
1
+ {"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,4BAA4B,EAC5B,gBAAgB,EAChB,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAEH,eAAe,EAGlB,MAAM,kBAAkB,CAAC;AAO1B,sEAAsE;AACtE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO;aAiBhD,MAAM;;;EAkBrC;AAED,6CAA6C;AAC7C,eAAO,MAAM,eAAe,MAAO,GAAG,uBAAwE,CAAC;AAW/G,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAgDD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,WAAW,GAAE,sBAA2B,GACzC,eAAe,CA+BjB;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAWlD;AAED;;;;;;;GAOG;AACF,wBAAgB,SAAS,CAAC,CAAC,SAAS,eAAe,EAChD,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,GACnC,CAAC,CAsBH;AAED,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACrD,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,EAClC,MAAM,EAAE,gBAAgB,KAa3B;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,4BAA4B,CAExF;AAiCD;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAM,YAAW,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAYlG,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAXzC,QAAQ,CAAC,eAAe,SAAU;IAElC;;;;;OAKG;gBAEC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,oBAAoB,EACX,oBAAoB,GAAE,GAAG,CAAC,MAAM,CAAa;IAYlE;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,oBAAoB;IAIzD;;OAEG;IACI,sBAAsB,IAAI,oBAAoB;CASxD"}
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { v4 as uuid } from "uuid";
6
- import { isFluidError, isValidLegacyError, } from "./fluidErrorBase";
6
+ import { hasErrorInstanceId, isFluidError, isValidLegacyError, } from "./fluidErrorBase";
7
7
  /** @returns true if value is an object but neither null nor an array */
8
8
  const isRegularObject = (value) => {
9
9
  return value !== null && !Array.isArray(value) && typeof value === "object";
@@ -69,10 +69,10 @@ class SimpleFluidError {
69
69
  }
70
70
  }
71
71
  /** For backwards compatibility with pre-fluidErrorCode valid errors */
72
- function patchWithErrorCode(legacyError, errorCode = "<error predates fluidErrorCode>") {
72
+ function patchWithErrorCode(legacyError) {
73
73
  const patchMe = legacyError;
74
74
  if (patchMe.fluidErrorCode === undefined) {
75
- patchMe.fluidErrorCode = errorCode;
75
+ patchMe.fluidErrorCode = "<error predates fluidErrorCode>";
76
76
  }
77
77
  }
78
78
  /**
@@ -82,10 +82,10 @@ function patchWithErrorCode(legacyError, errorCode = "<error predates fluidError
82
82
  * @param annotations - Annotations to apply to the normalized error
83
83
  */
84
84
  export function normalizeError(error, annotations = {}) {
85
- var _a, _b;
85
+ var _a;
86
86
  // Back-compat, while IFluidErrorBase is rolled out
87
87
  if (isValidLegacyError(error)) {
88
- patchWithErrorCode(error, annotations.errorCodeIfNone);
88
+ patchWithErrorCode(error);
89
89
  }
90
90
  if (isFluidError(error)) {
91
91
  // We can simply add the telemetry props to the error and return it
@@ -96,7 +96,7 @@ export function normalizeError(error, annotations = {}) {
96
96
  const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
97
97
  const fluidError = new SimpleFluidError({
98
98
  errorType: "genericError",
99
- fluidErrorCode: (_b = annotations.errorCodeIfNone) !== null && _b !== void 0 ? _b : "none",
99
+ fluidErrorCode: "",
100
100
  message,
101
101
  stack: stack !== null && stack !== void 0 ? stack : generateStack(),
102
102
  });
@@ -120,6 +120,43 @@ export function generateStack() {
120
120
  }
121
121
  return stack;
122
122
  }
123
+ /**
124
+ * Create a new error, wrapping and caused by the given unknown error.
125
+ * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.
126
+ * The inner error's instance id will also be logged for telemetry analysis.
127
+ * @param innerError - An error from untrusted/unknown origins
128
+ * @param newErrorFn - callback that will create a new error given the original error's message
129
+ * @returns A new error object "wrapping" the given error
130
+ */
131
+ export function wrapError(innerError, newErrorFn) {
132
+ const { message, stack, } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);
133
+ const newError = newErrorFn(message);
134
+ if (stack !== undefined) {
135
+ // supposedly setting stack on an Error can throw.
136
+ try {
137
+ Object.assign(newError, { stack });
138
+ }
139
+ catch (errorSettingStack) {
140
+ newError.addTelemetryProperties({ stack2: stack });
141
+ }
142
+ }
143
+ if (hasErrorInstanceId(innerError)) {
144
+ newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });
145
+ }
146
+ return newError;
147
+ }
148
+ /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
149
+ export function wrapErrorAndLog(innerError, newErrorFn, logger) {
150
+ const newError = wrapError(innerError, newErrorFn);
151
+ const wrappedByErrorInstanceId = hasErrorInstanceId(newError)
152
+ ? newError.errorInstanceId
153
+ : undefined;
154
+ logger.sendTelemetryEvent({
155
+ eventName: "WrapError",
156
+ wrappedByErrorInstanceId,
157
+ }, innerError);
158
+ return newError;
159
+ }
123
160
  /**
124
161
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
125
162
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n /** fluidErrorCode to mention if error isn't already an IFluidErrorBase */\n errorCodeIfNone?: string;\n}\n\n/** Simplest possible implementation of IFluidErrorBase */\nclass SimpleFluidError implements IFluidErrorBase {\n private readonly telemetryProps: ITelemetryProperties = {};\n\n readonly errorType: string;\n readonly fluidErrorCode: string;\n readonly message: string;\n readonly stack?: string;\n readonly name?: string;\n readonly errorInstanceId: string;\n\n constructor(\n errorProps: Omit<IFluidErrorBase,\n \"getTelemetryProperties\" |\n \"addTelemetryProperties\" |\n \"errorInstanceId\">,\n ) {\n this.errorType = errorProps.errorType;\n this.fluidErrorCode = errorProps.fluidErrorCode;\n this.message = errorProps.message;\n this.stack = errorProps.stack;\n this.name = errorProps.name;\n this.errorInstanceId = uuid();\n\n this.addTelemetryProperties(errorProps);\n }\n\n getTelemetryProperties(): ITelemetryProperties {\n return this.telemetryProps;\n }\n\n addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this.telemetryProps, props);\n }\n}\n\n/** For backwards compatibility with pre-fluidErrorCode valid errors */\nfunction patchWithErrorCode(\n legacyError: Omit<IFluidErrorBase, \"fluidErrorCode\">,\n errorCode: string = \"<error predates fluidErrorCode>\",\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { fluidErrorCode?: string } = legacyError as any;\n if (patchMe.fluidErrorCode === undefined) {\n patchMe.fluidErrorCode = errorCode;\n }\n}\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchWithErrorCode(error, annotations.errorCodeIfNone);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: \"genericError\", // Match Container/Driver generic error type\n fluidErrorCode: annotations.errorCodeIfNone ?? \"none\",\n message,\n stack: stack ?? generateStack(),\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nexport function generateStack(): string | undefined {\n // Some browsers will populate stack right away, others require throwing Error\n let stack = new Error(\"<<generated stack>>\").stack;\n if (!stack) {\n try {\n throw new Error(\"<<generated stack>>\");\n } catch (e) {\n stack = e.stack;\n }\n }\n return stack;\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n readonly errorInstanceId = uuid();\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself either\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n hasErrorInstanceId,\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n}\n\n/** Simplest possible implementation of IFluidErrorBase */\nclass SimpleFluidError implements IFluidErrorBase {\n private readonly telemetryProps: ITelemetryProperties = {};\n\n readonly errorType: string;\n readonly fluidErrorCode: string;\n readonly message: string;\n readonly stack?: string;\n readonly name?: string;\n readonly errorInstanceId: string;\n\n constructor(\n errorProps: Omit<IFluidErrorBase,\n \"getTelemetryProperties\" |\n \"addTelemetryProperties\" |\n \"errorInstanceId\">,\n ) {\n this.errorType = errorProps.errorType;\n this.fluidErrorCode = errorProps.fluidErrorCode;\n this.message = errorProps.message;\n this.stack = errorProps.stack;\n this.name = errorProps.name;\n this.errorInstanceId = uuid();\n\n this.addTelemetryProperties(errorProps);\n }\n\n getTelemetryProperties(): ITelemetryProperties {\n return this.telemetryProps;\n }\n\n addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this.telemetryProps, props);\n }\n}\n\n/** For backwards compatibility with pre-fluidErrorCode valid errors */\nfunction patchWithErrorCode(\n legacyError: Omit<IFluidErrorBase, \"fluidErrorCode\">,\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { -readonly [P in \"fluidErrorCode\"]?: IFluidErrorBase[P] } = legacyError as any;\n if (patchMe.fluidErrorCode === undefined) {\n patchMe.fluidErrorCode = \"<error predates fluidErrorCode>\";\n }\n}\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchWithErrorCode(error);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: \"genericError\", // Match Container/Driver generic error type\n fluidErrorCode: \"\",\n message,\n stack: stack ?? generateStack(),\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nexport function generateStack(): string | undefined {\n // Some browsers will populate stack right away, others require throwing Error\n let stack = new Error(\"<<generated stack>>\").stack;\n if (!stack) {\n try {\n throw new Error(\"<<generated stack>>\");\n } catch (e) {\n stack = e.stack;\n }\n }\n return stack;\n}\n\n/**\n * Create a new error, wrapping and caused by the given unknown error.\n * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.\n * The inner error's instance id will also be logged for telemetry analysis.\n * @param innerError - An error from untrusted/unknown origins\n * @param newErrorFn - callback that will create a new error given the original error's message\n * @returns A new error object \"wrapping\" the given error\n */\n export function wrapError<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n): T {\n const {\n message,\n stack,\n } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);\n\n const newError = newErrorFn(message);\n\n if (stack !== undefined) {\n // supposedly setting stack on an Error can throw.\n try {\n Object.assign(newError, { stack });\n } catch (errorSettingStack) {\n newError.addTelemetryProperties({ stack2: stack });\n }\n }\n\n if (hasErrorInstanceId(innerError)) {\n newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });\n }\n\n return newError;\n}\n\n/** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */\nexport function wrapErrorAndLog<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n logger: ITelemetryLogger,\n) {\n const newError = wrapError(innerError, newErrorFn);\n const wrappedByErrorInstanceId = hasErrorInstanceId(newError)\n ? newError.errorInstanceId\n : undefined;\n\n logger.sendTelemetryEvent({\n eventName: \"WrapError\",\n wrappedByErrorInstanceId,\n }, innerError);\n\n return newError;\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n readonly errorInstanceId = uuid();\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself either\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n };\n }\n}\n"]}
@@ -11,8 +11,7 @@ import { TypedEventEmitter, EventEmitterEventType } from "@fluidframework/common
11
11
  */
12
12
  export declare class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {
13
13
  private readonly errorHandler;
14
- constructor(errorHandler?: (eventName: EventEmitterEventType, error: any) => void);
15
- private defaultErrorHandler;
14
+ constructor(errorHandler: (eventName: EventEmitterEventType, error: any) => void);
16
15
  emit(event: EventEmitterEventType, ...args: any[]): boolean;
17
16
  }
18
17
  //# sourceMappingURL=eventEmitterWithErrorHandling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"eventEmitterWithErrorHandling.d.ts","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,qBAAa,6BAA6B,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IACxG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;gBAE1E,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;IAKjF,OAAO,CAAC,mBAAmB;IAWpB,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;CAQrE"}
1
+ {"version":3,"file":"eventEmitterWithErrorHandling.d.ts","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,qBAAa,6BAA6B,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IAC5F,OAAO,CAAC,QAAQ,CAAE,YAAY;gBAAZ,YAAY,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;IAI3F,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;CAQrE"}
@@ -7,16 +7,7 @@ import { TypedEventEmitter } from "@fluidframework/common-utils";
7
7
  export class EventEmitterWithErrorHandling extends TypedEventEmitter {
8
8
  constructor(errorHandler) {
9
9
  super();
10
- this.errorHandler = errorHandler !== null && errorHandler !== void 0 ? errorHandler : this.defaultErrorHandler.bind(this);
11
- }
12
- defaultErrorHandler(event, error) {
13
- // Some listener threw an error, we'll try emitting that error via the error event
14
- // But not if we're already dealing with the error event, in that case just let the error be thrown
15
- if (event === "error") {
16
- throw error;
17
- }
18
- // Note: This will throw if no listeners are registered for the error event
19
- super.emit("error", error);
10
+ this.errorHandler = errorHandler;
20
11
  }
21
12
  emit(event, ...args) {
22
13
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"eventEmitterWithErrorHandling.js","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,MAAM,OAAO,6BAA8D,SAAQ,iBAAyB;IAGxG,YAAY,YAAqE;QAC7E,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,YAAY,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAEO,mBAAmB,CAAC,KAAK,EAAE,KAAK;QACpC,kFAAkF;QAClF,mGAAmG;QACnG,IAAI,KAAK,KAAK,OAAO,EAAE;YACnB,MAAM,KAAK,CAAC;SACf;QAED,2EAA2E;QAC3E,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAEM,IAAI,CAAC,KAA4B,EAAE,GAAG,IAAW;QACpD,IAAI;YACA,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;SACrC;QAAC,OAAO,KAAK,EAAE;YACZ,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;SACf;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { IEvent } from \"@fluidframework/common-definitions\";\nimport { TypedEventEmitter, EventEmitterEventType } from \"@fluidframework/common-utils\";\n\n/**\n * Event Emitter helper class\n * Any exceptions thrown by listeners will be caught and raised through \"error\" event.\n * Any exception thrown by \"error\" listeners will propagate to the caller.\n */\nexport class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {\n private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void;\n\n constructor(errorHandler?: (eventName: EventEmitterEventType, error: any) => void) {\n super();\n this.errorHandler = errorHandler ?? this.defaultErrorHandler.bind(this);\n }\n\n private defaultErrorHandler(event, error) {\n // Some listener threw an error, we'll try emitting that error via the error event\n // But not if we're already dealing with the error event, in that case just let the error be thrown\n if (event === \"error\") {\n throw error;\n }\n\n // Note: This will throw if no listeners are registered for the error event\n super.emit(\"error\", error);\n }\n\n public emit(event: EventEmitterEventType, ...args: any[]): boolean {\n try {\n return super.emit(event, ...args);\n } catch (error) {\n this.errorHandler(event, error);\n return true;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"eventEmitterWithErrorHandling.js","sourceRoot":"","sources":["../src/eventEmitterWithErrorHandling.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,8BAA8B,CAAC;AAExF;;;;GAIG;AACH,MAAM,OAAO,6BAA8D,SAAQ,iBAAyB;IACxG,YAA8B,YAAoE;QAC9F,KAAK,EAAE,CAAC;QADkB,iBAAY,GAAZ,YAAY,CAAwD;IAElG,CAAC;IAEM,IAAI,CAAC,KAA4B,EAAE,GAAG,IAAW;QACpD,IAAI;YACA,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;SACrC;QAAC,OAAO,KAAK,EAAE;YACZ,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;SACf;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { IEvent } from \"@fluidframework/common-definitions\";\nimport { TypedEventEmitter, EventEmitterEventType } from \"@fluidframework/common-utils\";\n\n/**\n * Event Emitter helper class\n * Any exceptions thrown by listeners will be caught and raised through \"error\" event.\n * Any exception thrown by \"error\" listeners will propagate to the caller.\n */\nexport class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {\n constructor(private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void) {\n super();\n }\n\n public emit(event: EventEmitterEventType, ...args: any[]): boolean {\n try {\n return super.emit(event, ...args);\n } catch (error) {\n this.errorHandler(event, error);\n return true;\n }\n }\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/telemetry-utils";
8
- export declare const pkgVersion = "0.50.3";
8
+ export declare const pkgVersion = "0.51.2";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/telemetry-utils";
8
- export const pkgVersion = "0.50.3";
8
+ export const pkgVersion = "0.51.2";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"0.50.3\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"0.51.2\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/telemetry-utils",
3
- "version": "0.50.3",
3
+ "version": "0.51.2",
4
4
  "description": "Collection of telemetry relates utilities for Fluid",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": "https://github.com/microsoft/FluidFramework",
@@ -67,8 +67,8 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@fluidframework/build-common": "^0.23.0",
70
- "@fluidframework/eslint-config-fluid": "^0.23.0",
71
- "@fluidframework/mocha-test-setup": "^0.50.3",
70
+ "@fluidframework/eslint-config-fluid": "^0.24.0",
71
+ "@fluidframework/mocha-test-setup": "^0.51.2",
72
72
  "@microsoft/api-extractor": "^7.16.1",
73
73
  "@types/debug": "^4.1.5",
74
74
  "@types/events": "^3.0.0",
@@ -76,7 +76,7 @@
76
76
  "@types/node": "^12.19.0",
77
77
  "@typescript-eslint/eslint-plugin": "~4.14.0",
78
78
  "@typescript-eslint/parser": "~4.14.0",
79
- "concurrently": "^5.2.0",
79
+ "concurrently": "^6.2.0",
80
80
  "copyfiles": "^2.1.0",
81
81
  "cross-env": "^7.0.2",
82
82
  "eslint": "~7.18.0",
@@ -6,10 +6,12 @@
6
6
  import {
7
7
  ILoggingError,
8
8
  ITaggedTelemetryPropertyType,
9
+ ITelemetryLogger,
9
10
  ITelemetryProperties,
10
11
  } from "@fluidframework/common-definitions";
11
12
  import { v4 as uuid } from "uuid";
12
13
  import {
14
+ hasErrorInstanceId,
13
15
  IFluidErrorBase,
14
16
  isFluidError,
15
17
  isValidLegacyError,
@@ -74,8 +76,6 @@ function copyProps(target: ITelemetryProperties | LoggingError, source: ITelemet
74
76
  export interface IFluidErrorAnnotations {
75
77
  /** Telemetry props to log with the error */
76
78
  props?: ITelemetryProperties;
77
- /** fluidErrorCode to mention if error isn't already an IFluidErrorBase */
78
- errorCodeIfNone?: string;
79
79
  }
80
80
 
81
81
  /** Simplest possible implementation of IFluidErrorBase */
@@ -117,11 +117,10 @@ class SimpleFluidError implements IFluidErrorBase {
117
117
  /** For backwards compatibility with pre-fluidErrorCode valid errors */
118
118
  function patchWithErrorCode(
119
119
  legacyError: Omit<IFluidErrorBase, "fluidErrorCode">,
120
- errorCode: string = "<error predates fluidErrorCode>",
121
120
  ): asserts legacyError is IFluidErrorBase {
122
- const patchMe: { fluidErrorCode?: string } = legacyError as any;
121
+ const patchMe: { -readonly [P in "fluidErrorCode"]?: IFluidErrorBase[P] } = legacyError as any;
123
122
  if (patchMe.fluidErrorCode === undefined) {
124
- patchMe.fluidErrorCode = errorCode;
123
+ patchMe.fluidErrorCode = "<error predates fluidErrorCode>";
125
124
  }
126
125
  }
127
126
 
@@ -137,7 +136,7 @@ export function normalizeError(
137
136
  ): IFluidErrorBase {
138
137
  // Back-compat, while IFluidErrorBase is rolled out
139
138
  if (isValidLegacyError(error)) {
140
- patchWithErrorCode(error, annotations.errorCodeIfNone);
139
+ patchWithErrorCode(error);
141
140
  }
142
141
 
143
142
  if (isFluidError(error)) {
@@ -150,7 +149,7 @@ export function normalizeError(
150
149
  const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
151
150
  const fluidError: IFluidErrorBase = new SimpleFluidError({
152
151
  errorType: "genericError", // Match Container/Driver generic error type
153
- fluidErrorCode: annotations.errorCodeIfNone ?? "none",
152
+ fluidErrorCode: "",
154
153
  message,
155
154
  stack: stack ?? generateStack(),
156
155
  });
@@ -180,6 +179,60 @@ export function generateStack(): string | undefined {
180
179
  return stack;
181
180
  }
182
181
 
182
+ /**
183
+ * Create a new error, wrapping and caused by the given unknown error.
184
+ * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.
185
+ * The inner error's instance id will also be logged for telemetry analysis.
186
+ * @param innerError - An error from untrusted/unknown origins
187
+ * @param newErrorFn - callback that will create a new error given the original error's message
188
+ * @returns A new error object "wrapping" the given error
189
+ */
190
+ export function wrapError<T extends IFluidErrorBase>(
191
+ innerError: unknown,
192
+ newErrorFn: (message: string) => T,
193
+ ): T {
194
+ const {
195
+ message,
196
+ stack,
197
+ } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);
198
+
199
+ const newError = newErrorFn(message);
200
+
201
+ if (stack !== undefined) {
202
+ // supposedly setting stack on an Error can throw.
203
+ try {
204
+ Object.assign(newError, { stack });
205
+ } catch (errorSettingStack) {
206
+ newError.addTelemetryProperties({ stack2: stack });
207
+ }
208
+ }
209
+
210
+ if (hasErrorInstanceId(innerError)) {
211
+ newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });
212
+ }
213
+
214
+ return newError;
215
+ }
216
+
217
+ /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
218
+ export function wrapErrorAndLog<T extends IFluidErrorBase>(
219
+ innerError: unknown,
220
+ newErrorFn: (message: string) => T,
221
+ logger: ITelemetryLogger,
222
+ ) {
223
+ const newError = wrapError(innerError, newErrorFn);
224
+ const wrappedByErrorInstanceId = hasErrorInstanceId(newError)
225
+ ? newError.errorInstanceId
226
+ : undefined;
227
+
228
+ logger.sendTelemetryEvent({
229
+ eventName: "WrapError",
230
+ wrappedByErrorInstanceId,
231
+ }, innerError);
232
+
233
+ return newError;
234
+ }
235
+
183
236
  /**
184
237
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
185
238
  */
@@ -11,22 +11,8 @@ import { TypedEventEmitter, EventEmitterEventType } from "@fluidframework/common
11
11
  * Any exception thrown by "error" listeners will propagate to the caller.
12
12
  */
13
13
  export class EventEmitterWithErrorHandling<TEvent extends IEvent = IEvent> extends TypedEventEmitter<TEvent> {
14
- private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void;
15
-
16
- constructor(errorHandler?: (eventName: EventEmitterEventType, error: any) => void) {
14
+ constructor(private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void) {
17
15
  super();
18
- this.errorHandler = errorHandler ?? this.defaultErrorHandler.bind(this);
19
- }
20
-
21
- private defaultErrorHandler(event, error) {
22
- // Some listener threw an error, we'll try emitting that error via the error event
23
- // But not if we're already dealing with the error event, in that case just let the error be thrown
24
- if (event === "error") {
25
- throw error;
26
- }
27
-
28
- // Note: This will throw if no listeners are registered for the error event
29
- super.emit("error", error);
30
16
  }
31
17
 
32
18
  public emit(event: EventEmitterEventType, ...args: any[]): boolean {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/telemetry-utils";
9
- export const pkgVersion = "0.50.3";
9
+ export const pkgVersion = "0.51.2";