@fluidframework/telemetry-utils 2.0.0-internal.3.0.1 → 2.0.0-internal.3.1.0
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 +11 -13
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/debugLogger.d.ts.map +1 -1
- package/dist/debugLogger.js.map +1 -1
- package/dist/errorLogging.d.ts +5 -5
- package/dist/errorLogging.d.ts.map +1 -1
- package/dist/errorLogging.js +20 -18
- package/dist/errorLogging.js.map +1 -1
- package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/dist/eventEmitterWithErrorHandling.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/fluidErrorBase.d.ts.map +1 -1
- package/dist/fluidErrorBase.js +4 -4
- package/dist/fluidErrorBase.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +13 -12
- package/dist/logger.js.map +1 -1
- package/dist/mockLogger.d.ts +5 -5
- package/dist/mockLogger.d.ts.map +1 -1
- package/dist/mockLogger.js +5 -5
- package/dist/mockLogger.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sampledTelemetryHelper.d.ts.map +1 -1
- package/dist/sampledTelemetryHelper.js.map +1 -1
- package/dist/telemetryTypes.d.ts.map +1 -1
- package/dist/telemetryTypes.js.map +1 -1
- package/dist/thresholdCounter.d.ts.map +1 -1
- package/dist/thresholdCounter.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +3 -3
- package/lib/config.js.map +1 -1
- package/lib/debugLogger.d.ts.map +1 -1
- package/lib/debugLogger.js +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/errorLogging.d.ts +5 -5
- package/lib/errorLogging.d.ts.map +1 -1
- package/lib/errorLogging.js +20 -18
- package/lib/errorLogging.js.map +1 -1
- package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/lib/eventEmitterWithErrorHandling.js.map +1 -1
- package/lib/events.d.ts.map +1 -1
- package/lib/events.js.map +1 -1
- package/lib/fluidErrorBase.d.ts.map +1 -1
- package/lib/fluidErrorBase.js +4 -4
- package/lib/fluidErrorBase.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +14 -13
- package/lib/logger.js.map +1 -1
- package/lib/mockLogger.d.ts +5 -5
- package/lib/mockLogger.d.ts.map +1 -1
- package/lib/mockLogger.js +5 -5
- package/lib/mockLogger.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sampledTelemetryHelper.d.ts.map +1 -1
- package/lib/sampledTelemetryHelper.js.map +1 -1
- package/lib/telemetryTypes.d.ts.map +1 -1
- package/lib/telemetryTypes.js.map +1 -1
- package/lib/thresholdCounter.d.ts.map +1 -1
- package/lib/thresholdCounter.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js.map +1 -1
- package/package.json +107 -110
- package/prettier.config.cjs +1 -1
- package/src/config.ts +173 -172
- package/src/debugLogger.ts +118 -111
- package/src/errorLogging.ts +302 -299
- package/src/eventEmitterWithErrorHandling.ts +16 -12
- package/src/events.ts +26 -26
- package/src/fluidErrorBase.ts +42 -38
- package/src/index.ts +26 -16
- package/src/logger.ts +541 -533
- package/src/mockLogger.ts +113 -107
- package/src/packageVersion.ts +1 -1
- package/src/sampledTelemetryHelper.ts +122 -122
- package/src/telemetryTypes.ts +37 -37
- package/src/thresholdCounter.ts +34 -34
- package/src/utils.ts +15 -15
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
package/src/config.ts
CHANGED
|
@@ -11,30 +11,31 @@ export type ConfigTypes = string | number | boolean | number[] | string[] | bool
|
|
|
11
11
|
* Base interface for providing configurations to enable/disable/control features
|
|
12
12
|
*/
|
|
13
13
|
export interface IConfigProviderBase {
|
|
14
|
-
|
|
14
|
+
getRawConfig(name: string): ConfigTypes;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Explicitly typed interface for reading configurations
|
|
19
19
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
export interface IConfigProvider extends IConfigProviderBase {
|
|
21
|
+
getBoolean(name: string): boolean | undefined;
|
|
22
|
+
getNumber(name: string): number | undefined;
|
|
23
|
+
getString(name: string): string | undefined;
|
|
24
|
+
getBooleanArray(name: string): boolean[] | undefined;
|
|
25
|
+
getNumberArray(name: string): number[] | undefined;
|
|
26
|
+
getStringArray(name: string): string[] | undefined;
|
|
27
|
+
}
|
|
28
28
|
/**
|
|
29
29
|
* Creates a base configuration provider based on `sessionStorage`
|
|
30
30
|
*
|
|
31
31
|
* @returns A lazy initialized base configuration provider with `sessionStorage` as the underlying config store
|
|
32
32
|
*/
|
|
33
|
-
export const sessionStorageConfigProvider =
|
|
34
|
-
|
|
33
|
+
export const sessionStorageConfigProvider = new Lazy<IConfigProviderBase>(() =>
|
|
34
|
+
inMemoryConfigProvider(safeSessionStorage()),
|
|
35
|
+
);
|
|
35
36
|
|
|
36
37
|
const NullConfigProvider: IConfigProviderBase = {
|
|
37
|
-
|
|
38
|
+
getRawConfig: () => undefined,
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -44,45 +45,44 @@ const NullConfigProvider: IConfigProviderBase = {
|
|
|
44
45
|
* @returns A base configuration provider with
|
|
45
46
|
* the supplied `Storage` instance as the underlying config store
|
|
46
47
|
*/
|
|
47
|
-
export const inMemoryConfigProvider =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return NullConfigProvider;
|
|
48
|
+
export const inMemoryConfigProvider = (storage: Storage | undefined): IConfigProviderBase => {
|
|
49
|
+
if (storage !== undefined && storage !== null) {
|
|
50
|
+
return new CachedConfigProvider({
|
|
51
|
+
getRawConfig: (name: string) => {
|
|
52
|
+
try {
|
|
53
|
+
return stronglyTypedParse(storage.getItem(name) ?? undefined)?.raw;
|
|
54
|
+
} catch {}
|
|
55
|
+
return undefined;
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return NullConfigProvider;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
interface ConfigTypeStringToType {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
number: number;
|
|
64
|
+
string: string;
|
|
65
|
+
boolean: boolean;
|
|
66
|
+
["number[]"]: number[];
|
|
67
|
+
["string[]"]: string[];
|
|
68
|
+
["boolean[]"]: boolean[];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
type PrimitiveTypeStrings = "number" | "string" | "boolean";
|
|
72
72
|
|
|
73
73
|
function isPrimitiveType(type: string): type is PrimitiveTypeStrings {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
switch (type) {
|
|
75
|
+
case "boolean":
|
|
76
|
+
case "number":
|
|
77
|
+
case "string":
|
|
78
|
+
return true;
|
|
79
|
+
default:
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
interface StronglyTypedValue extends Partial<ConfigTypeStringToType> {
|
|
85
|
-
|
|
85
|
+
raw: ConfigTypes;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Takes any supported config type, and returns the value with a strong type. If the type of
|
|
@@ -94,170 +94,171 @@ interface StronglyTypedValue extends Partial<ConfigTypeStringToType> {
|
|
|
94
94
|
* will be return with a string type for the consumer to handle further if necessary.
|
|
95
95
|
*/
|
|
96
96
|
function stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
97
|
+
let output: ConfigTypes = input;
|
|
98
|
+
let defaultReturn: Pick<StronglyTypedValue, "raw" | "string"> | undefined;
|
|
99
|
+
// we do special handling for strings to try and coerce
|
|
100
|
+
// them into a config type if we can. This makes it easy
|
|
101
|
+
// for config sources like sessionStorage which only
|
|
102
|
+
// holds strings
|
|
103
|
+
if (typeof input === "string") {
|
|
104
|
+
try {
|
|
105
|
+
output = JSON.parse(input);
|
|
106
|
+
// we succeeded in parsing, but we don't support parsing
|
|
107
|
+
// for any object as we can't do it type safely
|
|
108
|
+
// so in this case, the default return will be string
|
|
109
|
+
// rather than undefined, and the consumer
|
|
110
|
+
// can parse, as we don't want to provide
|
|
111
|
+
// a false sense of security by just
|
|
112
|
+
// casting.
|
|
113
|
+
defaultReturn = { raw: input, string: input };
|
|
114
|
+
} catch {}
|
|
115
|
+
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (output === undefined) {
|
|
118
|
+
return defaultReturn;
|
|
119
|
+
}
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
const outputType = typeof output;
|
|
122
|
+
if (isPrimitiveType(outputType)) {
|
|
123
|
+
return { ...defaultReturn, raw: input, [outputType]: output };
|
|
124
|
+
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
126
|
+
if (Array.isArray(output)) {
|
|
127
|
+
const firstType = typeof output[0];
|
|
128
|
+
// ensure the first elements is a primitive type
|
|
129
|
+
if (!isPrimitiveType(firstType)) {
|
|
130
|
+
return defaultReturn;
|
|
131
|
+
}
|
|
132
|
+
// ensue all the elements types are homogeneous
|
|
133
|
+
// aka they all have the same type as the first
|
|
134
|
+
for (const v of output) {
|
|
135
|
+
if (typeof v !== firstType) {
|
|
136
|
+
return defaultReturn;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { ...defaultReturn, raw: input, [`${firstType}[]`]: output };
|
|
140
|
+
}
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
return defaultReturn;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
/** `sessionStorage` is undefined in some environments such as Node */
|
|
146
146
|
const safeSessionStorage = (): Storage | undefined => {
|
|
147
|
-
|
|
147
|
+
return globalThis.sessionStorage;
|
|
148
148
|
};
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
151
|
* Implementation of {@link IConfigProvider} which contains nested {@link IConfigProviderBase} instances
|
|
152
152
|
*/
|
|
153
153
|
export class CachedConfigProvider implements IConfigProvider {
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
private readonly configCache = new Map<string, StronglyTypedValue>();
|
|
155
|
+
private readonly orderedBaseProviders: (IConfigProviderBase | undefined)[];
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
157
|
+
constructor(...orderedBaseProviders: (IConfigProviderBase | undefined)[]) {
|
|
158
|
+
this.orderedBaseProviders = [];
|
|
159
|
+
const knownProviders = new Set<IConfigProviderBase>();
|
|
160
|
+
const candidateProviders = [...orderedBaseProviders];
|
|
161
|
+
while (candidateProviders.length > 0) {
|
|
162
|
+
const baseProvider = candidateProviders.shift()!;
|
|
163
|
+
if (
|
|
164
|
+
baseProvider !== undefined &&
|
|
165
|
+
isConfigProviderBase(baseProvider) &&
|
|
166
|
+
!knownProviders.has(baseProvider)
|
|
167
|
+
) {
|
|
168
|
+
knownProviders.add(baseProvider);
|
|
169
|
+
if (baseProvider instanceof CachedConfigProvider) {
|
|
170
|
+
candidateProviders.push(...baseProvider.orderedBaseProviders);
|
|
171
|
+
} else {
|
|
172
|
+
this.orderedBaseProviders.push(baseProvider);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
getBoolean(name: string): boolean | undefined {
|
|
178
|
+
return this.getCacheEntry(name)?.boolean;
|
|
179
|
+
}
|
|
180
|
+
getNumber(name: string): number | undefined {
|
|
181
|
+
return this.getCacheEntry(name)?.number;
|
|
182
|
+
}
|
|
183
|
+
getString(name: string): string | undefined {
|
|
184
|
+
return this.getCacheEntry(name)?.string;
|
|
185
|
+
}
|
|
186
|
+
getBooleanArray(name: string): boolean[] | undefined {
|
|
187
|
+
return this.getCacheEntry(name)?.["boolean[]"];
|
|
188
|
+
}
|
|
189
|
+
getNumberArray(name: string): number[] | undefined {
|
|
190
|
+
return this.getCacheEntry(name)?.["number[]"];
|
|
191
|
+
}
|
|
192
|
+
getStringArray(name: string): string[] | undefined {
|
|
193
|
+
return this.getCacheEntry(name)?.["string[]"];
|
|
194
|
+
}
|
|
196
195
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
getRawConfig(name: string): ConfigTypes {
|
|
197
|
+
return this.getCacheEntry(name)?.raw;
|
|
198
|
+
}
|
|
200
199
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
200
|
+
private getCacheEntry(name: string): StronglyTypedValue | undefined {
|
|
201
|
+
if (!this.configCache.has(name)) {
|
|
202
|
+
for (const provider of this.orderedBaseProviders) {
|
|
203
|
+
const parsed = stronglyTypedParse(provider?.getRawConfig(name));
|
|
204
|
+
if (parsed !== undefined) {
|
|
205
|
+
this.configCache.set(name, parsed);
|
|
206
|
+
return parsed;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// configs are immutable, if the first lookup returned no results, all lookups should
|
|
210
|
+
this.configCache.set(name, { raw: undefined });
|
|
211
|
+
}
|
|
212
|
+
return this.configCache.get(name);
|
|
213
|
+
}
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
/**
|
|
218
217
|
* A type containing both a telemetry logger and a configuration provider
|
|
219
218
|
*/
|
|
220
|
-
export interface MonitoringContext<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
config: IConfigProvider;
|
|
224
|
-
logger: L;
|
|
219
|
+
export interface MonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger> {
|
|
220
|
+
config: IConfigProvider;
|
|
221
|
+
logger: L;
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
export function loggerIsMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
obj: L,
|
|
226
|
+
): obj is L & MonitoringContext<L> {
|
|
227
|
+
const maybeConfig = obj as Partial<MonitoringContext<L>> | undefined;
|
|
228
|
+
return isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;
|
|
231
229
|
}
|
|
232
230
|
|
|
233
231
|
export function loggerToMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
232
|
+
logger: L,
|
|
233
|
+
): MonitoringContext<L> {
|
|
234
|
+
if (loggerIsMonitoringContext<L>(logger)) {
|
|
235
|
+
return logger;
|
|
236
|
+
}
|
|
237
|
+
return mixinMonitoringContext<L>(logger, sessionStorageConfigProvider.value);
|
|
239
238
|
}
|
|
240
239
|
|
|
241
240
|
export function mixinMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
241
|
+
logger: L,
|
|
242
|
+
...configs: (IConfigProviderBase | undefined)[]
|
|
243
|
+
) {
|
|
244
|
+
if (loggerIsMonitoringContext<L>(logger)) {
|
|
245
|
+
throw new Error("Logger is already a monitoring context");
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* this is the tricky bit we use for now to smuggle monitoring context around.
|
|
249
|
+
* To the logger we mixin both config and itself, so mc.logger === logger as it is self-referential.
|
|
250
|
+
* We then expose it as a Monitoring context, so via types we hide the outer logger methods.
|
|
251
|
+
* To layers that expect just a logger we can pass mc.logger, but this is still a MonitoringContext
|
|
252
|
+
* so if a deeper layer then converts that logger to a monitoring context it can find the smuggled properties
|
|
253
|
+
* of the MonitoringContext and get the config provider.
|
|
254
|
+
*/
|
|
255
|
+
const mc: L & Partial<MonitoringContext<L>> = logger;
|
|
256
|
+
mc.config = new CachedConfigProvider(...configs);
|
|
257
|
+
mc.logger = logger;
|
|
258
|
+
return mc as MonitoringContext<L>;
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
function isConfigProviderBase(obj: unknown): obj is IConfigProviderBase {
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
const maybeConfig = obj as Partial<IConfigProviderBase> | undefined;
|
|
263
|
+
return typeof maybeConfig?.getRawConfig === "function";
|
|
263
264
|
}
|