@fluidframework/telemetry-utils 1.2.7 → 2.0.0-dev.1.3.0.96595

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 (70) hide show
  1. package/.mocharc.js +12 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +2 -7
  4. package/dist/config.js.map +1 -1
  5. package/dist/errorLogging.d.ts +10 -1
  6. package/dist/errorLogging.d.ts.map +1 -1
  7. package/dist/errorLogging.js +75 -31
  8. package/dist/errorLogging.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/logger.d.ts +1 -6
  14. package/dist/logger.d.ts.map +1 -1
  15. package/dist/logger.js +4 -8
  16. package/dist/logger.js.map +1 -1
  17. package/dist/mockLogger.d.ts +2 -0
  18. package/dist/mockLogger.d.ts.map +1 -1
  19. package/dist/mockLogger.js +12 -0
  20. package/dist/mockLogger.js.map +1 -1
  21. package/dist/packageVersion.d.ts +1 -1
  22. package/dist/packageVersion.d.ts.map +1 -1
  23. package/dist/packageVersion.js +1 -1
  24. package/dist/packageVersion.js.map +1 -1
  25. package/dist/sampledTelemetryHelper.d.ts +53 -0
  26. package/dist/sampledTelemetryHelper.d.ts.map +1 -0
  27. package/dist/sampledTelemetryHelper.js +92 -0
  28. package/dist/sampledTelemetryHelper.js.map +1 -0
  29. package/dist/thresholdCounter.d.ts +1 -1
  30. package/dist/thresholdCounter.js +1 -1
  31. package/dist/thresholdCounter.js.map +1 -1
  32. package/lib/config.d.ts.map +1 -1
  33. package/lib/config.js +2 -7
  34. package/lib/config.js.map +1 -1
  35. package/lib/errorLogging.d.ts +10 -1
  36. package/lib/errorLogging.d.ts.map +1 -1
  37. package/lib/errorLogging.js +74 -30
  38. package/lib/errorLogging.js.map +1 -1
  39. package/lib/index.d.ts +1 -0
  40. package/lib/index.d.ts.map +1 -1
  41. package/lib/index.js +1 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/logger.d.ts +1 -6
  44. package/lib/logger.d.ts.map +1 -1
  45. package/lib/logger.js +4 -8
  46. package/lib/logger.js.map +1 -1
  47. package/lib/mockLogger.d.ts +2 -0
  48. package/lib/mockLogger.d.ts.map +1 -1
  49. package/lib/mockLogger.js +12 -0
  50. package/lib/mockLogger.js.map +1 -1
  51. package/lib/packageVersion.d.ts +1 -1
  52. package/lib/packageVersion.d.ts.map +1 -1
  53. package/lib/packageVersion.js +1 -1
  54. package/lib/packageVersion.js.map +1 -1
  55. package/lib/sampledTelemetryHelper.d.ts +53 -0
  56. package/lib/sampledTelemetryHelper.d.ts.map +1 -0
  57. package/lib/sampledTelemetryHelper.js +88 -0
  58. package/lib/sampledTelemetryHelper.js.map +1 -0
  59. package/lib/thresholdCounter.d.ts +1 -1
  60. package/lib/thresholdCounter.js +1 -1
  61. package/lib/thresholdCounter.js.map +1 -1
  62. package/package.json +21 -10
  63. package/src/config.ts +2 -4
  64. package/src/errorLogging.ts +77 -30
  65. package/src/index.ts +1 -0
  66. package/src/logger.ts +4 -8
  67. package/src/mockLogger.ts +13 -0
  68. package/src/packageVersion.ts +1 -1
  69. package/src/sampledTelemetryHelper.ts +142 -0
  70. package/src/thresholdCounter.ts +1 -1
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/telemetry-utils";
8
- export declare const pkgVersion = "1.2.7";
8
+ export declare const pkgVersion = "2.0.0-dev.1.3.0.96595";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,oCAAoC,CAAC;AACzD,eAAO,MAAM,UAAU,UAAU,CAAC"}
1
+ {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,oCAAoC,CAAC;AACzD,eAAO,MAAM,UAAU,0BAA0B,CAAC"}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/telemetry-utils";
8
- export const pkgVersion = "1.2.7";
8
+ export const pkgVersion = "2.0.0-dev.1.3.0.96595";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"1.2.7\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,uBAAuB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/telemetry-utils\";\nexport const pkgVersion = \"2.0.0-dev.1.3.0.96595\";\n"]}
@@ -0,0 +1,53 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { IDisposable, ITelemetryGenericEvent, ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
6
+ /**
7
+ * Helper class that executes a specified code block and writes an
8
+ * {@link @fluidframework/common-definitions#ITelemetryPerformanceEvent} to a specified logger every time a specified
9
+ * number of executions is reached (or when the class is disposed). The `duration` field in the telemetry event is
10
+ * the duration of the latest execution (sample) of the specified function. See the documentation of the
11
+ * `includeAggregateMetrics` parameter for additional details that can be included.
12
+ */
13
+ export declare class SampledTelemetryHelper implements IDisposable {
14
+ private readonly eventBase;
15
+ private readonly logger;
16
+ private readonly sampleThreshold;
17
+ private readonly includeAggregateMetrics;
18
+ private readonly perBucketProperties;
19
+ disposed: boolean;
20
+ private readonly measurementsMap;
21
+ /**
22
+ * @param eventBase -
23
+ * Custom properties to include in the telemetry performance event when it is written.
24
+ * @param logger -
25
+ * The logger to use to write the telemetry performance event.
26
+ * @param sampleThreshold -
27
+ * Telemetry performance events will be generated every time we hit this many executions of the code block.
28
+ * @param includeAggregateMetrics -
29
+ * If set to `true`, the telemetry performance event will include aggregated metrics (total duration, min duration,
30
+ * max duration) for all the executions in between generated events.
31
+ * @param perBucketProperties -
32
+ * Map of strings that represent different buckets (which can be specified when calling the 'measure' method), to
33
+ * properties which should be added to the telemetry event for that bucket. If a bucket being measured does not
34
+ * have an entry in this map, no additional properties will be added to its telemetry events. The following keys are
35
+ * reserved for use by this class: "duration", "count", "totalDuration", "minDuration", "maxDuration". If any of
36
+ * them is specified as a key in one of the ITelemetryProperties objects in this map, that key-value pair will be
37
+ * ignored.
38
+ */
39
+ constructor(eventBase: ITelemetryGenericEvent, logger: ITelemetryLogger, sampleThreshold: number, includeAggregateMetrics?: boolean, perBucketProperties?: Map<string, ITelemetryProperties>);
40
+ /**
41
+ * @param codeToMeasure -
42
+ * The code to be executed and measured.
43
+ * @param bucket -
44
+ * A key to track executions of the code block separately. Each different value of this parameter has a separate
45
+ * set of executions and metrics tracked by the class. If no such distinction needs to be made, do not provide a
46
+ * value.
47
+ * @returns Whatever the passed-in code block returns.
48
+ */
49
+ measure<T>(codeToMeasure: () => T, bucket?: string): T;
50
+ private flushBucket;
51
+ dispose(error?: Error | undefined): void;
52
+ }
53
+ //# sourceMappingURL=sampledTelemetryHelper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampledTelemetryHelper.d.ts","sourceRoot":"","sources":["../src/sampledTelemetryHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,WAAW,EACX,sBAAsB,EACtB,gBAAgB,EAEhB,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAkC5C;;;;;;GAMG;AACF,qBAAa,sBAAuB,YAAW,WAAW;IAwBnD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IA3BxC,QAAQ,EAAE,OAAO,CAAS;IAE1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmC;IAEnE;;;;;;;;;;;;;;;;;OAiBG;gBAEkB,SAAS,EAAE,sBAAsB,EACjC,MAAM,EAAE,gBAAgB,EACxB,eAAe,EAAE,MAAM,EACvB,uBAAuB,GAAE,OAAe,EACxC,mBAAmB,oCAA0C;IAGlF;;;;;;;;OAQG;IACI,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,GAAE,MAAW,GAAG,CAAC;IA0BjE,OAAO,CAAC,WAAW;IAoBZ,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,IAAI;CAGlD"}
@@ -0,0 +1,88 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { performance } from "@fluidframework/common-utils";
6
+ /**
7
+ * Helper class that executes a specified code block and writes an
8
+ * {@link @fluidframework/common-definitions#ITelemetryPerformanceEvent} to a specified logger every time a specified
9
+ * number of executions is reached (or when the class is disposed). The `duration` field in the telemetry event is
10
+ * the duration of the latest execution (sample) of the specified function. See the documentation of the
11
+ * `includeAggregateMetrics` parameter for additional details that can be included.
12
+ */
13
+ export class SampledTelemetryHelper {
14
+ /**
15
+ * @param eventBase -
16
+ * Custom properties to include in the telemetry performance event when it is written.
17
+ * @param logger -
18
+ * The logger to use to write the telemetry performance event.
19
+ * @param sampleThreshold -
20
+ * Telemetry performance events will be generated every time we hit this many executions of the code block.
21
+ * @param includeAggregateMetrics -
22
+ * If set to `true`, the telemetry performance event will include aggregated metrics (total duration, min duration,
23
+ * max duration) for all the executions in between generated events.
24
+ * @param perBucketProperties -
25
+ * Map of strings that represent different buckets (which can be specified when calling the 'measure' method), to
26
+ * properties which should be added to the telemetry event for that bucket. If a bucket being measured does not
27
+ * have an entry in this map, no additional properties will be added to its telemetry events. The following keys are
28
+ * reserved for use by this class: "duration", "count", "totalDuration", "minDuration", "maxDuration". If any of
29
+ * them is specified as a key in one of the ITelemetryProperties objects in this map, that key-value pair will be
30
+ * ignored.
31
+ */
32
+ constructor(eventBase, logger, sampleThreshold, includeAggregateMetrics = false, perBucketProperties = new Map()) {
33
+ this.eventBase = eventBase;
34
+ this.logger = logger;
35
+ this.sampleThreshold = sampleThreshold;
36
+ this.includeAggregateMetrics = includeAggregateMetrics;
37
+ this.perBucketProperties = perBucketProperties;
38
+ this.disposed = false;
39
+ this.measurementsMap = new Map();
40
+ }
41
+ /**
42
+ * @param codeToMeasure -
43
+ * The code to be executed and measured.
44
+ * @param bucket -
45
+ * A key to track executions of the code block separately. Each different value of this parameter has a separate
46
+ * set of executions and metrics tracked by the class. If no such distinction needs to be made, do not provide a
47
+ * value.
48
+ * @returns Whatever the passed-in code block returns.
49
+ */
50
+ measure(codeToMeasure, bucket = "") {
51
+ var _a, _b, _c;
52
+ const start = performance.now();
53
+ const returnValue = codeToMeasure();
54
+ const duration = performance.now() - start;
55
+ let m = this.measurementsMap.get(bucket);
56
+ if (m === undefined) {
57
+ m = { count: 0, duration: -1 };
58
+ this.measurementsMap.set(bucket, m);
59
+ }
60
+ m.count++;
61
+ m.duration = duration;
62
+ if (this.includeAggregateMetrics) {
63
+ m.totalDuration = ((_a = m.totalDuration) !== null && _a !== void 0 ? _a : 0) + duration;
64
+ m.minDuration = Math.min((_b = m.minDuration) !== null && _b !== void 0 ? _b : duration, duration);
65
+ m.maxDuration = Math.max((_c = m.maxDuration) !== null && _c !== void 0 ? _c : 0, duration);
66
+ }
67
+ if (m.count >= this.sampleThreshold) {
68
+ this.flushBucket(bucket);
69
+ }
70
+ return returnValue;
71
+ }
72
+ flushBucket(bucket) {
73
+ const measurements = this.measurementsMap.get(bucket);
74
+ if (measurements === undefined) {
75
+ return;
76
+ }
77
+ if (measurements.count !== 0) {
78
+ const bucketProperties = this.perBucketProperties.get(bucket);
79
+ const telemetryEvent = Object.assign(Object.assign(Object.assign({}, this.eventBase), bucketProperties), measurements);
80
+ this.logger.sendPerformanceEvent(telemetryEvent);
81
+ this.measurementsMap.delete(bucket);
82
+ }
83
+ }
84
+ dispose(error) {
85
+ this.measurementsMap.forEach((_, k) => this.flushBucket(k));
86
+ }
87
+ }
88
+ //# sourceMappingURL=sampledTelemetryHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampledTelemetryHelper.js","sourceRoot":"","sources":["../src/sampledTelemetryHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAiC3D;;;;;;GAMG;AACF,MAAM,OAAO,sBAAsB;IAKhC;;;;;;;;;;;;;;;;;OAiBG;IACH,YACqB,SAAiC,EACjC,MAAwB,EACxB,eAAuB,EACvB,0BAAmC,KAAK,EACxC,sBAAsB,IAAI,GAAG,EAAgC;QAJ7D,cAAS,GAAT,SAAS,CAAwB;QACjC,WAAM,GAAN,MAAM,CAAkB;QACxB,oBAAe,GAAf,eAAe,CAAQ;QACvB,4BAAuB,GAAvB,uBAAuB,CAAiB;QACxC,wBAAmB,GAAnB,mBAAmB,CAA0C;QA3BlF,aAAQ,GAAY,KAAK,CAAC;QAET,oBAAe,GAAG,IAAI,GAAG,EAAwB,CAAC;IA0BnE,CAAC;IAED;;;;;;;;OAQG;IACI,OAAO,CAAI,aAAsB,EAAE,SAAiB,EAAE;;QACzD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,aAAa,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAE3C,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS,EAAE;YACjB,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;SACvC;QACD,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAEtB,IAAI,IAAI,CAAC,uBAAuB,EAAE;YAC9B,CAAC,CAAC,aAAa,GAAG,CAAC,MAAA,CAAC,CAAC,aAAa,mCAAI,CAAC,CAAC,GAAG,QAAQ,CAAC;YACpD,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAA,CAAC,CAAC,WAAW,mCAAI,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC9D,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAA,CAAC,CAAC,WAAW,mCAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;SAC1D;QAED,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,eAAe,EAAE;YACjC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;SAC5B;QAED,OAAO,WAAW,CAAC;IACvB,CAAC;IAEO,WAAW,CAAC,MAAc;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,YAAY,KAAK,SAAS,EAAE;YAC5B,OAAO;SACV;QAED,IAAI,YAAY,CAAC,KAAK,KAAK,CAAC,EAAE;YAC1B,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAE9D,MAAM,cAAc,iDACb,IAAI,CAAC,SAAS,GACd,gBAAgB,GAChB,YAAY,CAClB,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC;YACjD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;SACvC;IACL,CAAC;IAEM,OAAO,CAAC,KAAyB;QACpC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n IDisposable,\n ITelemetryGenericEvent,\n ITelemetryLogger,\n ITelemetryPerformanceEvent,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { performance } from \"@fluidframework/common-utils\";\n\ninterface Measurements {\n // The names of the properties in this interface are the ones that will get stamped in the\n // telemetry event, changes should be considered carefully. The optional properties should\n // only be populated if 'includeAggregateMetrics' is true.\n\n /**\n * The duration of the latest execution.\n */\n duration: number;\n\n /**\n * The number of executions since the last time an event was generated.\n */\n count: number;\n\n /**\n * Total duration across all the executions since the last event was generated.\n */\n totalDuration?: number;\n\n /**\n * Min duration across all the executions since the last event was generated.\n */\n minDuration?: number;\n\n /**\n * Max duration across all the executions since the last event was generated.\n */\n maxDuration?: number;\n}\n\n/**\n * Helper class that executes a specified code block and writes an\n * {@link @fluidframework/common-definitions#ITelemetryPerformanceEvent} to a specified logger every time a specified\n * number of executions is reached (or when the class is disposed). The `duration` field in the telemetry event is\n * the duration of the latest execution (sample) of the specified function. See the documentation of the\n * `includeAggregateMetrics` parameter for additional details that can be included.\n */\n export class SampledTelemetryHelper implements IDisposable {\n disposed: boolean = false;\n\n private readonly measurementsMap = new Map<string, Measurements>();\n\n /**\n * @param eventBase -\n * Custom properties to include in the telemetry performance event when it is written.\n * @param logger -\n * The logger to use to write the telemetry performance event.\n * @param sampleThreshold -\n * Telemetry performance events will be generated every time we hit this many executions of the code block.\n * @param includeAggregateMetrics -\n * If set to `true`, the telemetry performance event will include aggregated metrics (total duration, min duration,\n * max duration) for all the executions in between generated events.\n * @param perBucketProperties -\n * Map of strings that represent different buckets (which can be specified when calling the 'measure' method), to\n * properties which should be added to the telemetry event for that bucket. If a bucket being measured does not\n * have an entry in this map, no additional properties will be added to its telemetry events. The following keys are\n * reserved for use by this class: \"duration\", \"count\", \"totalDuration\", \"minDuration\", \"maxDuration\". If any of\n * them is specified as a key in one of the ITelemetryProperties objects in this map, that key-value pair will be\n * ignored.\n */\n public constructor(\n private readonly eventBase: ITelemetryGenericEvent,\n private readonly logger: ITelemetryLogger,\n private readonly sampleThreshold: number,\n private readonly includeAggregateMetrics: boolean = false,\n private readonly perBucketProperties = new Map<string, ITelemetryProperties>()) {\n }\n\n /**\n * @param codeToMeasure -\n * The code to be executed and measured.\n * @param bucket -\n * A key to track executions of the code block separately. Each different value of this parameter has a separate\n * set of executions and metrics tracked by the class. If no such distinction needs to be made, do not provide a\n * value.\n * @returns Whatever the passed-in code block returns.\n */\n public measure<T>(codeToMeasure: () => T, bucket: string = \"\"): T {\n const start = performance.now();\n const returnValue = codeToMeasure();\n const duration = performance.now() - start;\n\n let m = this.measurementsMap.get(bucket);\n if (m === undefined) {\n m = { count: 0, duration: -1 };\n this.measurementsMap.set(bucket, m);\n }\n m.count++;\n m.duration = duration;\n\n if (this.includeAggregateMetrics) {\n m.totalDuration = (m.totalDuration ?? 0) + duration;\n m.minDuration = Math.min(m.minDuration ?? duration, duration);\n m.maxDuration = Math.max(m.maxDuration ?? 0, duration);\n }\n\n if (m.count >= this.sampleThreshold) {\n this.flushBucket(bucket);\n }\n\n return returnValue;\n }\n\n private flushBucket(bucket: string) {\n const measurements = this.measurementsMap.get(bucket);\n if (measurements === undefined) {\n return;\n }\n\n if (measurements.count !== 0) {\n const bucketProperties = this.perBucketProperties.get(bucket);\n\n const telemetryEvent: ITelemetryPerformanceEvent = {\n ...this.eventBase,\n ...bucketProperties, // If the bucket doesn't exist and this is undefined, things work as expected\n ...measurements,\n };\n\n this.logger.sendPerformanceEvent(telemetryEvent);\n this.measurementsMap.delete(bucket);\n }\n }\n\n public dispose(error?: Error | undefined): void {\n this.measurementsMap.forEach((_, k) => this.flushBucket(k));\n }\n}\n"]}
@@ -17,7 +17,7 @@ export declare class ThresholdCounter {
17
17
  */
18
18
  send(eventName: string, value: number): void;
19
19
  /**
20
- * Sends the value if it's above the treshold
20
+ * Sends the value if it's above the threshold
21
21
  * and a multiple of the threshold.
22
22
  *
23
23
  * To be used in scenarios where we'd like to record a
@@ -25,7 +25,7 @@ export class ThresholdCounter {
25
25
  });
26
26
  }
27
27
  /**
28
- * Sends the value if it's above the treshold
28
+ * Sends the value if it's above the threshold
29
29
  * and a multiple of the threshold.
30
30
  *
31
31
  * To be used in scenarios where we'd like to record a
@@ -1 +1 @@
1
- {"version":3,"file":"thresholdCounter.js","sourceRoot":"","sources":["../src/thresholdCounter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACzB,YACqB,SAAiB,EACjB,MAAwB,EACjC,oBAAoB,SAAS;QAFpB,cAAS,GAAT,SAAS,CAAQ;QACjB,WAAM,GAAN,MAAM,CAAkB;QACjC,sBAAiB,GAAjB,iBAAiB,CAAY;IACtC,CAAC;IAEJ;;OAEG;IACI,IAAI,CAAC,SAAiB,EAAE,KAAa;QACxC,IAAI,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;YACxB,OAAO;SACV;QACD,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YAC7B,SAAS;YACT,KAAK;SACR,CAAC,CAAC;IACP,CAAC;IAED;;;;;;OAMG;IACI,cAAc,CAAC,SAAiB,EAAE,KAAa;QAClD,IAAI,KAAK,KAAK,IAAI,CAAC,iBAAiB,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS;gBACT,KAAK;aACR,CAAC,CAAC;YACH,sCAAsC;YACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;SACvD;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\n\n/**\n * Utility counter which will send event only if the provided value\n * is above a configured threshold\n */\nexport class ThresholdCounter {\n public constructor(\n private readonly threshold: number,\n private readonly logger: ITelemetryLogger,\n private thresholdMultiple = threshold,\n ) {}\n\n /**\n * Sends the value if it's above the treshold.\n */\n public send(eventName: string, value: number) {\n if (value < this.threshold) {\n return;\n }\n this.logger.sendPerformanceEvent({\n eventName,\n value,\n });\n }\n\n /**\n * Sends the value if it's above the treshold\n * and a multiple of the threshold.\n *\n * To be used in scenarios where we'd like to record a\n * threshold violation while reducing telemetry noise.\n */\n public sendIfMultiple(eventName: string, value: number) {\n if (value === this.thresholdMultiple) {\n this.logger.sendPerformanceEvent({\n eventName,\n value,\n });\n // reduce number of \"multiple\" events.\n this.thresholdMultiple = this.thresholdMultiple * 2;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"thresholdCounter.js","sourceRoot":"","sources":["../src/thresholdCounter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACzB,YACqB,SAAiB,EACjB,MAAwB,EACjC,oBAAoB,SAAS;QAFpB,cAAS,GAAT,SAAS,CAAQ;QACjB,WAAM,GAAN,MAAM,CAAkB;QACjC,sBAAiB,GAAjB,iBAAiB,CAAY;IACtC,CAAC;IAEJ;;OAEG;IACI,IAAI,CAAC,SAAiB,EAAE,KAAa;QACxC,IAAI,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;YACxB,OAAO;SACV;QACD,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YAC7B,SAAS;YACT,KAAK;SACR,CAAC,CAAC;IACP,CAAC;IAED;;;;;;OAMG;IACI,cAAc,CAAC,SAAiB,EAAE,KAAa;QAClD,IAAI,KAAK,KAAK,IAAI,CAAC,iBAAiB,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS;gBACT,KAAK;aACR,CAAC,CAAC;YACH,sCAAsC;YACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;SACvD;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\n\n/**\n * Utility counter which will send event only if the provided value\n * is above a configured threshold\n */\nexport class ThresholdCounter {\n public constructor(\n private readonly threshold: number,\n private readonly logger: ITelemetryLogger,\n private thresholdMultiple = threshold,\n ) {}\n\n /**\n * Sends the value if it's above the treshold.\n */\n public send(eventName: string, value: number) {\n if (value < this.threshold) {\n return;\n }\n this.logger.sendPerformanceEvent({\n eventName,\n value,\n });\n }\n\n /**\n * Sends the value if it's above the threshold\n * and a multiple of the threshold.\n *\n * To be used in scenarios where we'd like to record a\n * threshold violation while reducing telemetry noise.\n */\n public sendIfMultiple(eventName: string, value: number) {\n if (value === this.thresholdMultiple) {\n this.logger.sendPerformanceEvent({\n eventName,\n value,\n });\n // reduce number of \"multiple\" events.\n this.thresholdMultiple = this.thresholdMultiple * 2;\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/telemetry-utils",
3
- "version": "1.2.7",
3
+ "version": "2.0.0-dev.1.3.0.96595",
4
4
  "description": "Collection of telemetry relates utilities for Fluid",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -65,17 +65,17 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@fluidframework/common-definitions": "^0.20.1",
68
- "@fluidframework/common-utils": "^0.32.1",
68
+ "@fluidframework/common-utils": "^1.0.0",
69
69
  "debug": "^4.1.1",
70
70
  "events": "^3.1.0",
71
71
  "uuid": "^8.3.1"
72
72
  },
73
73
  "devDependencies": {
74
- "@fluidframework/build-common": "^0.24.0",
75
- "@fluidframework/build-tools": "^0.2.74327",
76
- "@fluidframework/eslint-config-fluid": "^0.28.2000",
77
- "@fluidframework/mocha-test-setup": "^1.2.7",
78
- "@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@1.2.1",
74
+ "@fluidframework/build-common": "^1.0.0",
75
+ "@fluidframework/build-tools": "^0.4.6000",
76
+ "@fluidframework/eslint-config-fluid": "^1.0.0",
77
+ "@fluidframework/mocha-test-setup": "2.0.0-dev.1.3.0.96595",
78
+ "@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@^1.0.0",
79
79
  "@microsoft/api-extractor": "^7.22.2",
80
80
  "@rushstack/eslint-config": "^2.5.1",
81
81
  "@types/debug": "^4.1.5",
@@ -83,7 +83,7 @@
83
83
  "@types/mocha": "^9.1.1",
84
84
  "@types/node": "^14.18.0",
85
85
  "concurrently": "^6.2.0",
86
- "copyfiles": "^2.1.0",
86
+ "copyfiles": "^2.4.1",
87
87
  "cross-env": "^7.0.2",
88
88
  "eslint": "~8.6.0",
89
89
  "mocha": "^10.0.0",
@@ -93,7 +93,18 @@
93
93
  "typescript": "~4.5.5"
94
94
  },
95
95
  "typeValidation": {
96
- "version": "1.2.2",
97
- "broken": {}
96
+ "version": "2.0.0",
97
+ "broken": {
98
+ "RemovedFunctionDeclaration_originatedAsExternalError": {
99
+ "forwardCompat": false,
100
+ "backCompat": false
101
+ },
102
+ "EnumDeclaration_TelemetryDataTag": {
103
+ "forwardCompat": false
104
+ },
105
+ "ClassDeclaration_MockLogger": {
106
+ "forwardCompat": false
107
+ }
108
+ }
98
109
  }
99
110
  }
package/src/config.ts CHANGED
@@ -142,11 +142,9 @@ function stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined
142
142
  return defaultReturn;
143
143
  }
144
144
 
145
- /** Referencing the `sessionStorage` variable can throw in some environments such as Node */
145
+ /** `sessionStorage` is undefined in some environments such as Node */
146
146
  const safeSessionStorage = (): Storage | undefined => {
147
- try {
148
- return sessionStorage !== null ? sessionStorage : undefined;
149
- } catch { return undefined; }
147
+ return globalThis.sessionStorage;
150
148
  };
151
149
 
152
150
  /**
@@ -8,6 +8,7 @@ import {
8
8
  ITaggedTelemetryPropertyType,
9
9
  ITelemetryLogger,
10
10
  ITelemetryProperties,
11
+ TelemetryEventPropertyType,
11
12
  } from "@fluidframework/common-definitions";
12
13
  import { v4 as uuid } from "uuid";
13
14
  import {
@@ -111,7 +112,7 @@ export function normalizeError(
111
112
 
112
113
  // We have to construct a new Fluid Error, copying safe properties over
113
114
  const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
114
- const fluidError: IFluidErrorBase = new NormalizedExternalError({
115
+ const fluidError: IFluidErrorBase = new NormalizedLoggingError({
115
116
  message,
116
117
  stack,
117
118
  });
@@ -129,11 +130,13 @@ export function normalizeError(
129
130
  fluidError.addTelemetryProperties({ typeofError: typeof (error) });
130
131
  }
131
132
 
132
- const originalErrorTelemetryProps = isILoggingError(error) ? error.getTelemetryProperties() : {};
133
+ const errorTelemetryProps = LoggingError.typeCheck(error)
134
+ ? error.getTelemetryProperties()
135
+ : { untrustedOrigin: 1 }; // This will let us filter errors that did not originate from our own codebase
136
+
133
137
  fluidError.addTelemetryProperties({
134
- ...originalErrorTelemetryProps,
138
+ ...errorTelemetryProps,
135
139
  ...annotations.props,
136
- untrustedOrigin: 1, // This will let us filter to errors not originated by our own code
137
140
  });
138
141
 
139
142
  return fluidError;
@@ -150,7 +153,7 @@ let stackPopulatedOnCreation: boolean | undefined;
150
153
  * Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.
151
154
  * @returns Error object that has stack populated.
152
155
  */
153
- export function generateErrorWithStack(): Error {
156
+ export function generateErrorWithStack(): Error {
154
157
  const err = new Error("<<generated stack>>");
155
158
 
156
159
  if (stackPopulatedOnCreation === undefined) {
@@ -250,21 +253,59 @@ function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string) {
250
253
 
251
254
  /**
252
255
  * True for any error object that is an (optionally normalized) external error
253
- * False for any error we created and raised within the FF codebase, or wrapped in a well-known error type
256
+ * False for any error we created and raised within the FF codebase via LoggingError base class,
257
+ * or wrapped in a well-known error type
254
258
  */
255
259
  export function isExternalError(e: any): boolean {
256
- return !isValidLegacyError(e) ||
257
- (e.getTelemetryProperties().untrustedOrigin === 1 &&
258
- e.errorType === NormalizedExternalError.normalizedErrorType);
260
+ // LoggingErrors are an internal FF error type. However, an external error can be converted
261
+ // into a LoggingError if it is normalized. In this case we must use the untrustedOrigin flag to
262
+ // determine whether the original error was infact external.
263
+ if (LoggingError.typeCheck(e)) {
264
+ if ((e as NormalizedLoggingError).errorType === NORMALIZED_ERROR_TYPE) {
265
+ return e.getTelemetryProperties().untrustedOrigin === 1;
266
+ }
267
+ return false;
268
+ }
269
+ return !isValidLegacyError(e);
259
270
  }
260
271
 
261
272
  /**
262
273
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
263
274
  */
264
275
  export function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {
265
- return (typeof (x?.value) !== "object" && typeof (x?.tag) === "string");
276
+ return typeof (x?.tag) === "string";
277
+ }
278
+
279
+ /**
280
+ * Filter serializable telemetry properties
281
+ * @param x - any telemetry prop
282
+ * @returns - as-is if x is primitive. returns stringified if x is an array of primitive.
283
+ * otherwise returns null since this is what we support at the moment.
284
+ */
285
+ function filterValidTelemetryProps(x: any, key: string): TelemetryEventPropertyType {
286
+ if (Array.isArray(x) && x.every((val) => isTelemetryEventPropertyValue(val))) {
287
+ return JSON.stringify(x);
288
+ }
289
+ if (isTelemetryEventPropertyValue(x)) {
290
+ return x;
291
+ }
292
+ // We don't support logging arbitrary objects
293
+ console.error(`UnSupported Format of Logging Error Property for key ${key}:`, x);
294
+ return "REDACTED (arbitrary object)";
266
295
  }
267
296
 
297
+ // checking type of x, returns false if x is null
298
+ function isTelemetryEventPropertyValue(x: any): x is TelemetryEventPropertyType {
299
+ switch (typeof x) {
300
+ case "string":
301
+ case "number":
302
+ case "boolean":
303
+ case "undefined":
304
+ return true;
305
+ default:
306
+ return false;
307
+ }
308
+ }
268
309
  /**
269
310
  * Walk an object's enumerable properties to find those fit for telemetry.
270
311
  */
@@ -275,22 +316,15 @@ function getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryPr
275
316
  continue;
276
317
  }
277
318
  const val = obj[key];
278
- switch (typeof val) {
279
- case "string":
280
- case "number":
281
- case "boolean":
282
- case "undefined":
283
- props[key] = val;
284
- break;
285
- default: {
286
- if (isTaggedTelemetryPropertyValue(val)) {
287
- props[key] = val;
288
- } else {
289
- // We don't support logging arbitrary objects
290
- props[key] = "REDACTED (arbitrary object)";
291
- }
292
- break;
293
- }
319
+
320
+ // ensure only valid props get logged, since props of logging error could be in any shape
321
+ if (isTaggedTelemetryPropertyValue(val)) {
322
+ props[key] = {
323
+ value: filterValidTelemetryProps(val.value, key),
324
+ tag: val.tag,
325
+ };
326
+ } else {
327
+ props[key] = filterValidTelemetryProps(val, key);
294
328
  }
295
329
  }
296
330
  return props;
@@ -354,6 +388,20 @@ export class LoggingError extends Error implements ILoggingError, Omit<IFluidErr
354
388
  }
355
389
  }
356
390
 
391
+ /**
392
+ * Determines if a given object is an instance of a LoggingError
393
+ * @param object - any object
394
+ * @returns - true if the object is an instance of a LoggingError, false if not.
395
+ */
396
+ public static typeCheck(object: unknown): object is LoggingError {
397
+ if (typeof object === "object" && object !== null) {
398
+ return typeof (object as LoggingError).addTelemetryProperties === "function"
399
+ && typeof (object as LoggingError).getTelemetryProperties === "function"
400
+ && typeof (object as LoggingError).errorInstanceId === "string";
401
+ }
402
+ return false;
403
+ }
404
+
357
405
  /**
358
406
  * Add additional properties to be logged
359
407
  */
@@ -377,12 +425,11 @@ export class LoggingError extends Error implements ILoggingError, Omit<IFluidErr
377
425
  }
378
426
 
379
427
  /** The Error class used when normalizing an external error */
380
- class NormalizedExternalError extends LoggingError {
428
+ export const NORMALIZED_ERROR_TYPE = "genericError";
429
+ class NormalizedLoggingError extends LoggingError {
381
430
  // errorType "genericError" is used as a default value throughout the code.
382
431
  // Note that this matches ContainerErrorType/DriverErrorType's genericError
383
- static readonly normalizedErrorType = "genericError";
384
-
385
- errorType = NormalizedExternalError.normalizedErrorType;
432
+ errorType = NORMALIZED_ERROR_TYPE;
386
433
 
387
434
  constructor(
388
435
  errorProps: Pick<IFluidErrorBase,
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ export * from "./logger";
11
11
  export * from "./mockLogger";
12
12
  export * from "./thresholdCounter";
13
13
  export * from "./utils";
14
+ export * from "./sampledTelemetryHelper";
14
15
  export {
15
16
  MonitoringContext,
16
17
  IConfigProviderBase,
package/src/logger.ts CHANGED
@@ -32,11 +32,6 @@ import {
32
32
  * Please do not modify existing entries for backwards compatibility.
33
33
  */
34
34
  export enum TelemetryDataTag {
35
- /**
36
- * Data containing terms from code packages that may have been dynamically loaded
37
- * @deprecated 1.0, will be removed in next release (see issue #6603). Use `TelemetryDataTag.CodeArtifact` instead.
38
- */
39
- PackageData = "PackageData",
40
35
  /** Data containing terms or IDs from code packages that may have been dynamically loaded */
41
36
  CodeArtifact = "CodeArtifact",
42
37
  /** Personal data of a variety of classifications that pertains to the user */
@@ -249,8 +244,9 @@ export abstract class TelemetryLogger implements ITelemetryLogger {
249
244
  // No tag means we can log plainly
250
245
  newEvent[key] = value;
251
246
  break;
252
- case TelemetryDataTag.PackageData:
253
- // For Microsoft applications, PackageData is safe for now
247
+ case "PackageData": // For back-compat
248
+ case TelemetryDataTag.CodeArtifact:
249
+ // For Microsoft applications, CodeArtifact is safe for now
254
250
  // (we don't load 3P code in 1P apps)
255
251
  newEvent[key] = value;
256
252
  break;
@@ -279,7 +275,7 @@ export class ChildLogger extends TelemetryLogger {
279
275
  /**
280
276
  * Create child logger
281
277
  * @param baseLogger - Base logger to use to output events. If undefined, proper child logger
282
- * is created, but it does not sends telemetry events anywhere.
278
+ * is created, but it does not send telemetry events anywhere.
283
279
  * @param namespace - Telemetry event name prefix to add to all events
284
280
  * @param properties - Base properties to add to all events
285
281
  * @param propertyGetters - Getters to add additional properties to all events
package/src/mockLogger.ts CHANGED
@@ -71,6 +71,19 @@ ${JSON.stringify(actualEvents)}`);
71
71
  expected:
72
72
  ${JSON.stringify(expectedEvents)}
73
73
 
74
+ actual:
75
+ ${JSON.stringify(actualEvents)}`);
76
+ }
77
+ }
78
+
79
+ /** Asserts that matchAnyEvent is false for the given events, and prints the actual/expected output if not */
80
+ assertMatchNone(disallowedEvents: Omit<ITelemetryBaseEvent, "category">[], message?: string) {
81
+ const actualEvents = this.events;
82
+ if (this.matchAnyEvent(disallowedEvents)) {
83
+ throw new Error(`${message}
84
+ disallowed events:
85
+ ${JSON.stringify(disallowedEvents)}
86
+
74
87
  actual:
75
88
  ${JSON.stringify(actualEvents)}`);
76
89
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/telemetry-utils";
9
- export const pkgVersion = "1.2.7";
9
+ export const pkgVersion = "2.0.0-dev.1.3.0.96595";