@fluidframework/telemetry-utils 2.0.0-dev-rc.5.0.0.271717 → 2.0.0-dev-rc.5.0.0.272889

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 (108) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/api-extractor/api-extractor-lint-bundle.json +5 -0
  3. package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
  4. package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
  5. package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
  6. package/api-extractor/api-extractor-lint-public.esm.json +5 -0
  7. package/api-extractor.json +1 -1
  8. package/api-report/telemetry-utils.alpha.api.md +13 -28
  9. package/api-report/telemetry-utils.beta.api.md +7 -7
  10. package/api-report/telemetry-utils.public.api.md +7 -7
  11. package/dist/config.d.ts +2 -2
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/error.d.ts +5 -5
  15. package/dist/error.d.ts.map +1 -1
  16. package/dist/error.js.map +1 -1
  17. package/dist/errorLogging.d.ts +1 -1
  18. package/dist/errorLogging.d.ts.map +1 -1
  19. package/dist/errorLogging.js +21 -8
  20. package/dist/errorLogging.js.map +1 -1
  21. package/dist/eventEmitterWithErrorHandling.d.ts +2 -2
  22. package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
  23. package/dist/eventEmitterWithErrorHandling.js.map +1 -1
  24. package/dist/index.d.ts +8 -8
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/logger.d.ts +3 -5
  29. package/dist/logger.d.ts.map +1 -1
  30. package/dist/logger.js.map +1 -1
  31. package/dist/mockLogger.d.ts +34 -7
  32. package/dist/mockLogger.d.ts.map +1 -1
  33. package/dist/mockLogger.js +31 -10
  34. package/dist/mockLogger.js.map +1 -1
  35. package/dist/sampledTelemetryHelper.d.ts +6 -2
  36. package/dist/sampledTelemetryHelper.d.ts.map +1 -1
  37. package/dist/sampledTelemetryHelper.js +10 -2
  38. package/dist/sampledTelemetryHelper.js.map +1 -1
  39. package/dist/telemetryEventBatcher.d.ts +4 -27
  40. package/dist/telemetryEventBatcher.d.ts.map +1 -1
  41. package/dist/telemetryEventBatcher.js +5 -28
  42. package/dist/telemetryEventBatcher.js.map +1 -1
  43. package/dist/telemetryTypes.d.ts +32 -18
  44. package/dist/telemetryTypes.d.ts.map +1 -1
  45. package/dist/telemetryTypes.js.map +1 -1
  46. package/dist/thresholdCounter.d.ts +1 -1
  47. package/dist/thresholdCounter.d.ts.map +1 -1
  48. package/dist/thresholdCounter.js.map +1 -1
  49. package/dist/utils.d.ts +13 -1
  50. package/dist/utils.d.ts.map +1 -1
  51. package/dist/utils.js +16 -1
  52. package/dist/utils.js.map +1 -1
  53. package/lib/config.d.ts +2 -2
  54. package/lib/config.d.ts.map +1 -1
  55. package/lib/config.js.map +1 -1
  56. package/lib/error.d.ts +5 -5
  57. package/lib/error.d.ts.map +1 -1
  58. package/lib/error.js.map +1 -1
  59. package/lib/errorLogging.d.ts +1 -1
  60. package/lib/errorLogging.d.ts.map +1 -1
  61. package/lib/errorLogging.js +21 -8
  62. package/lib/errorLogging.js.map +1 -1
  63. package/lib/eventEmitterWithErrorHandling.d.ts +2 -2
  64. package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
  65. package/lib/eventEmitterWithErrorHandling.js.map +1 -1
  66. package/lib/index.d.ts +8 -8
  67. package/lib/index.d.ts.map +1 -1
  68. package/lib/index.js +2 -2
  69. package/lib/index.js.map +1 -1
  70. package/lib/logger.d.ts +3 -5
  71. package/lib/logger.d.ts.map +1 -1
  72. package/lib/logger.js.map +1 -1
  73. package/lib/mockLogger.d.ts +34 -7
  74. package/lib/mockLogger.d.ts.map +1 -1
  75. package/lib/mockLogger.js +29 -9
  76. package/lib/mockLogger.js.map +1 -1
  77. package/lib/sampledTelemetryHelper.d.ts +6 -2
  78. package/lib/sampledTelemetryHelper.d.ts.map +1 -1
  79. package/lib/sampledTelemetryHelper.js +10 -2
  80. package/lib/sampledTelemetryHelper.js.map +1 -1
  81. package/lib/telemetryEventBatcher.d.ts +4 -27
  82. package/lib/telemetryEventBatcher.d.ts.map +1 -1
  83. package/lib/telemetryEventBatcher.js +5 -28
  84. package/lib/telemetryEventBatcher.js.map +1 -1
  85. package/lib/telemetryTypes.d.ts +32 -18
  86. package/lib/telemetryTypes.d.ts.map +1 -1
  87. package/lib/telemetryTypes.js.map +1 -1
  88. package/lib/thresholdCounter.d.ts +1 -1
  89. package/lib/thresholdCounter.d.ts.map +1 -1
  90. package/lib/thresholdCounter.js.map +1 -1
  91. package/lib/utils.d.ts +13 -1
  92. package/lib/utils.d.ts.map +1 -1
  93. package/lib/utils.js +14 -0
  94. package/lib/utils.js.map +1 -1
  95. package/package.json +18 -9
  96. package/src/config.ts +16 -16
  97. package/src/error.ts +13 -13
  98. package/src/errorLogging.ts +31 -15
  99. package/src/eventEmitterWithErrorHandling.ts +3 -3
  100. package/src/index.ts +17 -12
  101. package/src/logger.ts +21 -21
  102. package/src/mockLogger.ts +71 -21
  103. package/src/sampledTelemetryHelper.ts +15 -5
  104. package/src/telemetryEventBatcher.ts +6 -47
  105. package/src/telemetryTypes.ts +38 -19
  106. package/src/thresholdCounter.ts +1 -1
  107. package/src/utils.ts +17 -2
  108. package/tsdoc.json +4 -0
package/src/config.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import {
6
+ import type {
7
7
  ConfigTypes,
8
8
  IConfigProviderBase,
9
9
  ITelemetryBaseLogger,
@@ -11,7 +11,7 @@ import {
11
11
  import { Lazy } from "@fluidframework/core-utils/internal";
12
12
 
13
13
  import { createChildLogger, tagCodeArtifacts } from "./logger.js";
14
- import { ITelemetryLoggerExt } from "./telemetryTypes.js";
14
+ import type { ITelemetryLoggerExt } from "./telemetryTypes.js";
15
15
 
16
16
  /**
17
17
  * Explicitly typed interface for reading configurations.
@@ -193,7 +193,7 @@ export class CachedConfigProvider implements IConfigProvider {
193
193
  private readonly configCache = new Map<string, StronglyTypedValue>();
194
194
  private readonly orderedBaseProviders: (IConfigProviderBase | undefined)[];
195
195
 
196
- constructor(
196
+ public constructor(
197
197
  private readonly logger?: ITelemetryBaseLogger,
198
198
  ...orderedBaseProviders: (IConfigProviderBase | undefined)[]
199
199
  ) {
@@ -216,26 +216,26 @@ export class CachedConfigProvider implements IConfigProvider {
216
216
  }
217
217
  }
218
218
  }
219
- getBoolean(name: string): boolean | undefined {
219
+ public getBoolean(name: string): boolean | undefined {
220
220
  return this.getCacheEntry(name)?.boolean;
221
221
  }
222
- getNumber(name: string): number | undefined {
222
+ public getNumber(name: string): number | undefined {
223
223
  return this.getCacheEntry(name)?.number;
224
224
  }
225
- getString(name: string): string | undefined {
225
+ public getString(name: string): string | undefined {
226
226
  return this.getCacheEntry(name)?.string;
227
227
  }
228
- getBooleanArray(name: string): boolean[] | undefined {
228
+ public getBooleanArray(name: string): boolean[] | undefined {
229
229
  return this.getCacheEntry(name)?.["boolean[]"];
230
230
  }
231
- getNumberArray(name: string): number[] | undefined {
231
+ public getNumberArray(name: string): number[] | undefined {
232
232
  return this.getCacheEntry(name)?.["number[]"];
233
233
  }
234
- getStringArray(name: string): string[] | undefined {
234
+ public getStringArray(name: string): string[] | undefined {
235
235
  return this.getCacheEntry(name)?.["string[]"];
236
236
  }
237
237
 
238
- getRawConfig(name: string): ConfigTypes {
238
+ public getRawConfig(name: string): ConfigTypes {
239
239
  return this.getCacheEntry(name)?.raw;
240
240
  }
241
241
 
@@ -279,9 +279,9 @@ export interface MonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLo
279
279
  *
280
280
  * @internal
281
281
  */
282
- export function loggerIsMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(
283
- obj: L,
284
- ): obj is L & MonitoringContext<L> {
282
+ export function loggerIsMonitoringContext<
283
+ L extends ITelemetryBaseLogger = ITelemetryLoggerExt,
284
+ >(obj: L): obj is L & MonitoringContext<L> {
285
285
  const maybeConfig = obj as Partial<MonitoringContext<L>> | undefined;
286
286
  return isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;
287
287
  }
@@ -291,9 +291,9 @@ export function loggerIsMonitoringContext<L extends ITelemetryBaseLogger = ITele
291
291
  *
292
292
  * @internal
293
293
  */
294
- export function loggerToMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(
295
- logger: L,
296
- ): MonitoringContext<L> {
294
+ export function loggerToMonitoringContext<
295
+ L extends ITelemetryBaseLogger = ITelemetryLoggerExt,
296
+ >(logger: L): MonitoringContext<L> {
297
297
  if (loggerIsMonitoringContext<L>(logger)) {
298
298
  return logger;
299
299
  }
package/src/error.ts CHANGED
@@ -3,13 +3,13 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IErrorBase, ITelemetryBaseProperties } from "@fluidframework/core-interfaces";
6
+ import type { IErrorBase, ITelemetryBaseProperties } from "@fluidframework/core-interfaces";
7
7
  import {
8
8
  FluidErrorTypes,
9
- IGenericError,
10
- IUsageError,
9
+ type IGenericError,
10
+ type IUsageError,
11
11
  } from "@fluidframework/core-interfaces/internal";
12
- import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
12
+ import type { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
13
13
 
14
14
  import {
15
15
  LoggingError,
@@ -18,8 +18,8 @@ import {
18
18
  normalizeError,
19
19
  wrapError,
20
20
  } from "./errorLogging.js";
21
- import { IFluidErrorBase } from "./fluidErrorBase.js";
22
- import { ITelemetryPropertiesExt } from "./telemetryTypes.js";
21
+ import type { IFluidErrorBase } from "./fluidErrorBase.js";
22
+ import type { ITelemetryPropertiesExt } from "./telemetryTypes.js";
23
23
 
24
24
  /**
25
25
  * Throws a UsageError with the given message if the condition is not met.
@@ -46,7 +46,7 @@ export function validatePrecondition(
46
46
  * @internal
47
47
  */
48
48
  export class GenericError extends LoggingError implements IGenericError, IFluidErrorBase {
49
- readonly errorType = FluidErrorTypes.genericError;
49
+ public readonly errorType = FluidErrorTypes.genericError;
50
50
 
51
51
  /**
52
52
  * Create a new GenericError
@@ -54,7 +54,7 @@ export class GenericError extends LoggingError implements IGenericError, IFluidE
54
54
  * @param error - inner error object
55
55
  * @param props - Telemetry props to include when the error is logged
56
56
  */
57
- constructor(
57
+ public constructor(
58
58
  message: string,
59
59
  // TODO: Use `unknown` instead (API breaking change because error is not just an input parameter, but a public member of the class)
60
60
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
@@ -72,9 +72,9 @@ export class GenericError extends LoggingError implements IGenericError, IFluidE
72
72
  * @internal
73
73
  */
74
74
  export class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
75
- readonly errorType = FluidErrorTypes.usageError;
75
+ public readonly errorType = FluidErrorTypes.usageError;
76
76
 
77
- constructor(message: string, props?: ITelemetryBaseProperties) {
77
+ public constructor(message: string, props?: ITelemetryBaseProperties) {
78
78
  super(message, { ...props, usageError: true });
79
79
  }
80
80
  }
@@ -86,10 +86,10 @@ export class UsageError extends LoggingError implements IUsageError, IFluidError
86
86
  * @internal
87
87
  */
88
88
  export class DataCorruptionError extends LoggingError implements IErrorBase, IFluidErrorBase {
89
- readonly errorType = FluidErrorTypes.dataCorruptionError;
90
- readonly canRetry = false;
89
+ public readonly errorType = FluidErrorTypes.dataCorruptionError;
90
+ public readonly canRetry = false;
91
91
 
92
- constructor(message: string, props: ITelemetryBaseProperties) {
92
+ public constructor(message: string, props: ITelemetryBaseProperties) {
93
93
  super(message, { ...props, dataProcessingError: 1 });
94
94
  }
95
95
  }
@@ -7,7 +7,7 @@ import type { ITelemetryBaseProperties, Tagged } from "@fluidframework/core-inte
7
7
  import type { ILoggingError } from "@fluidframework/core-interfaces/internal";
8
8
  import { v4 as uuid } from "uuid";
9
9
 
10
- import { IFluidErrorBase, hasErrorInstanceId, isFluidError } from "./fluidErrorBase.js";
10
+ import { type IFluidErrorBase, hasErrorInstanceId, isFluidError } from "./fluidErrorBase.js";
11
11
  import { convertToBasePropertyType } from "./logger.js";
12
12
  import type {
13
13
  ITelemetryLoggerExt,
@@ -157,7 +157,12 @@ export function normalizeError(
157
157
 
158
158
  const errorTelemetryProps = LoggingError.typeCheck(error)
159
159
  ? error.getTelemetryProperties()
160
- : { untrustedOrigin: 1 }; // This will let us filter errors that did not originate from our own codebase
160
+ : {
161
+ untrustedOrigin: 1, // This will let us filter errors that did not originate from our own codebase
162
+ // FUTURE: Once 2.0 becomes LTS, switch to this more explicit property name
163
+ // Consider using a string to distinguish cases like "dependency" v. "callback"
164
+ // errorRunningExternalCode: 1,
165
+ };
161
166
 
162
167
  fluidError.addTelemetryProperties({
163
168
  ...errorTelemetryProps,
@@ -221,7 +226,10 @@ export function wrapError<T extends LoggingError>(
221
226
  innerError: unknown,
222
227
  newErrorFn: (message: string) => T,
223
228
  ): T {
224
- const { message, stack } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);
229
+ const { message, stack } = extractLogSafeErrorProperties(
230
+ innerError,
231
+ false /* sanitizeStack */,
232
+ );
225
233
 
226
234
  const newError = newErrorFn(message);
227
235
 
@@ -231,7 +239,12 @@ export function wrapError<T extends LoggingError>(
231
239
 
232
240
  // Mark external errors with untrustedOrigin flag
233
241
  if (isExternalError(innerError)) {
234
- newError.addTelemetryProperties({ untrustedOrigin: 1 });
242
+ newError.addTelemetryProperties({
243
+ untrustedOrigin: 1,
244
+ // FUTURE: Once 2.0 becomes LTS, switch to this more explicit property name
245
+ // Consider using a string to distinguish cases like "dependency" v. "callback"
246
+ // errorRunningExternalCode: 1,
247
+ });
235
248
  }
236
249
 
237
250
  // Reuse errorInstanceId
@@ -243,7 +256,7 @@ export function wrapError<T extends LoggingError>(
243
256
  }
244
257
 
245
258
  // Lastly, copy over all other telemetry properties. Note these will not overwrite existing properties
246
- // This will include the untrustedOrigin property if the inner error itself was created from an external error
259
+ // This will include the untrustedOrigin/errorRunningExternalCode info if the inner error itself was created from an external error
247
260
  if (isILoggingError(innerError)) {
248
261
  newError.addTelemetryProperties(innerError.getTelemetryProperties());
249
262
  }
@@ -308,11 +321,14 @@ export function overwriteStack(error: IFluidErrorBase | LoggingError, stack: str
308
321
  */
309
322
  export function isExternalError(error: unknown): boolean {
310
323
  // LoggingErrors are an internal FF error type. However, an external error can be converted
311
- // into a LoggingError if it is normalized. In this case we must use the untrustedOrigin flag to
312
- // determine whether the original error was infact external.
324
+ // into a LoggingError if it is normalized. In this case we must use the untrustedOrigin/errorRunningExternalCode flag to
325
+ // determine whether the original error was in fact external.
313
326
  if (LoggingError.typeCheck(error)) {
314
327
  if ((error as NormalizedLoggingError).errorType === NORMALIZED_ERROR_TYPE) {
315
- return error.getTelemetryProperties().untrustedOrigin === 1;
328
+ const props = error.getTelemetryProperties();
329
+ // NOTE: errorRunningExternalCode is not currently used - once this "read" code reaches LTS,
330
+ // we can switch to writing this more explicit property
331
+ return props.untrustedOrigin === 1 || !!props.errorRunningExternalCode;
316
332
  }
317
333
  return false;
318
334
  }
@@ -331,6 +347,8 @@ export function isTaggedTelemetryPropertyValue(
331
347
  return typeof (x as Partial<Tagged<unknown>>)?.tag === "string";
332
348
  }
333
349
 
350
+ // TODO: Use `unknown` instead (API breaking change)
351
+ /* eslint-disable @typescript-eslint/no-explicit-any */
334
352
  /**
335
353
  * Borrowed from
336
354
  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples}
@@ -340,8 +358,6 @@ export function isTaggedTelemetryPropertyValue(
340
358
  *
341
359
  * @internal
342
360
  */
343
- // TODO: Use `unknown` instead (API breaking change)
344
- /* eslint-disable @typescript-eslint/no-explicit-any */
345
361
  export const getCircularReplacer = (): ((key: string, value: unknown) => any) => {
346
362
  const seen = new WeakSet();
347
363
  return (key: string, value: unknown): any => {
@@ -370,10 +386,10 @@ export class LoggingError
370
386
  implements ILoggingError, Omit<IFluidErrorBase, "errorType">
371
387
  {
372
388
  private _errorInstanceId = uuid();
373
- get errorInstanceId(): string {
389
+ public get errorInstanceId(): string {
374
390
  return this._errorInstanceId;
375
391
  }
376
- overwriteErrorInstanceId(id: string): void {
392
+ public overwriteErrorInstanceId(id: string): void {
377
393
  this._errorInstanceId = id;
378
394
  }
379
395
 
@@ -383,7 +399,7 @@ export class LoggingError
383
399
  * @param props - telemetry props to include on the error for when it's logged
384
400
  * @param omitPropsFromLogging - properties by name to omit from telemetry props
385
401
  */
386
- constructor(
402
+ public constructor(
387
403
  message: string,
388
404
  props?: ITelemetryBaseProperties,
389
405
  private readonly omitPropsFromLogging: Set<string> = new Set(),
@@ -464,9 +480,9 @@ export const NORMALIZED_ERROR_TYPE = "genericError";
464
480
  class NormalizedLoggingError extends LoggingError {
465
481
  // errorType "genericError" is used as a default value throughout the code.
466
482
  // Note that this matches ContainerErrorTypes/DriverErrorTypes' genericError
467
- errorType = NORMALIZED_ERROR_TYPE;
483
+ public readonly errorType = NORMALIZED_ERROR_TYPE;
468
484
 
469
- constructor(errorProps: Pick<IFluidErrorBase, "message" | "stack">) {
485
+ public constructor(errorProps: Pick<IFluidErrorBase, "message" | "stack">) {
470
486
  super(errorProps.message);
471
487
 
472
488
  if (errorProps.stack !== undefined) {
@@ -3,8 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { EventEmitterEventType, TypedEventEmitter } from "@fluid-internal/client-utils";
7
- import { IEvent } from "@fluidframework/core-interfaces";
6
+ import { type EventEmitterEventType, TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import type { IEvent } from "@fluidframework/core-interfaces";
8
8
 
9
9
  /**
10
10
  * Event Emitter helper class
@@ -20,7 +20,7 @@ import { IEvent } from "@fluidframework/core-interfaces";
20
20
  export class EventEmitterWithErrorHandling<
21
21
  TEvent extends IEvent = IEvent,
22
22
  > extends TypedEventEmitter<TEvent> {
23
- constructor(
23
+ public constructor(
24
24
  // TODO: use `unknown` instead (breaking API change)
25
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
26
  private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void,
package/src/index.ts CHANGED
@@ -5,10 +5,10 @@
5
5
 
6
6
  export {
7
7
  createChildMonitoringContext,
8
- MonitoringContext,
8
+ type MonitoringContext,
9
9
  sessionStorageConfigProvider,
10
10
  mixinMonitoringContext,
11
- IConfigProvider,
11
+ type IConfigProvider,
12
12
  loggerToMonitoringContext,
13
13
  wrapConfigProviderWithDefaults,
14
14
  } from "./config.js";
@@ -25,7 +25,7 @@ export {
25
25
  generateErrorWithStack,
26
26
  generateStack,
27
27
  getCircularReplacer,
28
- IFluidErrorAnnotations,
28
+ type IFluidErrorAnnotations,
29
29
  isExternalError,
30
30
  isILoggingError,
31
31
  isTaggedTelemetryPropertyValue,
@@ -43,29 +43,34 @@ export {
43
43
  raiseConnectedEvent,
44
44
  safeRaiseEvent,
45
45
  } from "./events.js";
46
- export { hasErrorInstanceId, IFluidErrorBase, isFluidError } from "./fluidErrorBase.js";
46
+ export { hasErrorInstanceId, type IFluidErrorBase, isFluidError } from "./fluidErrorBase.js";
47
47
  export {
48
48
  eventNamespaceSeparator,
49
49
  createChildLogger,
50
50
  createMultiSinkLogger,
51
51
  formatTick,
52
- IPerformanceEventMarkers,
53
- ITelemetryLoggerPropertyBag,
54
- ITelemetryLoggerPropertyBags,
55
- MultiSinkLoggerProperties,
52
+ type IPerformanceEventMarkers,
53
+ type ITelemetryLoggerPropertyBag,
54
+ type ITelemetryLoggerPropertyBags,
55
+ type MultiSinkLoggerProperties,
56
56
  numberFromString,
57
57
  PerformanceEvent,
58
58
  TaggedLoggerAdapter,
59
59
  tagData,
60
60
  tagCodeArtifacts,
61
61
  TelemetryDataTag,
62
- TelemetryEventPropertyTypes,
62
+ type TelemetryEventPropertyTypes,
63
63
  } from "./logger.js";
64
- export { MockLogger } from "./mockLogger.js";
64
+ export { createMockLoggerExt, type IMockLoggerExt, MockLogger } from "./mockLogger.js";
65
65
  export { ThresholdCounter } from "./thresholdCounter.js";
66
66
  export { SampledTelemetryHelper } from "./sampledTelemetryHelper.js";
67
- export { createSampledLogger, IEventSampler, ISampledTelemetryLogger } from "./utils.js";
68
67
  export {
68
+ createSampledLogger,
69
+ type IEventSampler,
70
+ type ISampledTelemetryLogger,
71
+ measure,
72
+ } from "./utils.js";
73
+ export type {
69
74
  TelemetryEventPropertyTypeExt,
70
75
  ITelemetryEventExt,
71
76
  ITelemetryGenericEventExt,
@@ -76,4 +81,4 @@ export {
76
81
  ITelemetryPropertiesExt,
77
82
  TelemetryEventCategory,
78
83
  } from "./telemetryTypes.js";
79
- export { type IMeasuredCodeResult, TelemetryEventBatcher } from "./telemetryEventBatcher.js";
84
+ export { TelemetryEventBatcher } from "./telemetryEventBatcher.js";
package/src/logger.ts CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  import { performance } from "@fluid-internal/client-utils";
7
7
  import {
8
- ITelemetryBaseEvent,
9
- ITelemetryBaseLogger,
8
+ type ITelemetryBaseEvent,
9
+ type ITelemetryBaseLogger,
10
10
  LogLevel,
11
- Tagged,
12
- TelemetryBaseEventPropertyType,
11
+ type Tagged,
12
+ type TelemetryBaseEventPropertyType,
13
13
  } from "@fluidframework/core-interfaces";
14
14
 
15
15
  import {
@@ -23,8 +23,8 @@ import {
23
23
  isILoggingError,
24
24
  isTaggedTelemetryPropertyValue,
25
25
  } from "./errorLogging.js";
26
- import {
27
- type ITelemetryErrorEventExt,
26
+ import type {
27
+ ITelemetryErrorEventExt,
28
28
  ITelemetryEventExt,
29
29
  ITelemetryGenericEventExt,
30
30
  ITelemetryLoggerExt,
@@ -60,9 +60,10 @@ export type TelemetryEventPropertyTypes = ITelemetryPropertiesExt[string];
60
60
  /**
61
61
  * @alpha
62
62
  */
63
- export interface ITelemetryLoggerPropertyBag {
64
- [index: string]: TelemetryEventPropertyTypes | (() => TelemetryEventPropertyTypes);
65
- }
63
+ export type ITelemetryLoggerPropertyBag = Record<
64
+ string,
65
+ TelemetryEventPropertyTypes | (() => TelemetryEventPropertyTypes)
66
+ >;
66
67
 
67
68
  /**
68
69
  * @alpha
@@ -273,10 +274,9 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
273
274
  return this.extendProperties(newEvent, includeErrorProps);
274
275
  }
275
276
 
276
- private extendProperties<T extends ITelemetryLoggerPropertyBag = ITelemetryLoggerPropertyBag>(
277
- toExtend: T,
278
- includeErrorProps: boolean,
279
- ): T {
277
+ private extendProperties<
278
+ T extends ITelemetryLoggerPropertyBag = ITelemetryLoggerPropertyBag,
279
+ >(toExtend: T, includeErrorProps: boolean): T {
280
280
  const eventLike: ITelemetryLoggerPropertyBag = toExtend;
281
281
  if (this.properties) {
282
282
  const properties: (undefined | ITelemetryLoggerPropertyBag)[] = [];
@@ -421,8 +421,8 @@ export class ChildLogger extends TelemetryLogger {
421
421
  baseLogger.namespace === undefined
422
422
  ? namespace
423
423
  : namespace === undefined
424
- ? baseLogger.namespace
425
- : `${baseLogger.namespace}${TelemetryLogger.eventNamespaceSeparator}${namespace}`;
424
+ ? baseLogger.namespace
425
+ : `${baseLogger.namespace}${TelemetryLogger.eventNamespaceSeparator}${namespace}`;
426
426
 
427
427
  const child = new ChildLogger(
428
428
  baseLogger.baseLogger,
@@ -533,7 +533,7 @@ export class MultiSinkLogger extends TelemetryLogger {
533
533
  * @param loggers - The list of loggers to use as sinks
534
534
  * @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
535
535
  */
536
- constructor(
536
+ public constructor(
537
537
  namespace?: string,
538
538
  properties?: ITelemetryLoggerPropertyBags,
539
539
  loggers: ITelemetryBaseLogger[] = [],
@@ -855,7 +855,7 @@ export function convertToBasePropertyType(
855
855
  ? {
856
856
  value: convertToBasePropertyTypeUntagged(x.value),
857
857
  tag: x.tag,
858
- }
858
+ }
859
859
  : convertToBasePropertyTypeUntagged(x);
860
860
  }
861
861
 
@@ -920,11 +920,11 @@ export const tagData = <
920
920
  ? () => {
921
921
  value: ReturnType<V[P]>;
922
922
  tag: T;
923
- }
923
+ }
924
924
  : {
925
925
  value: Exclude<V[P], undefined>;
926
926
  tag: T;
927
- })
927
+ })
928
928
  | (V[P] extends undefined ? undefined : never);
929
929
  } =>
930
930
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
@@ -982,10 +982,10 @@ export const tagCodeArtifacts = <
982
982
  ? () => {
983
983
  value: ReturnType<T[P]>;
984
984
  tag: TelemetryDataTag.CodeArtifact;
985
- }
985
+ }
986
986
  : {
987
987
  value: Exclude<T[P], undefined>;
988
988
  tag: TelemetryDataTag.CodeArtifact;
989
- })
989
+ })
990
990
  | (T[P] extends undefined ? undefined : never);
991
991
  } => tagData<TelemetryDataTag.CodeArtifact, T>(TelemetryDataTag.CodeArtifact, values);
package/src/mockLogger.ts CHANGED
@@ -4,36 +4,55 @@
4
4
  */
5
5
 
6
6
  import {
7
- ITelemetryBaseEvent,
8
- ITelemetryBaseLogger,
7
+ type ITelemetryBaseEvent,
8
+ type ITelemetryBaseLogger,
9
9
  LogLevel,
10
10
  } from "@fluidframework/core-interfaces";
11
11
  import { assert } from "@fluidframework/core-utils/internal";
12
12
 
13
13
  import { createChildLogger } from "./logger.js";
14
- import { ITelemetryLoggerExt, ITelemetryPropertiesExt } from "./telemetryTypes.js";
14
+ import type {
15
+ ITelemetryEventExt,
16
+ ITelemetryLoggerExt,
17
+ ITelemetryPropertiesExt,
18
+ } from "./telemetryTypes.js";
15
19
 
16
20
  /**
17
- * The MockLogger records events sent to it, and then can walk back over those events
18
- * searching for a set of expected events to match against the logged events.
21
+ * Mock {@link @fluidframework/core-interfaces#ITelemetryBaseLogger} implementation.
22
+ *
23
+ * Records events sent to it, and then can walk back over those events, searching for a set of expected events to
24
+ * match against the logged events.
19
25
  *
20
26
  * @alpha
21
27
  */
22
28
  export class MockLogger implements ITelemetryBaseLogger {
23
- events: ITelemetryBaseEvent[] = [];
29
+ // TODO: don't expose mutability to external consumers
30
+ public events: ITelemetryBaseEvent[] = [];
31
+
32
+ /**
33
+ * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseLogger.minLogLevel}
34
+ */
35
+ public readonly minLogLevel: LogLevel;
24
36
 
25
- constructor(public readonly minLogLevel?: LogLevel) {}
37
+ public constructor(minLogLevel?: LogLevel) {
38
+ this.minLogLevel = minLogLevel ?? LogLevel.default;
39
+ }
26
40
 
27
- clear(): void {
41
+ public clear(): void {
28
42
  this.events = [];
29
43
  }
30
44
 
31
- toTelemetryLogger(): ITelemetryLoggerExt {
45
+ public toTelemetryLogger(): ITelemetryLoggerExt {
32
46
  return createChildLogger({ logger: this });
33
47
  }
34
48
 
35
- send(event: ITelemetryBaseEvent): void {
36
- this.events.push(event);
49
+ /**
50
+ * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseLogger.send}
51
+ */
52
+ public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void {
53
+ if (logLevel ?? LogLevel.default >= this.minLogLevel) {
54
+ this.events.push(event);
55
+ }
37
56
  }
38
57
 
39
58
  /**
@@ -44,7 +63,7 @@ export class MockLogger implements ITelemetryBaseLogger {
44
63
  * These event objects may be subsets of the logged events.
45
64
  * Note: category is omitted from the type because it's usually uninteresting and tedious to type.
46
65
  */
47
- matchEvents(
66
+ public matchEvents(
48
67
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
49
68
  inlineDetailsProp: boolean = false,
50
69
  ): boolean {
@@ -60,7 +79,7 @@ export class MockLogger implements ITelemetryBaseLogger {
60
79
  /**
61
80
  * Asserts that matchEvents is true, and prints the actual/expected output if not.
62
81
  */
63
- assertMatch(
82
+ public assertMatch(
64
83
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
65
84
  message?: string,
66
85
  inlineDetailsProp: boolean = false,
@@ -85,7 +104,7 @@ ${JSON.stringify(actualEvents)}`);
85
104
  * Note: category is omitted from the type because it's usually uninteresting and tedious to type.
86
105
  * @returns if any of the expected events is found.
87
106
  */
88
- matchAnyEvent(
107
+ public matchAnyEvent(
89
108
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
90
109
  inlineDetailsProp: boolean = false,
91
110
  ): boolean {
@@ -99,7 +118,7 @@ ${JSON.stringify(actualEvents)}`);
99
118
  /**
100
119
  * Asserts that matchAnyEvent is true, and prints the actual/expected output if not.
101
120
  */
102
- assertMatchAny(
121
+ public assertMatchAny(
103
122
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
104
123
  message?: string,
105
124
  inlineDetailsProp: boolean = false,
@@ -123,7 +142,7 @@ ${JSON.stringify(actualEvents)}`);
123
142
  * These event objects may be subsets of the logged events.
124
143
  * Note: category is omitted from the type because it's usually uninteresting and tedious to type.
125
144
  */
126
- matchEventStrict(
145
+ public matchEventStrict(
127
146
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
128
147
  inlineDetailsProp: boolean = false,
129
148
  ): boolean {
@@ -136,7 +155,7 @@ ${JSON.stringify(actualEvents)}`);
136
155
  /**
137
156
  * Asserts that matchEvents is true, and prints the actual/expected output if not
138
157
  */
139
- assertMatchStrict(
158
+ public assertMatchStrict(
140
159
  expectedEvents: Omit<ITelemetryBaseEvent, "category">[],
141
160
  message?: string,
142
161
  inlineDetailsProp: boolean = false,
@@ -155,7 +174,7 @@ ${JSON.stringify(actualEvents)}`);
155
174
  /**
156
175
  * Asserts that matchAnyEvent is false for the given events, and prints the actual/expected output if not
157
176
  */
158
- assertMatchNone(
177
+ public assertMatchNone(
159
178
  disallowedEvents: Omit<ITelemetryBaseEvent, "category">[],
160
179
  message?: string,
161
180
  inlineDetailsProp: boolean = false,
@@ -187,7 +206,7 @@ ${JSON.stringify(actualEvents)}`);
187
206
  }
188
207
 
189
208
  // Remove the events so far; next call will just compare subsequent events from here
190
- this.events = [];
209
+ this.clear();
191
210
 
192
211
  // Return the count of matched events.
193
212
  return iExpectedEvent;
@@ -208,7 +227,6 @@ ${JSON.stringify(actualEvents)}`);
208
227
  if (inlineDetailsProp && details !== undefined) {
209
228
  assert(
210
229
  typeof details === "string",
211
- // eslint-disable-next-line unicorn/numeric-separators-style
212
230
  0x6c9 /* Details should a JSON stringified string if inlineDetailsProp is true */,
213
231
  );
214
232
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -220,7 +238,10 @@ ${JSON.stringify(actualEvents)}`);
220
238
  }
221
239
  }
222
240
 
223
- function matchObjects(actual: ITelemetryPropertiesExt, expected: ITelemetryPropertiesExt): boolean {
241
+ function matchObjects(
242
+ actual: ITelemetryPropertiesExt,
243
+ expected: ITelemetryPropertiesExt,
244
+ ): boolean {
224
245
  for (const [expectedKey, expectedValue] of Object.entries(expected)) {
225
246
  const actualValue = actual[expectedKey];
226
247
  if (
@@ -245,3 +266,32 @@ function matchObjects(actual: ITelemetryPropertiesExt, expected: ITelemetryPrope
245
266
  }
246
267
  return true;
247
268
  }
269
+
270
+ /**
271
+ * Mock {@link ITelemetryLoggerExt} implementation.
272
+ *
273
+ * @remarks Can be created via {@link createMockLoggerExt}.
274
+ *
275
+ * @internal
276
+ */
277
+ export interface IMockLoggerExt extends ITelemetryLoggerExt {
278
+ /**
279
+ * Gets the events that have been logged so far.
280
+ */
281
+ events(): readonly ITelemetryEventExt[];
282
+ }
283
+
284
+ /**
285
+ * Creates an {@link IMockLoggerExt}.
286
+ *
287
+ * @internal
288
+ */
289
+ export function createMockLoggerExt(minLogLevel?: LogLevel): IMockLoggerExt {
290
+ const mockLogger = new MockLogger(minLogLevel);
291
+ const childLogger = createChildLogger({ logger: mockLogger });
292
+ Object.assign(childLogger, {
293
+ events: (): readonly ITelemetryEventExt[] =>
294
+ mockLogger.events.map((e) => e as ITelemetryEventExt),
295
+ });
296
+ return childLogger as IMockLoggerExt;
297
+ }