@fluidframework/telemetry-utils 2.0.2 → 2.1.0-276326
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/README.md +9 -0
- package/api-extractor/api-extractor.legacy.json +4 -0
- package/api-report/telemetry-utils.beta.api.md +0 -20
- package/api-report/{telemetry-utils.alpha.api.md → telemetry-utils.legacy.alpha.api.md} +0 -20
- package/api-report/telemetry-utils.public.api.md +0 -20
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -1
- package/dist/config.js.map +1 -1
- package/dist/eventEmitterWithErrorHandling.d.ts +1 -0
- package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/dist/eventEmitterWithErrorHandling.js +1 -0
- package/dist/eventEmitterWithErrorHandling.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -0
- package/dist/logger.js.map +1 -1
- package/dist/mockLogger.d.ts +23 -0
- package/dist/mockLogger.d.ts.map +1 -1
- package/dist/mockLogger.js +37 -1
- package/dist/mockLogger.js.map +1 -1
- package/dist/sampledTelemetryHelper.d.ts +59 -6
- package/dist/sampledTelemetryHelper.d.ts.map +1 -1
- package/dist/sampledTelemetryHelper.js +67 -10
- package/dist/sampledTelemetryHelper.js.map +1 -1
- package/dist/telemetryTypes.d.ts +7 -0
- package/dist/telemetryTypes.d.ts.map +1 -1
- package/dist/telemetryTypes.js.map +1 -1
- package/lib/config.d.ts +18 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +38 -0
- package/lib/config.js.map +1 -1
- package/lib/eventEmitterWithErrorHandling.d.ts +1 -0
- package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/lib/eventEmitterWithErrorHandling.js +1 -0
- package/lib/eventEmitterWithErrorHandling.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/logger.d.ts +4 -0
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +1 -0
- package/lib/logger.js.map +1 -1
- package/lib/mockLogger.d.ts +23 -0
- package/lib/mockLogger.d.ts.map +1 -1
- package/lib/mockLogger.js +35 -0
- package/lib/mockLogger.js.map +1 -1
- package/lib/sampledTelemetryHelper.d.ts +59 -6
- package/lib/sampledTelemetryHelper.d.ts.map +1 -1
- package/lib/sampledTelemetryHelper.js +67 -10
- package/lib/sampledTelemetryHelper.js.map +1 -1
- package/lib/telemetryTypes.d.ts +7 -0
- package/lib/telemetryTypes.d.ts.map +1 -1
- package/lib/telemetryTypes.js.map +1 -1
- package/package.json +17 -22
- package/src/config.ts +56 -0
- package/src/eventEmitterWithErrorHandling.ts +1 -0
- package/src/index.ts +14 -2
- package/src/logger.ts +4 -0
- package/src/mockLogger.ts +36 -0
- package/src/sampledTelemetryHelper.ts +164 -13
- package/src/telemetryTypes.ts +7 -0
|
@@ -3,14 +3,22 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { performance } from "@fluid-internal/client-utils";
|
|
6
|
+
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
+
import { roundToDecimalPlaces } from "./mathTools.js";
|
|
6
8
|
/**
|
|
7
9
|
* Helper class that executes a specified code block and writes an
|
|
8
10
|
* {@link @fluidframework/core-interfaces#ITelemetryPerformanceEvent} to a specified logger every time a specified
|
|
9
11
|
* number of executions is reached (or when the class is disposed).
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The `duration` field in the telemetry event this class generates is the duration of the latest execution (sample)
|
|
15
|
+
* of the specified code block.
|
|
16
|
+
* See the documentation of the `includeAggregateMetrics` parameter for additional details that can be included.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam TMeasurementReturn - The return type (in a vacuum) of the code block that will be measured, ignoring
|
|
19
|
+
* any custom metric data that might be required by this class. E.g., the code might just return a boolean.
|
|
20
|
+
* @typeParam TCustomMetrics - A type that contains the custom properties that will be used by an instance of this class
|
|
21
|
+
* for custom metrics. Each property in this type should be a number.
|
|
14
22
|
*
|
|
15
23
|
* @internal
|
|
16
24
|
*/
|
|
@@ -50,7 +58,12 @@ export class SampledTelemetryHelper {
|
|
|
50
58
|
}
|
|
51
59
|
/**
|
|
52
60
|
* Executes the specified code and keeps track of execution time statistics.
|
|
53
|
-
*
|
|
61
|
+
* When it's been called enough times (the sampleThreshold for the class) then it generates a log message with the
|
|
62
|
+
* necessary information.
|
|
63
|
+
*
|
|
64
|
+
* @remarks It's the responsibility of the caller to ensure that the same same set of custom metric properties is
|
|
65
|
+
* provided each time this method is called on a given instance of {@link SampledTelemetryHelper}.
|
|
66
|
+
* Otherwise the final measurements in the telemetry event may not be accurate.
|
|
54
67
|
*
|
|
55
68
|
* @param codeToMeasure - The code to be executed and measured.
|
|
56
69
|
* @param bucket - A key to track executions of the code block separately.
|
|
@@ -62,11 +75,16 @@ export class SampledTelemetryHelper {
|
|
|
62
75
|
const start = performance.now();
|
|
63
76
|
const returnValue = codeToMeasure();
|
|
64
77
|
const duration = performance.now() - start;
|
|
65
|
-
let
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
let loggerData = this.measurementsMap.get(bucket);
|
|
79
|
+
if (loggerData === undefined) {
|
|
80
|
+
loggerData = {
|
|
81
|
+
measurements: { count: 0, duration: -1 },
|
|
82
|
+
dataSums: {},
|
|
83
|
+
dataMaxes: {},
|
|
84
|
+
};
|
|
85
|
+
this.measurementsMap.set(bucket, loggerData);
|
|
69
86
|
}
|
|
87
|
+
const m = loggerData.measurements;
|
|
70
88
|
m.count++;
|
|
71
89
|
m.duration = duration;
|
|
72
90
|
if (this.includeAggregateMetrics) {
|
|
@@ -74,22 +92,61 @@ export class SampledTelemetryHelper {
|
|
|
74
92
|
m.minDuration = Math.min(m.minDuration ?? duration, duration);
|
|
75
93
|
m.maxDuration = Math.max(m.maxDuration ?? 0, duration);
|
|
76
94
|
}
|
|
95
|
+
if (this.isCustomData(returnValue)) {
|
|
96
|
+
loggerData = this.accumulateCustomData(returnValue.customData, loggerData);
|
|
97
|
+
}
|
|
77
98
|
if (m.count >= this.sampleThreshold) {
|
|
99
|
+
// Computed separately to avoid multiple division operations.
|
|
100
|
+
if (this.includeAggregateMetrics) {
|
|
101
|
+
m.averageDuration = (m.totalDuration ?? 0) / m.count;
|
|
102
|
+
}
|
|
78
103
|
this.flushBucket(bucket);
|
|
79
104
|
}
|
|
80
105
|
return returnValue;
|
|
81
106
|
}
|
|
107
|
+
isCustomData(data) {
|
|
108
|
+
return (typeof data === "object" &&
|
|
109
|
+
data !== null &&
|
|
110
|
+
"customData" in data &&
|
|
111
|
+
typeof data.customData === "object");
|
|
112
|
+
}
|
|
113
|
+
accumulateCustomData(customData, loggerData) {
|
|
114
|
+
for (const [key, val] of Object.entries(customData)) {
|
|
115
|
+
assert(typeof key === "string", "Key should be a string");
|
|
116
|
+
assert(typeof val === "number", "Value should be a number");
|
|
117
|
+
loggerData.dataSums[key] = (loggerData.dataSums[key] ?? 0) + val;
|
|
118
|
+
loggerData.dataMaxes[key] = Math.max(loggerData.dataMaxes[key] ?? Number.NEGATIVE_INFINITY, val);
|
|
119
|
+
}
|
|
120
|
+
return loggerData;
|
|
121
|
+
}
|
|
122
|
+
processCustomData(loggerData, count) {
|
|
123
|
+
const processedCustomData = {};
|
|
124
|
+
if (loggerData.dataSums === undefined || loggerData.dataMaxes === undefined) {
|
|
125
|
+
return processedCustomData;
|
|
126
|
+
}
|
|
127
|
+
const dataSums = loggerData.dataSums;
|
|
128
|
+
const dataMaxes = loggerData.dataMaxes;
|
|
129
|
+
for (const [key, val] of Object.entries(dataSums)) {
|
|
130
|
+
// implementation of class guarantees the keys between dataMaxes and dataSums align.
|
|
131
|
+
processedCustomData[`avg_${key}`] = roundToDecimalPlaces(val / count, 6);
|
|
132
|
+
processedCustomData[`max_${key}`] = dataMaxes[key] ?? 0;
|
|
133
|
+
}
|
|
134
|
+
return processedCustomData;
|
|
135
|
+
}
|
|
82
136
|
flushBucket(bucket) {
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
137
|
+
const loggerData = this.measurementsMap.get(bucket);
|
|
138
|
+
if (loggerData === undefined) {
|
|
85
139
|
return;
|
|
86
140
|
}
|
|
141
|
+
const measurements = loggerData.measurements;
|
|
142
|
+
const processedCustomData = this.processCustomData(loggerData, measurements.count);
|
|
87
143
|
if (measurements.count !== 0) {
|
|
88
144
|
const bucketProperties = this.perBucketProperties.get(bucket);
|
|
89
145
|
const telemetryEvent = {
|
|
90
146
|
...this.eventBase,
|
|
91
147
|
...bucketProperties, // If the bucket doesn't exist and this is undefined, things work as expected
|
|
92
148
|
...measurements,
|
|
149
|
+
...processedCustomData,
|
|
93
150
|
};
|
|
94
151
|
this.logger.sendPerformanceEvent(telemetryEvent);
|
|
95
152
|
this.measurementsMap.delete(bucket);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sampledTelemetryHelper.js","sourceRoot":"","sources":["../src/sampledTelemetryHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performance } from \"@fluid-internal/client-utils\";\nimport type { IDisposable, ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\n\nimport type {\n\tITelemetryGenericEventExt,\n\tITelemetryLoggerExt,\n\tITelemetryPerformanceEventExt,\n} from \"./telemetryTypes.js\";\n\n/**\n * @privateRemarks\n *\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 */\ninterface Measurements {\n\t/**\n\t * The duration of the latest execution.\n\t */\n\tduration: number;\n\n\t/**\n\t * The number of executions since the last time an event was generated.\n\t */\n\tcount: number;\n\n\t/**\n\t * Total duration across all the executions since the last event was generated.\n\t */\n\ttotalDuration?: number;\n\n\t/**\n\t * Min duration across all the executions since the last event was generated.\n\t */\n\tminDuration?: number;\n\n\t/**\n\t * Max duration across all the executions since the last event was generated.\n\t */\n\tmaxDuration?: number;\n}\n\n/**\n * Helper class that executes a specified code block and writes an\n * {@link @fluidframework/core-interfaces#ITelemetryPerformanceEvent} to a specified logger every time a specified\n * number of executions is reached (or when the class is disposed).\n *\n * The `duration` field in the telemetry event is the duration of the latest execution (sample) of the specified\n * function. See the documentation of the `includeAggregateMetrics` parameter for additional details that can be\n * included.\n *\n * @internal\n */\nexport class SampledTelemetryHelper implements IDisposable {\n\tprivate _disposed: boolean = false;\n\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#IDisposable.disposed}\n\t */\n\tpublic get disposed(): boolean {\n\t\treturn this._disposed;\n\t}\n\n\tprivate readonly measurementsMap = new Map<string, Measurements>();\n\n\t/**\n\t * @param eventBase -\n\t * Custom properties to include in the telemetry performance event when it is written.\n\t * @param logger -\n\t * The logger to use to write the telemetry performance event.\n\t * @param sampleThreshold -\n\t * Telemetry performance events will be generated every time we hit this many executions of the code block.\n\t * @param includeAggregateMetrics -\n\t * If set to `true`, the telemetry performance event will include aggregated metrics (total duration, min duration,\n\t * max duration) for all the executions in between generated events.\n\t * @param perBucketProperties -\n\t * Map of strings that represent different buckets (which can be specified when calling the 'measure' method), to\n\t * properties which should be added to the telemetry event for that bucket. If a bucket being measured does not\n\t * have an entry in this map, no additional properties will be added to its telemetry events. The following keys are\n\t * reserved for use by this class: \"duration\", \"count\", \"totalDuration\", \"minDuration\", \"maxDuration\". If any of\n\t * them is specified as a key in one of the ITelemetryBaseProperties objects in this map, that key-value pair will be\n\t * ignored.\n\t */\n\tpublic constructor(\n\t\tprivate readonly eventBase: ITelemetryGenericEventExt,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t\tprivate readonly sampleThreshold: number,\n\t\tprivate readonly includeAggregateMetrics: boolean = false,\n\t\tprivate readonly perBucketProperties = new Map<string, ITelemetryBaseProperties>(),\n\t) {}\n\n\t/**\n\t * Executes the specified code and keeps track of execution time statistics.\n\t * If it's been called enough times (the sampleThreshold for the class) then it generates a log message with the necessary information.\n\t *\n\t * @param codeToMeasure - The code to be executed and measured.\n\t * @param bucket - A key to track executions of the code block separately.\n\t * Each different value of this parameter has a separate set of executions and metrics tracked by the class.\n\t * If no such distinction needs to be made, do not provide a value.\n\t * @returns Whatever the passed-in code block returns.\n\t */\n\tpublic measure<T>(codeToMeasure: () => T, bucket: string = \"\"): T {\n\t\tconst start = performance.now();\n\t\tconst returnValue = codeToMeasure();\n\t\tconst duration = performance.now() - start;\n\n\t\tlet m = this.measurementsMap.get(bucket);\n\t\tif (m === undefined) {\n\t\t\tm = { count: 0, duration: -1 };\n\t\t\tthis.measurementsMap.set(bucket, m);\n\t\t}\n\t\tm.count++;\n\t\tm.duration = duration;\n\n\t\tif (this.includeAggregateMetrics) {\n\t\t\tm.totalDuration = (m.totalDuration ?? 0) + duration;\n\t\t\tm.minDuration = Math.min(m.minDuration ?? duration, duration);\n\t\t\tm.maxDuration = Math.max(m.maxDuration ?? 0, duration);\n\t\t}\n\n\t\tif (m.count >= this.sampleThreshold) {\n\t\t\tthis.flushBucket(bucket);\n\t\t}\n\n\t\treturn returnValue;\n\t}\n\n\tprivate flushBucket(bucket: string): void {\n\t\tconst measurements = this.measurementsMap.get(bucket);\n\t\tif (measurements === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (measurements.count !== 0) {\n\t\t\tconst bucketProperties = this.perBucketProperties.get(bucket);\n\n\t\t\tconst telemetryEvent: ITelemetryPerformanceEventExt = {\n\t\t\t\t...this.eventBase,\n\t\t\t\t...bucketProperties, // If the bucket doesn't exist and this is undefined, things work as expected\n\t\t\t\t...measurements,\n\t\t\t};\n\n\t\t\tthis.logger.sendPerformanceEvent(telemetryEvent);\n\t\t\tthis.measurementsMap.delete(bucket);\n\t\t}\n\t}\n\n\tpublic dispose(error?: Error | undefined): void {\n\t\tfor (const [k] of this.measurementsMap.entries()) {\n\t\t\tthis.flushBucket(k);\n\t\t}\n\t\tthis._disposed = true;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sampledTelemetryHelper.js","sourceRoot":"","sources":["../src/sampledTelemetryHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performance } from \"@fluid-internal/client-utils\";\nimport type { IDisposable, ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\n\nimport { roundToDecimalPlaces } from \"./mathTools.js\";\nimport type {\n\tITelemetryGenericEventExt,\n\tITelemetryLoggerExt,\n\tITelemetryPerformanceEventExt,\n} from \"./telemetryTypes.js\";\n\n/**\n * @privateRemarks\n *\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 */\ninterface Measurements {\n\t/**\n\t * The duration of the latest execution.\n\t */\n\tduration: number;\n\n\t/**\n\t * The number of executions since the last time an event was generated.\n\t */\n\tcount: number;\n\n\t/**\n\t * Total duration across all the executions since the last event was generated.\n\t */\n\ttotalDuration?: number;\n\n\t/**\n\t * Min duration across all the executions since the last event was generated.\n\t */\n\tminDuration?: number;\n\n\t/**\n\t * Max duration across all the executions since the last event was generated.\n\t */\n\tmaxDuration?: number;\n\n\t/**\n\t * Average duration across all the executions since the last event was generated.\n\t */\n\taverageDuration?: number;\n}\n\n/**\n * The data that will be logged in the telemetry event.\n */\ninterface LoggerData {\n\tmeasurements: Measurements;\n\n\t/**\n\t * The sum of the custom data passed into the logger for each key.\n\t * Absence of a given key should be interpreted as 0.\n\t */\n\tdataSums: Record<string, number>;\n\n\t/**\n\t * The max of the custom data passed into the logger for each key.\n\t */\n\tdataMaxes: Record<string, number>;\n}\n\n/**\n * Helper type for an object whose properties are all numbers\n *\n * @internal\n */\nexport type CustomMetrics<TKey> = {\n\t[K in keyof TKey]: K extends string ? number : never;\n};\n\n/**\n * Potentially part of the structure of the return value of the function provided to {@link SampledTelemetryHelper.measure}.\n *\n * @see {@link MeasureReturnType} for more details on how this type is used.\n *\n * @internal\n */\nexport interface ICustomData<T> {\n\tcustomData: CustomMetrics<T>;\n}\n\n/**\n * Encapsulates the type-level logic for {@link SampledTelemetryHelper.measure}, to determine the expected return type\n * for the function that method receives (and by extension, its own return type). In words: {@link SampledTelemetryHelper}\n * is optionally provided with two generic types: one for custom metrics, and one for the actual return value of the\n * code that will be measured.\n *\n * - If no generic type is provided for custom metrics, then this type is simply the generic type provided for the actual\n * return value of the measured code (which could be void!).\n * - If a generic type is provided for custom metrics, then this type has a `customData` property whose type matches that\n * generic. Then if the generic type for the actual return value is not void, this type also has a property `returnValue`\n * whose type matches the generic type for the actual return value; if the generic type for the actual return value is\n * void, then this type _forbids_ a `returnValue` property (technically, it can exist but must be undefined in that case),\n * to try to ensure that the caller doesn't accidentally provide a function that actually returns a value.\n *\n * @internal\n */\nexport type MeasureReturnType<TMeasureReturn, TCustomMetrics> = TCustomMetrics extends void\n\t? TMeasureReturn\n\t: ICustomData<TCustomMetrics> &\n\t\t\t(TMeasureReturn extends void\n\t\t\t\t? { [K in \"returnValue\"]?: never }\n\t\t\t\t: { returnValue: TMeasureReturn });\n\n/**\n * Helper class that executes a specified code block and writes an\n * {@link @fluidframework/core-interfaces#ITelemetryPerformanceEvent} to a specified logger every time a specified\n * number of executions is reached (or when the class is disposed).\n *\n * @remarks\n * The `duration` field in the telemetry event this class generates is the duration of the latest execution (sample)\n * of the specified code block.\n * See the documentation of the `includeAggregateMetrics` parameter for additional details that can be included.\n *\n * @typeParam TMeasurementReturn - The return type (in a vacuum) of the code block that will be measured, ignoring\n * any custom metric data that might be required by this class. E.g., the code might just return a boolean.\n * @typeParam TCustomMetrics - A type that contains the custom properties that will be used by an instance of this class\n * for custom metrics. Each property in this type should be a number.\n *\n * @internal\n */\nexport class SampledTelemetryHelper<\n\tTMeasureReturn = void,\n\tTCustomMetrics extends CustomMetrics<TCustomMetrics> = void,\n> implements IDisposable\n{\n\tprivate _disposed: boolean = false;\n\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#IDisposable.disposed}\n\t */\n\tpublic get disposed(): boolean {\n\t\treturn this._disposed;\n\t}\n\n\tprivate readonly measurementsMap = new Map<string, LoggerData>();\n\n\t/**\n\t * @param eventBase -\n\t * Custom properties to include in the telemetry performance event when it is written.\n\t * @param logger -\n\t * The logger to use to write the telemetry performance event.\n\t * @param sampleThreshold -\n\t * Telemetry performance events will be generated every time we hit this many executions of the code block.\n\t * @param includeAggregateMetrics -\n\t * If set to `true`, the telemetry performance event will include aggregated metrics (total duration, min duration,\n\t * max duration) for all the executions in between generated events.\n\t * @param perBucketProperties -\n\t * Map of strings that represent different buckets (which can be specified when calling the 'measure' method), to\n\t * properties which should be added to the telemetry event for that bucket. If a bucket being measured does not\n\t * have an entry in this map, no additional properties will be added to its telemetry events. The following keys are\n\t * reserved for use by this class: \"duration\", \"count\", \"totalDuration\", \"minDuration\", \"maxDuration\". If any of\n\t * them is specified as a key in one of the ITelemetryBaseProperties objects in this map, that key-value pair will be\n\t * ignored.\n\t */\n\tpublic constructor(\n\t\tprivate readonly eventBase: ITelemetryGenericEventExt,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t\tprivate readonly sampleThreshold: number,\n\t\tprivate readonly includeAggregateMetrics: boolean = false,\n\t\tprivate readonly perBucketProperties = new Map<string, ITelemetryBaseProperties>(),\n\t) {}\n\n\t/**\n\t * Executes the specified code and keeps track of execution time statistics.\n\t * When it's been called enough times (the sampleThreshold for the class) then it generates a log message with the\n\t * necessary information.\n\t *\n\t * @remarks It's the responsibility of the caller to ensure that the same same set of custom metric properties is\n\t * provided each time this method is called on a given instance of {@link SampledTelemetryHelper}.\n\t * Otherwise the final measurements in the telemetry event may not be accurate.\n\t *\n\t * @param codeToMeasure - The code to be executed and measured.\n\t * @param bucket - A key to track executions of the code block separately.\n\t * Each different value of this parameter has a separate set of executions and metrics tracked by the class.\n\t * If no such distinction needs to be made, do not provide a value.\n\t * @returns Whatever the passed-in code block returns.\n\t */\n\tpublic measure(\n\t\tcodeToMeasure: () => MeasureReturnType<TMeasureReturn, TCustomMetrics>,\n\t\tbucket: string = \"\",\n\t): MeasureReturnType<TMeasureReturn, TCustomMetrics> {\n\t\tconst start = performance.now();\n\t\tconst returnValue = codeToMeasure();\n\t\tconst duration = performance.now() - start;\n\n\t\tlet loggerData = this.measurementsMap.get(bucket);\n\t\tif (loggerData === undefined) {\n\t\t\tloggerData = {\n\t\t\t\tmeasurements: { count: 0, duration: -1 },\n\t\t\t\tdataSums: {},\n\t\t\t\tdataMaxes: {},\n\t\t\t};\n\t\t\tthis.measurementsMap.set(bucket, loggerData);\n\t\t}\n\n\t\tconst m = loggerData.measurements;\n\t\tm.count++;\n\t\tm.duration = duration;\n\n\t\tif (this.includeAggregateMetrics) {\n\t\t\tm.totalDuration = (m.totalDuration ?? 0) + duration;\n\t\t\tm.minDuration = Math.min(m.minDuration ?? duration, duration);\n\t\t\tm.maxDuration = Math.max(m.maxDuration ?? 0, duration);\n\t\t}\n\n\t\tif (this.isCustomData(returnValue)) {\n\t\t\tloggerData = this.accumulateCustomData(returnValue.customData, loggerData);\n\t\t}\n\n\t\tif (m.count >= this.sampleThreshold) {\n\t\t\t// Computed separately to avoid multiple division operations.\n\t\t\tif (this.includeAggregateMetrics) {\n\t\t\t\tm.averageDuration = (m.totalDuration ?? 0) / m.count;\n\t\t\t}\n\t\t\tthis.flushBucket(bucket);\n\t\t}\n\n\t\treturn returnValue;\n\t}\n\n\tprivate isCustomData(data: unknown): data is ICustomData<TCustomMetrics> {\n\t\treturn (\n\t\t\ttypeof data === \"object\" &&\n\t\t\tdata !== null &&\n\t\t\t\"customData\" in data &&\n\t\t\ttypeof data.customData === \"object\"\n\t\t);\n\t}\n\n\tprivate accumulateCustomData(\n\t\tcustomData: CustomMetrics<TCustomMetrics>,\n\t\tloggerData: LoggerData,\n\t): LoggerData {\n\t\tfor (const [key, val] of Object.entries(customData)) {\n\t\t\tassert(typeof key === \"string\", \"Key should be a string\");\n\t\t\tassert(typeof val === \"number\", \"Value should be a number\");\n\n\t\t\tloggerData.dataSums[key] = (loggerData.dataSums[key] ?? 0) + val;\n\t\t\tloggerData.dataMaxes[key] = Math.max(\n\t\t\t\tloggerData.dataMaxes[key] ?? Number.NEGATIVE_INFINITY,\n\t\t\t\tval,\n\t\t\t);\n\t\t}\n\n\t\treturn loggerData;\n\t}\n\n\tprivate processCustomData(loggerData: LoggerData, count: number): Record<string, number> {\n\t\tconst processedCustomData: Record<string, number> = {};\n\n\t\tif (loggerData.dataSums === undefined || loggerData.dataMaxes === undefined) {\n\t\t\treturn processedCustomData;\n\t\t}\n\n\t\tconst dataSums = loggerData.dataSums;\n\t\tconst dataMaxes = loggerData.dataMaxes;\n\n\t\tfor (const [key, val] of Object.entries(dataSums)) {\n\t\t\t// implementation of class guarantees the keys between dataMaxes and dataSums align.\n\t\t\tprocessedCustomData[`avg_${key}`] = roundToDecimalPlaces(val / count, 6);\n\t\t\tprocessedCustomData[`max_${key}`] = dataMaxes[key] ?? 0;\n\t\t}\n\n\t\treturn processedCustomData;\n\t}\n\n\tprivate flushBucket(bucket: string): void {\n\t\tconst loggerData = this.measurementsMap.get(bucket);\n\t\tif (loggerData === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst measurements = loggerData.measurements;\n\n\t\tconst processedCustomData = this.processCustomData(loggerData, measurements.count);\n\n\t\tif (measurements.count !== 0) {\n\t\t\tconst bucketProperties = this.perBucketProperties.get(bucket);\n\n\t\t\tconst telemetryEvent: ITelemetryPerformanceEventExt = {\n\t\t\t\t...this.eventBase,\n\t\t\t\t...bucketProperties, // If the bucket doesn't exist and this is undefined, things work as expected\n\t\t\t\t...measurements,\n\t\t\t\t...processedCustomData,\n\t\t\t};\n\n\t\t\tthis.logger.sendPerformanceEvent(telemetryEvent);\n\t\t\tthis.measurementsMap.delete(bucket);\n\t\t}\n\t}\n\n\tpublic dispose(error?: Error | undefined): void {\n\t\tfor (const [k] of this.measurementsMap.entries()) {\n\t\t\tthis.flushBucket(k);\n\t\t}\n\t\tthis._disposed = true;\n\t}\n}\n"]}
|
package/lib/telemetryTypes.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { ITelemetryBaseLogger, LogLevel, Tagged } from "@fluidframework/cor
|
|
|
11
11
|
* error - Error log event, ideally 0 of these are logged during a session
|
|
12
12
|
*
|
|
13
13
|
* performance - Includes duration, and often has _start, _end, or _cancel suffixes for activity tracking
|
|
14
|
+
* @legacy
|
|
14
15
|
* @alpha
|
|
15
16
|
*/
|
|
16
17
|
export type TelemetryEventCategory = "generic" | "error" | "performance";
|
|
@@ -20,6 +21,7 @@ export type TelemetryEventCategory = "generic" | "error" | "performance";
|
|
|
20
21
|
* @remarks
|
|
21
22
|
* Includes extra types beyond {@link @fluidframework/core-interfaces#TelemetryBaseEventPropertyType}, which must be
|
|
22
23
|
* converted before sending to a base logger.
|
|
24
|
+
* @legacy
|
|
23
25
|
* @alpha
|
|
24
26
|
*/
|
|
25
27
|
export type TelemetryEventPropertyTypeExt = string | number | boolean | undefined | (string | number | boolean)[] | Record<string, string | number | boolean | undefined | (string | number | boolean)[]>;
|
|
@@ -37,6 +39,7 @@ export interface ITaggedTelemetryPropertyTypeExt {
|
|
|
37
39
|
}
|
|
38
40
|
/**
|
|
39
41
|
* JSON-serializable properties, which will be logged with telemetry.
|
|
42
|
+
* @legacy
|
|
40
43
|
* @alpha
|
|
41
44
|
*/
|
|
42
45
|
export type ITelemetryPropertiesExt = Record<string, TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>>;
|
|
@@ -61,6 +64,7 @@ export interface ITelemetryEventExt extends ITelemetryPropertiesExt {
|
|
|
61
64
|
/**
|
|
62
65
|
* Informational (non-error) telemetry event
|
|
63
66
|
* @remarks Maps to category = "generic"
|
|
67
|
+
* @legacy
|
|
64
68
|
* @alpha
|
|
65
69
|
*/
|
|
66
70
|
export interface ITelemetryGenericEventExt extends ITelemetryPropertiesExt {
|
|
@@ -77,6 +81,7 @@ export interface ITelemetryGenericEventExt extends ITelemetryPropertiesExt {
|
|
|
77
81
|
/**
|
|
78
82
|
* Error telemetry event.
|
|
79
83
|
* @remarks Maps to category = "error"
|
|
84
|
+
* @legacy
|
|
80
85
|
* @alpha
|
|
81
86
|
*/
|
|
82
87
|
export interface ITelemetryErrorEventExt extends ITelemetryPropertiesExt {
|
|
@@ -88,6 +93,7 @@ export interface ITelemetryErrorEventExt extends ITelemetryPropertiesExt {
|
|
|
88
93
|
/**
|
|
89
94
|
* Performance telemetry event.
|
|
90
95
|
* @remarks Maps to category = "performance"
|
|
96
|
+
* @legacy
|
|
91
97
|
* @alpha
|
|
92
98
|
*/
|
|
93
99
|
export interface ITelemetryPerformanceEventExt extends ITelemetryGenericEventExt {
|
|
@@ -102,6 +108,7 @@ export interface ITelemetryPerformanceEventExt extends ITelemetryGenericEventExt
|
|
|
102
108
|
* @remarks
|
|
103
109
|
* This interface is meant to be used internally within the Fluid Framework,
|
|
104
110
|
* and `ITelemetryBaseLogger` should be used when loggers are passed between layers.
|
|
111
|
+
* @legacy
|
|
105
112
|
* @alpha
|
|
106
113
|
*/
|
|
107
114
|
export interface ITelemetryLoggerExt extends ITelemetryBaseLogger {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetryTypes.d.ts","sourceRoot":"","sources":["../src/telemetryTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9F
|
|
1
|
+
{"version":3,"file":"telemetryTypes.d.ts","sourceRoot":"","sources":["../src/telemetryTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9F;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAEzE;;;;;;;;GAQG;AACH,MAAM,MAAM,6BAA6B,GACtC,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,GACT,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,GAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;AAEzF;;;;;;;GAOG;AACH,MAAM,WAAW,+BAA+B;IAC/C,KAAK,EAAE,6BAA6B,CAAC;IACrC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAC3C,MAAM,EACN,6BAA6B,GAAG,MAAM,CAAC,6BAA6B,CAAC,CACrE,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACzE;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACvE;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,6BAA8B,SAAQ,yBAAyB;IAC/E;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAChE;;;;;OAKG;IACH,kBAAkB,CACjB,KAAK,EAAE,yBAAyB,EAChC,KAAK,CAAC,EAAE,OAAO,EACf,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC,OAAO,GAAG,OAAO,QAAQ,CAAC,OAAO,GAC1D,IAAI,CAAC;IAER;;;;OAIG;IACH,cAAc,CAAC,KAAK,EAAE,uBAAuB,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAEtE;;;;;OAKG;IACH,oBAAoB,CACnB,KAAK,EAAE,6BAA6B,EACpC,KAAK,CAAC,EAAE,OAAO,EACf,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC,OAAO,GAAG,OAAO,QAAQ,CAAC,OAAO,GAC1D,IAAI,CAAC;CACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetryTypes.js","sourceRoot":"","sources":["../src/telemetryTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger, LogLevel, Tagged } from \"@fluidframework/core-interfaces\";\n\n/**\n * The categories FF uses when instrumenting the code.\n *\n * generic - Informational log event\n *\n * error - Error log event, ideally 0 of these are logged during a session\n *\n * performance - Includes duration, and often has _start, _end, or _cancel suffixes for activity tracking\n * @alpha\n */\nexport type TelemetryEventCategory = \"generic\" | \"error\" | \"performance\";\n\n/**\n * Property types that can be logged.\n *\n * @remarks\n * Includes extra types beyond {@link @fluidframework/core-interfaces#TelemetryBaseEventPropertyType}, which must be\n * converted before sending to a base logger.\n * @alpha\n */\nexport type TelemetryEventPropertyTypeExt =\n\t| string\n\t| number\n\t| boolean\n\t| undefined\n\t| (string | number | boolean)[]\n\t| Record<string, string | number | boolean | undefined | (string | number | boolean)[]>;\n\n/**\n * A property to be logged to telemetry containing both the value and a tag. Tags are generic strings that can be used\n * to mark pieces of information that should be organized or handled differently by loggers in various first or third\n * party scenarios. For example, tags are used to mark personal information that should not be stored in logs.\n *\n * @deprecated Use {@link @fluidframework/core-interfaces#Tagged}\\<{@link TelemetryEventPropertyTypeExt}\\>\n * @internal\n */\nexport interface ITaggedTelemetryPropertyTypeExt {\n\tvalue: TelemetryEventPropertyTypeExt;\n\ttag: string;\n}\n\n/**\n * JSON-serializable properties, which will be logged with telemetry.\n * @alpha\n */\nexport type ITelemetryPropertiesExt = Record<\n\tstring,\n\tTelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>\n>;\n\n/**\n * Interface for logging telemetry statements.\n * @remarks May contain any number of properties that get serialized as json payload.\n * @param category - category of the event, like \"error\", \"performance\", \"generic\", etc.\n * @param eventName - name of the event.\n *\n * @internal\n */\nexport interface ITelemetryEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.category}\n\t */\n\tcategory: string;\n\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n}\n\n/**\n * Informational (non-error) telemetry event\n * @remarks Maps to category = \"generic\"\n * @alpha\n */\nexport interface ITelemetryGenericEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n\n\t/**\n\t * Optional event {@link @fluidframework/core-interfaces#ITelemetryBaseEvent.category}.\n\t * @defaultValue \"generic\"\n\t */\n\tcategory?: TelemetryEventCategory;\n}\n\n/**\n * Error telemetry event.\n * @remarks Maps to category = \"error\"\n * @alpha\n */\nexport interface ITelemetryErrorEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n}\n\n/**\n * Performance telemetry event.\n * @remarks Maps to category = \"performance\"\n * @alpha\n */\nexport interface ITelemetryPerformanceEventExt extends ITelemetryGenericEventExt {\n\t/**\n\t * Duration of event (optional)\n\t */\n\tduration?: number;\n}\n\n/**\n * An extended {@link @fluidframework/core-interfaces#ITelemetryBaseLogger} which allows for more lenient event types.\n *\n * @remarks\n * This interface is meant to be used internally within the Fluid Framework,\n * and `ITelemetryBaseLogger` should be used when loggers are passed between layers.\n * @alpha\n */\nexport interface ITelemetryLoggerExt extends ITelemetryBaseLogger {\n\t/**\n\t * Send an information telemetry event.\n\t * @param event - Event to send.\n\t * @param error - Optional error object to log.\n\t * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.default}.\n\t */\n\tsendTelemetryEvent(\n\t\tevent: ITelemetryGenericEventExt,\n\t\terror?: unknown,\n\t\tlogLevel?: typeof LogLevel.verbose | typeof LogLevel.default,\n\t): void;\n\n\t/**\n\t * Send an error telemetry event.\n\t * @param event - Event to send.\n\t * @param error - Optional error object to log.\n\t */\n\tsendErrorEvent(event: ITelemetryErrorEventExt, error?: unknown): void;\n\n\t/**\n\t * Send a performance telemetry event.\n\t * @param event - Event to send\n\t * @param error - Optional error object to log.\n\t * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.default}.\n\t */\n\tsendPerformanceEvent(\n\t\tevent: ITelemetryPerformanceEventExt,\n\t\terror?: unknown,\n\t\tlogLevel?: typeof LogLevel.verbose | typeof LogLevel.default,\n\t): void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"telemetryTypes.js","sourceRoot":"","sources":["../src/telemetryTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger, LogLevel, Tagged } from \"@fluidframework/core-interfaces\";\n\n/**\n * The categories FF uses when instrumenting the code.\n *\n * generic - Informational log event\n *\n * error - Error log event, ideally 0 of these are logged during a session\n *\n * performance - Includes duration, and often has _start, _end, or _cancel suffixes for activity tracking\n * @legacy\n * @alpha\n */\nexport type TelemetryEventCategory = \"generic\" | \"error\" | \"performance\";\n\n/**\n * Property types that can be logged.\n *\n * @remarks\n * Includes extra types beyond {@link @fluidframework/core-interfaces#TelemetryBaseEventPropertyType}, which must be\n * converted before sending to a base logger.\n * @legacy\n * @alpha\n */\nexport type TelemetryEventPropertyTypeExt =\n\t| string\n\t| number\n\t| boolean\n\t| undefined\n\t| (string | number | boolean)[]\n\t| Record<string, string | number | boolean | undefined | (string | number | boolean)[]>;\n\n/**\n * A property to be logged to telemetry containing both the value and a tag. Tags are generic strings that can be used\n * to mark pieces of information that should be organized or handled differently by loggers in various first or third\n * party scenarios. For example, tags are used to mark personal information that should not be stored in logs.\n *\n * @deprecated Use {@link @fluidframework/core-interfaces#Tagged}\\<{@link TelemetryEventPropertyTypeExt}\\>\n * @internal\n */\nexport interface ITaggedTelemetryPropertyTypeExt {\n\tvalue: TelemetryEventPropertyTypeExt;\n\ttag: string;\n}\n\n/**\n * JSON-serializable properties, which will be logged with telemetry.\n * @legacy\n * @alpha\n */\nexport type ITelemetryPropertiesExt = Record<\n\tstring,\n\tTelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>\n>;\n\n/**\n * Interface for logging telemetry statements.\n * @remarks May contain any number of properties that get serialized as json payload.\n * @param category - category of the event, like \"error\", \"performance\", \"generic\", etc.\n * @param eventName - name of the event.\n *\n * @internal\n */\nexport interface ITelemetryEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.category}\n\t */\n\tcategory: string;\n\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n}\n\n/**\n * Informational (non-error) telemetry event\n * @remarks Maps to category = \"generic\"\n * @legacy\n * @alpha\n */\nexport interface ITelemetryGenericEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n\n\t/**\n\t * Optional event {@link @fluidframework/core-interfaces#ITelemetryBaseEvent.category}.\n\t * @defaultValue \"generic\"\n\t */\n\tcategory?: TelemetryEventCategory;\n}\n\n/**\n * Error telemetry event.\n * @remarks Maps to category = \"error\"\n * @legacy\n * @alpha\n */\nexport interface ITelemetryErrorEventExt extends ITelemetryPropertiesExt {\n\t/**\n\t * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseEvent.eventName}\n\t */\n\teventName: string;\n}\n\n/**\n * Performance telemetry event.\n * @remarks Maps to category = \"performance\"\n * @legacy\n * @alpha\n */\nexport interface ITelemetryPerformanceEventExt extends ITelemetryGenericEventExt {\n\t/**\n\t * Duration of event (optional)\n\t */\n\tduration?: number;\n}\n\n/**\n * An extended {@link @fluidframework/core-interfaces#ITelemetryBaseLogger} which allows for more lenient event types.\n *\n * @remarks\n * This interface is meant to be used internally within the Fluid Framework,\n * and `ITelemetryBaseLogger` should be used when loggers are passed between layers.\n * @legacy\n * @alpha\n */\nexport interface ITelemetryLoggerExt extends ITelemetryBaseLogger {\n\t/**\n\t * Send an information telemetry event.\n\t * @param event - Event to send.\n\t * @param error - Optional error object to log.\n\t * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.default}.\n\t */\n\tsendTelemetryEvent(\n\t\tevent: ITelemetryGenericEventExt,\n\t\terror?: unknown,\n\t\tlogLevel?: typeof LogLevel.verbose | typeof LogLevel.default,\n\t): void;\n\n\t/**\n\t * Send an error telemetry event.\n\t * @param event - Event to send.\n\t * @param error - Optional error object to log.\n\t */\n\tsendErrorEvent(event: ITelemetryErrorEventExt, error?: unknown): void;\n\n\t/**\n\t * Send a performance telemetry event.\n\t * @param event - Event to send\n\t * @param error - Optional error object to log.\n\t * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.default}.\n\t */\n\tsendPerformanceEvent(\n\t\tevent: ITelemetryPerformanceEventExt,\n\t\terror?: unknown,\n\t\tlogLevel?: typeof LogLevel.verbose | typeof LogLevel.default,\n\t): void;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/telemetry-utils",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0-276326",
|
|
4
4
|
"description": "Collection of telemetry relates utilities for Fluid",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -67,22 +67,22 @@
|
|
|
67
67
|
"temp-directory": "nyc/.nyc_output"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@fluid-internal/client-utils": "
|
|
71
|
-
"@fluidframework/core-interfaces": "
|
|
72
|
-
"@fluidframework/core-utils": "
|
|
73
|
-
"@fluidframework/driver-definitions": "
|
|
70
|
+
"@fluid-internal/client-utils": "2.1.0-276326",
|
|
71
|
+
"@fluidframework/core-interfaces": "2.1.0-276326",
|
|
72
|
+
"@fluidframework/core-utils": "2.1.0-276326",
|
|
73
|
+
"@fluidframework/driver-definitions": "2.1.0-276326",
|
|
74
74
|
"debug": "^4.3.4",
|
|
75
75
|
"uuid": "^9.0.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@arethetypeswrong/cli": "^0.15.2",
|
|
79
79
|
"@biomejs/biome": "^1.7.3",
|
|
80
|
-
"@fluid-internal/mocha-test-setup": "
|
|
80
|
+
"@fluid-internal/mocha-test-setup": "2.1.0-276326",
|
|
81
81
|
"@fluid-tools/build-cli": "^0.39.0",
|
|
82
82
|
"@fluidframework/build-common": "^2.0.3",
|
|
83
83
|
"@fluidframework/build-tools": "^0.39.0",
|
|
84
84
|
"@fluidframework/eslint-config-fluid": "^5.3.0",
|
|
85
|
-
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-rc.
|
|
85
|
+
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-rc.5.0.0",
|
|
86
86
|
"@microsoft/api-extractor": "^7.45.1",
|
|
87
87
|
"@types/debug": "^4.1.5",
|
|
88
88
|
"@types/mocha": "^9.1.1",
|
|
@@ -104,25 +104,18 @@
|
|
|
104
104
|
"typescript": "~5.4.5"
|
|
105
105
|
},
|
|
106
106
|
"typeValidation": {
|
|
107
|
-
"broken": {
|
|
108
|
-
"ClassDeclaration_MockLogger": {
|
|
109
|
-
"backCompat": false,
|
|
110
|
-
"forwardCompat": false
|
|
111
|
-
},
|
|
112
|
-
"RemovedFunctionDeclaration_isValidLegacyError": {
|
|
113
|
-
"forwardCompat": false,
|
|
114
|
-
"backCompat": false
|
|
115
|
-
}
|
|
116
|
-
}
|
|
107
|
+
"broken": {}
|
|
117
108
|
},
|
|
118
109
|
"scripts": {
|
|
119
110
|
"api": "fluid-build . --task api",
|
|
120
|
-
"api-extractor:commonjs": "flub generate entrypoints --
|
|
121
|
-
"api-extractor:esnext": "flub generate entrypoints --
|
|
111
|
+
"api-extractor:commonjs": "flub generate entrypoints --outDir ./dist",
|
|
112
|
+
"api-extractor:esnext": "flub generate entrypoints --outDir ./lib --node10TypeCompat",
|
|
122
113
|
"build": "fluid-build . --task build",
|
|
123
114
|
"build:commonjs": "fluid-build . --task commonjs",
|
|
124
115
|
"build:compile": "fluid-build . --task compile",
|
|
125
|
-
"build:docs": "
|
|
116
|
+
"build:docs": "concurrently \"npm:build:docs:*\"",
|
|
117
|
+
"build:docs:current": "api-extractor run --local",
|
|
118
|
+
"build:docs:legacy": "api-extractor run --local --config api-extractor/api-extractor.legacy.json",
|
|
126
119
|
"build:esnext": "tsc --project ./tsconfig.json",
|
|
127
120
|
"build:test": "npm run build:test:esm && npm run build:test:cjs",
|
|
128
121
|
"build:test:cjs": "fluid-tsc commonjs --project ./src/test/tsconfig.cjs.json",
|
|
@@ -138,7 +131,9 @@
|
|
|
138
131
|
"check:exports:esm:public": "api-extractor run --config api-extractor/api-extractor-lint-public.esm.json",
|
|
139
132
|
"check:format": "npm run check:biome",
|
|
140
133
|
"check:prettier": "prettier --check . --cache --ignore-path ../../../.prettierignore",
|
|
141
|
-
"ci:build:docs": "
|
|
134
|
+
"ci:build:docs": "concurrently \"npm:ci:build:docs:*\"",
|
|
135
|
+
"ci:build:docs:current": "api-extractor run",
|
|
136
|
+
"ci:build:docs:legacy": "api-extractor run --config api-extractor/api-extractor.legacy.json",
|
|
142
137
|
"clean": "rimraf --glob dist lib \"*.d.ts\" \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
|
|
143
138
|
"eslint": "eslint --format stylish src",
|
|
144
139
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
@@ -154,7 +149,7 @@
|
|
|
154
149
|
"test:mocha:esm": "mocha --recursive \"lib/test/**/*.spec.*js\" --exit",
|
|
155
150
|
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
156
151
|
"tsc": "fluid-tsc commonjs --project ./tsconfig.cjs.json && copyfiles -f ../../../common/build/build-common/src/cjs/package.json ./dist",
|
|
157
|
-
"typetests:gen": "flub generate typetests --dir . -v
|
|
152
|
+
"typetests:gen": "flub generate typetests --dir . -v",
|
|
158
153
|
"typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
|
|
159
154
|
}
|
|
160
155
|
}
|
package/src/config.ts
CHANGED
|
@@ -348,3 +348,59 @@ export function createChildMonitoringContext(
|
|
|
348
348
|
): MonitoringContext {
|
|
349
349
|
return loggerToMonitoringContext(createChildLogger(props));
|
|
350
350
|
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* @internal
|
|
354
|
+
* */
|
|
355
|
+
export type OptionConfigReaders<T extends object> = {
|
|
356
|
+
[K in keyof T]?: K extends string
|
|
357
|
+
? (config: IConfigProvider, name: `Fluid.${string}.${K}`) => T[K] | undefined
|
|
358
|
+
: undefined;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Creates a proxy object that allows for reading configuration values from a IConfigProviderBase,
|
|
363
|
+
* and default to the provided options if the configuration value is not present.
|
|
364
|
+
*
|
|
365
|
+
* @param config - the configuration provider to read values from.
|
|
366
|
+
* @param namespace - the namespace to use when reading configuration values.
|
|
367
|
+
* @param configReaders - a mapping of option keys to configuration value readers.
|
|
368
|
+
* @param defaultOptions - the default options to use if the configuration value is not present.
|
|
369
|
+
*
|
|
370
|
+
* @internal
|
|
371
|
+
* */
|
|
372
|
+
export function createConfigBasedOptionsProxy<T extends object>(
|
|
373
|
+
config: IConfigProviderBase,
|
|
374
|
+
namespace: `Fluid.${string}`,
|
|
375
|
+
configReaders: OptionConfigReaders<T>,
|
|
376
|
+
defaultOptions?: Partial<T>,
|
|
377
|
+
): Readonly<Partial<T>> {
|
|
378
|
+
const realConfig =
|
|
379
|
+
config instanceof CachedConfigProvider
|
|
380
|
+
? config
|
|
381
|
+
: new CachedConfigProvider(undefined, config);
|
|
382
|
+
|
|
383
|
+
const keys = new Set<string>([
|
|
384
|
+
...Object.keys(defaultOptions ?? {}),
|
|
385
|
+
...Object.keys(configReaders),
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
return new Proxy<Partial<T>>(Object.freeze({}), {
|
|
389
|
+
get: (_, prop: string & keyof T): unknown => {
|
|
390
|
+
const reader = configReaders[prop];
|
|
391
|
+
const value = reader?.(realConfig, `${namespace}.${prop}`);
|
|
392
|
+
if (value !== undefined) {
|
|
393
|
+
return value;
|
|
394
|
+
}
|
|
395
|
+
return defaultOptions?.[prop];
|
|
396
|
+
},
|
|
397
|
+
has: (_, prop: string): boolean => keys.has(prop),
|
|
398
|
+
// we don't want the keys of this object to be enumerable
|
|
399
|
+
// as accessing them will trigger a config read, which
|
|
400
|
+
// should only happen when the value is accessed via
|
|
401
|
+
// a previously known key.
|
|
402
|
+
ownKeys: (): (string | symbol)[] => {
|
|
403
|
+
throw new TypeError("OptionsProxy keys are not enumerable");
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
}
|
|
@@ -15,6 +15,7 @@ import type { IEvent } from "@fluidframework/core-interfaces";
|
|
|
15
15
|
* @privateRemarks
|
|
16
16
|
* This probably doesn't belong in this package, as it is not telemetry-specific, and is really only intended for internal fluid-framework use.
|
|
17
17
|
* We should consider moving it to the `core-utils` package.
|
|
18
|
+
* @legacy
|
|
18
19
|
* @alpha
|
|
19
20
|
*/
|
|
20
21
|
export class EventEmitterWithErrorHandling<
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ export {
|
|
|
11
11
|
type IConfigProvider,
|
|
12
12
|
loggerToMonitoringContext,
|
|
13
13
|
wrapConfigProviderWithDefaults,
|
|
14
|
+
createConfigBasedOptionsProxy,
|
|
15
|
+
type OptionConfigReaders,
|
|
14
16
|
} from "./config.js";
|
|
15
17
|
export {
|
|
16
18
|
DataCorruptionError,
|
|
@@ -61,9 +63,19 @@ export {
|
|
|
61
63
|
TelemetryDataTag,
|
|
62
64
|
type TelemetryEventPropertyTypes,
|
|
63
65
|
} from "./logger.js";
|
|
64
|
-
export {
|
|
66
|
+
export {
|
|
67
|
+
createMockLoggerExt,
|
|
68
|
+
type IMockLoggerExt,
|
|
69
|
+
MockLogger,
|
|
70
|
+
MockLogger2,
|
|
71
|
+
} from "./mockLogger.js";
|
|
65
72
|
export { ThresholdCounter } from "./thresholdCounter.js";
|
|
66
|
-
export {
|
|
73
|
+
export {
|
|
74
|
+
SampledTelemetryHelper,
|
|
75
|
+
type CustomMetrics,
|
|
76
|
+
type ICustomData,
|
|
77
|
+
type MeasureReturnType,
|
|
78
|
+
} from "./sampledTelemetryHelper.js";
|
|
67
79
|
export {
|
|
68
80
|
createSampledLogger,
|
|
69
81
|
type IEventSampler,
|
package/src/logger.ts
CHANGED
|
@@ -53,11 +53,13 @@ export enum TelemetryDataTag {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
+
* @legacy
|
|
56
57
|
* @alpha
|
|
57
58
|
*/
|
|
58
59
|
export type TelemetryEventPropertyTypes = ITelemetryPropertiesExt[string];
|
|
59
60
|
|
|
60
61
|
/**
|
|
62
|
+
* @legacy
|
|
61
63
|
* @alpha
|
|
62
64
|
*/
|
|
63
65
|
export type ITelemetryLoggerPropertyBag = Record<
|
|
@@ -66,6 +68,7 @@ export type ITelemetryLoggerPropertyBag = Record<
|
|
|
66
68
|
>;
|
|
67
69
|
|
|
68
70
|
/**
|
|
71
|
+
* @legacy
|
|
69
72
|
* @alpha
|
|
70
73
|
*/
|
|
71
74
|
export interface ITelemetryLoggerPropertyBags {
|
|
@@ -368,6 +371,7 @@ export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
|
|
|
368
371
|
*
|
|
369
372
|
* @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.
|
|
370
373
|
*
|
|
374
|
+
* @legacy
|
|
371
375
|
* @alpha
|
|
372
376
|
*/
|
|
373
377
|
export function createChildLogger(props?: {
|
package/src/mockLogger.ts
CHANGED
|
@@ -33,6 +33,17 @@ import type {
|
|
|
33
33
|
*
|
|
34
34
|
* @privateRemarks TODO: When we are ready, this type should be made `internal`, and the deprecation notice should be removed.
|
|
35
35
|
*
|
|
36
|
+
* @deprecated
|
|
37
|
+
*
|
|
38
|
+
* This class is not intended for use outside of the `fluid-framework` repo, and will be removed from
|
|
39
|
+
* package exports in the near future.
|
|
40
|
+
*
|
|
41
|
+
* Please migrate usages by either creating your own mock {@link @fluidframework/core-interfaces#ITelemetryBaseLogger}
|
|
42
|
+
* implementation, or by copying this code as-is into your own repo.
|
|
43
|
+
*
|
|
44
|
+
* @privateRemarks TODO: When we are ready, this type should be made `internal`, and the deprecation notice should be removed.
|
|
45
|
+
*
|
|
46
|
+
* @legacy
|
|
36
47
|
* @alpha
|
|
37
48
|
*/
|
|
38
49
|
export class MockLogger implements ITelemetryBaseLogger {
|
|
@@ -375,3 +386,28 @@ export function createMockLoggerExt(minLogLevel?: LogLevel): IMockLoggerExt {
|
|
|
375
386
|
});
|
|
376
387
|
return childLogger as IMockLoggerExt;
|
|
377
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Temporary extension to add new functionality during breaking change freeze,
|
|
392
|
+
* since MockLogger wasn't able to be made internal yet.
|
|
393
|
+
*
|
|
394
|
+
* @internal
|
|
395
|
+
*/
|
|
396
|
+
export class MockLogger2 extends MockLogger {
|
|
397
|
+
/**
|
|
398
|
+
* Throws if any errors were logged
|
|
399
|
+
*/
|
|
400
|
+
public assertNoErrors(message?: string, clearEventsAfterCheck: boolean = true): void {
|
|
401
|
+
const actualEvents = this.events;
|
|
402
|
+
const errors = actualEvents.filter((event) => event.category === "error");
|
|
403
|
+
if (clearEventsAfterCheck) {
|
|
404
|
+
this.clear();
|
|
405
|
+
}
|
|
406
|
+
if (errors.length > 0) {
|
|
407
|
+
throw new Error(`${message ?? "Errors found in logs"}
|
|
408
|
+
|
|
409
|
+
error logs:
|
|
410
|
+
${JSON.stringify(errors)}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|