@fluidframework/telemetry-utils 2.0.0-internal.5.3.4 → 2.0.0-internal.5.4.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.
- package/CHANGELOG.md +15 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -10
- package/dist/config.js.map +1 -1
- package/dist/debugLogger.d.ts +3 -3
- package/dist/debugLogger.d.ts.map +1 -1
- package/dist/debugLogger.js +6 -13
- package/dist/debugLogger.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +56 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +90 -9
- package/dist/logger.js.map +1 -1
- package/dist/mockLogger.d.ts.map +1 -1
- package/dist/mockLogger.js +22 -5
- package/dist/mockLogger.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +8 -10
- package/lib/config.js.map +1 -1
- package/lib/debugLogger.d.ts +3 -3
- package/lib/debugLogger.d.ts.map +1 -1
- package/lib/debugLogger.js +7 -14
- package/lib/debugLogger.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 +56 -2
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +83 -8
- package/lib/logger.js.map +1 -1
- package/lib/mockLogger.d.ts.map +1 -1
- package/lib/mockLogger.js +22 -5
- package/lib/mockLogger.js.map +1 -1
- package/package.json +6 -6
- package/src/config.ts +11 -6
- package/src/debugLogger.ts +12 -17
- package/src/index.ts +8 -0
- package/src/logger.ts +127 -9
- package/src/mockLogger.ts +30 -6
package/src/logger.ts
CHANGED
|
@@ -59,14 +59,39 @@ export interface ITelemetryLoggerPropertyBags {
|
|
|
59
59
|
error?: ITelemetryLoggerPropertyBag;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Attempts to parse number from string.
|
|
64
|
+
* If fails,returns original string.
|
|
65
|
+
* Used to make telemetry data typed (and support math operations, like comparison),
|
|
66
|
+
* in places where we do expect numbers (like contentsize/duration property in http header)
|
|
67
|
+
*/
|
|
68
|
+
export function numberFromString(str: string | null | undefined): string | number | undefined {
|
|
69
|
+
if (str === undefined || str === null) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const num = Number(str);
|
|
73
|
+
return Number.isNaN(num) ? str : num;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function formatTick(tick: number): number {
|
|
77
|
+
return Math.floor(tick);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const eventNamespaceSeparator = ":" as const;
|
|
81
|
+
|
|
62
82
|
/**
|
|
63
83
|
* TelemetryLogger class contains various helper telemetry methods,
|
|
64
84
|
* encoding in one place schemas for various types of Fluid telemetry events.
|
|
65
85
|
* Creates sub-logger that appends properties to all events
|
|
86
|
+
*
|
|
87
|
+
* @deprecated - In a subsequent release this type will no longer be exported, use ITelemetryLogger instead
|
|
66
88
|
*/
|
|
67
89
|
export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
68
|
-
public static readonly eventNamespaceSeparator =
|
|
90
|
+
public static readonly eventNamespaceSeparator = eventNamespaceSeparator;
|
|
69
91
|
|
|
92
|
+
/**
|
|
93
|
+
* @deprecated - use formatTick
|
|
94
|
+
*/
|
|
70
95
|
public static formatTick(tick: number): number {
|
|
71
96
|
return Math.floor(tick);
|
|
72
97
|
}
|
|
@@ -76,6 +101,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
76
101
|
* If fails,returns original string.
|
|
77
102
|
* Used to make telemetry data typed (and support math operations, like comparison),
|
|
78
103
|
* in places where we do expect numbers (like contentsize/duration property in http header)
|
|
104
|
+
* @deprecated - use numberFromString
|
|
79
105
|
*/
|
|
80
106
|
public static numberFromString(str: string | null | undefined): string | number | undefined {
|
|
81
107
|
if (str === undefined || str === null) {
|
|
@@ -163,7 +189,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
163
189
|
|
|
164
190
|
// Will include Nan & Infinity, but probably we do not care
|
|
165
191
|
if (typeof newEvent.duration === "number") {
|
|
166
|
-
newEvent.duration =
|
|
192
|
+
newEvent.duration = formatTick(newEvent.duration);
|
|
167
193
|
}
|
|
168
194
|
|
|
169
195
|
this.send(newEvent);
|
|
@@ -211,6 +237,14 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
211
237
|
if (this.namespace !== undefined) {
|
|
212
238
|
newEvent.eventName = `${this.namespace}${TelemetryLogger.eventNamespaceSeparator}${newEvent.eventName}`;
|
|
213
239
|
}
|
|
240
|
+
return this.extendProperties(newEvent, includeErrorProps);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private extendProperties<T extends ITelemetryLoggerPropertyBag = ITelemetryLoggerPropertyBag>(
|
|
244
|
+
toExtend: T,
|
|
245
|
+
includeErrorProps: boolean,
|
|
246
|
+
) {
|
|
247
|
+
const eventLike: ITelemetryLoggerPropertyBag = toExtend;
|
|
214
248
|
if (this.properties) {
|
|
215
249
|
const properties: (undefined | ITelemetryLoggerPropertyBag)[] = [];
|
|
216
250
|
properties.push(this.properties.all);
|
|
@@ -220,7 +254,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
220
254
|
for (const props of properties) {
|
|
221
255
|
if (props !== undefined) {
|
|
222
256
|
for (const key of Object.keys(props)) {
|
|
223
|
-
if (
|
|
257
|
+
if (eventLike[key] !== undefined) {
|
|
224
258
|
continue;
|
|
225
259
|
}
|
|
226
260
|
const getterOrValue = props[key];
|
|
@@ -228,13 +262,13 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
228
262
|
const value =
|
|
229
263
|
typeof getterOrValue === "function" ? getterOrValue() : getterOrValue;
|
|
230
264
|
if (value !== undefined) {
|
|
231
|
-
|
|
265
|
+
eventLike[key] = value;
|
|
232
266
|
}
|
|
233
267
|
}
|
|
234
268
|
}
|
|
235
269
|
}
|
|
236
270
|
}
|
|
237
|
-
return
|
|
271
|
+
return toExtend;
|
|
238
272
|
}
|
|
239
273
|
}
|
|
240
274
|
|
|
@@ -284,10 +318,29 @@ export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
|
|
|
284
318
|
}
|
|
285
319
|
}
|
|
286
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Create a child logger based on the provided props object
|
|
323
|
+
* @param props - logger is the base logger the child will log to after it's processing, namespace will be prefixed to all event names, properties are default properties that will be applied events.
|
|
324
|
+
*
|
|
325
|
+
* @remarks
|
|
326
|
+
* Passing in no props object (i.e. undefined) will return a logger that is effectively a no-op.
|
|
327
|
+
*/
|
|
328
|
+
export function createChildLogger(props?: {
|
|
329
|
+
logger?: ITelemetryBaseLogger;
|
|
330
|
+
namespace?: string;
|
|
331
|
+
properties?: ITelemetryLoggerPropertyBags;
|
|
332
|
+
}): ITelemetryLoggerExt {
|
|
333
|
+
if (props === undefined) {
|
|
334
|
+
return new TelemetryNullLogger();
|
|
335
|
+
}
|
|
336
|
+
return ChildLogger.create(props?.logger, props?.namespace, props?.properties);
|
|
337
|
+
}
|
|
338
|
+
|
|
287
339
|
/**
|
|
288
340
|
* ChildLogger class contains various helper telemetry methods,
|
|
289
341
|
* encoding in one place schemas for various types of Fluid telemetry events.
|
|
290
342
|
* Creates sub-logger that appends properties to all events
|
|
343
|
+
* @deprecated - Use createChildLogger instead
|
|
291
344
|
*/
|
|
292
345
|
export class ChildLogger extends TelemetryLogger {
|
|
293
346
|
/**
|
|
@@ -363,20 +416,60 @@ export class ChildLogger extends TelemetryLogger {
|
|
|
363
416
|
}
|
|
364
417
|
}
|
|
365
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Create a logger which logs to multiple other loggers based on the provided props object
|
|
421
|
+
* @param props - loggers are the base loggers that will logged to after it's processing, namespace will be prefixed to all event names, properties are default properties that will be applied events.
|
|
422
|
+
* tryInheritProperties will attempted to copy those loggers properties to this loggers if they are of a known type e.g. one from this package
|
|
423
|
+
*/
|
|
424
|
+
export function createMultiSinkLogger(props: {
|
|
425
|
+
namespace?: string;
|
|
426
|
+
properties?: ITelemetryLoggerPropertyBags;
|
|
427
|
+
loggers?: (ITelemetryBaseLogger | undefined)[];
|
|
428
|
+
tryInheritProperties?: true;
|
|
429
|
+
}): ITelemetryLoggerExt {
|
|
430
|
+
return new MultiSinkLogger(
|
|
431
|
+
props.namespace,
|
|
432
|
+
props.properties,
|
|
433
|
+
props.loggers?.filter((l): l is ITelemetryBaseLogger => l !== undefined),
|
|
434
|
+
props.tryInheritProperties,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
366
438
|
/**
|
|
367
439
|
* Multi-sink logger
|
|
368
440
|
* Takes multiple ITelemetryBaseLogger objects (sinks) and logs all events into each sink
|
|
441
|
+
* @deprecated - use createMultiSinkLogger instead
|
|
369
442
|
*/
|
|
370
443
|
export class MultiSinkLogger extends TelemetryLogger {
|
|
371
|
-
protected loggers: ITelemetryBaseLogger[]
|
|
372
|
-
|
|
444
|
+
protected loggers: ITelemetryBaseLogger[];
|
|
373
445
|
/**
|
|
374
446
|
* Create multiple sink logger (i.e. logger that sends events to multiple sinks)
|
|
375
447
|
* @param namespace - Telemetry event name prefix to add to all events
|
|
376
448
|
* @param properties - Base properties to add to all events
|
|
449
|
+
* @param loggers - The list of loggers to use as sinks
|
|
450
|
+
* @param tryInheritProperties - Will attempted to copy those loggers properties to this loggers if they are of a known type e.g. one from this package
|
|
377
451
|
*/
|
|
378
|
-
constructor(
|
|
379
|
-
|
|
452
|
+
constructor(
|
|
453
|
+
namespace?: string,
|
|
454
|
+
properties?: ITelemetryLoggerPropertyBags,
|
|
455
|
+
loggers: ITelemetryBaseLogger[] = [],
|
|
456
|
+
tryInheritProperties?: true,
|
|
457
|
+
) {
|
|
458
|
+
let realProperties = properties !== undefined ? { ...properties } : undefined;
|
|
459
|
+
if (tryInheritProperties === true) {
|
|
460
|
+
const merge = (realProperties ??= {});
|
|
461
|
+
loggers
|
|
462
|
+
.filter((l): l is this => l instanceof TelemetryLogger)
|
|
463
|
+
.map((l) => l.properties ?? {})
|
|
464
|
+
.forEach((cv) => {
|
|
465
|
+
Object.keys(cv).forEach((k) => {
|
|
466
|
+
merge[k] = { ...cv[k], ...merge?.[k] };
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
super(namespace, realProperties);
|
|
472
|
+
this.loggers = loggers;
|
|
380
473
|
}
|
|
381
474
|
|
|
382
475
|
/**
|
|
@@ -561,6 +654,7 @@ export class PerformanceEvent {
|
|
|
561
654
|
/**
|
|
562
655
|
* Logger that is useful for UT
|
|
563
656
|
* It can be used in places where logger instance is required, but events should be not send over.
|
|
657
|
+
* @deprecated - Use createChildLogger instead
|
|
564
658
|
*/
|
|
565
659
|
export class TelemetryUTLogger implements ITelemetryLoggerExt {
|
|
566
660
|
public send(event: ITelemetryBaseEvent): void {}
|
|
@@ -596,6 +690,7 @@ export class TelemetryUTLogger implements ITelemetryLoggerExt {
|
|
|
596
690
|
/**
|
|
597
691
|
* Null logger
|
|
598
692
|
* It can be used in places where logger instance is required, but events should be not send over.
|
|
693
|
+
* @deprecated - for internal use only
|
|
599
694
|
*/
|
|
600
695
|
export class BaseTelemetryNullLogger implements ITelemetryBaseLogger {
|
|
601
696
|
/**
|
|
@@ -611,6 +706,7 @@ export class BaseTelemetryNullLogger implements ITelemetryBaseLogger {
|
|
|
611
706
|
/**
|
|
612
707
|
* Null logger
|
|
613
708
|
* It can be used in places where logger instance is required, but events should be not send over.
|
|
709
|
+
* @deprecated - for internal use only
|
|
614
710
|
*/
|
|
615
711
|
export class TelemetryNullLogger implements ITelemetryLoggerExt {
|
|
616
712
|
public send(event: ITelemetryBaseEvent): void {}
|
|
@@ -676,3 +772,25 @@ function convertToBasePropertyTypeUntagged(
|
|
|
676
772
|
return `INVALID PROPERTY (typed as ${typeof x})`;
|
|
677
773
|
}
|
|
678
774
|
}
|
|
775
|
+
|
|
776
|
+
export const tagData = <
|
|
777
|
+
T extends TelemetryDataTag,
|
|
778
|
+
V extends Record<string, TelemetryEventPropertyTypeExt>,
|
|
779
|
+
>(
|
|
780
|
+
tag: T,
|
|
781
|
+
values: V,
|
|
782
|
+
) =>
|
|
783
|
+
(Object.entries(values) as [keyof V, V[keyof V]][])
|
|
784
|
+
.filter((e): e is [keyof V, Exclude<V[keyof V], undefined>] => e[1] !== undefined)
|
|
785
|
+
.reduce<{
|
|
786
|
+
[P in keyof V]:
|
|
787
|
+
| (V[P] extends undefined ? undefined : never)
|
|
788
|
+
| { value: Exclude<V[P], undefined>; tag: T };
|
|
789
|
+
}>((pv, cv) => {
|
|
790
|
+
pv[cv[0]] = { tag, value: cv[1] };
|
|
791
|
+
return pv;
|
|
792
|
+
}, {} as any);
|
|
793
|
+
|
|
794
|
+
export const tagCodeArtifacts = <T extends Record<string, TelemetryEventPropertyTypeExt>>(
|
|
795
|
+
values: T,
|
|
796
|
+
) => tagData(TelemetryDataTag.CodeArtifact, values);
|
package/src/mockLogger.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { ITelemetryBaseEvent } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert } from "@fluidframework/common-utils";
|
|
8
8
|
import { TelemetryLogger } from "./logger";
|
|
9
|
-
import { ITelemetryLoggerExt } from "./telemetryTypes";
|
|
9
|
+
import { ITelemetryLoggerExt, ITelemetryPropertiesExt } from "./telemetryTypes";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The MockLogger records events sent to it, and then can walk back over those events
|
|
@@ -185,7 +185,6 @@ ${JSON.stringify(actualEvents)}`);
|
|
|
185
185
|
inlineDetailsProp: boolean,
|
|
186
186
|
): boolean {
|
|
187
187
|
const { details, ...actualForMatching } = actual;
|
|
188
|
-
let detailsExpanded = { details };
|
|
189
188
|
// "details" is used in a lot of telemetry logs to group a bunch of properties together and stringify them.
|
|
190
189
|
// Some of the properties in the expected event may be inside "details". So, if inlineDetailsProp is true,
|
|
191
190
|
// extract the properties from "details" in the actual event and inline them in the actual event.
|
|
@@ -194,10 +193,35 @@ ${JSON.stringify(actualEvents)}`);
|
|
|
194
193
|
typeof details === "string",
|
|
195
194
|
0x6c9 /* Details should a JSON stringified string if inlineDetailsProp is true */,
|
|
196
195
|
);
|
|
197
|
-
detailsExpanded = JSON.parse(details);
|
|
196
|
+
const detailsExpanded = JSON.parse(details);
|
|
197
|
+
return matchObjects({ ...actualForMatching, ...detailsExpanded }, expected);
|
|
198
198
|
}
|
|
199
|
-
|
|
200
|
-
const masked = { ...actualExpanded, ...expected };
|
|
201
|
-
return JSON.stringify(masked) === JSON.stringify(actualExpanded);
|
|
199
|
+
return matchObjects(actual, expected);
|
|
202
200
|
}
|
|
203
201
|
}
|
|
202
|
+
|
|
203
|
+
function matchObjects(actual: ITelemetryPropertiesExt, expected: ITelemetryPropertiesExt) {
|
|
204
|
+
for (const [expectedKey, expectedValue] of Object.entries(expected)) {
|
|
205
|
+
const actualValue = actual[expectedKey];
|
|
206
|
+
if (
|
|
207
|
+
!Array.isArray(expectedValue) &&
|
|
208
|
+
expectedValue !== null &&
|
|
209
|
+
typeof expectedValue === "object"
|
|
210
|
+
) {
|
|
211
|
+
if (
|
|
212
|
+
Array.isArray(actualValue) ||
|
|
213
|
+
actualValue === null ||
|
|
214
|
+
typeof actualValue !== "object" ||
|
|
215
|
+
!matchObjects(
|
|
216
|
+
actualValue as ITelemetryPropertiesExt,
|
|
217
|
+
expectedValue as ITelemetryPropertiesExt,
|
|
218
|
+
)
|
|
219
|
+
) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
} else if (JSON.stringify(actualValue) !== JSON.stringify(expectedValue)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|