@azure/monitor-opentelemetry-exporter 1.0.0-beta.33 → 1.0.0-beta.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/commonjs/Declarations/Constants.d.ts +7 -2
  3. package/dist/commonjs/Declarations/Constants.d.ts.map +1 -1
  4. package/dist/commonjs/Declarations/Constants.js +8 -3
  5. package/dist/commonjs/Declarations/Constants.js.map +1 -1
  6. package/dist/commonjs/export/statsbeat/{customerStatsbeat.d.ts → customerSDKStats.d.ts} +30 -22
  7. package/dist/commonjs/export/statsbeat/customerSDKStats.d.ts.map +1 -0
  8. package/dist/commonjs/export/statsbeat/{customerStatsbeat.js → customerSDKStats.js} +175 -127
  9. package/dist/commonjs/export/statsbeat/customerSDKStats.js.map +1 -0
  10. package/dist/commonjs/export/statsbeat/types.d.ts +34 -9
  11. package/dist/commonjs/export/statsbeat/types.d.ts.map +1 -1
  12. package/dist/commonjs/export/statsbeat/types.js +45 -15
  13. package/dist/commonjs/export/statsbeat/types.js.map +1 -1
  14. package/dist/commonjs/generated/applicationInsightsClient.js +1 -1
  15. package/dist/commonjs/generated/applicationInsightsClient.js.map +1 -1
  16. package/dist/commonjs/platform/nodejs/baseSender.d.ts +1 -1
  17. package/dist/commonjs/platform/nodejs/baseSender.d.ts.map +1 -1
  18. package/dist/commonjs/platform/nodejs/baseSender.js +41 -26
  19. package/dist/commonjs/platform/nodejs/baseSender.js.map +1 -1
  20. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.d.ts +3 -3
  21. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.d.ts.map +1 -1
  22. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.js +8 -8
  23. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.js.map +1 -1
  24. package/dist/commonjs/tsdoc-metadata.json +1 -1
  25. package/dist/commonjs/types.d.ts +3 -1
  26. package/dist/commonjs/types.d.ts.map +1 -1
  27. package/dist/commonjs/types.js +4 -0
  28. package/dist/commonjs/types.js.map +1 -1
  29. package/dist/commonjs/utils/constants/applicationinsights.d.ts +2 -1
  30. package/dist/commonjs/utils/constants/applicationinsights.d.ts.map +1 -1
  31. package/dist/commonjs/utils/constants/applicationinsights.js +3 -2
  32. package/dist/commonjs/utils/constants/applicationinsights.js.map +1 -1
  33. package/dist/commonjs/utils/logUtils.d.ts.map +1 -1
  34. package/dist/commonjs/utils/logUtils.js +24 -6
  35. package/dist/commonjs/utils/logUtils.js.map +1 -1
  36. package/dist/commonjs/utils/spanUtils.d.ts.map +1 -1
  37. package/dist/commonjs/utils/spanUtils.js +16 -3
  38. package/dist/commonjs/utils/spanUtils.js.map +1 -1
  39. package/dist/esm/Declarations/Constants.d.ts +7 -2
  40. package/dist/esm/Declarations/Constants.d.ts.map +1 -1
  41. package/dist/esm/Declarations/Constants.js +7 -2
  42. package/dist/esm/Declarations/Constants.js.map +1 -1
  43. package/dist/esm/export/statsbeat/{customerStatsbeat.d.ts → customerSDKStats.d.ts} +30 -22
  44. package/dist/esm/export/statsbeat/customerSDKStats.d.ts.map +1 -0
  45. package/dist/esm/export/statsbeat/{customerStatsbeat.js → customerSDKStats.js} +175 -127
  46. package/dist/esm/export/statsbeat/customerSDKStats.js.map +1 -0
  47. package/dist/esm/export/statsbeat/types.d.ts +34 -9
  48. package/dist/esm/export/statsbeat/types.d.ts.map +1 -1
  49. package/dist/esm/export/statsbeat/types.js +43 -13
  50. package/dist/esm/export/statsbeat/types.js.map +1 -1
  51. package/dist/esm/generated/applicationInsightsClient.js +1 -1
  52. package/dist/esm/generated/applicationInsightsClient.js.map +1 -1
  53. package/dist/esm/platform/nodejs/baseSender.d.ts +1 -1
  54. package/dist/esm/platform/nodejs/baseSender.d.ts.map +1 -1
  55. package/dist/esm/platform/nodejs/baseSender.js +39 -24
  56. package/dist/esm/platform/nodejs/baseSender.js.map +1 -1
  57. package/dist/esm/platform/nodejs/persist/fileSystemPersist.d.ts +3 -3
  58. package/dist/esm/platform/nodejs/persist/fileSystemPersist.d.ts.map +1 -1
  59. package/dist/esm/platform/nodejs/persist/fileSystemPersist.js +9 -9
  60. package/dist/esm/platform/nodejs/persist/fileSystemPersist.js.map +1 -1
  61. package/dist/esm/types.d.ts +3 -1
  62. package/dist/esm/types.d.ts.map +1 -1
  63. package/dist/esm/types.js +4 -0
  64. package/dist/esm/types.js.map +1 -1
  65. package/dist/esm/utils/constants/applicationinsights.d.ts +2 -1
  66. package/dist/esm/utils/constants/applicationinsights.d.ts.map +1 -1
  67. package/dist/esm/utils/constants/applicationinsights.js +2 -1
  68. package/dist/esm/utils/constants/applicationinsights.js.map +1 -1
  69. package/dist/esm/utils/logUtils.d.ts.map +1 -1
  70. package/dist/esm/utils/logUtils.js +21 -3
  71. package/dist/esm/utils/logUtils.js.map +1 -1
  72. package/dist/esm/utils/spanUtils.d.ts.map +1 -1
  73. package/dist/esm/utils/spanUtils.js +19 -6
  74. package/dist/esm/utils/spanUtils.js.map +1 -1
  75. package/package.json +42 -43
  76. package/dist/commonjs/export/statsbeat/customerStatsbeat.d.ts.map +0 -1
  77. package/dist/commonjs/export/statsbeat/customerStatsbeat.js.map +0 -1
  78. package/dist/esm/export/statsbeat/customerStatsbeat.d.ts.map +0 -1
  79. package/dist/esm/export/statsbeat/customerStatsbeat.js.map +0 -1
@@ -4,26 +4,26 @@ import { diag } from "@opentelemetry/api";
4
4
  import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
5
5
  import * as ai from "../../utils/constants/applicationinsights.js";
6
6
  import { StatsbeatMetrics } from "./statsbeatMetrics.js";
7
- import { CustomerStatsbeat, DropCode, RetryCode } from "./types.js";
8
- import { CustomStatsbeatCounter, STATSBEAT_LANGUAGE, TelemetryType } from "./types.js";
7
+ import { CustomerSDKStats, DropCode, RetryCode, ExceptionType, DropReason, RetryReason, } from "./types.js";
8
+ import { CustomSDKStatsCounter, STATSBEAT_LANGUAGE, TelemetryType } from "./types.js";
9
9
  import { getAttachType } from "../../utils/metricUtils.js";
10
10
  import { AzureMonitorStatsbeatExporter } from "./statsbeatExporter.js";
11
11
  import { BreezePerformanceCounterNames } from "../../types.js";
12
12
  /**
13
- * Class that handles customer-facing statsbeat metrics
13
+ * Class that handles customer-facing SDK Stats metrics
14
14
  * These metrics are sent to the customer's breeze endpoint
15
15
  *
16
- * Implements a singleton pattern to ensure only one set of customer statsbeat metrics
16
+ * Implements a singleton pattern to ensure only one set of customer SDK Stats metrics
17
17
  * is exported every 15 minutes, regardless of the number of exporters or senders.
18
18
  */
19
- export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
19
+ export class CustomerSDKStatsMetrics extends StatsbeatMetrics {
20
20
  static _instance;
21
21
  statsCollectionInterval = 900000; // 15 minutes
22
- customerStatsbeatMeter;
23
- customerStatsbeatMeterProvider;
24
- customerStatsbeatExporter;
25
- customerStatsbeatCounter;
26
- customerStatsbeatMetricReader;
22
+ customerSDKStatsMeter;
23
+ customerSDKStatsMeterProvider;
24
+ customerSDKStatsExporter;
25
+ customerSDKStatsCounter;
26
+ customerSDKStatsMetricReader;
27
27
  isInitialized = false;
28
28
  // Custom dimensions
29
29
  language;
@@ -33,35 +33,35 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
33
33
  itemSuccessCountGauge;
34
34
  itemDropCountGauge;
35
35
  itemRetryCountGauge;
36
- // Customer statsbeat properties
36
+ // Customer SDK Stats properties
37
37
  customerProperties;
38
38
  constructor(options) {
39
39
  super();
40
40
  const exporterConfig = {
41
41
  connectionString: `InstrumentationKey=${options.instrumentationKey};IngestionEndpoint=${options.endpointUrl}`,
42
42
  };
43
- this.customerStatsbeatExporter = new AzureMonitorStatsbeatExporter(exporterConfig);
44
- // Exports Customer Statsbeat every 15 minutes
43
+ this.customerSDKStatsExporter = new AzureMonitorStatsbeatExporter(exporterConfig);
44
+ // Exports Customer SDK Stats every 15 minutes
45
45
  const customerMetricReaderOptions = {
46
- exporter: this.customerStatsbeatExporter,
46
+ exporter: this.customerSDKStatsExporter,
47
47
  exportIntervalMillis: options.networkCollectionInterval || this.statsCollectionInterval,
48
48
  };
49
- this.customerStatsbeatMetricReader = new PeriodicExportingMetricReader(customerMetricReaderOptions);
50
- this.customerStatsbeatMeterProvider = new MeterProvider({
51
- readers: [this.customerStatsbeatMetricReader],
49
+ this.customerSDKStatsMetricReader = new PeriodicExportingMetricReader(customerMetricReaderOptions);
50
+ this.customerSDKStatsMeterProvider = new MeterProvider({
51
+ readers: [this.customerSDKStatsMetricReader],
52
52
  });
53
- this.customerStatsbeatMeter = this.customerStatsbeatMeterProvider.getMeter("Azure Monitor Customer Statsbeat");
53
+ this.customerSDKStatsMeter = this.customerSDKStatsMeterProvider.getMeter("Azure Monitor Customer SDK Stats");
54
54
  this.language = STATSBEAT_LANGUAGE;
55
55
  this.version = ai.packageVersion;
56
- this.itemSuccessCountGauge = this.customerStatsbeatMeter.createObservableGauge(CustomStatsbeatCounter.ITEM_SUCCESS_COUNT);
57
- this.itemDropCountGauge = this.customerStatsbeatMeter.createObservableGauge(CustomStatsbeatCounter.ITEM_DROP_COUNT);
58
- this.itemRetryCountGauge = this.customerStatsbeatMeter.createObservableGauge(CustomStatsbeatCounter.ITEM_RETRY_COUNT);
56
+ this.itemSuccessCountGauge = this.customerSDKStatsMeter.createObservableGauge(CustomSDKStatsCounter.ITEM_SUCCESS_COUNT);
57
+ this.itemDropCountGauge = this.customerSDKStatsMeter.createObservableGauge(CustomSDKStatsCounter.ITEM_DROP_COUNT);
58
+ this.itemRetryCountGauge = this.customerSDKStatsMeter.createObservableGauge(CustomSDKStatsCounter.ITEM_RETRY_COUNT);
59
59
  if (!this.isInitialized) {
60
60
  this.initialize();
61
61
  }
62
62
  this.isInitialized = true;
63
- // Initialize the single customer statsbeat counter
64
- this.customerStatsbeatCounter = new CustomerStatsbeat();
63
+ // Initialize the single customer SDK Stats counter
64
+ this.customerSDKStatsCounter = new CustomerSDKStats();
65
65
  this.customerProperties = {
66
66
  language: this.language,
67
67
  version: this.version,
@@ -69,99 +69,113 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
69
69
  };
70
70
  }
71
71
  /**
72
- * Get singleton instance of CustomerStatsbeatMetrics
73
- * @param options - Configuration options for customer statsbeat metrics
72
+ * Get singleton instance of CustomerSDKStatsMetrics
73
+ * @param options - Configuration options for customer SDK Stats metrics
74
74
  * @returns The singleton instance
75
75
  */
76
76
  static getInstance(options) {
77
- if (!CustomerStatsbeatMetrics._instance) {
78
- CustomerStatsbeatMetrics._instance = new CustomerStatsbeatMetrics(options);
77
+ if (!CustomerSDKStatsMetrics._instance) {
78
+ CustomerSDKStatsMetrics._instance = new CustomerSDKStatsMetrics(options);
79
79
  }
80
- return CustomerStatsbeatMetrics._instance;
80
+ return CustomerSDKStatsMetrics._instance;
81
81
  }
82
82
  /**
83
83
  * Shutdown the singleton instance
84
84
  * Used for cleanup and complete shutdown
85
85
  */
86
86
  static shutdown() {
87
- if (CustomerStatsbeatMetrics._instance) {
88
- const shutdownPromise = CustomerStatsbeatMetrics._instance.shutdown();
89
- CustomerStatsbeatMetrics._instance = undefined;
87
+ if (CustomerSDKStatsMetrics._instance) {
88
+ const shutdownPromise = CustomerSDKStatsMetrics._instance.shutdown();
89
+ CustomerSDKStatsMetrics._instance = undefined;
90
90
  return shutdownPromise;
91
91
  }
92
92
  return undefined;
93
93
  }
94
94
  /**
95
- * Shuts down the customer statsbeat metrics provider
95
+ * Shuts down the customer SDK Stats metrics provider
96
96
  * @returns Promise<void>
97
97
  */
98
98
  shutdown() {
99
- return this.customerStatsbeatMeterProvider.shutdown();
99
+ return this.customerSDKStatsMeterProvider.shutdown();
100
100
  }
101
101
  /**
102
- * Initializes the customer statsbeat metrics
102
+ * Initializes the customer SDK Stats metrics
103
103
  * Sets up the resource provider and adds observable callbacks for each metric
104
104
  * @returns Promise<void>
105
105
  */
106
106
  async initialize() {
107
107
  try {
108
108
  await super.getResourceProvider();
109
- this.customerStatsbeatMeter.addBatchObservableCallback(this.itemSuccessCallback.bind(this), [
109
+ this.customerSDKStatsMeter.addBatchObservableCallback(this.itemSuccessCallback.bind(this), [
110
110
  this.itemSuccessCountGauge,
111
111
  ]);
112
- this.customerStatsbeatMeter.addBatchObservableCallback(this.itemDropCallback.bind(this), [
112
+ this.customerSDKStatsMeter.addBatchObservableCallback(this.itemDropCallback.bind(this), [
113
113
  this.itemDropCountGauge,
114
114
  ]);
115
- this.customerStatsbeatMeter.addBatchObservableCallback(this.itemRetryCallback.bind(this), [
115
+ this.customerSDKStatsMeter.addBatchObservableCallback(this.itemRetryCallback.bind(this), [
116
116
  this.itemRetryCountGauge,
117
117
  ]);
118
118
  }
119
119
  catch (error) {
120
- diag.debug("Call to get the resource provider failed for customer statsbeat metrics.");
120
+ diag.debug("Call to get the resource provider failed for customer SDK Stats metrics.");
121
121
  }
122
122
  }
123
123
  // Observable gauge callbacks
124
124
  itemSuccessCallback(observableResult) {
125
- const counter = this.customerStatsbeatCounter;
125
+ const counter = this.customerSDKStatsCounter;
126
126
  const attributes = { ...this.customerProperties, telemetry_type: TelemetryType.UNKNOWN };
127
127
  // For each { telemetry_type -> count } mapping, call observe, passing the count and attributes that include the telemetry_type
128
128
  for (const [telemetry_type, count] of counter.totalItemSuccessCount.entries()) {
129
- attributes.telemetry_type = telemetry_type;
130
- observableResult.observe(this.itemSuccessCountGauge, count, {
131
- ...attributes,
132
- });
133
- counter.totalItemSuccessCount.set(telemetry_type, 0);
129
+ // Only send metrics if count is greater than zero
130
+ if (count > 0) {
131
+ attributes.telemetry_type = telemetry_type;
132
+ observableResult.observe(this.itemSuccessCountGauge, count, {
133
+ ...attributes,
134
+ });
135
+ counter.totalItemSuccessCount.set(telemetry_type, 0);
136
+ }
134
137
  }
135
138
  }
136
139
  itemDropCallback(observableResult) {
137
- const counter = this.customerStatsbeatCounter;
140
+ const counter = this.customerSDKStatsCounter;
138
141
  const baseAttributes = {
139
142
  ...this.customerProperties,
140
143
  "drop.code": DropCode.UNKNOWN,
141
144
  telemetry_type: TelemetryType.UNKNOWN,
142
145
  };
143
- // Iterate through the nested Map structure: telemetry_type -> drop.code -> reason -> count
146
+ // Iterate through the nested Map structure: telemetry_type -> drop.code -> reason -> telemetry_success -> count
144
147
  for (const [telemetryType, dropCodeMap] of counter.totalItemDropCount.entries()) {
145
148
  for (const [dropCode, reasonMap] of dropCodeMap.entries()) {
146
- for (const [reason, count] of reasonMap.entries()) {
147
- const attributes = { ...baseAttributes };
148
- attributes.telemetry_type = telemetryType;
149
- attributes["drop.code"] = dropCode;
150
- // Include drop.reason for all case
151
- if (reason) {
152
- attributes["drop.reason"] = reason;
149
+ for (const [reason, successMap] of reasonMap.entries()) {
150
+ for (const [success, count] of successMap.entries()) {
151
+ const attributes = { ...baseAttributes };
152
+ attributes.telemetry_type = telemetryType;
153
+ attributes["drop.code"] = dropCode;
154
+ // Include drop.reason for all cases
155
+ if (reason) {
156
+ attributes["drop.reason"] = reason;
157
+ }
158
+ // Include telemetry_success only for request/dependency telemetry when success is not null
159
+ if ((telemetryType === TelemetryType.REQUEST ||
160
+ telemetryType === TelemetryType.DEPENDENCY) &&
161
+ success !== null) {
162
+ attributes["telemetry_success"] = success;
163
+ }
164
+ // Only send metrics if count is greater than zero
165
+ if (count > 0) {
166
+ observableResult.observe(this.itemDropCountGauge, count, {
167
+ ...attributes,
168
+ });
169
+ }
170
+ // Reset the count to 0
171
+ successMap.set(success, 0);
153
172
  }
154
- observableResult.observe(this.itemDropCountGauge, count, {
155
- ...attributes,
156
- });
157
- // Reset the count to 0
158
- reasonMap.set(reason, 0);
159
173
  }
160
174
  }
161
175
  }
162
176
  }
163
177
  itemRetryCallback(observableResult) {
164
- const counter = this.customerStatsbeatCounter;
178
+ const counter = this.customerSDKStatsCounter;
165
179
  const baseAttributes = {
166
180
  ...this.customerProperties,
167
181
  "retry.code": RetryCode.UNKNOWN,
@@ -178,9 +192,12 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
178
192
  if (reason) {
179
193
  attributes["retry.reason"] = reason;
180
194
  }
181
- observableResult.observe(this.itemRetryCountGauge, count, {
182
- ...attributes,
183
- });
195
+ // Only send metrics if count is greater than zero
196
+ if (count > 0) {
197
+ observableResult.observe(this.itemRetryCountGauge, count, {
198
+ ...attributes,
199
+ });
200
+ }
184
201
  // Reset the count to 0
185
202
  reasonMap.set(reason, 0);
186
203
  }
@@ -194,7 +211,7 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
194
211
  * @param telemetry_type - The type of telemetry being tracked
195
212
  */
196
213
  countSuccessfulItems(envelopes) {
197
- const counter = this.customerStatsbeatCounter;
214
+ const counter = this.customerSDKStatsCounter;
198
215
  let telemetry_type;
199
216
  // Get the current count for this telemetry type, or 0 if it doesn't exist
200
217
  for (const envelope of envelopes) {
@@ -205,17 +222,16 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
205
222
  }
206
223
  /**
207
224
  * Tracks dropped items
208
- * @param envelopes - Number of envelopes dropped
225
+ * @param envelopes - Array of envelopes dropped
209
226
  * @param dropCode - The drop code indicating the reason for drop
210
- * @param telemetry_type - The type of telemetry being tracked
211
227
  * @param exceptionMessage - Optional exception message when dropCode is CLIENT_EXCEPTION
228
+ * @param exceptionType - Optional explicit exception type override when dropCode is CLIENT_EXCEPTION
212
229
  */
213
- countDroppedItems(envelopes, dropCode, exceptionMessage) {
214
- const counter = this.customerStatsbeatCounter;
230
+ countDroppedItems(envelopes, dropCode, exceptionMessage, exceptionType) {
231
+ const counter = this.customerSDKStatsCounter;
215
232
  let telemetry_type;
216
233
  for (const envelope of envelopes) {
217
234
  telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);
218
- // Get or create the dropCode map for this telemetry_type
219
235
  let dropCodeMap = counter.totalItemDropCount.get(telemetry_type);
220
236
  if (!dropCodeMap) {
221
237
  dropCodeMap = new Map();
@@ -228,25 +244,42 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
228
244
  dropCodeMap.set(dropCode, reasonMap);
229
245
  }
230
246
  // Generate a low-cardinality, informative reason description
231
- const reason = this.getDropReason(dropCode, exceptionMessage);
232
- // Update the count for this reason
233
- const currentCount = reasonMap.get(reason) || 0;
234
- reasonMap.set(reason, currentCount + 1);
247
+ const reason = this.getDropReason(dropCode, exceptionMessage, exceptionType);
248
+ // Get or create the success map for this reason
249
+ let successMap = reasonMap.get(reason);
250
+ if (!successMap) {
251
+ successMap = new Map();
252
+ reasonMap.set(reason, successMap);
253
+ }
254
+ // For non-request/dependency telemetry or when success is not provided, use null as the success key
255
+ const individualTelemetrySuccess = this.getTelemetrySuccessFromEnvelope(envelope);
256
+ const successKey = (telemetry_type === TelemetryType.REQUEST || telemetry_type === TelemetryType.DEPENDENCY) &&
257
+ individualTelemetrySuccess !== undefined
258
+ ? individualTelemetrySuccess
259
+ : null;
260
+ // Update the count for this reason and success combination
261
+ const currentCount = successMap.get(successKey) || 0;
262
+ successMap.set(successKey, currentCount + 1);
235
263
  }
236
264
  }
237
265
  /**
238
266
  * Generates a low-cardinality, informative description for drop reasons
239
267
  * @param dropCode - The drop code (enum value or status code number)
240
268
  * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION
269
+ * @param exceptionType - Optional explicit exception type override for CLIENT_EXCEPTION
241
270
  * @returns A descriptive reason string with low cardinality
242
271
  */
243
- getDropReason(dropCode, exceptionMessage) {
272
+ getDropReason(dropCode, exceptionMessage, exceptionType) {
244
273
  if (dropCode === DropCode.CLIENT_EXCEPTION) {
245
- // For client exceptions, derive a low-cardinality reason from the exception message
274
+ // If an explicit exception type is provided, use it
275
+ if (exceptionType) {
276
+ return exceptionType;
277
+ }
278
+ // For client exceptions, derive a well-known exception category from the exception message
246
279
  if (exceptionMessage) {
247
280
  return this.categorizeExceptionMessage(exceptionMessage);
248
281
  }
249
- return "unknown_exception";
282
+ return ExceptionType.CLIENT_EXCEPTION; // Default to "Client exception" if no message provided
250
283
  }
251
284
  // Handle status code drop codes (numeric values)
252
285
  if (typeof dropCode === "number") {
@@ -254,49 +287,41 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
254
287
  }
255
288
  // Handle other enum drop codes
256
289
  switch (dropCode) {
257
- case DropCode.CLIENT_EXPIRED_DATA:
258
- return "expired_data";
259
290
  case DropCode.CLIENT_READONLY:
260
- return "readonly_mode";
261
- case DropCode.CLIENT_STALE_DATA:
262
- return "stale_data";
291
+ return DropReason.CLIENT_READONLY;
263
292
  case DropCode.CLIENT_PERSISTENCE_CAPACITY:
264
- return "persistence_full";
265
- case DropCode.NON_RETRYABLE_STATUS_CODE:
266
- return "non_retryable_status";
293
+ return DropReason.CLIENT_PERSISTENCE_CAPACITY;
294
+ case DropCode.CLIENT_STORAGE_DISABLED:
295
+ return DropReason.CLIENT_STORAGE_DISABLED;
267
296
  case DropCode.UNKNOWN:
268
297
  default:
269
- return "unknown_reason";
298
+ return DropReason.UNKNOWN;
270
299
  }
271
300
  }
272
301
  /**
273
- * Categorizes exception messages into low-cardinality groups
302
+ * Categorizes exception messages into well-known exception categories
274
303
  * @param exceptionMessage - The exception message to categorize
275
- * @returns A low-cardinality category string
304
+ * @returns A well-known exception category string
276
305
  */
277
306
  categorizeExceptionMessage(exceptionMessage) {
278
307
  const message = exceptionMessage.toLowerCase();
279
308
  if (message.includes("timeout") || message.includes("timed out")) {
280
- return "timeout_exception";
281
- }
282
- if (message.includes("network") || message.includes("connection")) {
283
- return "network_exception";
284
- }
285
- if (message.includes("auth") ||
286
- message.includes("unauthorized") ||
287
- message.includes("forbidden")) {
288
- return "auth_exception";
289
- }
290
- if (message.includes("parsing") || message.includes("parse") || message.includes("invalid")) {
291
- return "parsing_exception";
309
+ return ExceptionType.TIMEOUT_EXCEPTION;
292
310
  }
293
- if (message.includes("disk") || message.includes("storage") || message.includes("file")) {
294
- return "storage_exception";
311
+ if (message.includes("network") ||
312
+ message.includes("connection") ||
313
+ message.includes("dns") ||
314
+ message.includes("socket")) {
315
+ return ExceptionType.NETWORK_EXCEPTION;
295
316
  }
296
- if (message.includes("memory") || message.includes("out of memory")) {
297
- return "memory_exception";
317
+ if (message.includes("disk") ||
318
+ message.includes("storage") ||
319
+ message.includes("file") ||
320
+ message.includes("persist")) {
321
+ return ExceptionType.STORAGE_EXCEPTION;
298
322
  }
299
- return "other_exception";
323
+ // Default to Client exception for any other cases
324
+ return ExceptionType.CLIENT_EXCEPTION;
300
325
  }
301
326
  /**
302
327
  * Categorizes HTTP status codes into informative descriptions
@@ -307,35 +332,35 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
307
332
  if (statusCode >= 400 && statusCode < 500) {
308
333
  switch (statusCode) {
309
334
  case 400:
310
- return "bad_request";
335
+ return "Bad request";
311
336
  case 401:
312
- return "unauthorized";
337
+ return "Unauthorized";
313
338
  case 403:
314
- return "forbidden";
339
+ return "Forbidden";
315
340
  case 404:
316
- return "not_found";
341
+ return "Not found";
317
342
  case 408:
318
- return "request_timeout";
343
+ return "Request timeout";
319
344
  case 413:
320
- return "payload_too_large";
345
+ return "Payload too large";
321
346
  case 429:
322
- return "too_many_requests";
347
+ return "Too many requests";
323
348
  default:
324
- return "client_error_4xx";
349
+ return "Client error 4xx";
325
350
  }
326
351
  }
327
352
  if (statusCode >= 500 && statusCode < 600) {
328
353
  switch (statusCode) {
329
354
  case 500:
330
- return "internal_server_error";
355
+ return "Internal server error";
331
356
  case 502:
332
- return "bad_gateway";
357
+ return "Bad gateway";
333
358
  case 503:
334
- return "service_unavailable";
359
+ return "Service unavailable";
335
360
  case 504:
336
- return "gateway_timeout";
361
+ return "Gateway timeout";
337
362
  default:
338
- return "server_error_5xx";
363
+ return "Server error 5xx";
339
364
  }
340
365
  }
341
366
  return `status_${statusCode}`;
@@ -344,11 +369,11 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
344
369
  * Tracks retried envelopes
345
370
  * @param envelopes - Number of envelopes retried
346
371
  * @param retryCode - The retry code indicating the reason for retry
347
- * @param telemetry_type - The type of telemetry being tracked
348
372
  * @param exceptionMessage - Optional exception message when retryCode is CLIENT_EXCEPTION
373
+ * @param exceptionType - Optional explicit exception type override when retryCode is CLIENT_EXCEPTION
349
374
  */
350
- countRetryItems(envelopes, retryCode, exceptionMessage) {
351
- const counter = this.customerStatsbeatCounter;
375
+ countRetryItems(envelopes, retryCode, exceptionMessage, exceptionType) {
376
+ const counter = this.customerSDKStatsCounter;
352
377
  let telemetry_type;
353
378
  for (const envelope of envelopes) {
354
379
  telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);
@@ -365,7 +390,7 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
365
390
  retryCodeMap.set(retryCode, reasonMap);
366
391
  }
367
392
  // Generate a low-cardinality, informative reason description
368
- const reason = this.getRetryReason(retryCode, exceptionMessage);
393
+ const reason = this.getRetryReason(retryCode, exceptionMessage, exceptionType);
369
394
  // Update the count for this reason
370
395
  const currentCount = reasonMap.get(reason) || 0;
371
396
  reasonMap.set(reason, currentCount + 1);
@@ -375,15 +400,20 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
375
400
  * Generates a low-cardinality, informative description for retry reasons
376
401
  * @param retryCode - The retry code (enum value or status code number)
377
402
  * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION
403
+ * @param exceptionType - Optional explicit exception type override for CLIENT_EXCEPTION
378
404
  * @returns A descriptive reason string with low cardinality
379
405
  */
380
- getRetryReason(retryCode, exceptionMessage) {
406
+ getRetryReason(retryCode, exceptionMessage, exceptionType) {
381
407
  if (retryCode === RetryCode.CLIENT_EXCEPTION) {
408
+ // If an explicit exception type is provided, use it
409
+ if (exceptionType) {
410
+ return exceptionType;
411
+ }
382
412
  // For client exceptions, derive a low-cardinality reason from the exception message
383
413
  if (exceptionMessage) {
384
414
  return this.categorizeExceptionMessage(exceptionMessage);
385
415
  }
386
- return "unknown_exception";
416
+ return ExceptionType.CLIENT_EXCEPTION;
387
417
  }
388
418
  // Handle status code retry codes (numeric values)
389
419
  if (typeof retryCode === "number") {
@@ -392,12 +422,10 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
392
422
  // Handle other enum retry codes
393
423
  switch (retryCode) {
394
424
  case RetryCode.CLIENT_TIMEOUT:
395
- return "client_timeout";
396
- case RetryCode.RETRYABLE_STATUS_CODE:
397
- return "retryable_status";
425
+ return RetryReason.CLIENT_TIMEOUT;
398
426
  case RetryCode.UNKNOWN:
399
427
  default:
400
- return "unknown_reason";
428
+ return RetryReason.UNKNOWN;
401
429
  }
402
430
  }
403
431
  /**
@@ -447,6 +475,26 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
447
475
  }
448
476
  return TelemetryType.UNKNOWN;
449
477
  }
478
+ /**
479
+ * Extract telemetry success value from an envelope for REQUEST and DEPENDENCY telemetry types
480
+ * @param envelope - The envelope to extract success value from
481
+ * @returns The success value if available, undefined otherwise
482
+ */
483
+ getTelemetrySuccessFromEnvelope(envelope) {
484
+ if (!envelope.data || !envelope.data.baseData) {
485
+ return undefined;
486
+ }
487
+ const baseType = envelope.data.baseType;
488
+ if (baseType === "RequestData") {
489
+ const requestData = envelope.data.baseData;
490
+ return requestData.success;
491
+ }
492
+ else if (baseType === "RemoteDependencyData") {
493
+ const dependencyData = envelope.data.baseData;
494
+ return dependencyData.success;
495
+ }
496
+ return undefined;
497
+ }
450
498
  /**
451
499
  * Checks if the given error is a timeout-related error
452
500
  * @param error - The error to check
@@ -472,4 +520,4 @@ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
472
520
  return false;
473
521
  }
474
522
  }
475
- //# sourceMappingURL=customerStatsbeat.js.map
523
+ //# sourceMappingURL=customerSDKStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customerSDKStats.js","sourceRoot":"","sources":["../../../../src/export/statsbeat/customerSDKStats.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { BatchObservableResult, Meter, ObservableGauge } from \"@opentelemetry/api\";\nimport { diag } from \"@opentelemetry/api\";\nimport type { PeriodicExportingMetricReaderOptions } from \"@opentelemetry/sdk-metrics\";\nimport { MeterProvider, PeriodicExportingMetricReader } from \"@opentelemetry/sdk-metrics\";\nimport type { AzureMonitorExporterOptions } from \"../../index.js\";\nimport * as ai from \"../../utils/constants/applicationinsights.js\";\nimport { StatsbeatMetrics } from \"./statsbeatMetrics.js\";\nimport type { CustomerSDKStatsProperties, StatsbeatOptions } from \"./types.js\";\nimport {\n CustomerSDKStats,\n DropCode,\n RetryCode,\n ExceptionType,\n DropReason,\n RetryReason,\n} from \"./types.js\";\nimport { CustomSDKStatsCounter, STATSBEAT_LANGUAGE, TelemetryType } from \"./types.js\";\nimport { getAttachType } from \"../../utils/metricUtils.js\";\nimport { AzureMonitorStatsbeatExporter } from \"./statsbeatExporter.js\";\nimport { BreezePerformanceCounterNames } from \"../../types.js\";\nimport type { MetricsData, RemoteDependencyData, RequestData } from \"../../generated/index.js\";\nimport type { TelemetryItem as Envelope } from \"../../generated/index.js\";\n\n/**\n * Class that handles customer-facing SDK Stats metrics\n * These metrics are sent to the customer's breeze endpoint\n *\n * Implements a singleton pattern to ensure only one set of customer SDK Stats metrics\n * is exported every 15 minutes, regardless of the number of exporters or senders.\n */\nexport class CustomerSDKStatsMetrics extends StatsbeatMetrics {\n private static _instance: CustomerSDKStatsMetrics | undefined;\n\n private statsCollectionInterval: number = 900000; // 15 minutes\n private customerSDKStatsMeter: Meter;\n private customerSDKStatsMeterProvider: MeterProvider;\n private customerSDKStatsExporter: AzureMonitorStatsbeatExporter;\n private customerSDKStatsCounter: CustomerSDKStats;\n private customerSDKStatsMetricReader: PeriodicExportingMetricReader;\n private isInitialized: boolean = false;\n\n // Custom dimensions\n private language: string;\n private version: string;\n private attach: string = getAttachType();\n\n // Observable Gauges\n private itemSuccessCountGauge: ObservableGauge;\n private itemDropCountGauge: ObservableGauge;\n private itemRetryCountGauge: ObservableGauge;\n\n // Customer SDK Stats properties\n private customerProperties: CustomerSDKStatsProperties;\n\n private constructor(options: StatsbeatOptions) {\n super();\n const exporterConfig: AzureMonitorExporterOptions = {\n connectionString: `InstrumentationKey=${options.instrumentationKey};IngestionEndpoint=${options.endpointUrl}`,\n };\n\n this.customerSDKStatsExporter = new AzureMonitorStatsbeatExporter(exporterConfig);\n // Exports Customer SDK Stats every 15 minutes\n const customerMetricReaderOptions: PeriodicExportingMetricReaderOptions = {\n exporter: this.customerSDKStatsExporter,\n exportIntervalMillis: options.networkCollectionInterval || this.statsCollectionInterval,\n };\n this.customerSDKStatsMetricReader = new PeriodicExportingMetricReader(\n customerMetricReaderOptions,\n );\n this.customerSDKStatsMeterProvider = new MeterProvider({\n readers: [this.customerSDKStatsMetricReader],\n });\n\n this.customerSDKStatsMeter = this.customerSDKStatsMeterProvider.getMeter(\n \"Azure Monitor Customer SDK Stats\",\n );\n\n this.language = STATSBEAT_LANGUAGE;\n this.version = ai.packageVersion;\n\n this.itemSuccessCountGauge = this.customerSDKStatsMeter.createObservableGauge(\n CustomSDKStatsCounter.ITEM_SUCCESS_COUNT,\n );\n this.itemDropCountGauge = this.customerSDKStatsMeter.createObservableGauge(\n CustomSDKStatsCounter.ITEM_DROP_COUNT,\n );\n this.itemRetryCountGauge = this.customerSDKStatsMeter.createObservableGauge(\n CustomSDKStatsCounter.ITEM_RETRY_COUNT,\n );\n\n if (!this.isInitialized) {\n this.initialize();\n }\n this.isInitialized = true;\n\n // Initialize the single customer SDK Stats counter\n this.customerSDKStatsCounter = new CustomerSDKStats();\n\n this.customerProperties = {\n language: this.language,\n version: this.version,\n computeType: this.attach,\n };\n }\n\n /**\n * Get singleton instance of CustomerSDKStatsMetrics\n * @param options - Configuration options for customer SDK Stats metrics\n * @returns The singleton instance\n */\n public static getInstance(options: StatsbeatOptions): CustomerSDKStatsMetrics {\n if (!CustomerSDKStatsMetrics._instance) {\n CustomerSDKStatsMetrics._instance = new CustomerSDKStatsMetrics(options);\n }\n return CustomerSDKStatsMetrics._instance;\n }\n\n /**\n * Shutdown the singleton instance\n * Used for cleanup and complete shutdown\n */\n public static shutdown(): Promise<void> | undefined {\n if (CustomerSDKStatsMetrics._instance) {\n const shutdownPromise = CustomerSDKStatsMetrics._instance.shutdown();\n CustomerSDKStatsMetrics._instance = undefined;\n return shutdownPromise;\n }\n return undefined;\n }\n\n /**\n * Shuts down the customer SDK Stats metrics provider\n * @returns Promise<void>\n */\n public shutdown(): Promise<void> {\n return this.customerSDKStatsMeterProvider.shutdown();\n }\n\n /**\n * Initializes the customer SDK Stats metrics\n * Sets up the resource provider and adds observable callbacks for each metric\n * @returns Promise<void>\n */\n private async initialize(): Promise<void> {\n try {\n await super.getResourceProvider();\n this.customerSDKStatsMeter.addBatchObservableCallback(this.itemSuccessCallback.bind(this), [\n this.itemSuccessCountGauge,\n ]);\n this.customerSDKStatsMeter.addBatchObservableCallback(this.itemDropCallback.bind(this), [\n this.itemDropCountGauge,\n ]);\n this.customerSDKStatsMeter.addBatchObservableCallback(this.itemRetryCallback.bind(this), [\n this.itemRetryCountGauge,\n ]);\n } catch (error) {\n diag.debug(\"Call to get the resource provider failed for customer SDK Stats metrics.\");\n }\n }\n\n // Observable gauge callbacks\n private itemSuccessCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n const attributes = { ...this.customerProperties, telemetry_type: TelemetryType.UNKNOWN };\n\n // For each { telemetry_type -> count } mapping, call observe, passing the count and attributes that include the telemetry_type\n for (const [telemetry_type, count] of counter.totalItemSuccessCount.entries()) {\n // Only send metrics if count is greater than zero\n if (count > 0) {\n attributes.telemetry_type = telemetry_type;\n observableResult.observe(this.itemSuccessCountGauge, count, {\n ...attributes,\n });\n counter.totalItemSuccessCount.set(telemetry_type, 0);\n }\n }\n }\n\n private itemDropCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n const baseAttributes: CustomerSDKStatsProperties & {\n \"drop.code\": DropCode | number;\n telemetry_type: TelemetryType;\n } = {\n ...this.customerProperties,\n \"drop.code\": DropCode.UNKNOWN,\n telemetry_type: TelemetryType.UNKNOWN,\n };\n\n // Iterate through the nested Map structure: telemetry_type -> drop.code -> reason -> telemetry_success -> count\n for (const [telemetryType, dropCodeMap] of counter.totalItemDropCount.entries()) {\n for (const [dropCode, reasonMap] of dropCodeMap.entries()) {\n for (const [reason, successMap] of reasonMap.entries()) {\n for (const [success, count] of successMap.entries()) {\n const attributes = { ...baseAttributes };\n attributes.telemetry_type = telemetryType;\n attributes[\"drop.code\"] = dropCode;\n\n // Include drop.reason for all cases\n if (reason) {\n (attributes as any)[\"drop.reason\"] = reason;\n }\n\n // Include telemetry_success only for request/dependency telemetry when success is not null\n if (\n (telemetryType === TelemetryType.REQUEST ||\n telemetryType === TelemetryType.DEPENDENCY) &&\n success !== null\n ) {\n (attributes as any)[\"telemetry_success\"] = success;\n }\n\n // Only send metrics if count is greater than zero\n if (count > 0) {\n observableResult.observe(this.itemDropCountGauge, count, {\n ...attributes,\n });\n }\n\n // Reset the count to 0\n successMap.set(success, 0);\n }\n }\n }\n }\n }\n\n private itemRetryCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n const baseAttributes: CustomerSDKStatsProperties & {\n \"retry.code\": RetryCode | number;\n telemetry_type: TelemetryType;\n } = {\n ...this.customerProperties,\n \"retry.code\": RetryCode.UNKNOWN,\n telemetry_type: TelemetryType.UNKNOWN,\n };\n\n // Iterate through the nested Map structure: telemetry_type -> retry.code -> reason -> count\n for (const [telemetryType, retryCodeMap] of counter.totalItemRetryCount.entries()) {\n for (const [retryCode, reasonMap] of retryCodeMap.entries()) {\n for (const [reason, count] of reasonMap.entries()) {\n const attributes = { ...baseAttributes };\n attributes.telemetry_type = telemetryType;\n attributes[\"retry.code\"] = retryCode;\n\n // Include retry.reason for all cases\n if (reason) {\n (attributes as any)[\"retry.reason\"] = reason;\n }\n\n // Only send metrics if count is greater than zero\n if (count > 0) {\n observableResult.observe(this.itemRetryCountGauge, count, {\n ...attributes,\n });\n }\n\n // Reset the count to 0\n reasonMap.set(reason, 0);\n }\n }\n }\n }\n\n // Public methods to track metrics\n /**\n * Tracks succcessful items\n * @param envelopes - Number of successful envelopes\n * @param telemetry_type - The type of telemetry being tracked\n */\n public countSuccessfulItems(envelopes: Envelope[]): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n let telemetry_type: TelemetryType;\n\n // Get the current count for this telemetry type, or 0 if it doesn't exist\n for (const envelope of envelopes) {\n telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);\n const currentCount = counter.totalItemSuccessCount.get(telemetry_type) || 0;\n counter.totalItemSuccessCount.set(telemetry_type, currentCount + 1);\n }\n }\n\n /**\n * Tracks dropped items\n * @param envelopes - Array of envelopes dropped\n * @param dropCode - The drop code indicating the reason for drop\n * @param exceptionMessage - Optional exception message when dropCode is CLIENT_EXCEPTION\n * @param exceptionType - Optional explicit exception type override when dropCode is CLIENT_EXCEPTION\n */\n public countDroppedItems(\n envelopes: Envelope[],\n dropCode: DropCode | number,\n exceptionMessage?: string,\n exceptionType?: ExceptionType,\n ): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n let telemetry_type: TelemetryType;\n\n for (const envelope of envelopes) {\n telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);\n\n let dropCodeMap = counter.totalItemDropCount.get(telemetry_type);\n if (!dropCodeMap) {\n dropCodeMap = new Map<DropCode | number, Map<string, Map<boolean | null, number>>>();\n counter.totalItemDropCount.set(telemetry_type, dropCodeMap);\n }\n\n // Get or create the reason map for this dropCode\n let reasonMap = dropCodeMap.get(dropCode);\n if (!reasonMap) {\n reasonMap = new Map<string, Map<boolean | null, number>>();\n dropCodeMap.set(dropCode, reasonMap);\n }\n\n // Generate a low-cardinality, informative reason description\n const reason = this.getDropReason(dropCode, exceptionMessage, exceptionType);\n\n // Get or create the success map for this reason\n let successMap = reasonMap.get(reason);\n if (!successMap) {\n successMap = new Map<boolean | null, number>();\n reasonMap.set(reason, successMap);\n }\n\n // For non-request/dependency telemetry or when success is not provided, use null as the success key\n const individualTelemetrySuccess = this.getTelemetrySuccessFromEnvelope(envelope);\n const successKey =\n (telemetry_type === TelemetryType.REQUEST || telemetry_type === TelemetryType.DEPENDENCY) &&\n individualTelemetrySuccess !== undefined\n ? individualTelemetrySuccess\n : null;\n\n // Update the count for this reason and success combination\n const currentCount = successMap.get(successKey) || 0;\n successMap.set(successKey, currentCount + 1);\n }\n }\n\n /**\n * Generates a low-cardinality, informative description for drop reasons\n * @param dropCode - The drop code (enum value or status code number)\n * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION\n * @param exceptionType - Optional explicit exception type override for CLIENT_EXCEPTION\n * @returns A descriptive reason string with low cardinality\n */\n private getDropReason(\n dropCode: DropCode | number,\n exceptionMessage?: string,\n exceptionType?: ExceptionType,\n ): string {\n if (dropCode === DropCode.CLIENT_EXCEPTION) {\n // If an explicit exception type is provided, use it\n if (exceptionType) {\n return exceptionType;\n }\n // For client exceptions, derive a well-known exception category from the exception message\n if (exceptionMessage) {\n return this.categorizeExceptionMessage(exceptionMessage);\n }\n return ExceptionType.CLIENT_EXCEPTION; // Default to \"Client exception\" if no message provided\n }\n\n // Handle status code drop codes (numeric values)\n if (typeof dropCode === \"number\") {\n return this.categorizeStatusCode(dropCode);\n }\n\n // Handle other enum drop codes\n switch (dropCode) {\n case DropCode.CLIENT_READONLY:\n return DropReason.CLIENT_READONLY;\n case DropCode.CLIENT_PERSISTENCE_CAPACITY:\n return DropReason.CLIENT_PERSISTENCE_CAPACITY;\n case DropCode.CLIENT_STORAGE_DISABLED:\n return DropReason.CLIENT_STORAGE_DISABLED;\n case DropCode.UNKNOWN:\n default:\n return DropReason.UNKNOWN;\n }\n }\n\n /**\n * Categorizes exception messages into well-known exception categories\n * @param exceptionMessage - The exception message to categorize\n * @returns A well-known exception category string\n */\n private categorizeExceptionMessage(exceptionMessage: string): ExceptionType {\n const message = exceptionMessage.toLowerCase();\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return ExceptionType.TIMEOUT_EXCEPTION;\n }\n if (\n message.includes(\"network\") ||\n message.includes(\"connection\") ||\n message.includes(\"dns\") ||\n message.includes(\"socket\")\n ) {\n return ExceptionType.NETWORK_EXCEPTION;\n }\n if (\n message.includes(\"disk\") ||\n message.includes(\"storage\") ||\n message.includes(\"file\") ||\n message.includes(\"persist\")\n ) {\n return ExceptionType.STORAGE_EXCEPTION;\n }\n\n // Default to Client exception for any other cases\n return ExceptionType.CLIENT_EXCEPTION;\n }\n\n /**\n * Categorizes HTTP status codes into informative descriptions\n * @param statusCode - The HTTP status code\n * @returns A descriptive category string\n */\n private categorizeStatusCode(statusCode: number): string {\n if (statusCode >= 400 && statusCode < 500) {\n switch (statusCode) {\n case 400:\n return \"Bad request\";\n case 401:\n return \"Unauthorized\";\n case 403:\n return \"Forbidden\";\n case 404:\n return \"Not found\";\n case 408:\n return \"Request timeout\";\n case 413:\n return \"Payload too large\";\n case 429:\n return \"Too many requests\";\n default:\n return \"Client error 4xx\";\n }\n }\n\n if (statusCode >= 500 && statusCode < 600) {\n switch (statusCode) {\n case 500:\n return \"Internal server error\";\n case 502:\n return \"Bad gateway\";\n case 503:\n return \"Service unavailable\";\n case 504:\n return \"Gateway timeout\";\n default:\n return \"Server error 5xx\";\n }\n }\n\n return `status_${statusCode}`;\n }\n /**\n * Tracks retried envelopes\n * @param envelopes - Number of envelopes retried\n * @param retryCode - The retry code indicating the reason for retry\n * @param exceptionMessage - Optional exception message when retryCode is CLIENT_EXCEPTION\n * @param exceptionType - Optional explicit exception type override when retryCode is CLIENT_EXCEPTION\n */\n public countRetryItems(\n envelopes: Envelope[],\n retryCode: RetryCode | number,\n exceptionMessage?: string,\n exceptionType?: ExceptionType,\n ): void {\n const counter: CustomerSDKStats = this.customerSDKStatsCounter;\n let telemetry_type: TelemetryType;\n\n for (const envelope of envelopes) {\n telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);\n // Get or create the retryCode map for this telemetry_type\n let retryCodeMap = counter.totalItemRetryCount.get(telemetry_type);\n if (!retryCodeMap) {\n retryCodeMap = new Map<RetryCode | number, Map<string, number>>();\n counter.totalItemRetryCount.set(telemetry_type, retryCodeMap);\n }\n\n // Get or create the reason map for this retryCode\n let reasonMap = retryCodeMap.get(retryCode);\n if (!reasonMap) {\n reasonMap = new Map<string, number>();\n retryCodeMap.set(retryCode, reasonMap);\n }\n\n // Generate a low-cardinality, informative reason description\n const reason = this.getRetryReason(retryCode, exceptionMessage, exceptionType);\n\n // Update the count for this reason\n const currentCount = reasonMap.get(reason) || 0;\n reasonMap.set(reason, currentCount + 1);\n }\n }\n\n /**\n * Generates a low-cardinality, informative description for retry reasons\n * @param retryCode - The retry code (enum value or status code number)\n * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION\n * @param exceptionType - Optional explicit exception type override for CLIENT_EXCEPTION\n * @returns A descriptive reason string with low cardinality\n */\n private getRetryReason(\n retryCode: RetryCode | number,\n exceptionMessage?: string,\n exceptionType?: ExceptionType,\n ): string {\n if (retryCode === RetryCode.CLIENT_EXCEPTION) {\n // If an explicit exception type is provided, use it\n if (exceptionType) {\n return exceptionType;\n }\n // For client exceptions, derive a low-cardinality reason from the exception message\n if (exceptionMessage) {\n return this.categorizeExceptionMessage(exceptionMessage);\n }\n return ExceptionType.CLIENT_EXCEPTION;\n }\n\n // Handle status code retry codes (numeric values)\n if (typeof retryCode === \"number\") {\n return this.categorizeStatusCode(retryCode);\n }\n\n // Handle other enum retry codes\n switch (retryCode) {\n case RetryCode.CLIENT_TIMEOUT:\n return RetryReason.CLIENT_TIMEOUT;\n case RetryCode.UNKNOWN:\n default:\n return RetryReason.UNKNOWN;\n }\n }\n\n /**\n * Check if a metric name corresponds to a performance counter\n * @param metricName - The name of the metric to check\n * @returns true if the metric name is a performance counter, false otherwise\n */\n private isPerformanceCounterMetric(metricName: string): boolean {\n return Object.values(BreezePerformanceCounterNames).includes(\n metricName as BreezePerformanceCounterNames,\n );\n }\n\n /**\n * Extract telemetry type from an envelope based on its baseType\n * @param envelope - The envelope to extract telemetry type from\n * @returns The corresponding telemetry type\n */\n public getTelemetryTypeFromEnvelope(envelope: Envelope): TelemetryType {\n if (envelope.data && envelope.data.baseType) {\n switch (envelope.data.baseType) {\n case \"MessageData\":\n return TelemetryType.TRACE;\n case \"AvailabilityData\":\n return TelemetryType.AVAILABILITY;\n case \"TelemetryEventData\":\n return TelemetryType.CUSTOM_EVENT;\n case \"TelemetryExceptionData\":\n return TelemetryType.EXCEPTION;\n case \"PageViewData\":\n return TelemetryType.PAGE_VIEW;\n case \"RemoteDependencyData\":\n return TelemetryType.DEPENDENCY;\n case \"RequestData\":\n return TelemetryType.REQUEST;\n case \"MetricData\": {\n const metricsData = envelope.data.baseData as MetricsData;\n if (metricsData && metricsData.metrics && metricsData.metrics.length > 0) {\n // Check if any of the metrics are performance counters\n const hasPerformanceCounter = metricsData.metrics.some((metric) =>\n this.isPerformanceCounterMetric(metric.name),\n );\n return hasPerformanceCounter\n ? TelemetryType.PERFORMANCE_COUNTER\n : TelemetryType.CUSTOM_METRIC;\n }\n return TelemetryType.CUSTOM_METRIC;\n }\n default:\n return TelemetryType.UNKNOWN;\n }\n }\n return TelemetryType.UNKNOWN;\n }\n\n /**\n * Extract telemetry success value from an envelope for REQUEST and DEPENDENCY telemetry types\n * @param envelope - The envelope to extract success value from\n * @returns The success value if available, undefined otherwise\n */\n public getTelemetrySuccessFromEnvelope(envelope: Envelope): boolean | undefined {\n if (!envelope.data || !envelope.data.baseData) {\n return undefined;\n }\n\n const baseType = envelope.data.baseType;\n if (baseType === \"RequestData\") {\n const requestData = envelope.data.baseData as RequestData;\n return requestData.success;\n } else if (baseType === \"RemoteDependencyData\") {\n const dependencyData = envelope.data.baseData as RemoteDependencyData;\n return dependencyData.success;\n }\n\n return undefined;\n }\n\n /**\n * Checks if the given error is a timeout-related error\n * @param error - The error to check\n * @returns true if the error is timeout-related, false otherwise\n */\n public isTimeoutError(error: { code?: string; message?: string }): boolean {\n // Check for various timeout error codes that indicate client timeouts\n const timeoutErrorCodes = [\n \"ETIMEDOUT\", // Connection timed out\n \"ESOCKETTIMEDOUT\", // Socket timeout\n \"ECONNRESET\", // Connection reset (often due to timeout)\n \"ENOTFOUND\", // DNS lookup failed/timeout\n ];\n\n if (error && error.code && timeoutErrorCodes.includes(error.code)) {\n return true;\n }\n\n // Also check if the error message contains timeout-related keywords\n if (error && error.message) {\n const timeoutKeywords = [\"timeout\", \"timed out\", \"connection reset\"];\n const errorMessage = error.message.toLowerCase();\n return timeoutKeywords.some((keyword) => errorMessage.includes(keyword));\n }\n\n return false;\n }\n}\n"]}