@fluidframework/telemetry-utils 2.0.0-internal.6.4.0 → 2.0.0-internal.7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/api-extractor.json +9 -1
- package/api-report/telemetry-utils.api.md +444 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +2 -1
- package/dist/error.js.map +1 -1
- package/dist/errorLogging.js +6 -6
- package/dist/errorLogging.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +49 -5
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +89 -35
- package/dist/logger.js.map +1 -1
- package/dist/telemetry-utils-alpha.d.ts +668 -0
- package/dist/telemetry-utils-beta.d.ts +668 -0
- package/dist/telemetry-utils-public.d.ts +668 -0
- package/dist/telemetry-utils.d.ts +909 -0
- package/dist/telemetryTypes.d.ts +2 -2
- package/dist/telemetryTypes.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +50 -1
- package/dist/utils.js.map +1 -1
- package/lib/config.d.ts +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js +2 -1
- package/lib/error.js.map +1 -1
- package/lib/errorLogging.js +6 -6
- package/lib/errorLogging.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/logger.d.ts +49 -5
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +88 -34
- package/lib/logger.js.map +1 -1
- package/lib/telemetryTypes.d.ts +2 -2
- package/lib/telemetryTypes.d.ts.map +1 -1
- package/lib/utils.d.ts +45 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +48 -0
- package/lib/utils.js.map +1 -1
- package/package.json +17 -17
- package/src/error.ts +7 -3
- package/src/index.ts +1 -1
- package/src/logger.ts +74 -6
- package/src/utils.ts +85 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/telemetry-utils",
|
|
3
|
-
"version": "2.0.0-internal.
|
|
3
|
+
"version": "2.0.0-internal.7.1.0",
|
|
4
4
|
"description": "Collection of telemetry relates utilities for Fluid",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -39,22 +39,22 @@
|
|
|
39
39
|
"temp-directory": "nyc/.nyc_output"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@fluid-internal/client-utils": ">=2.0.0-internal.
|
|
43
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.
|
|
44
|
-
"@fluidframework/core-utils": ">=2.0.0-internal.
|
|
45
|
-
"@fluidframework/protocol-definitions": "^
|
|
42
|
+
"@fluid-internal/client-utils": ">=2.0.0-internal.7.1.0 <2.0.0-internal.7.2.0",
|
|
43
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.7.1.0 <2.0.0-internal.7.2.0",
|
|
44
|
+
"@fluidframework/core-utils": ">=2.0.0-internal.7.1.0 <2.0.0-internal.7.2.0",
|
|
45
|
+
"@fluidframework/protocol-definitions": "^3.0.0",
|
|
46
46
|
"debug": "^4.1.1",
|
|
47
47
|
"events": "^3.1.0",
|
|
48
48
|
"uuid": "^9.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@fluid-tools/build-cli": "^0.
|
|
52
|
-
"@fluidframework/build-common": "^2.0.
|
|
53
|
-
"@fluidframework/build-tools": "^0.
|
|
54
|
-
"@fluidframework/eslint-config-fluid": "^
|
|
55
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.
|
|
56
|
-
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-internal.
|
|
57
|
-
"@microsoft/api-extractor": "^7.
|
|
51
|
+
"@fluid-tools/build-cli": "^0.25.0",
|
|
52
|
+
"@fluidframework/build-common": "^2.0.1",
|
|
53
|
+
"@fluidframework/build-tools": "^0.25.0",
|
|
54
|
+
"@fluidframework/eslint-config-fluid": "^3.0.0",
|
|
55
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.7.1.0 <2.0.0-internal.7.2.0",
|
|
56
|
+
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-internal.7.0.0",
|
|
57
|
+
"@microsoft/api-extractor": "^7.37.0",
|
|
58
58
|
"@types/debug": "^4.1.5",
|
|
59
59
|
"@types/events": "^3.0.0",
|
|
60
60
|
"@types/mocha": "^9.1.1",
|
|
@@ -63,15 +63,15 @@
|
|
|
63
63
|
"c8": "^7.7.1",
|
|
64
64
|
"copyfiles": "^2.4.1",
|
|
65
65
|
"cross-env": "^7.0.3",
|
|
66
|
-
"eslint": "~8.
|
|
66
|
+
"eslint": "~8.50.0",
|
|
67
67
|
"mocha": "^10.2.0",
|
|
68
68
|
"mocha-json-output-reporter": "^2.0.1",
|
|
69
69
|
"mocha-multi-reporters": "^1.5.1",
|
|
70
70
|
"moment": "^2.21.0",
|
|
71
|
-
"prettier": "~
|
|
71
|
+
"prettier": "~3.0.3",
|
|
72
72
|
"rimraf": "^4.4.0",
|
|
73
73
|
"sinon": "^7.4.2",
|
|
74
|
-
"typescript": "~
|
|
74
|
+
"typescript": "~5.1.6"
|
|
75
75
|
},
|
|
76
76
|
"typeValidation": {
|
|
77
77
|
"broken": {}
|
|
@@ -80,11 +80,11 @@
|
|
|
80
80
|
"build": "fluid-build . --task build",
|
|
81
81
|
"build:commonjs": "fluid-build . --task commonjs",
|
|
82
82
|
"build:compile": "fluid-build . --task compile",
|
|
83
|
-
"build:docs": "api-extractor run --local
|
|
83
|
+
"build:docs": "api-extractor run --local",
|
|
84
84
|
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
85
85
|
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
86
86
|
"bump-version": "npm version minor --no-push --no-git-tag-version && npm run build:genver",
|
|
87
|
-
"ci:build:docs": "api-extractor run
|
|
87
|
+
"ci:build:docs": "api-extractor run",
|
|
88
88
|
"clean": "rimraf --glob 'dist' 'lib' '*.tsbuildinfo' '*.build.log' '_api-extractor-temp' 'nyc'",
|
|
89
89
|
"eslint": "eslint --format stylish src",
|
|
90
90
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
package/src/error.ts
CHANGED
|
@@ -35,9 +35,13 @@ export class GenericError extends LoggingError implements IGenericError, IFluidE
|
|
|
35
35
|
* @param error - inner error object
|
|
36
36
|
* @param props - Telemetry props to include when the error is logged
|
|
37
37
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
constructor(
|
|
39
|
+
message: string,
|
|
40
|
+
// TODO: Use `unknown` instead (API breaking change because error is not just an input parameter, but a public member of the class)
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
42
|
+
public readonly error?: any,
|
|
43
|
+
props?: ITelemetryBaseProperties,
|
|
44
|
+
) {
|
|
41
45
|
// Don't try to log the inner error
|
|
42
46
|
super(message, props, new Set(["error"]));
|
|
43
47
|
}
|
package/src/index.ts
CHANGED
|
@@ -68,7 +68,7 @@ export {
|
|
|
68
68
|
export { MockLogger } from "./mockLogger";
|
|
69
69
|
export { ThresholdCounter } from "./thresholdCounter";
|
|
70
70
|
export { SampledTelemetryHelper } from "./sampledTelemetryHelper";
|
|
71
|
-
export { logIfFalse } from "./utils";
|
|
71
|
+
export { logIfFalse, createSampledLogger, IEventSampler, ISampledTelemetryLogger } from "./utils";
|
|
72
72
|
export {
|
|
73
73
|
TelemetryEventPropertyTypeExt,
|
|
74
74
|
ITelemetryEventExt,
|
package/src/logger.ts
CHANGED
|
@@ -566,22 +566,55 @@ export interface IPerformanceEventMarkers {
|
|
|
566
566
|
* Helper class to log performance events
|
|
567
567
|
*/
|
|
568
568
|
export class PerformanceEvent {
|
|
569
|
+
/**
|
|
570
|
+
* Creates an instance of {@link PerformanceEvent} and starts measurements
|
|
571
|
+
* @param logger - the logger to be used for publishing events
|
|
572
|
+
* @param event - the logging event details which will be published with the performance measurements
|
|
573
|
+
* @param markers - See {@link IPerformanceEventMarkers}
|
|
574
|
+
* @param recordHeapSize - whether or not to also record memory performance
|
|
575
|
+
* @param emitLogs - should this instance emit logs. If set to false, logs will not be emitted to the logger,
|
|
576
|
+
* but measurements will still be performed and any specified markers will be generated.
|
|
577
|
+
* @returns An instance of {@link PerformanceEvent}
|
|
578
|
+
*/
|
|
569
579
|
public static start(
|
|
570
580
|
logger: ITelemetryLoggerExt,
|
|
571
581
|
event: ITelemetryGenericEvent,
|
|
572
582
|
markers?: IPerformanceEventMarkers,
|
|
573
583
|
recordHeapSize: boolean = false,
|
|
584
|
+
emitLogs: boolean = true,
|
|
574
585
|
): PerformanceEvent {
|
|
575
|
-
return new PerformanceEvent(logger, event, markers, recordHeapSize);
|
|
586
|
+
return new PerformanceEvent(logger, event, markers, recordHeapSize, emitLogs);
|
|
576
587
|
}
|
|
577
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Measure a synchronous task
|
|
591
|
+
* @param logger - the logger to be used for publishing events
|
|
592
|
+
* @param event - the logging event details which will be published with the performance measurements
|
|
593
|
+
* @param callback - the task to be executed and measured
|
|
594
|
+
* @param markers - See {@link IPerformanceEventMarkers}
|
|
595
|
+
* @param sampleThreshold - events with the same name and category will be sent to the logger
|
|
596
|
+
* only when we hit this many executions of the task. If unspecified, all events will be sent.
|
|
597
|
+
* @returns The results of the executed task
|
|
598
|
+
*
|
|
599
|
+
* @remarks Note that if the "same" event (category + eventName) would be emitted by different
|
|
600
|
+
* tasks (`callback`), `sampleThreshold` is still applied only based on the event's category + eventName,
|
|
601
|
+
* so executing either of the tasks will increase the internal counter and they
|
|
602
|
+
* effectively "share" the sampling rate for the event.
|
|
603
|
+
*/
|
|
578
604
|
public static timedExec<T>(
|
|
579
605
|
logger: ITelemetryLoggerExt,
|
|
580
606
|
event: ITelemetryGenericEvent,
|
|
581
607
|
callback: (event: PerformanceEvent) => T,
|
|
582
608
|
markers?: IPerformanceEventMarkers,
|
|
609
|
+
sampleThreshold: number = 1,
|
|
583
610
|
): T {
|
|
584
|
-
const perfEvent = PerformanceEvent.start(
|
|
611
|
+
const perfEvent = PerformanceEvent.start(
|
|
612
|
+
logger,
|
|
613
|
+
event,
|
|
614
|
+
markers,
|
|
615
|
+
undefined, // recordHeapSize
|
|
616
|
+
PerformanceEvent.shouldReport(event, sampleThreshold),
|
|
617
|
+
);
|
|
585
618
|
try {
|
|
586
619
|
const ret = callback(perfEvent);
|
|
587
620
|
perfEvent.autoEnd();
|
|
@@ -592,14 +625,37 @@ export class PerformanceEvent {
|
|
|
592
625
|
}
|
|
593
626
|
}
|
|
594
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Measure an asynchronous task
|
|
630
|
+
* @param logger - the logger to be used for publishing events
|
|
631
|
+
* @param event - the logging event details which will be published with the performance measurements
|
|
632
|
+
* @param callback - the task to be executed and measured
|
|
633
|
+
* @param markers - See {@link IPerformanceEventMarkers}
|
|
634
|
+
* @param recordHeapSize - whether or not to also record memory performance
|
|
635
|
+
* @param sampleThreshold - events with the same name and category will be sent to the logger
|
|
636
|
+
* only when we hit this many executions of the task. If unspecified, all events will be sent.
|
|
637
|
+
* @returns The results of the executed task
|
|
638
|
+
*
|
|
639
|
+
* @remarks Note that if the "same" event (category + eventName) would be emitted by different
|
|
640
|
+
* tasks (`callback`), `sampleThreshold` is still applied only based on the event's category + eventName,
|
|
641
|
+
* so executing either of the tasks will increase the internal counter and they
|
|
642
|
+
* effectively "share" the sampling rate for the event.
|
|
643
|
+
*/
|
|
595
644
|
public static async timedExecAsync<T>(
|
|
596
645
|
logger: ITelemetryLoggerExt,
|
|
597
646
|
event: ITelemetryGenericEvent,
|
|
598
647
|
callback: (event: PerformanceEvent) => Promise<T>,
|
|
599
648
|
markers?: IPerformanceEventMarkers,
|
|
600
649
|
recordHeapSize?: boolean,
|
|
650
|
+
sampleThreshold: number = 1,
|
|
601
651
|
): Promise<T> {
|
|
602
|
-
const perfEvent = PerformanceEvent.start(
|
|
652
|
+
const perfEvent = PerformanceEvent.start(
|
|
653
|
+
logger,
|
|
654
|
+
event,
|
|
655
|
+
markers,
|
|
656
|
+
recordHeapSize,
|
|
657
|
+
PerformanceEvent.shouldReport(event, sampleThreshold),
|
|
658
|
+
);
|
|
603
659
|
try {
|
|
604
660
|
const ret = await callback(perfEvent);
|
|
605
661
|
perfEvent.autoEnd();
|
|
@@ -624,6 +680,7 @@ export class PerformanceEvent {
|
|
|
624
680
|
event: ITelemetryGenericEvent,
|
|
625
681
|
private readonly markers: IPerformanceEventMarkers = { end: true, cancel: "generic" },
|
|
626
682
|
private readonly recordHeapSize: boolean = false,
|
|
683
|
+
private readonly emitLogs: boolean = true,
|
|
627
684
|
) {
|
|
628
685
|
this.event = { ...event };
|
|
629
686
|
if (this.markers.start) {
|
|
@@ -686,6 +743,10 @@ export class PerformanceEvent {
|
|
|
686
743
|
return;
|
|
687
744
|
}
|
|
688
745
|
|
|
746
|
+
if (!this.emitLogs) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
689
750
|
const event: ITelemetryPerformanceEvent = { ...this.event, ...props };
|
|
690
751
|
event.eventName = `${event.eventName}_${eventNameSuffix}`;
|
|
691
752
|
if (eventNameSuffix !== "start") {
|
|
@@ -701,13 +762,20 @@ export class PerformanceEvent {
|
|
|
701
762
|
}
|
|
702
763
|
}
|
|
703
764
|
} else if (this.recordHeapSize) {
|
|
704
|
-
this.startMemoryCollection = (
|
|
705
|
-
|
|
706
|
-
)?.memory?.usedJSHeapSize;
|
|
765
|
+
this.startMemoryCollection = (performance as PerformanceWithMemory)?.memory
|
|
766
|
+
?.usedJSHeapSize;
|
|
707
767
|
}
|
|
708
768
|
|
|
709
769
|
this.logger.sendPerformanceEvent(event, error);
|
|
710
770
|
}
|
|
771
|
+
|
|
772
|
+
private static readonly eventHits = new Map<string, number>();
|
|
773
|
+
private static shouldReport(event: ITelemetryGenericEvent, sampleThreshold: number): boolean {
|
|
774
|
+
const eventKey = `.${event.category}.${event.eventName}`;
|
|
775
|
+
const hitCount = PerformanceEvent.eventHits.get(eventKey) ?? 0;
|
|
776
|
+
PerformanceEvent.eventHits.set(eventKey, hitCount >= sampleThreshold ? 1 : hitCount + 1);
|
|
777
|
+
return hitCount % sampleThreshold === 0;
|
|
778
|
+
}
|
|
711
779
|
}
|
|
712
780
|
|
|
713
781
|
/**
|
package/src/utils.ts
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
ITelemetryBaseLogger,
|
|
8
8
|
ITelemetryGenericEvent,
|
|
9
9
|
} from "@fluidframework/core-interfaces";
|
|
10
|
+
import { loggerToMonitoringContext } from "./config";
|
|
11
|
+
import { ITelemetryGenericEventExt, ITelemetryLoggerExt } from "./telemetryTypes";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Like assert, but logs only if the condition is false, rather than throwing
|
|
@@ -30,3 +32,86 @@ export function logIfFalse(
|
|
|
30
32
|
logger.send(newEvent);
|
|
31
33
|
return false;
|
|
32
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An object that contains a callback used in conjunction with the {@link createSampledLogger} utility function to provide custom logic for sampling events.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export interface IEventSampler {
|
|
42
|
+
/**
|
|
43
|
+
* @returns true if the event should be sampled or false if not
|
|
44
|
+
*/
|
|
45
|
+
sample: () => boolean | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A telemetry logger that has sampling capabilities
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export interface ISampledTelemetryLogger extends ITelemetryLoggerExt {
|
|
54
|
+
/**
|
|
55
|
+
* Indicates if the feature flag to disable sampling is set.
|
|
56
|
+
*
|
|
57
|
+
* @remarks Exposed to enable some advanced scenarios where the code using the sampled logger
|
|
58
|
+
* could take advantage of skipping the execution of some logic when it can determine
|
|
59
|
+
* it won't be necessary because the telemetry event that needs it wouldn't be
|
|
60
|
+
* emitted anyway.
|
|
61
|
+
*/
|
|
62
|
+
isSamplingDisabled: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Wraps around an existing logger matching the {@link ITelemetryLoggerExt} interface and provides the ability to only log a subset of events using a sampling strategy provided by an ${@link IEventSampler}.
|
|
67
|
+
* You can chose to not provide an event sampler which is effectively a no-op, meaning that it will be treated as if the sampler always returns true.
|
|
68
|
+
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* The sampling functionality uses the Fluid telemetry logging configuration along with the optionally provided event sampling callback to determine whether an event should
|
|
71
|
+
* be logged or not.
|
|
72
|
+
*
|
|
73
|
+
* Configuration object parameters:
|
|
74
|
+
* 'Fluid.Telemetry.DisableSampling': if this config value is set to true, all events will be unsampled and therefore logged.
|
|
75
|
+
* Otherwise only a sample will be logged according to the provided event sampler callback.
|
|
76
|
+
*
|
|
77
|
+
* Note that the same sampler is used for all APIs of the returned logger. If you want separate events flowing through the returned logger to be sampled separately, the {@link IEventSampler} you provide should track them separately.
|
|
78
|
+
*
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export function createSampledLogger(
|
|
82
|
+
logger: ITelemetryLoggerExt,
|
|
83
|
+
eventSampler?: IEventSampler,
|
|
84
|
+
): ISampledTelemetryLogger {
|
|
85
|
+
const monitoringContext = loggerToMonitoringContext(logger);
|
|
86
|
+
const isSamplingDisabled =
|
|
87
|
+
monitoringContext.config.getBoolean("Fluid.Telemetry.DisableSampling") ?? false;
|
|
88
|
+
|
|
89
|
+
const sampledLogger = {
|
|
90
|
+
send: (event: ITelemetryBaseEvent): void => {
|
|
91
|
+
// The sampler uses the following logic for sending events:
|
|
92
|
+
// 1. If isSamplingDisabled is true, then this means events should be unsampled. Therefore we send the event without any checks.
|
|
93
|
+
// 2. If isSamplingDisabled is false, then event should be sampled using the event sampler, if the sampler is not defined just send all events, other use the eventSampler.sample() method.
|
|
94
|
+
if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {
|
|
95
|
+
logger.send(event);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
sendTelemetryEvent: (event: ITelemetryGenericEventExt): void => {
|
|
99
|
+
if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {
|
|
100
|
+
logger.sendTelemetryEvent(event);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
sendErrorEvent: (event: ITelemetryGenericEventExt): void => {
|
|
104
|
+
if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {
|
|
105
|
+
logger.sendErrorEvent(event);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
sendPerformanceEvent: (event: ITelemetryGenericEventExt): void => {
|
|
109
|
+
if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {
|
|
110
|
+
logger.sendPerformanceEvent(event);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
isSamplingDisabled,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return sampledLogger;
|
|
117
|
+
}
|