@fluidframework/telemetry-utils 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.203917
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 +77 -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/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/error.d.ts +8 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +10 -1
- package/dist/error.js.map +1 -1
- package/dist/errorLogging.d.ts +26 -0
- package/dist/errorLogging.d.ts.map +1 -1
- package/dist/errorLogging.js +43 -15
- package/dist/errorLogging.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.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 +50 -6
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +108 -46
- 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-untrimmed.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/config.js +4 -2
- package/lib/config.js.map +1 -1
- package/lib/error.d.ts +8 -0
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js +10 -1
- package/lib/error.js.map +1 -1
- package/lib/errorLogging.d.ts +26 -0
- package/lib/errorLogging.d.ts.map +1 -1
- package/lib/errorLogging.js +43 -15
- package/lib/errorLogging.js.map +1 -1
- package/lib/events.d.ts.map +1 -1
- package/lib/events.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 +50 -6
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +107 -45
- 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 +18 -19
- package/src/config.ts +4 -2
- package/src/error.ts +15 -3
- package/src/errorLogging.ts +43 -10
- package/src/events.ts +2 -0
- package/src/index.ts +1 -1
- package/src/logger.ts +94 -22
- package/src/utils.ts +85 -0
package/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGrD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACzB,SAAkB,EAClB,MAA4B,EAC5B,KAAsC;IAEtC,IAAI,SAAS,EAAE;QACd,OAAO,IAAI,CAAC;KACZ;IACD,MAAM,QAAQ,GACb,OAAO,KAAK,KAAK,QAAQ;QACxB,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;QACzC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IACpC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,KAAK,CAAC;AACd,CAAC;AA+BD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CAClC,MAA2B,EAC3B,YAA4B;IAE5B,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,kBAAkB,GACvB,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,IAAI,KAAK,CAAC;IAEjF,MAAM,aAAa,GAAG;QACrB,IAAI,EAAE,CAAC,KAA0B,EAAQ,EAAE;YAC1C,2DAA2D;YAC3D,gIAAgI;YAChI,2LAA2L;YAC3L,IAAI,kBAAkB,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE;gBAC9E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACnB;QACF,CAAC;QACD,kBAAkB,EAAE,CAAC,KAAgC,EAAQ,EAAE;YAC9D,IAAI,kBAAkB,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE;gBAC9E,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;aACjC;QACF,CAAC;QACD,cAAc,EAAE,CAAC,KAAgC,EAAQ,EAAE;YAC1D,IAAI,kBAAkB,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE;gBAC9E,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;aAC7B;QACF,CAAC;QACD,oBAAoB,EAAE,CAAC,KAAgC,EAAQ,EAAE;YAChE,IAAI,kBAAkB,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE;gBAC9E,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;aACnC;QACF,CAAC;QACD,kBAAkB;KAClB,CAAC;IAEF,OAAO,aAAa,CAAC;AACtB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport {\n\tITelemetryBaseEvent,\n\tITelemetryBaseLogger,\n\tITelemetryGenericEvent,\n} from \"@fluidframework/core-interfaces\";\nimport { loggerToMonitoringContext } from \"./config\";\nimport { ITelemetryGenericEventExt, ITelemetryLoggerExt } from \"./telemetryTypes\";\n\n/**\n * Like assert, but logs only if the condition is false, rather than throwing\n * @param condition - The condition to attest too\n * @param logger - The logger to log with\n * @param event - The string or event to log\n * @returns The outcome of the condition\n */\nexport function logIfFalse(\n\tcondition: unknown,\n\tlogger: ITelemetryBaseLogger,\n\tevent: string | ITelemetryGenericEvent,\n): condition is true {\n\tif (condition) {\n\t\treturn true;\n\t}\n\tconst newEvent: ITelemetryBaseEvent =\n\t\ttypeof event === \"string\"\n\t\t\t? { eventName: event, category: \"error\" }\n\t\t\t: { category: \"error\", ...event };\n\tlogger.send(newEvent);\n\treturn false;\n}\n\n/**\n * An object that contains a callback used in conjunction with the {@link createSampledLogger} utility function to provide custom logic for sampling events.\n *\n * @internal\n */\nexport interface IEventSampler {\n\t/**\n\t * @returns true if the event should be sampled or false if not\n\t */\n\tsample: () => boolean | undefined;\n}\n\n/**\n * A telemetry logger that has sampling capabilities\n *\n * @internal\n */\nexport interface ISampledTelemetryLogger extends ITelemetryLoggerExt {\n\t/**\n\t * Indicates if the feature flag to disable sampling is set.\n\t *\n\t * @remarks Exposed to enable some advanced scenarios where the code using the sampled logger\n\t * could take advantage of skipping the execution of some logic when it can determine\n\t * it won't be necessary because the telemetry event that needs it wouldn't be\n\t * emitted anyway.\n\t */\n\tisSamplingDisabled: boolean;\n}\n\n/**\n * 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}.\n * 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.\n *\n * @remarks\n * The sampling functionality uses the Fluid telemetry logging configuration along with the optionally provided event sampling callback to determine whether an event should\n * be logged or not.\n *\n * Configuration object parameters:\n * 'Fluid.Telemetry.DisableSampling': if this config value is set to true, all events will be unsampled and therefore logged.\n * Otherwise only a sample will be logged according to the provided event sampler callback.\n *\n * 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.\n *\n * @internal\n */\nexport function createSampledLogger(\n\tlogger: ITelemetryLoggerExt,\n\teventSampler?: IEventSampler,\n): ISampledTelemetryLogger {\n\tconst monitoringContext = loggerToMonitoringContext(logger);\n\tconst isSamplingDisabled =\n\t\tmonitoringContext.config.getBoolean(\"Fluid.Telemetry.DisableSampling\") ?? false;\n\n\tconst sampledLogger = {\n\t\tsend: (event: ITelemetryBaseEvent): void => {\n\t\t\t// The sampler uses the following logic for sending events:\n\t\t\t// 1. If isSamplingDisabled is true, then this means events should be unsampled. Therefore we send the event without any checks.\n\t\t\t// 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.\n\t\t\tif (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {\n\t\t\t\tlogger.send(event);\n\t\t\t}\n\t\t},\n\t\tsendTelemetryEvent: (event: ITelemetryGenericEventExt): void => {\n\t\t\tif (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {\n\t\t\t\tlogger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t},\n\t\tsendErrorEvent: (event: ITelemetryGenericEventExt): void => {\n\t\t\tif (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {\n\t\t\t\tlogger.sendErrorEvent(event);\n\t\t\t}\n\t\t},\n\t\tsendPerformanceEvent: (event: ITelemetryGenericEventExt): void => {\n\t\t\tif (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) {\n\t\t\t\tlogger.sendPerformanceEvent(event);\n\t\t\t}\n\t\t},\n\t\tisSamplingDisabled,\n\t};\n\n\treturn sampledLogger;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/telemetry-utils",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.7.2.0.203917",
|
|
4
4
|
"description": "Collection of telemetry relates utilities for Fluid",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -39,39 +39,38 @@
|
|
|
39
39
|
"temp-directory": "nyc/.nyc_output"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@fluid-internal/client-utils": "2.0.0-dev.
|
|
43
|
-
"@fluidframework/core-interfaces": "2.0.0-dev.
|
|
44
|
-
"@fluidframework/core-utils": "2.0.0-dev.
|
|
45
|
-
"@fluidframework/protocol-definitions": "^
|
|
46
|
-
"debug": "^4.
|
|
42
|
+
"@fluid-internal/client-utils": "2.0.0-dev.7.2.0.203917",
|
|
43
|
+
"@fluidframework/core-interfaces": "2.0.0-dev.7.2.0.203917",
|
|
44
|
+
"@fluidframework/core-utils": "2.0.0-dev.7.2.0.203917",
|
|
45
|
+
"@fluidframework/protocol-definitions": "^3.0.0",
|
|
46
|
+
"debug": "^4.3.4",
|
|
47
47
|
"events": "^3.1.0",
|
|
48
48
|
"uuid": "^9.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@fluid-tools/build-cli": "
|
|
52
|
-
"@fluidframework/build-common": "^2.0.
|
|
53
|
-
"@fluidframework/build-tools": "
|
|
54
|
-
"@fluidframework/eslint-config-fluid": "^
|
|
55
|
-
"@fluidframework/mocha-test-setup": "2.0.0-dev.
|
|
56
|
-
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-internal.
|
|
57
|
-
"@microsoft/api-extractor": "^7.
|
|
51
|
+
"@fluid-tools/build-cli": "0.26.0-203096",
|
|
52
|
+
"@fluidframework/build-common": "^2.0.2",
|
|
53
|
+
"@fluidframework/build-tools": "0.26.0-203096",
|
|
54
|
+
"@fluidframework/eslint-config-fluid": "^3.0.0",
|
|
55
|
+
"@fluidframework/mocha-test-setup": "2.0.0-dev.7.2.0.203917",
|
|
56
|
+
"@fluidframework/telemetry-utils-previous": "npm:@fluidframework/telemetry-utils@2.0.0-internal.7.1.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",
|
|
61
61
|
"@types/node": "^16.18.38",
|
|
62
62
|
"@types/uuid": "^9.0.2",
|
|
63
63
|
"c8": "^7.7.1",
|
|
64
|
-
"copyfiles": "^2.4.1",
|
|
65
64
|
"cross-env": "^7.0.3",
|
|
66
|
-
"eslint": "~8.
|
|
65
|
+
"eslint": "~8.50.0",
|
|
67
66
|
"mocha": "^10.2.0",
|
|
68
67
|
"mocha-json-output-reporter": "^2.0.1",
|
|
69
68
|
"mocha-multi-reporters": "^1.5.1",
|
|
70
69
|
"moment": "^2.21.0",
|
|
71
|
-
"prettier": "~
|
|
70
|
+
"prettier": "~3.0.3",
|
|
72
71
|
"rimraf": "^4.4.0",
|
|
73
72
|
"sinon": "^7.4.2",
|
|
74
|
-
"typescript": "~
|
|
73
|
+
"typescript": "~5.1.6"
|
|
75
74
|
},
|
|
76
75
|
"typeValidation": {
|
|
77
76
|
"broken": {}
|
|
@@ -80,11 +79,11 @@
|
|
|
80
79
|
"build": "fluid-build . --task build",
|
|
81
80
|
"build:commonjs": "fluid-build . --task commonjs",
|
|
82
81
|
"build:compile": "fluid-build . --task compile",
|
|
83
|
-
"build:docs": "api-extractor run --local
|
|
82
|
+
"build:docs": "api-extractor run --local",
|
|
84
83
|
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
85
84
|
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
86
85
|
"bump-version": "npm version minor --no-push --no-git-tag-version && npm run build:genver",
|
|
87
|
-
"ci:build:docs": "api-extractor run
|
|
86
|
+
"ci:build:docs": "api-extractor run",
|
|
88
87
|
"clean": "rimraf --glob 'dist' 'lib' '*.tsbuildinfo' '*.build.log' '_api-extractor-temp' 'nyc'",
|
|
89
88
|
"eslint": "eslint --format stylish src",
|
|
90
89
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
package/src/config.ts
CHANGED
|
@@ -77,10 +77,12 @@ function isPrimitiveType(type: string): type is PrimitiveTypeStrings {
|
|
|
77
77
|
switch (type) {
|
|
78
78
|
case "boolean":
|
|
79
79
|
case "number":
|
|
80
|
-
case "string":
|
|
80
|
+
case "string": {
|
|
81
81
|
return true;
|
|
82
|
-
|
|
82
|
+
}
|
|
83
|
+
default: {
|
|
83
84
|
return false;
|
|
85
|
+
}
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
|
package/src/error.ts
CHANGED
|
@@ -23,6 +23,8 @@ import { IFluidErrorBase } from "./fluidErrorBase";
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Generic wrapper for an unrecognized/uncategorized error object
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
26
28
|
*/
|
|
27
29
|
export class GenericError extends LoggingError implements IGenericError, IFluidErrorBase {
|
|
28
30
|
readonly errorType = FluidErrorTypes.genericError;
|
|
@@ -33,9 +35,13 @@ export class GenericError extends LoggingError implements IGenericError, IFluidE
|
|
|
33
35
|
* @param error - inner error object
|
|
34
36
|
* @param props - Telemetry props to include when the error is logged
|
|
35
37
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
) {
|
|
39
45
|
// Don't try to log the inner error
|
|
40
46
|
super(message, props, new Set(["error"]));
|
|
41
47
|
}
|
|
@@ -43,6 +49,8 @@ export class GenericError extends LoggingError implements IGenericError, IFluidE
|
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Error indicating an API is being used improperly resulting in an invalid operation.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
46
54
|
*/
|
|
47
55
|
export class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
|
|
48
56
|
readonly errorType = FluidErrorTypes.usageError;
|
|
@@ -55,6 +63,8 @@ export class UsageError extends LoggingError implements IUsageError, IFluidError
|
|
|
55
63
|
/**
|
|
56
64
|
* DataCorruptionError indicates that we encountered definitive evidence that the data at rest
|
|
57
65
|
* backing this container is corrupted, and this container would never be expected to load properly again
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
58
68
|
*/
|
|
59
69
|
export class DataCorruptionError extends LoggingError implements IErrorBase, IFluidErrorBase {
|
|
60
70
|
readonly errorType = FluidErrorTypes.dataCorruptionError;
|
|
@@ -73,6 +83,8 @@ export class DataCorruptionError extends LoggingError implements IErrorBase, IFl
|
|
|
73
83
|
* The error will often originate in the dataStore or DDS implementation that is responding to incoming changes.
|
|
74
84
|
* This differs from {@link DataCorruptionError} in that this may be a transient error that will not repro in another
|
|
75
85
|
* client or session.
|
|
86
|
+
*
|
|
87
|
+
* @internal
|
|
76
88
|
*/
|
|
77
89
|
export class DataProcessingError extends LoggingError implements IErrorBase, IFluidErrorBase {
|
|
78
90
|
/**
|
package/src/errorLogging.ts
CHANGED
|
@@ -27,6 +27,8 @@ const isRegularObject = (value: unknown): boolean => {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Inspect the given error for common "safe" props and return them.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
30
32
|
*/
|
|
31
33
|
export function extractLogSafeErrorProperties(
|
|
32
34
|
error: unknown,
|
|
@@ -95,6 +97,8 @@ function copyProps(
|
|
|
95
97
|
|
|
96
98
|
/**
|
|
97
99
|
* Metadata to annotate an error object when annotating or normalizing it
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
98
102
|
*/
|
|
99
103
|
export interface IFluidErrorAnnotations {
|
|
100
104
|
/**
|
|
@@ -121,6 +125,8 @@ function patchLegacyError(
|
|
|
121
125
|
* @returns A valid Fluid Error with any provided annotations applied
|
|
122
126
|
* @param error - The error to normalize
|
|
123
127
|
* @param annotations - Annotations to apply to the normalized error
|
|
128
|
+
*
|
|
129
|
+
* @internal
|
|
124
130
|
*/
|
|
125
131
|
export function normalizeError(
|
|
126
132
|
error: unknown,
|
|
@@ -190,6 +196,8 @@ let stackPopulatedOnCreation: boolean | undefined;
|
|
|
190
196
|
* For such cases it's better to not read stack property right away, but rather delay it until / if it's needed
|
|
191
197
|
* Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.
|
|
192
198
|
* @returns Error object that has stack populated.
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
193
201
|
*/
|
|
194
202
|
export function generateErrorWithStack(): Error {
|
|
195
203
|
const err = new Error("<<generated stack>>");
|
|
@@ -209,6 +217,12 @@ export function generateErrorWithStack(): Error {
|
|
|
209
217
|
}
|
|
210
218
|
}
|
|
211
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Generate a stack at this callsite as if an error were thrown from here.
|
|
222
|
+
* @returns the callstack (does not throw)
|
|
223
|
+
*
|
|
224
|
+
* @internal
|
|
225
|
+
*/
|
|
212
226
|
export function generateStack(): string | undefined {
|
|
213
227
|
return generateErrorWithStack().stack;
|
|
214
228
|
}
|
|
@@ -219,6 +233,8 @@ export function generateStack(): string | undefined {
|
|
|
219
233
|
* @param innerError - An error from untrusted/unknown origins
|
|
220
234
|
* @param newErrorFn - callback that will create a new error given the original error's message
|
|
221
235
|
* @returns A new error object "wrapping" the given error
|
|
236
|
+
*
|
|
237
|
+
* @internal
|
|
222
238
|
*/
|
|
223
239
|
export function wrapError<T extends LoggingError>(
|
|
224
240
|
innerError: unknown,
|
|
@@ -258,6 +274,8 @@ export function wrapError<T extends LoggingError>(
|
|
|
258
274
|
* The same as wrapError, but also logs the innerError, including the wrapping error's instance ID.
|
|
259
275
|
*
|
|
260
276
|
* @typeParam T - The kind of wrapper error to create.
|
|
277
|
+
*
|
|
278
|
+
* @internal
|
|
261
279
|
*/
|
|
262
280
|
export function wrapErrorAndLog<T extends LoggingError>(
|
|
263
281
|
innerError: unknown,
|
|
@@ -304,6 +322,8 @@ export function overwriteStack(error: IFluidErrorBase | LoggingError, stack: str
|
|
|
304
322
|
* True for any error object that is an (optionally normalized) external error
|
|
305
323
|
* False for any error we created and raised within the FF codebase via LoggingError base class,
|
|
306
324
|
* or wrapped in a well-known error type
|
|
325
|
+
*
|
|
326
|
+
* @internal
|
|
307
327
|
*/
|
|
308
328
|
export function isExternalError(error: unknown): boolean {
|
|
309
329
|
// LoggingErrors are an internal FF error type. However, an external error can be converted
|
|
@@ -351,12 +371,15 @@ function isTelemetryEventPropertyValue(x: unknown): x is TelemetryBaseEventPrope
|
|
|
351
371
|
case "string":
|
|
352
372
|
case "number":
|
|
353
373
|
case "boolean":
|
|
354
|
-
case "undefined":
|
|
374
|
+
case "undefined": {
|
|
355
375
|
return true;
|
|
356
|
-
|
|
376
|
+
}
|
|
377
|
+
default: {
|
|
357
378
|
return false;
|
|
379
|
+
}
|
|
358
380
|
}
|
|
359
381
|
}
|
|
382
|
+
|
|
360
383
|
/**
|
|
361
384
|
* Walk an object's enumerable properties to find those fit for telemetry.
|
|
362
385
|
*/
|
|
@@ -371,14 +394,12 @@ function getValidTelemetryProps(obj: object, keysToOmit: Set<string>): ITelemetr
|
|
|
371
394
|
| Tagged<TelemetryEventPropertyTypeExt>;
|
|
372
395
|
|
|
373
396
|
// ensure only valid props get logged, since props of logging error could be in any shape
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
props[key] = filterValidTelemetryProps(val, key);
|
|
381
|
-
}
|
|
397
|
+
props[key] = isTaggedTelemetryPropertyValue(val)
|
|
398
|
+
? {
|
|
399
|
+
value: filterValidTelemetryProps(val.value, key),
|
|
400
|
+
tag: val.tag,
|
|
401
|
+
}
|
|
402
|
+
: filterValidTelemetryProps(val, key);
|
|
382
403
|
}
|
|
383
404
|
return props;
|
|
384
405
|
}
|
|
@@ -389,6 +410,8 @@ function getValidTelemetryProps(obj: object, keysToOmit: Set<string>): ITelemetr
|
|
|
389
410
|
* Avoids runtime errors with circular references.
|
|
390
411
|
* Not ideal, as will cut values that are not necessarily circular references.
|
|
391
412
|
* Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)
|
|
413
|
+
*
|
|
414
|
+
* @internal
|
|
392
415
|
*/
|
|
393
416
|
// TODO: Use `unknown` instead (API breaking change)
|
|
394
417
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -412,6 +435,8 @@ export const getCircularReplacer = (): ((key: string, value: unknown) => any) =>
|
|
|
412
435
|
* will be logged in accordance with their tag, if present.
|
|
413
436
|
*
|
|
414
437
|
* PLEASE take care to avoid setting sensitive data on this object without proper tagging!
|
|
438
|
+
*
|
|
439
|
+
* @internal
|
|
415
440
|
*/
|
|
416
441
|
export class LoggingError
|
|
417
442
|
extends Error
|
|
@@ -494,8 +519,16 @@ export class LoggingError
|
|
|
494
519
|
|
|
495
520
|
/**
|
|
496
521
|
* The Error class used when normalizing an external error
|
|
522
|
+
*
|
|
523
|
+
* @internal
|
|
497
524
|
*/
|
|
498
525
|
export const NORMALIZED_ERROR_TYPE = "genericError";
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Subclass of LoggingError returned by normalizeError
|
|
529
|
+
*
|
|
530
|
+
* @internal
|
|
531
|
+
*/
|
|
499
532
|
class NormalizedLoggingError extends LoggingError {
|
|
500
533
|
// errorType "genericError" is used as a default value throughout the code.
|
|
501
534
|
// Note that this matches ContainerErrorType/DriverErrorType's genericError
|
package/src/events.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// False positive: this is an import from the `events` package, not from Node.
|
|
7
|
+
// eslint-disable-next-line unicorn/prefer-node-protocol
|
|
6
8
|
import { EventEmitter } from "events";
|
|
7
9
|
import { ITelemetryLoggerExt } from "./telemetryTypes";
|
|
8
10
|
|
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
|
@@ -305,26 +305,30 @@ export class TaggedLoggerAdapter implements ITelemetryBaseLogger {
|
|
|
305
305
|
? taggableProp
|
|
306
306
|
: { value: taggableProp, tag: undefined };
|
|
307
307
|
switch (tag) {
|
|
308
|
-
case undefined:
|
|
308
|
+
case undefined: {
|
|
309
309
|
// No tag means we can log plainly
|
|
310
310
|
newEvent[key] = value;
|
|
311
311
|
break;
|
|
312
|
+
}
|
|
312
313
|
case "PackageData": // For back-compat
|
|
313
|
-
case TelemetryDataTag.CodeArtifact:
|
|
314
|
+
case TelemetryDataTag.CodeArtifact: {
|
|
314
315
|
// For Microsoft applications, CodeArtifact is safe for now
|
|
315
316
|
// (we don't load 3P code in 1P apps)
|
|
316
317
|
newEvent[key] = value;
|
|
317
318
|
break;
|
|
318
|
-
|
|
319
|
+
}
|
|
320
|
+
case TelemetryDataTag.UserData: {
|
|
319
321
|
// Strip out anything tagged explicitly as UserData.
|
|
320
322
|
// Alternate strategy would be to hash these props
|
|
321
323
|
newEvent[key] = "REDACTED (UserData)";
|
|
322
324
|
break;
|
|
323
|
-
|
|
325
|
+
}
|
|
326
|
+
default: {
|
|
324
327
|
// If we encounter a tag we don't recognize
|
|
325
328
|
// then we must assume we should scrub.
|
|
326
329
|
newEvent[key] = "REDACTED (unknown tag)";
|
|
327
330
|
break;
|
|
331
|
+
}
|
|
328
332
|
}
|
|
329
333
|
}
|
|
330
334
|
this.logger.send(newEvent);
|
|
@@ -404,11 +408,7 @@ export class ChildLogger extends TelemetryLogger {
|
|
|
404
408
|
return child;
|
|
405
409
|
}
|
|
406
410
|
|
|
407
|
-
return new ChildLogger(
|
|
408
|
-
baseLogger ? baseLogger : { send(): void {} },
|
|
409
|
-
namespace,
|
|
410
|
-
properties,
|
|
411
|
-
);
|
|
411
|
+
return new ChildLogger(baseLogger ?? { send(): void {} }, namespace, properties);
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
private constructor(
|
|
@@ -489,7 +489,7 @@ export class MultiSinkLogger extends TelemetryLogger {
|
|
|
489
489
|
loggers: ITelemetryBaseLogger[] = [],
|
|
490
490
|
tryInheritProperties?: true,
|
|
491
491
|
) {
|
|
492
|
-
let realProperties = properties
|
|
492
|
+
let realProperties = properties === undefined ? undefined : { ...properties };
|
|
493
493
|
if (tryInheritProperties === true) {
|
|
494
494
|
const merge = (realProperties ??= {});
|
|
495
495
|
loggers
|
|
@@ -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,14 +680,14 @@ 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) {
|
|
630
687
|
this.reportEvent("start");
|
|
631
688
|
}
|
|
632
689
|
|
|
633
|
-
|
|
634
|
-
if (typeof window === "object" && window != null && window.performance?.mark) {
|
|
690
|
+
if (typeof window === "object" && window?.performance?.mark) {
|
|
635
691
|
this.startMark = `${event.eventName}-start`;
|
|
636
692
|
window.performance.mark(this.startMark);
|
|
637
693
|
}
|
|
@@ -687,6 +743,10 @@ export class PerformanceEvent {
|
|
|
687
743
|
return;
|
|
688
744
|
}
|
|
689
745
|
|
|
746
|
+
if (!this.emitLogs) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
690
750
|
const event: ITelemetryPerformanceEvent = { ...this.event, ...props };
|
|
691
751
|
event.eventName = `${event.eventName}_${eventNameSuffix}`;
|
|
692
752
|
if (eventNameSuffix !== "start") {
|
|
@@ -702,18 +762,25 @@ export class PerformanceEvent {
|
|
|
702
762
|
}
|
|
703
763
|
}
|
|
704
764
|
} else if (this.recordHeapSize) {
|
|
705
|
-
this.startMemoryCollection = (
|
|
706
|
-
|
|
707
|
-
)?.memory?.usedJSHeapSize;
|
|
765
|
+
this.startMemoryCollection = (performance as PerformanceWithMemory)?.memory
|
|
766
|
+
?.usedJSHeapSize;
|
|
708
767
|
}
|
|
709
768
|
|
|
710
769
|
this.logger.sendPerformanceEvent(event, error);
|
|
711
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
|
+
}
|
|
712
779
|
}
|
|
713
780
|
|
|
714
781
|
/**
|
|
715
782
|
* Null logger that no-ops for all telemetry events passed to it.
|
|
716
|
-
* @deprecated
|
|
783
|
+
* @deprecated This will be removed in a future release.
|
|
717
784
|
* For internal use within the FluidFramework codebase, use {@link createChildLogger} with no arguments instead.
|
|
718
785
|
* For external consumers we recommend writing a trivial implementation of {@link @fluidframework/core-interfaces#ITelemetryBaseLogger}
|
|
719
786
|
* where the send() method does nothing and using that.
|
|
@@ -769,17 +836,20 @@ function convertToBasePropertyTypeUntagged(
|
|
|
769
836
|
case "string":
|
|
770
837
|
case "number":
|
|
771
838
|
case "boolean":
|
|
772
|
-
case "undefined":
|
|
839
|
+
case "undefined": {
|
|
773
840
|
return x;
|
|
774
|
-
|
|
841
|
+
}
|
|
842
|
+
case "object": {
|
|
775
843
|
// We assume this is an array or flat object based on the input types
|
|
776
844
|
return JSON.stringify(x);
|
|
777
|
-
|
|
845
|
+
}
|
|
846
|
+
default: {
|
|
778
847
|
// should never reach this case based on the input types
|
|
779
848
|
console.error(
|
|
780
849
|
`convertToBasePropertyTypeUntagged: INVALID PROPERTY (typed as ${typeof x})`,
|
|
781
850
|
);
|
|
782
851
|
return `INVALID PROPERTY (typed as ${typeof x})`;
|
|
852
|
+
}
|
|
783
853
|
}
|
|
784
854
|
}
|
|
785
855
|
|
|
@@ -811,6 +881,8 @@ export const tagData = <
|
|
|
811
881
|
// eslint-disable-next-line unicorn/no-array-reduce, unicorn/prefer-object-from-entries
|
|
812
882
|
.reduce((pv, cv) => {
|
|
813
883
|
const [key, value] = cv;
|
|
884
|
+
// The ternary form is less legible in this case.
|
|
885
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
814
886
|
if (typeof value === "function") {
|
|
815
887
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
816
888
|
pv[key] = () => {
|
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
|
+
}
|