@fluidframework/telemetry-utils 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191258
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/.eslintrc.js +2 -1
- package/CHANGELOG.md +108 -0
- package/README.md +4 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -36
- package/dist/config.js.map +1 -1
- package/dist/error.d.ts +92 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +133 -0
- package/dist/error.js.map +1 -0
- package/dist/errorLogging.d.ts +44 -19
- package/dist/errorLogging.d.ts.map +1 -1
- package/dist/errorLogging.js +70 -31
- package/dist/errorLogging.js.map +1 -1
- package/dist/eventEmitterWithErrorHandling.d.ts +3 -3
- package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/dist/eventEmitterWithErrorHandling.js +10 -3
- package/dist/eventEmitterWithErrorHandling.js.map +1 -1
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/fluidErrorBase.d.ts +48 -15
- package/dist/fluidErrorBase.d.ts.map +1 -1
- package/dist/fluidErrorBase.js +21 -14
- package/dist/fluidErrorBase.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -8
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +98 -60
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +193 -124
- package/dist/logger.js.map +1 -1
- package/dist/mockLogger.d.ts +17 -8
- package/dist/mockLogger.d.ts.map +1 -1
- package/dist/mockLogger.js +49 -28
- package/dist/mockLogger.js.map +1 -1
- package/dist/sampledTelemetryHelper.d.ts +8 -7
- package/dist/sampledTelemetryHelper.d.ts.map +1 -1
- package/dist/sampledTelemetryHelper.js +21 -16
- package/dist/sampledTelemetryHelper.js.map +1 -1
- package/dist/telemetryTypes.d.ts +20 -6
- package/dist/telemetryTypes.d.ts.map +1 -1
- package/dist/telemetryTypes.js.map +1 -1
- package/dist/thresholdCounter.d.ts.map +1 -1
- package/dist/thresholdCounter.js.map +1 -1
- package/dist/utils.d.ts +2 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +33 -36
- package/lib/config.js.map +1 -1
- package/lib/error.d.ts +92 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +125 -0
- package/lib/error.js.map +1 -0
- package/lib/errorLogging.d.ts +44 -19
- package/lib/errorLogging.d.ts.map +1 -1
- package/lib/errorLogging.js +69 -31
- package/lib/errorLogging.js.map +1 -1
- package/lib/eventEmitterWithErrorHandling.d.ts +3 -3
- package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
- package/lib/eventEmitterWithErrorHandling.js +9 -2
- package/lib/eventEmitterWithErrorHandling.js.map +1 -1
- package/lib/events.d.ts +1 -1
- package/lib/events.d.ts.map +1 -1
- package/lib/events.js.map +1 -1
- package/lib/fluidErrorBase.d.ts +48 -15
- package/lib/fluidErrorBase.d.ts.map +1 -1
- package/lib/fluidErrorBase.js +21 -14
- package/lib/fluidErrorBase.js.map +1 -1
- package/lib/index.d.ts +5 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -4
- package/lib/index.js.map +1 -1
- package/lib/logger.d.ts +98 -60
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +184 -119
- package/lib/logger.js.map +1 -1
- package/lib/mockLogger.d.ts +17 -8
- package/lib/mockLogger.d.ts.map +1 -1
- package/lib/mockLogger.js +50 -29
- package/lib/mockLogger.js.map +1 -1
- package/lib/sampledTelemetryHelper.d.ts +8 -7
- package/lib/sampledTelemetryHelper.d.ts.map +1 -1
- package/lib/sampledTelemetryHelper.js +19 -14
- package/lib/sampledTelemetryHelper.js.map +1 -1
- package/lib/telemetryTypes.d.ts +20 -6
- package/lib/telemetryTypes.d.ts.map +1 -1
- package/lib/telemetryTypes.js.map +1 -1
- package/lib/thresholdCounter.d.ts.map +1 -1
- package/lib/thresholdCounter.js.map +1 -1
- package/lib/utils.d.ts +2 -2
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -2
- package/lib/utils.js.map +1 -1
- package/package.json +19 -22
- package/src/config.ts +23 -13
- package/src/error.ts +202 -0
- package/src/errorLogging.ts +101 -56
- package/src/eventEmitterWithErrorHandling.ts +5 -3
- package/src/events.ts +3 -3
- package/src/fluidErrorBase.ts +62 -26
- package/src/index.ts +17 -6
- package/src/logger.ts +290 -120
- package/src/mockLogger.ts +65 -24
- package/src/sampledTelemetryHelper.ts +18 -14
- package/src/telemetryTypes.ts +29 -6
- package/src/thresholdCounter.ts +2 -2
- package/src/utils.ts +2 -2
- package/dist/debugLogger.d.ts +0 -39
- package/dist/debugLogger.d.ts.map +0 -1
- package/dist/debugLogger.js +0 -112
- package/dist/debugLogger.js.map +0 -1
- package/lib/debugLogger.d.ts +0 -39
- package/lib/debugLogger.d.ts.map +0 -1
- package/lib/debugLogger.js +0 -108
- package/lib/debugLogger.js.map +0 -1
- package/src/debugLogger.ts +0 -143
package/src/logger.ts
CHANGED
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
ITelemetryGenericEvent,
|
|
11
11
|
ITelemetryPerformanceEvent,
|
|
12
12
|
ITelemetryProperties,
|
|
13
|
-
TelemetryEventPropertyType,
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
TelemetryBaseEventPropertyType as TelemetryEventPropertyType,
|
|
14
|
+
LogLevel,
|
|
15
|
+
Tagged,
|
|
16
|
+
ITelemetryBaseProperties,
|
|
17
|
+
TelemetryBaseEventPropertyType,
|
|
16
18
|
} from "@fluidframework/core-interfaces";
|
|
17
|
-
import { IsomorphicPerformance, performance } from "@
|
|
19
|
+
import { IsomorphicPerformance, performance } from "@fluid-internal/client-utils";
|
|
18
20
|
import { CachedConfigProvider, loggerIsMonitoringContext, mixinMonitoringContext } from "./config";
|
|
19
21
|
import {
|
|
20
22
|
isILoggingError,
|
|
@@ -23,12 +25,12 @@ import {
|
|
|
23
25
|
isTaggedTelemetryPropertyValue,
|
|
24
26
|
} from "./errorLogging";
|
|
25
27
|
import {
|
|
26
|
-
ITaggedTelemetryPropertyTypeExt,
|
|
27
28
|
ITelemetryEventExt,
|
|
28
29
|
ITelemetryGenericEventExt,
|
|
29
30
|
ITelemetryLoggerExt,
|
|
30
31
|
ITelemetryPerformanceEventExt,
|
|
31
32
|
TelemetryEventPropertyTypeExt,
|
|
33
|
+
TelemetryEventCategory,
|
|
32
34
|
} from "./telemetryTypes";
|
|
33
35
|
|
|
34
36
|
export interface Memory {
|
|
@@ -43,13 +45,17 @@ export interface PerformanceWithMemory extends IsomorphicPerformance {
|
|
|
43
45
|
* Please do not modify existing entries for backwards compatibility.
|
|
44
46
|
*/
|
|
45
47
|
export enum TelemetryDataTag {
|
|
46
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Data containing terms or IDs from code packages that may have been dynamically loaded
|
|
50
|
+
*/
|
|
47
51
|
CodeArtifact = "CodeArtifact",
|
|
48
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Personal data of a variety of classifications that pertains to the user
|
|
54
|
+
*/
|
|
49
55
|
UserData = "UserData",
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
export type TelemetryEventPropertyTypes =
|
|
58
|
+
export type TelemetryEventPropertyTypes = ITelemetryBaseProperties[string];
|
|
53
59
|
|
|
54
60
|
export interface ITelemetryLoggerPropertyBag {
|
|
55
61
|
[index: string]: TelemetryEventPropertyTypes | (() => TelemetryEventPropertyTypes);
|
|
@@ -59,33 +65,36 @@ export interface ITelemetryLoggerPropertyBags {
|
|
|
59
65
|
error?: ITelemetryLoggerPropertyBag;
|
|
60
66
|
}
|
|
61
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Attempts to parse number from string.
|
|
70
|
+
* If fails,returns original string.
|
|
71
|
+
* Used to make telemetry data typed (and support math operations, like comparison),
|
|
72
|
+
* in places where we do expect numbers (like contentsize/duration property in http header)
|
|
73
|
+
*/
|
|
74
|
+
// eslint-disable-next-line @rushstack/no-new-null
|
|
75
|
+
export function numberFromString(str: string | null | undefined): string | number | undefined {
|
|
76
|
+
if (str === undefined || str === null) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const num = Number(str);
|
|
80
|
+
return Number.isNaN(num) ? str : num;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function formatTick(tick: number): number {
|
|
84
|
+
return Math.floor(tick);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const eventNamespaceSeparator = ":" as const;
|
|
88
|
+
|
|
62
89
|
/**
|
|
63
90
|
* TelemetryLogger class contains various helper telemetry methods,
|
|
64
91
|
* encoding in one place schemas for various types of Fluid telemetry events.
|
|
65
92
|
* Creates sub-logger that appends properties to all events
|
|
66
93
|
*/
|
|
67
94
|
export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
68
|
-
public static readonly eventNamespaceSeparator =
|
|
69
|
-
|
|
70
|
-
public static formatTick(tick: number): number {
|
|
71
|
-
return Math.floor(tick);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Attempts to parse number from string.
|
|
76
|
-
* If fails,returns original string.
|
|
77
|
-
* Used to make telemetry data typed (and support math operations, like comparison),
|
|
78
|
-
* in places where we do expect numbers (like contentsize/duration property in http header)
|
|
79
|
-
*/
|
|
80
|
-
public static numberFromString(str: string | null | undefined): string | number | undefined {
|
|
81
|
-
if (str === undefined || str === null) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
const num = Number(str);
|
|
85
|
-
return Number.isNaN(num) ? str : num;
|
|
86
|
-
}
|
|
95
|
+
public static readonly eventNamespaceSeparator = eventNamespaceSeparator;
|
|
87
96
|
|
|
88
|
-
public static sanitizePkgName(name: string) {
|
|
97
|
+
public static sanitizePkgName(name: string): string {
|
|
89
98
|
return name.replace("@", "").replace("/", "-");
|
|
90
99
|
}
|
|
91
100
|
|
|
@@ -96,7 +105,11 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
96
105
|
* @param error - Error to extract info from
|
|
97
106
|
* @param fetchStack - Whether to fetch the current callstack if error.stack is undefined
|
|
98
107
|
*/
|
|
99
|
-
public static prepareErrorObject(
|
|
108
|
+
public static prepareErrorObject(
|
|
109
|
+
event: ITelemetryBaseEvent,
|
|
110
|
+
error: unknown,
|
|
111
|
+
fetchStack: boolean,
|
|
112
|
+
): void {
|
|
100
113
|
const { message, errorType, stack } = extractLogSafeErrorProperties(
|
|
101
114
|
error,
|
|
102
115
|
true /* sanitizeStack */,
|
|
@@ -134,16 +147,26 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
134
147
|
*
|
|
135
148
|
* @param event - the event to send
|
|
136
149
|
*/
|
|
137
|
-
public abstract send(event: ITelemetryBaseEvent): void;
|
|
150
|
+
public abstract send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void;
|
|
138
151
|
|
|
139
152
|
/**
|
|
140
153
|
* Send a telemetry event with the logger
|
|
141
154
|
*
|
|
142
155
|
* @param event - the event to send
|
|
143
156
|
* @param error - optional error object to log
|
|
157
|
+
* @param logLevel - optional level of the log. It category of event is set as error,
|
|
158
|
+
* then the logLevel will be upgraded to be an error.
|
|
144
159
|
*/
|
|
145
|
-
public sendTelemetryEvent(
|
|
146
|
-
|
|
160
|
+
public sendTelemetryEvent(
|
|
161
|
+
event: ITelemetryGenericEventExt,
|
|
162
|
+
error?: unknown,
|
|
163
|
+
logLevel: typeof LogLevel.verbose | typeof LogLevel.default = LogLevel.default,
|
|
164
|
+
): void {
|
|
165
|
+
this.sendTelemetryEventCore(
|
|
166
|
+
{ ...event, category: event.category ?? "generic" },
|
|
167
|
+
error,
|
|
168
|
+
event.category === "error" ? LogLevel.error : logLevel,
|
|
169
|
+
);
|
|
147
170
|
}
|
|
148
171
|
|
|
149
172
|
/**
|
|
@@ -151,11 +174,13 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
151
174
|
*
|
|
152
175
|
* @param event - the event to send
|
|
153
176
|
* @param error - optional error object to log
|
|
177
|
+
* @param logLevel - optional level of the log.
|
|
154
178
|
*/
|
|
155
179
|
protected sendTelemetryEventCore(
|
|
156
180
|
event: ITelemetryGenericEventExt & { category: TelemetryEventCategory },
|
|
157
|
-
error?:
|
|
158
|
-
|
|
181
|
+
error?: unknown,
|
|
182
|
+
logLevel?: LogLevel,
|
|
183
|
+
): void {
|
|
159
184
|
const newEvent = convertToBaseEvent(event);
|
|
160
185
|
if (error !== undefined) {
|
|
161
186
|
TelemetryLogger.prepareErrorObject(newEvent, error, false);
|
|
@@ -163,10 +188,10 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
163
188
|
|
|
164
189
|
// Will include Nan & Infinity, but probably we do not care
|
|
165
190
|
if (typeof newEvent.duration === "number") {
|
|
166
|
-
newEvent.duration =
|
|
191
|
+
newEvent.duration = formatTick(newEvent.duration);
|
|
167
192
|
}
|
|
168
193
|
|
|
169
|
-
this.send(newEvent);
|
|
194
|
+
this.send(newEvent, logLevel);
|
|
170
195
|
}
|
|
171
196
|
|
|
172
197
|
/**
|
|
@@ -175,7 +200,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
175
200
|
* @param event - the event to send
|
|
176
201
|
* @param error - optional error object to log
|
|
177
202
|
*/
|
|
178
|
-
public sendErrorEvent(event: ITelemetryErrorEvent, error?:
|
|
203
|
+
public sendErrorEvent(event: ITelemetryErrorEvent, error?: unknown): void {
|
|
179
204
|
this.sendTelemetryEventCore(
|
|
180
205
|
{
|
|
181
206
|
// ensure the error field has some value,
|
|
@@ -185,6 +210,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
185
210
|
category: "error",
|
|
186
211
|
},
|
|
187
212
|
error,
|
|
213
|
+
LogLevel.error,
|
|
188
214
|
);
|
|
189
215
|
}
|
|
190
216
|
|
|
@@ -193,14 +219,24 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
193
219
|
*
|
|
194
220
|
* @param event - Event to send
|
|
195
221
|
* @param error - optional error object to log
|
|
222
|
+
* @param logLevel - optional level of the log. It category of event is set as error,
|
|
223
|
+
* then the logLevel will be upgraded to be an error.
|
|
196
224
|
*/
|
|
197
|
-
public sendPerformanceEvent(
|
|
225
|
+
public sendPerformanceEvent(
|
|
226
|
+
event: ITelemetryPerformanceEventExt,
|
|
227
|
+
error?: unknown,
|
|
228
|
+
logLevel: typeof LogLevel.verbose | typeof LogLevel.default = LogLevel.default,
|
|
229
|
+
): void {
|
|
198
230
|
const perfEvent = {
|
|
199
231
|
...event,
|
|
200
232
|
category: event.category ?? "performance",
|
|
201
233
|
};
|
|
202
234
|
|
|
203
|
-
this.sendTelemetryEventCore(
|
|
235
|
+
this.sendTelemetryEventCore(
|
|
236
|
+
perfEvent,
|
|
237
|
+
error,
|
|
238
|
+
perfEvent.category === "error" ? LogLevel.error : logLevel,
|
|
239
|
+
);
|
|
204
240
|
}
|
|
205
241
|
|
|
206
242
|
protected prepareEvent(event: ITelemetryBaseEvent): ITelemetryBaseEvent {
|
|
@@ -211,6 +247,14 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
211
247
|
if (this.namespace !== undefined) {
|
|
212
248
|
newEvent.eventName = `${this.namespace}${TelemetryLogger.eventNamespaceSeparator}${newEvent.eventName}`;
|
|
213
249
|
}
|
|
250
|
+
return this.extendProperties(newEvent, includeErrorProps);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private extendProperties<T extends ITelemetryLoggerPropertyBag = ITelemetryLoggerPropertyBag>(
|
|
254
|
+
toExtend: T,
|
|
255
|
+
includeErrorProps: boolean,
|
|
256
|
+
): T {
|
|
257
|
+
const eventLike: ITelemetryLoggerPropertyBag = toExtend;
|
|
214
258
|
if (this.properties) {
|
|
215
259
|
const properties: (undefined | ITelemetryLoggerPropertyBag)[] = [];
|
|
216
260
|
properties.push(this.properties.all);
|
|
@@ -220,7 +264,7 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
220
264
|
for (const props of properties) {
|
|
221
265
|
if (props !== undefined) {
|
|
222
266
|
for (const key of Object.keys(props)) {
|
|
223
|
-
if (
|
|
267
|
+
if (eventLike[key] !== undefined) {
|
|
224
268
|
continue;
|
|
225
269
|
}
|
|
226
270
|
const getterOrValue = props[key];
|
|
@@ -228,13 +272,13 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
228
272
|
const value =
|
|
229
273
|
typeof getterOrValue === "function" ? getterOrValue() : getterOrValue;
|
|
230
274
|
if (value !== undefined) {
|
|
231
|
-
|
|
275
|
+
eventLike[key] = value;
|
|
232
276
|
}
|
|
233
277
|
}
|
|
234
278
|
}
|
|
235
279
|
}
|
|
236
280
|
}
|
|
237
|
-
return
|
|
281
|
+
return toExtend;
|
|
238
282
|
}
|
|
239
283
|
}
|
|
240
284
|
|
|
@@ -246,7 +290,10 @@ export abstract class TelemetryLogger implements ITelemetryLoggerExt {
|
|
|
246
290
|
export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
|
|
247
291
|
public constructor(private readonly logger: ITelemetryBaseLogger) {}
|
|
248
292
|
|
|
249
|
-
|
|
293
|
+
/**
|
|
294
|
+
* {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseLogger.send}
|
|
295
|
+
*/
|
|
296
|
+
public send(eventWithTagsMaybe: ITelemetryBaseEvent): void {
|
|
250
297
|
const newEvent: ITelemetryBaseEvent = {
|
|
251
298
|
category: eventWithTagsMaybe.category,
|
|
252
299
|
eventName: eventWithTagsMaybe.eventName,
|
|
@@ -284,6 +331,21 @@ export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
|
|
|
284
331
|
}
|
|
285
332
|
}
|
|
286
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Create a child logger based on the provided props object
|
|
336
|
+
* @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.
|
|
337
|
+
*
|
|
338
|
+
* @remarks
|
|
339
|
+
* Passing in no props object (i.e. undefined) will return a logger that is effectively a no-op.
|
|
340
|
+
*/
|
|
341
|
+
export function createChildLogger(props?: {
|
|
342
|
+
logger?: ITelemetryBaseLogger;
|
|
343
|
+
namespace?: string;
|
|
344
|
+
properties?: ITelemetryLoggerPropertyBags;
|
|
345
|
+
}): ITelemetryLoggerExt {
|
|
346
|
+
return ChildLogger.create(props?.logger, props?.namespace, props?.properties);
|
|
347
|
+
}
|
|
348
|
+
|
|
287
349
|
/**
|
|
288
350
|
* ChildLogger class contains various helper telemetry methods,
|
|
289
351
|
* encoding in one place schemas for various types of Fluid telemetry events.
|
|
@@ -330,11 +392,20 @@ export class ChildLogger extends TelemetryLogger {
|
|
|
330
392
|
? baseLogger.namespace
|
|
331
393
|
: `${baseLogger.namespace}${TelemetryLogger.eventNamespaceSeparator}${namespace}`;
|
|
332
394
|
|
|
333
|
-
|
|
395
|
+
const child = new ChildLogger(
|
|
396
|
+
baseLogger.baseLogger,
|
|
397
|
+
combinedNamespace,
|
|
398
|
+
combinedProperties,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
if (!loggerIsMonitoringContext(child) && loggerIsMonitoringContext(baseLogger)) {
|
|
402
|
+
mixinMonitoringContext(child, baseLogger.config);
|
|
403
|
+
}
|
|
404
|
+
return child;
|
|
334
405
|
}
|
|
335
406
|
|
|
336
407
|
return new ChildLogger(
|
|
337
|
-
baseLogger ? baseLogger :
|
|
408
|
+
baseLogger ? baseLogger : { send(): void {} },
|
|
338
409
|
namespace,
|
|
339
410
|
properties,
|
|
340
411
|
);
|
|
@@ -353,39 +424,116 @@ export class ChildLogger extends TelemetryLogger {
|
|
|
353
424
|
}
|
|
354
425
|
}
|
|
355
426
|
|
|
427
|
+
public get minLogLevel(): LogLevel | undefined {
|
|
428
|
+
return this.baseLogger.minLogLevel;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel?: LogLevel): boolean {
|
|
432
|
+
const eventLogLevel = logLevel ?? LogLevel.default;
|
|
433
|
+
const configLogLevel = this.baseLogger.minLogLevel ?? LogLevel.default;
|
|
434
|
+
// Filter out in case event log level is below what is wanted in config.
|
|
435
|
+
return eventLogLevel < configLogLevel;
|
|
436
|
+
}
|
|
437
|
+
|
|
356
438
|
/**
|
|
357
439
|
* Send an event with the logger
|
|
358
440
|
*
|
|
359
441
|
* @param event - the event to send
|
|
360
442
|
*/
|
|
361
|
-
public send(event: ITelemetryBaseEvent): void {
|
|
362
|
-
|
|
443
|
+
public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void {
|
|
444
|
+
if (this.shouldFilterOutEvent(event, logLevel)) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this.baseLogger.send(this.prepareEvent(event), logLevel);
|
|
363
448
|
}
|
|
364
449
|
}
|
|
365
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Create a logger which logs to multiple other loggers based on the provided props object
|
|
453
|
+
* @param props - loggers are the base loggers that will logged to after it's processing, namespace will be prefixed to all event names, properties are default properties that will be applied events.
|
|
454
|
+
* tryInheritProperties will attempted to copy those loggers properties to this loggers if they are of a known type e.g. one from this package
|
|
455
|
+
*/
|
|
456
|
+
export function createMultiSinkLogger(props: {
|
|
457
|
+
namespace?: string;
|
|
458
|
+
properties?: ITelemetryLoggerPropertyBags;
|
|
459
|
+
loggers?: (ITelemetryBaseLogger | undefined)[];
|
|
460
|
+
tryInheritProperties?: true;
|
|
461
|
+
}): ITelemetryLoggerExt {
|
|
462
|
+
return new MultiSinkLogger(
|
|
463
|
+
props.namespace,
|
|
464
|
+
props.properties,
|
|
465
|
+
props.loggers?.filter((l): l is ITelemetryBaseLogger => l !== undefined),
|
|
466
|
+
props.tryInheritProperties,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
366
470
|
/**
|
|
367
471
|
* Multi-sink logger
|
|
368
472
|
* Takes multiple ITelemetryBaseLogger objects (sinks) and logs all events into each sink
|
|
369
473
|
*/
|
|
370
474
|
export class MultiSinkLogger extends TelemetryLogger {
|
|
371
|
-
protected loggers: ITelemetryBaseLogger[]
|
|
475
|
+
protected loggers: ITelemetryBaseLogger[];
|
|
476
|
+
// This is minimum of minLlogLevel of all loggers.
|
|
477
|
+
private _minLogLevelOfAllLoggers: LogLevel;
|
|
372
478
|
|
|
373
479
|
/**
|
|
374
480
|
* Create multiple sink logger (i.e. logger that sends events to multiple sinks)
|
|
375
481
|
* @param namespace - Telemetry event name prefix to add to all events
|
|
376
482
|
* @param properties - Base properties to add to all events
|
|
483
|
+
* @param loggers - The list of loggers to use as sinks
|
|
484
|
+
* @param tryInheritProperties - Will attempted to copy those loggers properties to this loggers if they are of a known type e.g. one from this package
|
|
377
485
|
*/
|
|
378
|
-
constructor(
|
|
379
|
-
|
|
486
|
+
constructor(
|
|
487
|
+
namespace?: string,
|
|
488
|
+
properties?: ITelemetryLoggerPropertyBags,
|
|
489
|
+
loggers: ITelemetryBaseLogger[] = [],
|
|
490
|
+
tryInheritProperties?: true,
|
|
491
|
+
) {
|
|
492
|
+
let realProperties = properties !== undefined ? { ...properties } : undefined;
|
|
493
|
+
if (tryInheritProperties === true) {
|
|
494
|
+
const merge = (realProperties ??= {});
|
|
495
|
+
loggers
|
|
496
|
+
.filter((l): l is this => l instanceof TelemetryLogger)
|
|
497
|
+
.map((l) => l.properties ?? {})
|
|
498
|
+
// eslint-disable-next-line unicorn/no-array-for-each
|
|
499
|
+
.forEach((cv) => {
|
|
500
|
+
// eslint-disable-next-line unicorn/no-array-for-each
|
|
501
|
+
Object.keys(cv).forEach((k) => {
|
|
502
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
503
|
+
merge[k] = { ...cv[k], ...merge?.[k] };
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
super(namespace, realProperties);
|
|
509
|
+
this.loggers = loggers;
|
|
510
|
+
this._minLogLevelOfAllLoggers = LogLevel.default;
|
|
511
|
+
this.calculateMinLogLevel();
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public get minLogLevel(): LogLevel {
|
|
515
|
+
return this._minLogLevelOfAllLoggers;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
private calculateMinLogLevel(): void {
|
|
519
|
+
if (this.loggers.length > 0) {
|
|
520
|
+
const logLevels: LogLevel[] = [];
|
|
521
|
+
for (const logger of this.loggers) {
|
|
522
|
+
logLevels.push(logger.minLogLevel ?? LogLevel.default);
|
|
523
|
+
}
|
|
524
|
+
this._minLogLevelOfAllLoggers = Math.min(...logLevels) as LogLevel;
|
|
525
|
+
}
|
|
380
526
|
}
|
|
381
527
|
|
|
382
528
|
/**
|
|
383
529
|
* Add logger to send all events to
|
|
384
530
|
* @param logger - Logger to add
|
|
385
531
|
*/
|
|
386
|
-
public addLogger(logger?: ITelemetryBaseLogger) {
|
|
532
|
+
public addLogger(logger?: ITelemetryBaseLogger): void {
|
|
387
533
|
if (logger !== undefined && logger !== null) {
|
|
388
534
|
this.loggers.push(logger);
|
|
535
|
+
// Update in case the logLevel of added logger is less than the current.
|
|
536
|
+
this.calculateMinLogLevel();
|
|
389
537
|
}
|
|
390
538
|
}
|
|
391
539
|
|
|
@@ -396,9 +544,9 @@ export class MultiSinkLogger extends TelemetryLogger {
|
|
|
396
544
|
*/
|
|
397
545
|
public send(event: ITelemetryBaseEvent): void {
|
|
398
546
|
const newEvent = this.prepareEvent(event);
|
|
399
|
-
this.loggers
|
|
547
|
+
for (const logger of this.loggers) {
|
|
400
548
|
logger.send(newEvent);
|
|
401
|
-
}
|
|
549
|
+
}
|
|
402
550
|
}
|
|
403
551
|
}
|
|
404
552
|
|
|
@@ -423,7 +571,7 @@ export class PerformanceEvent {
|
|
|
423
571
|
event: ITelemetryGenericEvent,
|
|
424
572
|
markers?: IPerformanceEventMarkers,
|
|
425
573
|
recordHeapSize: boolean = false,
|
|
426
|
-
) {
|
|
574
|
+
): PerformanceEvent {
|
|
427
575
|
return new PerformanceEvent(logger, event, markers, recordHeapSize);
|
|
428
576
|
}
|
|
429
577
|
|
|
@@ -432,7 +580,7 @@ export class PerformanceEvent {
|
|
|
432
580
|
event: ITelemetryGenericEvent,
|
|
433
581
|
callback: (event: PerformanceEvent) => T,
|
|
434
582
|
markers?: IPerformanceEventMarkers,
|
|
435
|
-
) {
|
|
583
|
+
): T {
|
|
436
584
|
const perfEvent = PerformanceEvent.start(logger, event, markers);
|
|
437
585
|
try {
|
|
438
586
|
const ret = callback(perfEvent);
|
|
@@ -450,7 +598,7 @@ export class PerformanceEvent {
|
|
|
450
598
|
callback: (event: PerformanceEvent) => Promise<T>,
|
|
451
599
|
markers?: IPerformanceEventMarkers,
|
|
452
600
|
recordHeapSize?: boolean,
|
|
453
|
-
) {
|
|
601
|
+
): Promise<T> {
|
|
454
602
|
const perfEvent = PerformanceEvent.start(logger, event, markers, recordHeapSize);
|
|
455
603
|
try {
|
|
456
604
|
const ret = await callback(perfEvent);
|
|
@@ -462,7 +610,7 @@ export class PerformanceEvent {
|
|
|
462
610
|
}
|
|
463
611
|
}
|
|
464
612
|
|
|
465
|
-
public get duration() {
|
|
613
|
+
public get duration(): number {
|
|
466
614
|
return performance.now() - this.startTime;
|
|
467
615
|
}
|
|
468
616
|
|
|
@@ -482,6 +630,7 @@ export class PerformanceEvent {
|
|
|
482
630
|
this.reportEvent("start");
|
|
483
631
|
}
|
|
484
632
|
|
|
633
|
+
// eslint-disable-next-line unicorn/no-null
|
|
485
634
|
if (typeof window === "object" && window != null && window.performance?.mark) {
|
|
486
635
|
this.startMark = `${event.eventName}-start`;
|
|
487
636
|
window.performance.mark(this.startMark);
|
|
@@ -492,7 +641,7 @@ export class PerformanceEvent {
|
|
|
492
641
|
this.reportEvent(eventNameSuffix, props);
|
|
493
642
|
}
|
|
494
643
|
|
|
495
|
-
private autoEnd() {
|
|
644
|
+
private autoEnd(): void {
|
|
496
645
|
// Event might have been cancelled or ended in the callback
|
|
497
646
|
if (this.event && this.markers.end) {
|
|
498
647
|
this.reportEvent("end");
|
|
@@ -507,7 +656,7 @@ export class PerformanceEvent {
|
|
|
507
656
|
this.event = undefined;
|
|
508
657
|
}
|
|
509
658
|
|
|
510
|
-
private performanceEndMark() {
|
|
659
|
+
private performanceEndMark(): void {
|
|
511
660
|
if (this.startMark && this.event) {
|
|
512
661
|
const endMark = `${this.event.eventName}-end`;
|
|
513
662
|
window.performance.mark(endMark);
|
|
@@ -516,7 +665,7 @@ export class PerformanceEvent {
|
|
|
516
665
|
}
|
|
517
666
|
}
|
|
518
667
|
|
|
519
|
-
public cancel(props?: ITelemetryProperties, error?:
|
|
668
|
+
public cancel(props?: ITelemetryProperties, error?: unknown): void {
|
|
520
669
|
if (this.markers.cancel !== undefined) {
|
|
521
670
|
this.reportEvent("cancel", { category: this.markers.cancel, ...props }, error);
|
|
522
671
|
}
|
|
@@ -526,7 +675,11 @@ export class PerformanceEvent {
|
|
|
526
675
|
/**
|
|
527
676
|
* Report the event, if it hasn't already been reported.
|
|
528
677
|
*/
|
|
529
|
-
public reportEvent(
|
|
678
|
+
public reportEvent(
|
|
679
|
+
eventNameSuffix: string,
|
|
680
|
+
props?: ITelemetryProperties,
|
|
681
|
+
error?: unknown,
|
|
682
|
+
): void {
|
|
530
683
|
// There are strange sequences involving multiple Promise chains
|
|
531
684
|
// where the event can be cancelled and then later a callback is invoked
|
|
532
685
|
// and the caller attempts to end directly, e.g. issue #3936. Just return.
|
|
@@ -559,64 +712,17 @@ export class PerformanceEvent {
|
|
|
559
712
|
}
|
|
560
713
|
|
|
561
714
|
/**
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
public sendTelemetryEvent(event: ITelemetryGenericEvent, error?: any) {}
|
|
568
|
-
public sendErrorEvent(event: ITelemetryErrorEvent, error?: any) {
|
|
569
|
-
this.reportError("errorEvent in UT logger!", event, error);
|
|
570
|
-
}
|
|
571
|
-
public sendPerformanceEvent(event: ITelemetryPerformanceEvent, error?: any): void {}
|
|
572
|
-
public logGenericError(eventName: string, error: any) {
|
|
573
|
-
this.reportError(`genericError in UT logger!`, { eventName }, error);
|
|
574
|
-
}
|
|
575
|
-
public logException(event: ITelemetryErrorEvent, exception: any): void {
|
|
576
|
-
this.reportError("exception in UT logger!", event, exception);
|
|
577
|
-
}
|
|
578
|
-
public debugAssert(condition: boolean, event?: ITelemetryErrorEvent): void {
|
|
579
|
-
this.reportError("debugAssert in UT logger!");
|
|
580
|
-
}
|
|
581
|
-
public shipAssert(condition: boolean, event?: ITelemetryErrorEvent): void {
|
|
582
|
-
this.reportError("shipAssert in UT logger!");
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
private reportError(message: string, event?: ITelemetryErrorEvent, err?: any) {
|
|
586
|
-
const error = new Error(message);
|
|
587
|
-
(error as any).error = error;
|
|
588
|
-
(error as any).event = event;
|
|
589
|
-
// report to console as exception can be eaten
|
|
590
|
-
console.error(message);
|
|
591
|
-
console.error(error);
|
|
592
|
-
throw error;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Null logger
|
|
598
|
-
* It can be used in places where logger instance is required, but events should be not send over.
|
|
599
|
-
*/
|
|
600
|
-
export class BaseTelemetryNullLogger implements ITelemetryBaseLogger {
|
|
601
|
-
/**
|
|
602
|
-
* Send an event with the logger
|
|
603
|
-
*
|
|
604
|
-
* @param event - the event to send
|
|
605
|
-
*/
|
|
606
|
-
public send(event: ITelemetryBaseEvent): void {
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Null logger
|
|
613
|
-
* It can be used in places where logger instance is required, but events should be not send over.
|
|
715
|
+
* Null logger that no-ops for all telemetry events passed to it.
|
|
716
|
+
* @deprecated - This will be removed in a future release.
|
|
717
|
+
* For internal use within the FluidFramework codebase, use {@link createChildLogger} with no arguments instead.
|
|
718
|
+
* For external consumers we recommend writing a trivial implementation of {@link @fluidframework/core-interfaces#ITelemetryBaseLogger}
|
|
719
|
+
* where the send() method does nothing and using that.
|
|
614
720
|
*/
|
|
615
721
|
export class TelemetryNullLogger implements ITelemetryLoggerExt {
|
|
616
722
|
public send(event: ITelemetryBaseEvent): void {}
|
|
617
|
-
public sendTelemetryEvent(event: ITelemetryGenericEvent, error?:
|
|
618
|
-
public sendErrorEvent(event: ITelemetryErrorEvent, error?:
|
|
619
|
-
public sendPerformanceEvent(event: ITelemetryPerformanceEvent, error?:
|
|
723
|
+
public sendTelemetryEvent(event: ITelemetryGenericEvent, error?: unknown): void {}
|
|
724
|
+
public sendErrorEvent(event: ITelemetryErrorEvent, error?: unknown): void {}
|
|
725
|
+
public sendPerformanceEvent(event: ITelemetryPerformanceEvent, error?: unknown): void {}
|
|
620
726
|
}
|
|
621
727
|
|
|
622
728
|
/**
|
|
@@ -639,15 +745,15 @@ function convertToBaseEvent({
|
|
|
639
745
|
/**
|
|
640
746
|
* Takes in value, and does one of 4 things.
|
|
641
747
|
* if value is of primitive type - returns the original value.
|
|
642
|
-
* If the value is
|
|
643
|
-
* If the value is an object of type
|
|
748
|
+
* If the value is a flat array or object - returns a stringified version of the array/object.
|
|
749
|
+
* If the value is an object of type Tagged<TelemetryEventPropertyType> - returns the object
|
|
644
750
|
* with its values recursively converted to base property Type.
|
|
645
751
|
* If none of these cases are reached - returns an error string
|
|
646
752
|
* @param x - value passed in to convert to a base property type
|
|
647
753
|
*/
|
|
648
754
|
export function convertToBasePropertyType(
|
|
649
|
-
x: TelemetryEventPropertyTypeExt |
|
|
650
|
-
): TelemetryEventPropertyType |
|
|
755
|
+
x: TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>,
|
|
756
|
+
): TelemetryEventPropertyType | Tagged<TelemetryEventPropertyType> {
|
|
651
757
|
return isTaggedTelemetryPropertyValue(x)
|
|
652
758
|
? {
|
|
653
759
|
value: convertToBasePropertyTypeUntagged(x.value),
|
|
@@ -676,3 +782,67 @@ function convertToBasePropertyTypeUntagged(
|
|
|
676
782
|
return `INVALID PROPERTY (typed as ${typeof x})`;
|
|
677
783
|
}
|
|
678
784
|
}
|
|
785
|
+
|
|
786
|
+
export const tagData = <
|
|
787
|
+
T extends TelemetryDataTag,
|
|
788
|
+
V extends Record<
|
|
789
|
+
string,
|
|
790
|
+
TelemetryBaseEventPropertyType | (() => TelemetryBaseEventPropertyType)
|
|
791
|
+
>,
|
|
792
|
+
>(
|
|
793
|
+
tag: T,
|
|
794
|
+
values: V,
|
|
795
|
+
): {
|
|
796
|
+
[P in keyof V]:
|
|
797
|
+
| (V[P] extends () => TelemetryBaseEventPropertyType
|
|
798
|
+
? () => {
|
|
799
|
+
value: ReturnType<V[P]>;
|
|
800
|
+
tag: T;
|
|
801
|
+
}
|
|
802
|
+
: {
|
|
803
|
+
value: Exclude<V[P], undefined>;
|
|
804
|
+
tag: T;
|
|
805
|
+
})
|
|
806
|
+
| (V[P] extends undefined ? undefined : never);
|
|
807
|
+
} =>
|
|
808
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
809
|
+
Object.entries(values)
|
|
810
|
+
.filter((e) => e[1] !== undefined)
|
|
811
|
+
// eslint-disable-next-line unicorn/no-array-reduce, unicorn/prefer-object-from-entries
|
|
812
|
+
.reduce((pv, cv) => {
|
|
813
|
+
const [key, value] = cv;
|
|
814
|
+
if (typeof value === "function") {
|
|
815
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
816
|
+
pv[key] = () => {
|
|
817
|
+
return { tag, value: value() };
|
|
818
|
+
};
|
|
819
|
+
} else {
|
|
820
|
+
pv[key] = { tag, value };
|
|
821
|
+
}
|
|
822
|
+
return pv;
|
|
823
|
+
}, {}) as ReturnType<typeof tagData>;
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Helper function to tag telemetry properties as CodeArtifacts. It supports properties of type
|
|
827
|
+
* TelemetryBaseEventPropertyType as well as getters that return TelemetryBaseEventPropertyType.
|
|
828
|
+
*/
|
|
829
|
+
export const tagCodeArtifacts = <
|
|
830
|
+
T extends Record<
|
|
831
|
+
string,
|
|
832
|
+
TelemetryBaseEventPropertyType | (() => TelemetryBaseEventPropertyType)
|
|
833
|
+
>,
|
|
834
|
+
>(
|
|
835
|
+
values: T,
|
|
836
|
+
): {
|
|
837
|
+
[P in keyof T]:
|
|
838
|
+
| (T[P] extends () => TelemetryBaseEventPropertyType
|
|
839
|
+
? () => {
|
|
840
|
+
value: ReturnType<T[P]>;
|
|
841
|
+
tag: TelemetryDataTag.CodeArtifact;
|
|
842
|
+
}
|
|
843
|
+
: {
|
|
844
|
+
value: Exclude<T[P], undefined>;
|
|
845
|
+
tag: TelemetryDataTag.CodeArtifact;
|
|
846
|
+
})
|
|
847
|
+
| (T[P] extends undefined ? undefined : never);
|
|
848
|
+
} => tagData<TelemetryDataTag.CodeArtifact, T>(TelemetryDataTag.CodeArtifact, values);
|