@fluidframework/telemetry-utils 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419
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.
- package/.eslintrc.js +12 -13
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +249 -0
- package/README.md +68 -1
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/telemetry-utils.api.md +444 -0
- package/dist/config.d.ts +47 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +88 -38
- package/dist/config.js.map +1 -1
- package/dist/error.d.ts +112 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +159 -0
- package/dist/error.js.map +1 -0
- package/dist/errorLogging.d.ts +86 -20
- package/dist/errorLogging.d.ts.map +1 -1
- package/dist/errorLogging.js +190 -60
- package/dist/errorLogging.js.map +1 -1
- package/dist/eventEmitterWithErrorHandling.d.ts +9 -3
- package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/dist/eventEmitterWithErrorHandling.js +16 -3
- package/dist/eventEmitterWithErrorHandling.js.map +1 -1
- package/dist/events.d.ts +27 -3
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +26 -2
- package/dist/events.js.map +1 -1
- package/dist/fluidErrorBase.d.ts +57 -16
- package/dist/fluidErrorBase.d.ts.map +1 -1
- package/dist/fluidErrorBase.js +27 -14
- package/dist/fluidErrorBase.js.map +1 -1
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -21
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +267 -51
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +423 -132
- package/dist/logger.js.map +1 -1
- package/dist/mockLogger.d.ts +39 -12
- package/dist/mockLogger.d.ts.map +1 -1
- package/dist/mockLogger.js +105 -22
- package/dist/mockLogger.js.map +1 -1
- package/dist/sampledTelemetryHelper.d.ts +18 -12
- package/dist/sampledTelemetryHelper.d.ts.map +1 -1
- package/dist/sampledTelemetryHelper.js +28 -19
- package/dist/sampledTelemetryHelper.js.map +1 -1
- package/dist/telemetry-utils-alpha.d.ts +290 -0
- package/dist/telemetry-utils-beta.d.ts +264 -0
- package/dist/telemetry-utils-public.d.ts +264 -0
- package/dist/telemetry-utils-untrimmed.d.ts +1102 -0
- package/dist/telemetryTypes.d.ts +115 -0
- package/dist/telemetryTypes.d.ts.map +1 -0
- package/dist/telemetryTypes.js +7 -0
- package/dist/telemetryTypes.js.map +1 -0
- package/dist/thresholdCounter.d.ts +6 -5
- package/dist/thresholdCounter.d.ts.map +1 -1
- package/dist/thresholdCounter.js +4 -3
- package/dist/thresholdCounter.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +54 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +58 -3
- package/dist/utils.js.map +1 -1
- package/lib/config.d.ts +47 -16
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +85 -36
- package/lib/config.js.map +1 -1
- package/lib/error.d.ts +112 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +150 -0
- package/lib/error.js.map +1 -0
- package/lib/errorLogging.d.ts +86 -20
- package/lib/errorLogging.d.ts.map +1 -1
- package/lib/errorLogging.js +189 -60
- package/lib/errorLogging.js.map +1 -1
- package/lib/eventEmitterWithErrorHandling.d.ts +9 -3
- package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/lib/eventEmitterWithErrorHandling.js +15 -2
- package/lib/eventEmitterWithErrorHandling.js.map +1 -1
- package/lib/events.d.ts +27 -3
- package/lib/events.d.ts.map +1 -1
- package/lib/events.js +26 -2
- package/lib/events.js.map +1 -1
- package/lib/fluidErrorBase.d.ts +57 -16
- package/lib/fluidErrorBase.d.ts.map +1 -1
- package/lib/fluidErrorBase.js +27 -14
- package/lib/fluidErrorBase.js.map +1 -1
- package/lib/index.d.ts +12 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +11 -11
- package/lib/index.js.map +1 -1
- package/lib/logger.d.ts +267 -51
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +415 -131
- package/lib/logger.js.map +1 -1
- package/lib/mockLogger.d.ts +39 -12
- package/lib/mockLogger.d.ts.map +1 -1
- package/lib/mockLogger.js +106 -23
- package/lib/mockLogger.js.map +1 -1
- package/lib/sampledTelemetryHelper.d.ts +18 -12
- package/lib/sampledTelemetryHelper.d.ts.map +1 -1
- package/lib/sampledTelemetryHelper.js +26 -17
- package/lib/sampledTelemetryHelper.js.map +1 -1
- package/lib/telemetry-utils-alpha.d.ts +290 -0
- package/lib/telemetry-utils-beta.d.ts +264 -0
- package/lib/telemetry-utils-public.d.ts +264 -0
- package/lib/telemetry-utils-untrimmed.d.ts +1102 -0
- package/lib/telemetryTypes.d.ts +115 -0
- package/lib/telemetryTypes.d.ts.map +1 -0
- package/lib/telemetryTypes.js +6 -0
- package/lib/telemetryTypes.js.map +1 -0
- package/lib/thresholdCounter.d.ts +6 -5
- package/lib/thresholdCounter.d.ts.map +1 -1
- package/lib/thresholdCounter.js +4 -3
- package/lib/thresholdCounter.js.map +1 -1
- package/lib/utils.d.ts +54 -3
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +56 -2
- package/lib/utils.js.map +1 -1
- package/package.json +86 -57
- package/prettier.config.cjs +8 -0
- package/src/config.ts +254 -189
- package/src/error.ts +235 -0
- package/src/errorLogging.ts +440 -290
- package/src/eventEmitterWithErrorHandling.ts +26 -14
- package/src/events.ts +54 -25
- package/src/fluidErrorBase.ts +94 -46
- package/src/index.ts +76 -17
- package/src/logger.ts +966 -505
- package/src/mockLogger.ts +225 -83
- package/src/sampledTelemetryHelper.ts +136 -128
- package/src/telemetryTypes.ts +140 -0
- package/src/thresholdCounter.ts +38 -37
- package/src/utils.ts +108 -17
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
- package/dist/debugLogger.d.ts +0 -39
- package/dist/debugLogger.d.ts.map +0 -1
- package/dist/debugLogger.js +0 -101
- package/dist/debugLogger.js.map +0 -1
- package/dist/packageVersion.d.ts +0 -9
- package/dist/packageVersion.d.ts.map +0 -1
- package/dist/packageVersion.js +0 -12
- package/dist/packageVersion.js.map +0 -1
- package/lib/debugLogger.d.ts +0 -39
- package/lib/debugLogger.d.ts.map +0 -1
- package/lib/debugLogger.js +0 -97
- package/lib/debugLogger.js.map +0 -1
- package/lib/packageVersion.d.ts +0 -9
- package/lib/packageVersion.d.ts.map +0 -1
- package/lib/packageVersion.js +0 -9
- package/lib/packageVersion.js.map +0 -1
- package/src/debugLogger.ts +0 -126
- package/src/packageVersion.ts +0 -9
package/lib/errorLogging.d.ts
CHANGED
|
@@ -2,26 +2,43 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { ILoggingError,
|
|
5
|
+
import { ILoggingError, ITelemetryBaseProperties, Tagged } from "@fluidframework/core-interfaces";
|
|
6
6
|
import { IFluidErrorBase } from "./fluidErrorBase";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { ITelemetryLoggerExt, TelemetryEventPropertyTypeExt } from "./telemetryTypes";
|
|
8
|
+
/**
|
|
9
|
+
* Inspect the given error for common "safe" props and return them.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractLogSafeErrorProperties(error: unknown, sanitizeStack: boolean): {
|
|
9
14
|
message: string;
|
|
10
15
|
errorType?: string | undefined;
|
|
11
16
|
stack?: string | undefined;
|
|
12
17
|
};
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Type-guard for {@link @fluidframework/core-interfaces#ILoggingError}.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare const isILoggingError: (x: unknown) => x is ILoggingError;
|
|
24
|
+
/**
|
|
25
|
+
* Metadata to annotate an error object when annotating or normalizing it
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
16
29
|
export interface IFluidErrorAnnotations {
|
|
17
|
-
/**
|
|
18
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Telemetry props to log with the error
|
|
32
|
+
*/
|
|
33
|
+
props?: ITelemetryBaseProperties;
|
|
19
34
|
}
|
|
20
35
|
/**
|
|
21
36
|
* Normalize the given error yielding a valid Fluid Error
|
|
22
37
|
* @returns A valid Fluid Error with any provided annotations applied
|
|
23
38
|
* @param error - The error to normalize
|
|
24
39
|
* @param annotations - Annotations to apply to the normalized error
|
|
40
|
+
*
|
|
41
|
+
* @internal
|
|
25
42
|
*/
|
|
26
43
|
export declare function normalizeError(error: unknown, annotations?: IFluidErrorAnnotations): IFluidErrorBase;
|
|
27
44
|
/**
|
|
@@ -32,8 +49,16 @@ export declare function normalizeError(error: unknown, annotations?: IFluidError
|
|
|
32
49
|
* For such cases it's better to not read stack property right away, but rather delay it until / if it's needed
|
|
33
50
|
* Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.
|
|
34
51
|
* @returns Error object that has stack populated.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
35
54
|
*/
|
|
36
55
|
export declare function generateErrorWithStack(): Error;
|
|
56
|
+
/**
|
|
57
|
+
* Generate a stack at this callsite as if an error were thrown from here.
|
|
58
|
+
* @returns the callstack (does not throw)
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
37
62
|
export declare function generateStack(): string | undefined;
|
|
38
63
|
/**
|
|
39
64
|
* Create a new error using newErrorFn, wrapping and caused by the given unknown error.
|
|
@@ -41,40 +66,69 @@ export declare function generateStack(): string | undefined;
|
|
|
41
66
|
* @param innerError - An error from untrusted/unknown origins
|
|
42
67
|
* @param newErrorFn - callback that will create a new error given the original error's message
|
|
43
68
|
* @returns A new error object "wrapping" the given error
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
44
71
|
*/
|
|
45
72
|
export declare function wrapError<T extends LoggingError>(innerError: unknown, newErrorFn: (message: string) => T): T;
|
|
46
|
-
/**
|
|
47
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The same as wrapError, but also logs the innerError, including the wrapping error's instance ID.
|
|
75
|
+
*
|
|
76
|
+
* @typeParam T - The kind of wrapper error to create.
|
|
77
|
+
*
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
export declare function wrapErrorAndLog<T extends LoggingError>(innerError: unknown, newErrorFn: (message: string) => T, logger: ITelemetryLoggerExt): T;
|
|
81
|
+
/**
|
|
82
|
+
* Attempts to overwrite the error's stack
|
|
83
|
+
*
|
|
84
|
+
* There have been reports of certain JS environments where overwriting stack will throw.
|
|
85
|
+
* If that happens, this adds the given stack as the telemetry property "stack2"
|
|
86
|
+
*
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
export declare function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string): void;
|
|
48
90
|
/**
|
|
49
91
|
* True for any error object that is an (optionally normalized) external error
|
|
50
|
-
* False for any error we created and raised within the FF codebase
|
|
92
|
+
* False for any error we created and raised within the FF codebase via LoggingError base class,
|
|
93
|
+
* or wrapped in a well-known error type
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
51
96
|
*/
|
|
52
|
-
export declare function isExternalError(
|
|
97
|
+
export declare function isExternalError(error: unknown): boolean;
|
|
53
98
|
/**
|
|
54
|
-
* Type guard to identify if a particular
|
|
99
|
+
* Type guard to identify if a particular telemetry property appears to be a
|
|
100
|
+
* {@link @fluidframework/core-interfaces#Tagged} telemetry property.
|
|
101
|
+
*
|
|
102
|
+
* @internal
|
|
55
103
|
*/
|
|
56
|
-
export declare function isTaggedTelemetryPropertyValue(x:
|
|
104
|
+
export declare function isTaggedTelemetryPropertyValue(x: Tagged<TelemetryEventPropertyTypeExt> | TelemetryEventPropertyTypeExt): x is Tagged<TelemetryEventPropertyTypeExt>;
|
|
57
105
|
/**
|
|
58
106
|
* Borrowed from
|
|
59
107
|
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples}
|
|
60
108
|
* Avoids runtime errors with circular references.
|
|
61
109
|
* Not ideal, as will cut values that are not necessarily circular references.
|
|
62
110
|
* Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)
|
|
63
|
-
|
|
64
|
-
|
|
111
|
+
*
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export declare const getCircularReplacer: () => (key: string, value: unknown) => any;
|
|
65
115
|
/**
|
|
66
116
|
* Base class for "trusted" errors we create, whose properties can generally be logged to telemetry safely.
|
|
67
117
|
* All properties set on the object, or passed in (via the constructor or addTelemetryProperties),
|
|
68
118
|
* will be logged in accordance with their tag, if present.
|
|
69
119
|
*
|
|
70
120
|
* PLEASE take care to avoid setting sensitive data on this object without proper tagging!
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
71
123
|
*/
|
|
72
124
|
export declare class LoggingError extends Error implements ILoggingError, Omit<IFluidErrorBase, "errorType"> {
|
|
73
125
|
private readonly omitPropsFromLogging;
|
|
74
126
|
private _errorInstanceId;
|
|
75
127
|
get errorInstanceId(): string;
|
|
76
128
|
overwriteErrorInstanceId(id: string): void;
|
|
77
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Backwards compatibility to appease {@link isFluidError} in old code that may handle this error.
|
|
131
|
+
*/
|
|
78
132
|
private readonly fluidErrorCode;
|
|
79
133
|
/**
|
|
80
134
|
* Create a new LoggingError
|
|
@@ -82,14 +136,26 @@ export declare class LoggingError extends Error implements ILoggingError, Omit<I
|
|
|
82
136
|
* @param props - telemetry props to include on the error for when it's logged
|
|
83
137
|
* @param omitPropsFromLogging - properties by name to omit from telemetry props
|
|
84
138
|
*/
|
|
85
|
-
constructor(message: string, props?:
|
|
139
|
+
constructor(message: string, props?: ITelemetryBaseProperties, omitPropsFromLogging?: Set<string>);
|
|
140
|
+
/**
|
|
141
|
+
* Determines if a given object is an instance of a LoggingError
|
|
142
|
+
* @param object - any object
|
|
143
|
+
* @returns true if the object is an instance of a LoggingError, false if not.
|
|
144
|
+
*/
|
|
145
|
+
static typeCheck(object: unknown): object is LoggingError;
|
|
86
146
|
/**
|
|
87
147
|
* Add additional properties to be logged
|
|
88
148
|
*/
|
|
89
|
-
addTelemetryProperties(props:
|
|
149
|
+
addTelemetryProperties(props: ITelemetryBaseProperties): void;
|
|
90
150
|
/**
|
|
91
151
|
* Get all properties fit to be logged to telemetry for this error
|
|
92
152
|
*/
|
|
93
|
-
getTelemetryProperties():
|
|
153
|
+
getTelemetryProperties(): ITelemetryBaseProperties;
|
|
94
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* The Error class used when normalizing an external error
|
|
157
|
+
*
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
export declare const NORMALIZED_ERROR_TYPE = "genericError";
|
|
95
161
|
//# sourceMappingURL=errorLogging.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"errorLogging.d.ts","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,aAAa,EACb,wBAAwB,EAExB,MAAM,EACN,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAEN,eAAe,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAStF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,OAAO,GACpB;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAoCA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,MAAO,OAAO,uBACkC,CAAC;AAgB7E;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,KAAK,CAAC,EAAE,wBAAwB,CAAC;CACjC;AAeD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,EACd,WAAW,GAAE,sBAA2B,GACtC,eAAe,CAqDjB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,IAAI,KAAK,CAgB9C;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,YAAY,EAC/C,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,GAChC,CAAC,CA6BH;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,YAAY,EACrD,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,EAClC,MAAM,EAAE,mBAAmB,GACzB,CAAC,CAmBH;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMzF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC7C,CAAC,EAAE,MAAM,CAAC,6BAA6B,CAAC,GAAG,6BAA6B,GACtE,CAAC,IAAI,MAAM,CAAC,6BAA6B,CAAC,CAE5C;AA2DD;;;;;;;;GAQG;AAGH,eAAO,MAAM,mBAAmB,cAAc,MAAM,SAAS,OAAO,KAAK,GAWxE,CAAC;AAGF;;;;;;;;GAQG;AACH,qBAAa,YACZ,SAAQ,KACR,YAAW,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC;IA0B3D,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAxBtC,OAAO,CAAC,gBAAgB,CAAU;IAClC,IAAI,eAAe,IAAI,MAAM,CAE5B;IACD,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IAGH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAY;IAE3C;;;;;OAKG;gBAEF,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,wBAAwB,EACf,oBAAoB,GAAE,GAAG,CAAC,MAAM,CAAa;IAa/D;;;;OAIG;WACW,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,YAAY;IAWhE;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IAIpE;;OAEG;IACI,sBAAsB,IAAI,wBAAwB;CAUzD;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,iBAAiB,CAAC"}
|
package/lib/errorLogging.js
CHANGED
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { v4 as uuid } from "uuid";
|
|
6
6
|
import { hasErrorInstanceId, isFluidError, isValidLegacyError, } from "./fluidErrorBase";
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Determines if the provided value is an object but neither null nor an array.
|
|
9
|
+
*/
|
|
8
10
|
const isRegularObject = (value) => {
|
|
9
11
|
return value !== null && !Array.isArray(value) && typeof value === "object";
|
|
10
12
|
};
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Inspect the given error for common "safe" props and return them.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
12
18
|
export function extractLogSafeErrorProperties(error, sanitizeStack) {
|
|
13
19
|
const removeMessageFromStack = (stack, errorName) => {
|
|
14
20
|
if (!sanitizeStack) {
|
|
@@ -21,7 +27,7 @@ export function extractLogSafeErrorProperties(error, sanitizeStack) {
|
|
|
21
27
|
}
|
|
22
28
|
return stackFrames.join("\n");
|
|
23
29
|
};
|
|
24
|
-
const message =
|
|
30
|
+
const message = typeof error?.message === "string"
|
|
25
31
|
? error.message
|
|
26
32
|
: String(error);
|
|
27
33
|
const safeProps = {
|
|
@@ -33,15 +39,21 @@ export function extractLogSafeErrorProperties(error, sanitizeStack) {
|
|
|
33
39
|
safeProps.errorType = errorType;
|
|
34
40
|
}
|
|
35
41
|
if (typeof stack === "string") {
|
|
36
|
-
const errorName =
|
|
42
|
+
const errorName = typeof name === "string" ? name : undefined;
|
|
37
43
|
safeProps.stack = removeMessageFromStack(stack, errorName);
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
return safeProps;
|
|
41
47
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Type-guard for {@link @fluidframework/core-interfaces#ILoggingError}.
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export const isILoggingError = (x) => typeof x?.getTelemetryProperties === "function";
|
|
54
|
+
/**
|
|
55
|
+
* Copy props from source onto target, but do not overwrite an existing prop that matches
|
|
56
|
+
*/
|
|
45
57
|
function copyProps(target, source) {
|
|
46
58
|
for (const key of Object.keys(source)) {
|
|
47
59
|
if (target[key] === undefined) {
|
|
@@ -49,8 +61,11 @@ function copyProps(target, source) {
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* For backwards compatibility with pre-errorInstanceId valid errors
|
|
66
|
+
*/
|
|
53
67
|
function patchLegacyError(legacyError) {
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
54
69
|
const patchMe = legacyError;
|
|
55
70
|
if (patchMe.errorInstanceId === undefined) {
|
|
56
71
|
patchMe.errorInstanceId = uuid();
|
|
@@ -61,21 +76,22 @@ function patchLegacyError(legacyError) {
|
|
|
61
76
|
* @returns A valid Fluid Error with any provided annotations applied
|
|
62
77
|
* @param error - The error to normalize
|
|
63
78
|
* @param annotations - Annotations to apply to the normalized error
|
|
79
|
+
*
|
|
80
|
+
* @internal
|
|
64
81
|
*/
|
|
65
82
|
export function normalizeError(error, annotations = {}) {
|
|
66
|
-
var _a;
|
|
67
83
|
// Back-compat, while IFluidErrorBase is rolled out
|
|
68
84
|
if (isValidLegacyError(error)) {
|
|
69
85
|
patchLegacyError(error);
|
|
70
86
|
}
|
|
71
87
|
if (isFluidError(error)) {
|
|
72
88
|
// We can simply add the telemetry props to the error and return it
|
|
73
|
-
error.addTelemetryProperties(
|
|
89
|
+
error.addTelemetryProperties(annotations.props ?? {});
|
|
74
90
|
return error;
|
|
75
91
|
}
|
|
76
92
|
// We have to construct a new Fluid Error, copying safe properties over
|
|
77
93
|
const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
|
|
78
|
-
const fluidError = new
|
|
94
|
+
const fluidError = new NormalizedLoggingError({
|
|
79
95
|
message,
|
|
80
96
|
stack,
|
|
81
97
|
});
|
|
@@ -83,15 +99,31 @@ export function normalizeError(error, annotations = {}) {
|
|
|
83
99
|
// Anywhere they are set should be on a valid Fluid Error that would have been returned above,
|
|
84
100
|
// but we can't prove it with the types, so adding this defensive measure.
|
|
85
101
|
if (typeof error === "object" && error !== null) {
|
|
86
|
-
const
|
|
87
|
-
|
|
102
|
+
const maybeHasRetry = error;
|
|
103
|
+
let retryProps;
|
|
104
|
+
if ("canRetry" in error) {
|
|
105
|
+
retryProps ?? (retryProps = {});
|
|
106
|
+
retryProps.canRetry = maybeHasRetry.canRetry;
|
|
107
|
+
}
|
|
108
|
+
if ("retryAfterSeconds" in error) {
|
|
109
|
+
retryProps ?? (retryProps = {});
|
|
110
|
+
retryProps.retryAfterSeconds = maybeHasRetry.retryAfterSeconds;
|
|
111
|
+
}
|
|
112
|
+
if (retryProps !== undefined) {
|
|
113
|
+
Object.assign(fluidError, retryProps);
|
|
114
|
+
}
|
|
88
115
|
}
|
|
89
|
-
if (typeof
|
|
116
|
+
if (typeof error !== "object") {
|
|
90
117
|
// This is only interesting for non-objects
|
|
91
|
-
fluidError.addTelemetryProperties({ typeofError: typeof
|
|
118
|
+
fluidError.addTelemetryProperties({ typeofError: typeof error });
|
|
92
119
|
}
|
|
93
|
-
const
|
|
94
|
-
|
|
120
|
+
const errorTelemetryProps = LoggingError.typeCheck(error)
|
|
121
|
+
? error.getTelemetryProperties()
|
|
122
|
+
: { untrustedOrigin: 1 }; // This will let us filter errors that did not originate from our own codebase
|
|
123
|
+
fluidError.addTelemetryProperties({
|
|
124
|
+
...errorTelemetryProps,
|
|
125
|
+
...annotations.props,
|
|
126
|
+
});
|
|
95
127
|
return fluidError;
|
|
96
128
|
}
|
|
97
129
|
let stackPopulatedOnCreation;
|
|
@@ -103,11 +135,13 @@ let stackPopulatedOnCreation;
|
|
|
103
135
|
* For such cases it's better to not read stack property right away, but rather delay it until / if it's needed
|
|
104
136
|
* Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.
|
|
105
137
|
* @returns Error object that has stack populated.
|
|
138
|
+
*
|
|
139
|
+
* @internal
|
|
106
140
|
*/
|
|
107
141
|
export function generateErrorWithStack() {
|
|
108
142
|
const err = new Error("<<generated stack>>");
|
|
109
143
|
if (stackPopulatedOnCreation === undefined) {
|
|
110
|
-
stackPopulatedOnCreation =
|
|
144
|
+
stackPopulatedOnCreation = err.stack !== undefined;
|
|
111
145
|
}
|
|
112
146
|
if (stackPopulatedOnCreation) {
|
|
113
147
|
return err;
|
|
@@ -115,10 +149,16 @@ export function generateErrorWithStack() {
|
|
|
115
149
|
try {
|
|
116
150
|
throw err;
|
|
117
151
|
}
|
|
118
|
-
catch (
|
|
119
|
-
return
|
|
152
|
+
catch (error) {
|
|
153
|
+
return error;
|
|
120
154
|
}
|
|
121
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Generate a stack at this callsite as if an error were thrown from here.
|
|
158
|
+
* @returns the callstack (does not throw)
|
|
159
|
+
*
|
|
160
|
+
* @internal
|
|
161
|
+
*/
|
|
122
162
|
export function generateStack() {
|
|
123
163
|
return generateErrorWithStack().stack;
|
|
124
164
|
}
|
|
@@ -128,9 +168,11 @@ export function generateStack() {
|
|
|
128
168
|
* @param innerError - An error from untrusted/unknown origins
|
|
129
169
|
* @param newErrorFn - callback that will create a new error given the original error's message
|
|
130
170
|
* @returns A new error object "wrapping" the given error
|
|
171
|
+
*
|
|
172
|
+
* @internal
|
|
131
173
|
*/
|
|
132
174
|
export function wrapError(innerError, newErrorFn) {
|
|
133
|
-
const { message, stack
|
|
175
|
+
const { message, stack } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);
|
|
134
176
|
const newError = newErrorFn(message);
|
|
135
177
|
if (stack !== undefined) {
|
|
136
178
|
overwriteStack(newError, stack);
|
|
@@ -152,7 +194,13 @@ export function wrapError(innerError, newErrorFn) {
|
|
|
152
194
|
}
|
|
153
195
|
return newError;
|
|
154
196
|
}
|
|
155
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* The same as wrapError, but also logs the innerError, including the wrapping error's instance ID.
|
|
199
|
+
*
|
|
200
|
+
* @typeParam T - The kind of wrapper error to create.
|
|
201
|
+
*
|
|
202
|
+
* @internal
|
|
203
|
+
*/
|
|
156
204
|
export function wrapErrorAndLog(innerError, newErrorFn, logger) {
|
|
157
205
|
const newError = wrapError(innerError, newErrorFn);
|
|
158
206
|
// This will match innerError.errorInstanceId if present (see wrapError)
|
|
@@ -166,29 +214,80 @@ export function wrapErrorAndLog(innerError, newErrorFn, logger) {
|
|
|
166
214
|
}, innerError);
|
|
167
215
|
return newError;
|
|
168
216
|
}
|
|
169
|
-
|
|
170
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Attempts to overwrite the error's stack
|
|
219
|
+
*
|
|
220
|
+
* There have been reports of certain JS environments where overwriting stack will throw.
|
|
221
|
+
* If that happens, this adds the given stack as the telemetry property "stack2"
|
|
222
|
+
*
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
export function overwriteStack(error, stack) {
|
|
171
226
|
try {
|
|
172
227
|
Object.assign(error, { stack });
|
|
173
228
|
}
|
|
174
|
-
catch
|
|
229
|
+
catch {
|
|
175
230
|
error.addTelemetryProperties({ stack2: stack });
|
|
176
231
|
}
|
|
177
232
|
}
|
|
178
233
|
/**
|
|
179
234
|
* True for any error object that is an (optionally normalized) external error
|
|
180
|
-
* False for any error we created and raised within the FF codebase
|
|
235
|
+
* False for any error we created and raised within the FF codebase via LoggingError base class,
|
|
236
|
+
* or wrapped in a well-known error type
|
|
237
|
+
*
|
|
238
|
+
* @internal
|
|
181
239
|
*/
|
|
182
|
-
export function isExternalError(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
240
|
+
export function isExternalError(error) {
|
|
241
|
+
// LoggingErrors are an internal FF error type. However, an external error can be converted
|
|
242
|
+
// into a LoggingError if it is normalized. In this case we must use the untrustedOrigin flag to
|
|
243
|
+
// determine whether the original error was infact external.
|
|
244
|
+
if (LoggingError.typeCheck(error)) {
|
|
245
|
+
if (error.errorType === NORMALIZED_ERROR_TYPE) {
|
|
246
|
+
return error.getTelemetryProperties().untrustedOrigin === 1;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return !isValidLegacyError(error);
|
|
186
251
|
}
|
|
187
252
|
/**
|
|
188
|
-
* Type guard to identify if a particular
|
|
253
|
+
* Type guard to identify if a particular telemetry property appears to be a
|
|
254
|
+
* {@link @fluidframework/core-interfaces#Tagged} telemetry property.
|
|
255
|
+
*
|
|
256
|
+
* @internal
|
|
189
257
|
*/
|
|
190
258
|
export function isTaggedTelemetryPropertyValue(x) {
|
|
191
|
-
return
|
|
259
|
+
return typeof x?.tag === "string";
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Filter serializable telemetry properties
|
|
263
|
+
* @param x - Any telemetry prop
|
|
264
|
+
* @returns As-is if x is primitive. returns stringified if x is an array of primitive.
|
|
265
|
+
* otherwise returns null since this is what we support at the moment.
|
|
266
|
+
*/
|
|
267
|
+
function filterValidTelemetryProps(x, key) {
|
|
268
|
+
if (Array.isArray(x) && x.every((val) => isTelemetryEventPropertyValue(val))) {
|
|
269
|
+
return JSON.stringify(x);
|
|
270
|
+
}
|
|
271
|
+
if (isTelemetryEventPropertyValue(x)) {
|
|
272
|
+
return x;
|
|
273
|
+
}
|
|
274
|
+
// We don't support logging arbitrary objects
|
|
275
|
+
console.error(`UnSupported Format of Logging Error Property for key ${key}:`, x);
|
|
276
|
+
return "REDACTED (arbitrary object)";
|
|
277
|
+
}
|
|
278
|
+
// checking type of x, returns false if x is null
|
|
279
|
+
function isTelemetryEventPropertyValue(x) {
|
|
280
|
+
switch (typeof x) {
|
|
281
|
+
case "string":
|
|
282
|
+
case "number":
|
|
283
|
+
case "boolean":
|
|
284
|
+
case "undefined": {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
default: {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
192
291
|
}
|
|
193
292
|
/**
|
|
194
293
|
* Walk an object's enumerable properties to find those fit for telemetry.
|
|
@@ -200,24 +299,13 @@ function getValidTelemetryProps(obj, keysToOmit) {
|
|
|
200
299
|
continue;
|
|
201
300
|
}
|
|
202
301
|
const val = obj[key];
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
props[key] = val;
|
|
209
|
-
break;
|
|
210
|
-
default: {
|
|
211
|
-
if (isTaggedTelemetryPropertyValue(val)) {
|
|
212
|
-
props[key] = val;
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
// We don't support logging arbitrary objects
|
|
216
|
-
props[key] = "REDACTED (arbitrary object)";
|
|
217
|
-
}
|
|
218
|
-
break;
|
|
302
|
+
// ensure only valid props get logged, since props of logging error could be in any shape
|
|
303
|
+
props[key] = isTaggedTelemetryPropertyValue(val)
|
|
304
|
+
? {
|
|
305
|
+
value: filterValidTelemetryProps(val.value, key),
|
|
306
|
+
tag: val.tag,
|
|
219
307
|
}
|
|
220
|
-
|
|
308
|
+
: filterValidTelemetryProps(val, key);
|
|
221
309
|
}
|
|
222
310
|
return props;
|
|
223
311
|
}
|
|
@@ -227,7 +315,11 @@ function getValidTelemetryProps(obj, keysToOmit) {
|
|
|
227
315
|
* Avoids runtime errors with circular references.
|
|
228
316
|
* Not ideal, as will cut values that are not necessarily circular references.
|
|
229
317
|
* Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)
|
|
230
|
-
|
|
318
|
+
*
|
|
319
|
+
* @internal
|
|
320
|
+
*/
|
|
321
|
+
// TODO: Use `unknown` instead (API breaking change)
|
|
322
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
231
323
|
export const getCircularReplacer = () => {
|
|
232
324
|
const seen = new WeakSet();
|
|
233
325
|
return (key, value) => {
|
|
@@ -240,14 +332,23 @@ export const getCircularReplacer = () => {
|
|
|
240
332
|
return value;
|
|
241
333
|
};
|
|
242
334
|
};
|
|
335
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
243
336
|
/**
|
|
244
337
|
* Base class for "trusted" errors we create, whose properties can generally be logged to telemetry safely.
|
|
245
338
|
* All properties set on the object, or passed in (via the constructor or addTelemetryProperties),
|
|
246
339
|
* will be logged in accordance with their tag, if present.
|
|
247
340
|
*
|
|
248
341
|
* PLEASE take care to avoid setting sensitive data on this object without proper tagging!
|
|
342
|
+
*
|
|
343
|
+
* @internal
|
|
249
344
|
*/
|
|
250
345
|
export class LoggingError extends Error {
|
|
346
|
+
get errorInstanceId() {
|
|
347
|
+
return this._errorInstanceId;
|
|
348
|
+
}
|
|
349
|
+
overwriteErrorInstanceId(id) {
|
|
350
|
+
this._errorInstanceId = id;
|
|
351
|
+
}
|
|
251
352
|
/**
|
|
252
353
|
* Create a new LoggingError
|
|
253
354
|
* @param message - Error message to use for Error base class
|
|
@@ -258,8 +359,11 @@ export class LoggingError extends Error {
|
|
|
258
359
|
super(message);
|
|
259
360
|
this.omitPropsFromLogging = omitPropsFromLogging;
|
|
260
361
|
this._errorInstanceId = uuid();
|
|
261
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* Backwards compatibility to appease {@link isFluidError} in old code that may handle this error.
|
|
364
|
+
*/
|
|
262
365
|
// @ts-expect-error - This field shouldn't be referenced in the current version, but needs to exist at runtime.
|
|
366
|
+
// eslint-disable-next-line @typescript-eslint/prefer-as-const
|
|
263
367
|
this.fluidErrorCode = "-";
|
|
264
368
|
// Don't log this list itself, or the private _errorInstanceId
|
|
265
369
|
omitPropsFromLogging.add("omitPropsFromLogging");
|
|
@@ -268,8 +372,19 @@ export class LoggingError extends Error {
|
|
|
268
372
|
this.addTelemetryProperties(props);
|
|
269
373
|
}
|
|
270
374
|
}
|
|
271
|
-
|
|
272
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Determines if a given object is an instance of a LoggingError
|
|
377
|
+
* @param object - any object
|
|
378
|
+
* @returns true if the object is an instance of a LoggingError, false if not.
|
|
379
|
+
*/
|
|
380
|
+
static typeCheck(object) {
|
|
381
|
+
if (typeof object === "object" && object !== null) {
|
|
382
|
+
return (typeof object.addTelemetryProperties === "function" &&
|
|
383
|
+
typeof object.getTelemetryProperties === "function" &&
|
|
384
|
+
typeof object.errorInstanceId === "string");
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
273
388
|
/**
|
|
274
389
|
* Add additional properties to be logged
|
|
275
390
|
*/
|
|
@@ -282,20 +397,34 @@ export class LoggingError extends Error {
|
|
|
282
397
|
getTelemetryProperties() {
|
|
283
398
|
const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);
|
|
284
399
|
// Include non-enumerable props that are not returned by getValidTelemetryProps
|
|
285
|
-
return
|
|
400
|
+
return {
|
|
401
|
+
...taggableProps,
|
|
402
|
+
stack: this.stack,
|
|
403
|
+
message: this.message,
|
|
404
|
+
errorInstanceId: this._errorInstanceId,
|
|
405
|
+
};
|
|
286
406
|
}
|
|
287
407
|
}
|
|
288
|
-
/**
|
|
289
|
-
class
|
|
408
|
+
/**
|
|
409
|
+
* The Error class used when normalizing an external error
|
|
410
|
+
*
|
|
411
|
+
* @internal
|
|
412
|
+
*/
|
|
413
|
+
export const NORMALIZED_ERROR_TYPE = "genericError";
|
|
414
|
+
/**
|
|
415
|
+
* Subclass of LoggingError returned by normalizeError
|
|
416
|
+
*
|
|
417
|
+
* @internal
|
|
418
|
+
*/
|
|
419
|
+
class NormalizedLoggingError extends LoggingError {
|
|
290
420
|
constructor(errorProps) {
|
|
291
421
|
super(errorProps.message);
|
|
292
|
-
|
|
422
|
+
// errorType "genericError" is used as a default value throughout the code.
|
|
423
|
+
// Note that this matches ContainerErrorType/DriverErrorType's genericError
|
|
424
|
+
this.errorType = NORMALIZED_ERROR_TYPE;
|
|
293
425
|
if (errorProps.stack !== undefined) {
|
|
294
426
|
overwriteStack(this, errorProps.stack);
|
|
295
427
|
}
|
|
296
428
|
}
|
|
297
429
|
}
|
|
298
|
-
// errorType "genericError" is used as a default value throughout the code.
|
|
299
|
-
// Note that this matches ContainerErrorType/DriverErrorType's genericError
|
|
300
|
-
NormalizedExternalError.normalizedErrorType = "genericError";
|
|
301
430
|
//# sourceMappingURL=errorLogging.js.map
|