@fluidframework/telemetry-utils 2.0.0-internal.6.3.2 → 2.0.0-internal.6.4.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +4 -2
  4. package/dist/config.js.map +1 -1
  5. package/dist/error.d.ts +13 -5
  6. package/dist/error.d.ts.map +1 -1
  7. package/dist/error.js +10 -2
  8. package/dist/error.js.map +1 -1
  9. package/dist/errorLogging.d.ts +36 -1
  10. package/dist/errorLogging.d.ts.map +1 -1
  11. package/dist/errorLogging.js +50 -14
  12. package/dist/errorLogging.js.map +1 -1
  13. package/dist/events.d.ts.map +1 -1
  14. package/dist/events.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/logger.d.ts +15 -5
  20. package/dist/logger.d.ts.map +1 -1
  21. package/dist/logger.js +37 -15
  22. package/dist/logger.js.map +1 -1
  23. package/dist/utils.d.ts +1 -1
  24. package/dist/utils.js +1 -1
  25. package/dist/utils.js.map +1 -1
  26. package/lib/config.d.ts.map +1 -1
  27. package/lib/config.js +4 -2
  28. package/lib/config.js.map +1 -1
  29. package/lib/error.d.ts +13 -5
  30. package/lib/error.d.ts.map +1 -1
  31. package/lib/error.js +10 -2
  32. package/lib/error.js.map +1 -1
  33. package/lib/errorLogging.d.ts +36 -1
  34. package/lib/errorLogging.d.ts.map +1 -1
  35. package/lib/errorLogging.js +49 -14
  36. package/lib/errorLogging.js.map +1 -1
  37. package/lib/events.d.ts.map +1 -1
  38. package/lib/events.js.map +1 -1
  39. package/lib/index.d.ts +1 -1
  40. package/lib/index.d.ts.map +1 -1
  41. package/lib/index.js +1 -1
  42. package/lib/index.js.map +1 -1
  43. package/lib/logger.d.ts +15 -5
  44. package/lib/logger.d.ts.map +1 -1
  45. package/lib/logger.js +37 -15
  46. package/lib/logger.js.map +1 -1
  47. package/lib/utils.d.ts +1 -1
  48. package/lib/utils.js +1 -1
  49. package/lib/utils.js.map +1 -1
  50. package/package.json +6 -6
  51. package/src/config.ts +4 -2
  52. package/src/error.ts +15 -7
  53. package/src/errorLogging.ts +55 -15
  54. package/src/events.ts +2 -0
  55. package/src/index.ts +1 -0
  56. package/src/logger.ts +68 -37
  57. package/src/utils.ts +1 -1
@@ -27,6 +27,8 @@ const isRegularObject = (value: unknown): boolean => {
27
27
 
28
28
  /**
29
29
  * Inspect the given error for common "safe" props and return them.
30
+ *
31
+ * @internal
30
32
  */
31
33
  export function extractLogSafeErrorProperties(
32
34
  error: unknown,
@@ -95,6 +97,8 @@ function copyProps(
95
97
 
96
98
  /**
97
99
  * Metadata to annotate an error object when annotating or normalizing it
100
+ *
101
+ * @internal
98
102
  */
99
103
  export interface IFluidErrorAnnotations {
100
104
  /**
@@ -121,6 +125,8 @@ function patchLegacyError(
121
125
  * @returns A valid Fluid Error with any provided annotations applied
122
126
  * @param error - The error to normalize
123
127
  * @param annotations - Annotations to apply to the normalized error
128
+ *
129
+ * @internal
124
130
  */
125
131
  export function normalizeError(
126
132
  error: unknown,
@@ -190,6 +196,8 @@ let stackPopulatedOnCreation: boolean | undefined;
190
196
  * For such cases it's better to not read stack property right away, but rather delay it until / if it's needed
191
197
  * Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.
192
198
  * @returns Error object that has stack populated.
199
+ *
200
+ * @internal
193
201
  */
194
202
  export function generateErrorWithStack(): Error {
195
203
  const err = new Error("<<generated stack>>");
@@ -209,6 +217,12 @@ export function generateErrorWithStack(): Error {
209
217
  }
210
218
  }
211
219
 
220
+ /**
221
+ * Generate a stack at this callsite as if an error were thrown from here.
222
+ * @returns the callstack (does not throw)
223
+ *
224
+ * @internal
225
+ */
212
226
  export function generateStack(): string | undefined {
213
227
  return generateErrorWithStack().stack;
214
228
  }
@@ -219,6 +233,8 @@ export function generateStack(): string | undefined {
219
233
  * @param innerError - An error from untrusted/unknown origins
220
234
  * @param newErrorFn - callback that will create a new error given the original error's message
221
235
  * @returns A new error object "wrapping" the given error
236
+ *
237
+ * @internal
222
238
  */
223
239
  export function wrapError<T extends LoggingError>(
224
240
  innerError: unknown,
@@ -258,6 +274,8 @@ export function wrapError<T extends LoggingError>(
258
274
  * The same as wrapError, but also logs the innerError, including the wrapping error's instance ID.
259
275
  *
260
276
  * @typeParam T - The kind of wrapper error to create.
277
+ *
278
+ * @internal
261
279
  */
262
280
  export function wrapErrorAndLog<T extends LoggingError>(
263
281
  innerError: unknown,
@@ -284,8 +302,15 @@ export function wrapErrorAndLog<T extends LoggingError>(
284
302
  return newError;
285
303
  }
286
304
 
287
- function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string): void {
288
- // supposedly setting stack on an Error can throw.
305
+ /**
306
+ * Attempts to overwrite the error's stack
307
+ *
308
+ * There have been reports of certain JS environments where overwriting stack will throw.
309
+ * If that happens, this adds the given stack as the telemetry property "stack2"
310
+ *
311
+ * @internal
312
+ */
313
+ export function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string): void {
289
314
  try {
290
315
  Object.assign(error, { stack });
291
316
  } catch {
@@ -297,6 +322,8 @@ function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string): v
297
322
  * True for any error object that is an (optionally normalized) external error
298
323
  * False for any error we created and raised within the FF codebase via LoggingError base class,
299
324
  * or wrapped in a well-known error type
325
+ *
326
+ * @internal
300
327
  */
301
328
  export function isExternalError(error: unknown): boolean {
302
329
  // LoggingErrors are an internal FF error type. However, an external error can be converted
@@ -322,8 +349,8 @@ export function isTaggedTelemetryPropertyValue(
322
349
 
323
350
  /**
324
351
  * Filter serializable telemetry properties
325
- * @param x - any telemetry prop
326
- * @returns - as-is if x is primitive. returns stringified if x is an array of primitive.
352
+ * @param x - Any telemetry prop
353
+ * @returns As-is if x is primitive. returns stringified if x is an array of primitive.
327
354
  * otherwise returns null since this is what we support at the moment.
328
355
  */
329
356
  function filterValidTelemetryProps(x: unknown, key: string): TelemetryBaseEventPropertyType {
@@ -344,12 +371,15 @@ function isTelemetryEventPropertyValue(x: unknown): x is TelemetryBaseEventPrope
344
371
  case "string":
345
372
  case "number":
346
373
  case "boolean":
347
- case "undefined":
374
+ case "undefined": {
348
375
  return true;
349
- default:
376
+ }
377
+ default: {
350
378
  return false;
379
+ }
351
380
  }
352
381
  }
382
+
353
383
  /**
354
384
  * Walk an object's enumerable properties to find those fit for telemetry.
355
385
  */
@@ -364,14 +394,12 @@ function getValidTelemetryProps(obj: object, keysToOmit: Set<string>): ITelemetr
364
394
  | Tagged<TelemetryEventPropertyTypeExt>;
365
395
 
366
396
  // ensure only valid props get logged, since props of logging error could be in any shape
367
- if (isTaggedTelemetryPropertyValue(val)) {
368
- props[key] = {
369
- value: filterValidTelemetryProps(val.value, key),
370
- tag: val.tag,
371
- };
372
- } else {
373
- props[key] = filterValidTelemetryProps(val, key);
374
- }
397
+ props[key] = isTaggedTelemetryPropertyValue(val)
398
+ ? {
399
+ value: filterValidTelemetryProps(val.value, key),
400
+ tag: val.tag,
401
+ }
402
+ : filterValidTelemetryProps(val, key);
375
403
  }
376
404
  return props;
377
405
  }
@@ -382,6 +410,8 @@ function getValidTelemetryProps(obj: object, keysToOmit: Set<string>): ITelemetr
382
410
  * Avoids runtime errors with circular references.
383
411
  * Not ideal, as will cut values that are not necessarily circular references.
384
412
  * Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)
413
+ *
414
+ * @internal
385
415
  */
386
416
  // TODO: Use `unknown` instead (API breaking change)
387
417
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -405,6 +435,8 @@ export const getCircularReplacer = (): ((key: string, value: unknown) => any) =>
405
435
  * will be logged in accordance with their tag, if present.
406
436
  *
407
437
  * PLEASE take care to avoid setting sensitive data on this object without proper tagging!
438
+ *
439
+ * @internal
408
440
  */
409
441
  export class LoggingError
410
442
  extends Error
@@ -450,7 +482,7 @@ export class LoggingError
450
482
  /**
451
483
  * Determines if a given object is an instance of a LoggingError
452
484
  * @param object - any object
453
- * @returns - true if the object is an instance of a LoggingError, false if not.
485
+ * @returns true if the object is an instance of a LoggingError, false if not.
454
486
  */
455
487
  public static typeCheck(object: unknown): object is LoggingError {
456
488
  if (typeof object === "object" && object !== null) {
@@ -487,8 +519,16 @@ export class LoggingError
487
519
 
488
520
  /**
489
521
  * The Error class used when normalizing an external error
522
+ *
523
+ * @internal
490
524
  */
491
525
  export const NORMALIZED_ERROR_TYPE = "genericError";
526
+
527
+ /**
528
+ * Subclass of LoggingError returned by normalizeError
529
+ *
530
+ * @internal
531
+ */
492
532
  class NormalizedLoggingError extends LoggingError {
493
533
  // errorType "genericError" is used as a default value throughout the code.
494
534
  // Note that this matches ContainerErrorType/DriverErrorType's genericError
package/src/events.ts CHANGED
@@ -3,6 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ // False positive: this is an import from the `events` package, not from Node.
7
+ // eslint-disable-next-line unicorn/prefer-node-protocol
6
8
  import { EventEmitter } from "events";
7
9
  import { ITelemetryLoggerExt } from "./telemetryTypes";
8
10
 
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ export {
31
31
  LoggingError,
32
32
  NORMALIZED_ERROR_TYPE,
33
33
  normalizeError,
34
+ overwriteStack,
34
35
  wrapError,
35
36
  wrapErrorAndLog,
36
37
  } from "./errorLogging";
package/src/logger.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  LogLevel,
15
15
  Tagged,
16
16
  ITelemetryBaseProperties,
17
+ TelemetryBaseEventPropertyType,
17
18
  } from "@fluidframework/core-interfaces";
18
19
  import { IsomorphicPerformance, performance } from "@fluid-internal/client-utils";
19
20
  import { CachedConfigProvider, loggerIsMonitoringContext, mixinMonitoringContext } from "./config";
@@ -304,26 +305,30 @@ export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
304
305
  ? taggableProp
305
306
  : { value: taggableProp, tag: undefined };
306
307
  switch (tag) {
307
- case undefined:
308
+ case undefined: {
308
309
  // No tag means we can log plainly
309
310
  newEvent[key] = value;
310
311
  break;
312
+ }
311
313
  case "PackageData": // For back-compat
312
- case TelemetryDataTag.CodeArtifact:
314
+ case TelemetryDataTag.CodeArtifact: {
313
315
  // For Microsoft applications, CodeArtifact is safe for now
314
316
  // (we don't load 3P code in 1P apps)
315
317
  newEvent[key] = value;
316
318
  break;
317
- case TelemetryDataTag.UserData:
319
+ }
320
+ case TelemetryDataTag.UserData: {
318
321
  // Strip out anything tagged explicitly as UserData.
319
322
  // Alternate strategy would be to hash these props
320
323
  newEvent[key] = "REDACTED (UserData)";
321
324
  break;
322
- default:
325
+ }
326
+ default: {
323
327
  // If we encounter a tag we don't recognize
324
328
  // then we must assume we should scrub.
325
329
  newEvent[key] = "REDACTED (unknown tag)";
326
330
  break;
331
+ }
327
332
  }
328
333
  }
329
334
  this.logger.send(newEvent);
@@ -403,11 +408,7 @@ export class ChildLogger extends TelemetryLogger {
403
408
  return child;
404
409
  }
405
410
 
406
- return new ChildLogger(
407
- baseLogger ? baseLogger : { send(): void {} },
408
- namespace,
409
- properties,
410
- );
411
+ return new ChildLogger(baseLogger ?? { send(): void {} }, namespace, properties);
411
412
  }
412
413
 
413
414
  private constructor(
@@ -488,7 +489,7 @@ export class MultiSinkLogger extends TelemetryLogger {
488
489
  loggers: ITelemetryBaseLogger[] = [],
489
490
  tryInheritProperties?: true,
490
491
  ) {
491
- let realProperties = properties !== undefined ? { ...properties } : undefined;
492
+ let realProperties = properties === undefined ? undefined : { ...properties };
492
493
  if (tryInheritProperties === true) {
493
494
  const merge = (realProperties ??= {});
494
495
  loggers
@@ -629,8 +630,7 @@ export class PerformanceEvent {
629
630
  this.reportEvent("start");
630
631
  }
631
632
 
632
- // eslint-disable-next-line unicorn/no-null
633
- if (typeof window === "object" && window != null && window.performance?.mark) {
633
+ if (typeof window === "object" && window?.performance?.mark) {
634
634
  this.startMark = `${event.eventName}-start`;
635
635
  window.performance.mark(this.startMark);
636
636
  }
@@ -768,54 +768,85 @@ function convertToBasePropertyTypeUntagged(
768
768
  case "string":
769
769
  case "number":
770
770
  case "boolean":
771
- case "undefined":
771
+ case "undefined": {
772
772
  return x;
773
- case "object":
773
+ }
774
+ case "object": {
774
775
  // We assume this is an array or flat object based on the input types
775
776
  return JSON.stringify(x);
776
- default:
777
+ }
778
+ default: {
777
779
  // should never reach this case based on the input types
778
780
  console.error(
779
781
  `convertToBasePropertyTypeUntagged: INVALID PROPERTY (typed as ${typeof x})`,
780
782
  );
781
783
  return `INVALID PROPERTY (typed as ${typeof x})`;
784
+ }
782
785
  }
783
786
  }
784
787
 
785
788
  export const tagData = <
786
789
  T extends TelemetryDataTag,
787
- V extends Record<string, TelemetryEventPropertyTypeExt>,
790
+ V extends Record<
791
+ string,
792
+ TelemetryBaseEventPropertyType | (() => TelemetryBaseEventPropertyType)
793
+ >,
788
794
  >(
789
795
  tag: T,
790
796
  values: V,
791
797
  ): {
792
798
  [P in keyof V]:
793
- | {
794
- value: Exclude<V[P], undefined>;
795
- tag: T;
796
- }
799
+ | (V[P] extends () => TelemetryBaseEventPropertyType
800
+ ? () => {
801
+ value: ReturnType<V[P]>;
802
+ tag: T;
803
+ }
804
+ : {
805
+ value: Exclude<V[P], undefined>;
806
+ tag: T;
807
+ })
797
808
  | (V[P] extends undefined ? undefined : never);
798
809
  } =>
799
- (Object.entries(values) as [keyof V, V[keyof V]][])
800
- .filter((e): e is [keyof V, Exclude<V[keyof V], undefined>] => e[1] !== undefined)
801
- // eslint-disable-next-line unicorn/no-array-reduce
802
- .reduce<{
803
- [P in keyof V]:
804
- | (V[P] extends undefined ? undefined : never)
805
- | { value: Exclude<V[P], undefined>; tag: T };
806
- }>((pv, cv) => {
807
- pv[cv[0]] = { tag, value: cv[1] };
810
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
811
+ Object.entries(values)
812
+ .filter((e) => e[1] !== undefined)
813
+ // eslint-disable-next-line unicorn/no-array-reduce, unicorn/prefer-object-from-entries
814
+ .reduce((pv, cv) => {
815
+ const [key, value] = cv;
816
+ // The ternary form is less legible in this case.
817
+ // eslint-disable-next-line unicorn/prefer-ternary
818
+ if (typeof value === "function") {
819
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
820
+ pv[key] = () => {
821
+ return { tag, value: value() };
822
+ };
823
+ } else {
824
+ pv[key] = { tag, value };
825
+ }
808
826
  return pv;
809
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
810
- }, {} as any);
827
+ }, {}) as ReturnType<typeof tagData>;
811
828
 
812
- export const tagCodeArtifacts = <T extends Record<string, TelemetryEventPropertyTypeExt>>(
829
+ /**
830
+ * Helper function to tag telemetry properties as CodeArtifacts. It supports properties of type
831
+ * TelemetryBaseEventPropertyType as well as getters that return TelemetryBaseEventPropertyType.
832
+ */
833
+ export const tagCodeArtifacts = <
834
+ T extends Record<
835
+ string,
836
+ TelemetryBaseEventPropertyType | (() => TelemetryBaseEventPropertyType)
837
+ >,
838
+ >(
813
839
  values: T,
814
840
  ): {
815
841
  [P in keyof T]:
816
- | {
817
- value: Exclude<T[P], undefined>;
818
- tag: TelemetryDataTag.CodeArtifact;
819
- }
842
+ | (T[P] extends () => TelemetryBaseEventPropertyType
843
+ ? () => {
844
+ value: ReturnType<T[P]>;
845
+ tag: TelemetryDataTag.CodeArtifact;
846
+ }
847
+ : {
848
+ value: Exclude<T[P], undefined>;
849
+ tag: TelemetryDataTag.CodeArtifact;
850
+ })
820
851
  | (T[P] extends undefined ? undefined : never);
821
- } => tagData(TelemetryDataTag.CodeArtifact, values);
852
+ } => tagData<TelemetryDataTag.CodeArtifact, T>(TelemetryDataTag.CodeArtifact, values);
package/src/utils.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  * @param condition - The condition to attest too
14
14
  * @param logger - The logger to log with
15
15
  * @param event - The string or event to log
16
- * @returns - The outcome of the condition
16
+ * @returns The outcome of the condition
17
17
  */
18
18
  export function logIfFalse(
19
19
  condition: unknown,