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

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 (180) hide show
  1. package/dist/commonjs/Declarations/Constants.d.ts +5 -0
  2. package/dist/commonjs/Declarations/Constants.d.ts.map +1 -1
  3. package/dist/commonjs/Declarations/Constants.js +6 -1
  4. package/dist/commonjs/Declarations/Constants.js.map +1 -1
  5. package/dist/commonjs/export/base.js +25 -14
  6. package/dist/commonjs/export/base.js.map +1 -1
  7. package/dist/commonjs/export/log.js +5 -4
  8. package/dist/commonjs/export/log.js.map +1 -1
  9. package/dist/commonjs/export/metric.js +5 -4
  10. package/dist/commonjs/export/metric.js.map +1 -1
  11. package/dist/commonjs/export/statsbeat/customerStatsbeat.d.ts +125 -0
  12. package/dist/commonjs/export/statsbeat/customerStatsbeat.d.ts.map +1 -0
  13. package/dist/commonjs/export/statsbeat/customerStatsbeat.js +480 -0
  14. package/dist/commonjs/export/statsbeat/customerStatsbeat.js.map +1 -0
  15. package/dist/commonjs/export/statsbeat/longIntervalStatsbeatMetrics.js +35 -11
  16. package/dist/commonjs/export/statsbeat/longIntervalStatsbeatMetrics.js.map +1 -1
  17. package/dist/commonjs/export/statsbeat/networkStatsbeatMetrics.js +52 -21
  18. package/dist/commonjs/export/statsbeat/networkStatsbeatMetrics.js.map +1 -1
  19. package/dist/commonjs/export/statsbeat/statsbeatExporter.d.ts +7 -0
  20. package/dist/commonjs/export/statsbeat/statsbeatExporter.d.ts.map +1 -1
  21. package/dist/commonjs/export/statsbeat/statsbeatExporter.js +25 -5
  22. package/dist/commonjs/export/statsbeat/statsbeatExporter.js.map +1 -1
  23. package/dist/commonjs/export/statsbeat/statsbeatMetrics.js +6 -8
  24. package/dist/commonjs/export/statsbeat/statsbeatMetrics.js.map +1 -1
  25. package/dist/commonjs/export/statsbeat/types.d.ts +52 -0
  26. package/dist/commonjs/export/statsbeat/types.d.ts.map +1 -1
  27. package/dist/commonjs/export/statsbeat/types.js +75 -1
  28. package/dist/commonjs/export/statsbeat/types.js.map +1 -1
  29. package/dist/commonjs/export/trace.js +6 -5
  30. package/dist/commonjs/export/trace.js.map +1 -1
  31. package/dist/commonjs/generated/applicationInsightsClient.js +9 -4
  32. package/dist/commonjs/generated/applicationInsightsClient.js.map +1 -1
  33. package/dist/commonjs/generated/models/mappers.js +145 -68
  34. package/dist/commonjs/generated/models/mappers.js.map +1 -1
  35. package/dist/commonjs/index.d.ts +2 -1
  36. package/dist/commonjs/index.d.ts.map +1 -1
  37. package/dist/commonjs/index.js +5 -3
  38. package/dist/commonjs/index.js.map +1 -1
  39. package/dist/commonjs/platform/nodejs/baseSender.d.ts +1 -0
  40. package/dist/commonjs/platform/nodejs/baseSender.d.ts.map +1 -1
  41. package/dist/commonjs/platform/nodejs/baseSender.js +74 -28
  42. package/dist/commonjs/platform/nodejs/baseSender.js.map +1 -1
  43. package/dist/commonjs/platform/nodejs/context/context.d.ts +0 -1
  44. package/dist/commonjs/platform/nodejs/context/context.d.ts.map +1 -1
  45. package/dist/commonjs/platform/nodejs/context/context.js +4 -8
  46. package/dist/commonjs/platform/nodejs/context/context.js.map +1 -1
  47. package/dist/commonjs/platform/nodejs/httpSender.js +11 -4
  48. package/dist/commonjs/platform/nodejs/httpSender.js.map +1 -1
  49. package/dist/commonjs/platform/nodejs/index.d.ts +1 -0
  50. package/dist/commonjs/platform/nodejs/index.d.ts.map +1 -1
  51. package/dist/commonjs/platform/nodejs/index.js +1 -0
  52. package/dist/commonjs/platform/nodejs/index.js.map +1 -1
  53. package/dist/commonjs/platform/nodejs/persist/fileAccessControl.js +7 -7
  54. package/dist/commonjs/platform/nodejs/persist/fileAccessControl.js.map +1 -1
  55. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.d.ts +9 -1
  56. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.d.ts.map +1 -1
  57. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.js +37 -16
  58. package/dist/commonjs/platform/nodejs/persist/fileSystemPersist.js.map +1 -1
  59. package/dist/commonjs/{sampling.d.ts → sampling/percentageSampler.d.ts} +2 -3
  60. package/dist/commonjs/sampling/percentageSampler.d.ts.map +1 -0
  61. package/dist/commonjs/{sampling.js → sampling/percentageSampler.js} +6 -39
  62. package/dist/commonjs/sampling/percentageSampler.js.map +1 -0
  63. package/dist/commonjs/sampling/rateLimitedSampler.d.ts +58 -0
  64. package/dist/commonjs/sampling/rateLimitedSampler.d.ts.map +1 -0
  65. package/dist/commonjs/sampling/rateLimitedSampler.js +118 -0
  66. package/dist/commonjs/sampling/rateLimitedSampler.js.map +1 -0
  67. package/dist/commonjs/sampling/samplingUtils.d.ts +17 -0
  68. package/dist/commonjs/sampling/samplingUtils.d.ts.map +1 -0
  69. package/dist/commonjs/sampling/samplingUtils.js +94 -0
  70. package/dist/commonjs/sampling/samplingUtils.js.map +1 -0
  71. package/dist/commonjs/tsdoc-metadata.json +11 -11
  72. package/dist/commonjs/utils/common.js +2 -3
  73. package/dist/commonjs/utils/common.js.map +1 -1
  74. package/dist/commonjs/utils/connectionStringParser.js +3 -3
  75. package/dist/commonjs/utils/connectionStringParser.js.map +1 -1
  76. package/dist/commonjs/utils/constants/applicationinsights.d.ts +1 -1
  77. package/dist/commonjs/utils/constants/applicationinsights.js +1 -1
  78. package/dist/commonjs/utils/constants/applicationinsights.js.map +1 -1
  79. package/dist/commonjs/utils/eventhub.js +5 -2
  80. package/dist/commonjs/utils/eventhub.js.map +1 -1
  81. package/dist/commonjs/utils/logUtils.js +10 -9
  82. package/dist/commonjs/utils/logUtils.js.map +1 -1
  83. package/dist/commonjs/utils/metricUtils.d.ts.map +1 -1
  84. package/dist/commonjs/utils/metricUtils.js +6 -6
  85. package/dist/commonjs/utils/metricUtils.js.map +1 -1
  86. package/dist/commonjs/utils/spanUtils.d.ts.map +1 -1
  87. package/dist/commonjs/utils/spanUtils.js +13 -11
  88. package/dist/commonjs/utils/spanUtils.js.map +1 -1
  89. package/dist/esm/Declarations/Constants.d.ts +5 -0
  90. package/dist/esm/Declarations/Constants.d.ts.map +1 -1
  91. package/dist/esm/Declarations/Constants.js +5 -0
  92. package/dist/esm/Declarations/Constants.js.map +1 -1
  93. package/dist/esm/export/base.js +25 -14
  94. package/dist/esm/export/base.js.map +1 -1
  95. package/dist/esm/export/log.js +5 -4
  96. package/dist/esm/export/log.js.map +1 -1
  97. package/dist/esm/export/metric.js +5 -4
  98. package/dist/esm/export/metric.js.map +1 -1
  99. package/dist/esm/export/statsbeat/customerStatsbeat.d.ts +125 -0
  100. package/dist/esm/export/statsbeat/customerStatsbeat.d.ts.map +1 -0
  101. package/dist/esm/export/statsbeat/customerStatsbeat.js +475 -0
  102. package/dist/esm/export/statsbeat/customerStatsbeat.js.map +1 -0
  103. package/dist/esm/export/statsbeat/longIntervalStatsbeatMetrics.js +35 -11
  104. package/dist/esm/export/statsbeat/longIntervalStatsbeatMetrics.js.map +1 -1
  105. package/dist/esm/export/statsbeat/networkStatsbeatMetrics.js +52 -21
  106. package/dist/esm/export/statsbeat/networkStatsbeatMetrics.js.map +1 -1
  107. package/dist/esm/export/statsbeat/statsbeatExporter.d.ts +7 -0
  108. package/dist/esm/export/statsbeat/statsbeatExporter.d.ts.map +1 -1
  109. package/dist/esm/export/statsbeat/statsbeatExporter.js +25 -5
  110. package/dist/esm/export/statsbeat/statsbeatExporter.js.map +1 -1
  111. package/dist/esm/export/statsbeat/statsbeatMetrics.js +6 -8
  112. package/dist/esm/export/statsbeat/statsbeatMetrics.js.map +1 -1
  113. package/dist/esm/export/statsbeat/types.d.ts +52 -0
  114. package/dist/esm/export/statsbeat/types.d.ts.map +1 -1
  115. package/dist/esm/export/statsbeat/types.js +73 -0
  116. package/dist/esm/export/statsbeat/types.js.map +1 -1
  117. package/dist/esm/export/trace.js +6 -5
  118. package/dist/esm/export/trace.js.map +1 -1
  119. package/dist/esm/generated/applicationInsightsClient.js +9 -4
  120. package/dist/esm/generated/applicationInsightsClient.js.map +1 -1
  121. package/dist/esm/generated/models/mappers.js +145 -68
  122. package/dist/esm/generated/models/mappers.js.map +1 -1
  123. package/dist/esm/index.d.ts +2 -1
  124. package/dist/esm/index.d.ts.map +1 -1
  125. package/dist/esm/index.js +2 -1
  126. package/dist/esm/index.js.map +1 -1
  127. package/dist/esm/platform/nodejs/baseSender.d.ts +1 -0
  128. package/dist/esm/platform/nodejs/baseSender.d.ts.map +1 -1
  129. package/dist/esm/platform/nodejs/baseSender.js +76 -30
  130. package/dist/esm/platform/nodejs/baseSender.js.map +1 -1
  131. package/dist/esm/platform/nodejs/context/context.d.ts +0 -1
  132. package/dist/esm/platform/nodejs/context/context.d.ts.map +1 -1
  133. package/dist/esm/platform/nodejs/context/context.js +4 -8
  134. package/dist/esm/platform/nodejs/context/context.js.map +1 -1
  135. package/dist/esm/platform/nodejs/httpSender.js +11 -4
  136. package/dist/esm/platform/nodejs/httpSender.js.map +1 -1
  137. package/dist/esm/platform/nodejs/index.d.ts +1 -0
  138. package/dist/esm/platform/nodejs/index.d.ts.map +1 -1
  139. package/dist/esm/platform/nodejs/index.js +1 -0
  140. package/dist/esm/platform/nodejs/index.js.map +1 -1
  141. package/dist/esm/platform/nodejs/persist/fileAccessControl.js +7 -7
  142. package/dist/esm/platform/nodejs/persist/fileAccessControl.js.map +1 -1
  143. package/dist/esm/platform/nodejs/persist/fileSystemPersist.d.ts +9 -1
  144. package/dist/esm/platform/nodejs/persist/fileSystemPersist.d.ts.map +1 -1
  145. package/dist/esm/platform/nodejs/persist/fileSystemPersist.js +37 -16
  146. package/dist/esm/platform/nodejs/persist/fileSystemPersist.js.map +1 -1
  147. package/dist/esm/{sampling.d.ts → sampling/percentageSampler.d.ts} +2 -3
  148. package/dist/esm/sampling/percentageSampler.d.ts.map +1 -0
  149. package/dist/esm/{sampling.js → sampling/percentageSampler.js} +6 -39
  150. package/dist/esm/sampling/percentageSampler.js.map +1 -0
  151. package/dist/esm/sampling/rateLimitedSampler.d.ts +58 -0
  152. package/dist/esm/sampling/rateLimitedSampler.d.ts.map +1 -0
  153. package/dist/esm/sampling/rateLimitedSampler.js +114 -0
  154. package/dist/esm/sampling/rateLimitedSampler.js.map +1 -0
  155. package/dist/esm/sampling/samplingUtils.d.ts +17 -0
  156. package/dist/esm/sampling/samplingUtils.d.ts.map +1 -0
  157. package/dist/esm/sampling/samplingUtils.js +89 -0
  158. package/dist/esm/sampling/samplingUtils.js.map +1 -0
  159. package/dist/esm/utils/common.js +2 -3
  160. package/dist/esm/utils/common.js.map +1 -1
  161. package/dist/esm/utils/connectionStringParser.js +3 -3
  162. package/dist/esm/utils/connectionStringParser.js.map +1 -1
  163. package/dist/esm/utils/constants/applicationinsights.d.ts +1 -1
  164. package/dist/esm/utils/constants/applicationinsights.js +1 -1
  165. package/dist/esm/utils/constants/applicationinsights.js.map +1 -1
  166. package/dist/esm/utils/eventhub.js +5 -2
  167. package/dist/esm/utils/eventhub.js.map +1 -1
  168. package/dist/esm/utils/logUtils.js +10 -9
  169. package/dist/esm/utils/logUtils.js.map +1 -1
  170. package/dist/esm/utils/metricUtils.d.ts.map +1 -1
  171. package/dist/esm/utils/metricUtils.js +6 -6
  172. package/dist/esm/utils/metricUtils.js.map +1 -1
  173. package/dist/esm/utils/spanUtils.d.ts.map +1 -1
  174. package/dist/esm/utils/spanUtils.js +13 -11
  175. package/dist/esm/utils/spanUtils.js.map +1 -1
  176. package/package.json +6 -6
  177. package/dist/commonjs/sampling.d.ts.map +0 -1
  178. package/dist/commonjs/sampling.js.map +0 -1
  179. package/dist/esm/sampling.d.ts.map +0 -1
  180. package/dist/esm/sampling.js.map +0 -1
@@ -0,0 +1,475 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { diag } from "@opentelemetry/api";
4
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
5
+ import * as ai from "../../utils/constants/applicationinsights.js";
6
+ import { StatsbeatMetrics } from "./statsbeatMetrics.js";
7
+ import { CustomerStatsbeat, DropCode, RetryCode } from "./types.js";
8
+ import { CustomStatsbeatCounter, STATSBEAT_LANGUAGE, TelemetryType } from "./types.js";
9
+ import { getAttachType } from "../../utils/metricUtils.js";
10
+ import { AzureMonitorStatsbeatExporter } from "./statsbeatExporter.js";
11
+ import { BreezePerformanceCounterNames } from "../../types.js";
12
+ /**
13
+ * Class that handles customer-facing statsbeat metrics
14
+ * These metrics are sent to the customer's breeze endpoint
15
+ *
16
+ * Implements a singleton pattern to ensure only one set of customer statsbeat metrics
17
+ * is exported every 15 minutes, regardless of the number of exporters or senders.
18
+ */
19
+ export class CustomerStatsbeatMetrics extends StatsbeatMetrics {
20
+ static _instance;
21
+ statsCollectionInterval = 900000; // 15 minutes
22
+ customerStatsbeatMeter;
23
+ customerStatsbeatMeterProvider;
24
+ customerStatsbeatExporter;
25
+ customerStatsbeatCounter;
26
+ customerStatsbeatMetricReader;
27
+ isInitialized = false;
28
+ // Custom dimensions
29
+ language;
30
+ version;
31
+ attach = getAttachType();
32
+ // Observable Gauges
33
+ itemSuccessCountGauge;
34
+ itemDropCountGauge;
35
+ itemRetryCountGauge;
36
+ // Customer statsbeat properties
37
+ customerProperties;
38
+ constructor(options) {
39
+ super();
40
+ const exporterConfig = {
41
+ connectionString: `InstrumentationKey=${options.instrumentationKey};IngestionEndpoint=${options.endpointUrl}`,
42
+ };
43
+ this.customerStatsbeatExporter = new AzureMonitorStatsbeatExporter(exporterConfig);
44
+ // Exports Customer Statsbeat every 15 minutes
45
+ const customerMetricReaderOptions = {
46
+ exporter: this.customerStatsbeatExporter,
47
+ exportIntervalMillis: options.networkCollectionInterval || this.statsCollectionInterval,
48
+ };
49
+ this.customerStatsbeatMetricReader = new PeriodicExportingMetricReader(customerMetricReaderOptions);
50
+ this.customerStatsbeatMeterProvider = new MeterProvider({
51
+ readers: [this.customerStatsbeatMetricReader],
52
+ });
53
+ this.customerStatsbeatMeter = this.customerStatsbeatMeterProvider.getMeter("Azure Monitor Customer Statsbeat");
54
+ this.language = STATSBEAT_LANGUAGE;
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);
59
+ if (!this.isInitialized) {
60
+ this.initialize();
61
+ }
62
+ this.isInitialized = true;
63
+ // Initialize the single customer statsbeat counter
64
+ this.customerStatsbeatCounter = new CustomerStatsbeat();
65
+ this.customerProperties = {
66
+ language: this.language,
67
+ version: this.version,
68
+ computeType: this.attach,
69
+ };
70
+ }
71
+ /**
72
+ * Get singleton instance of CustomerStatsbeatMetrics
73
+ * @param options - Configuration options for customer statsbeat metrics
74
+ * @returns The singleton instance
75
+ */
76
+ static getInstance(options) {
77
+ if (!CustomerStatsbeatMetrics._instance) {
78
+ CustomerStatsbeatMetrics._instance = new CustomerStatsbeatMetrics(options);
79
+ }
80
+ return CustomerStatsbeatMetrics._instance;
81
+ }
82
+ /**
83
+ * Shutdown the singleton instance
84
+ * Used for cleanup and complete shutdown
85
+ */
86
+ static shutdown() {
87
+ if (CustomerStatsbeatMetrics._instance) {
88
+ const shutdownPromise = CustomerStatsbeatMetrics._instance.shutdown();
89
+ CustomerStatsbeatMetrics._instance = undefined;
90
+ return shutdownPromise;
91
+ }
92
+ return undefined;
93
+ }
94
+ /**
95
+ * Shuts down the customer statsbeat metrics provider
96
+ * @returns Promise<void>
97
+ */
98
+ shutdown() {
99
+ return this.customerStatsbeatMeterProvider.shutdown();
100
+ }
101
+ /**
102
+ * Initializes the customer statsbeat metrics
103
+ * Sets up the resource provider and adds observable callbacks for each metric
104
+ * @returns Promise<void>
105
+ */
106
+ async initialize() {
107
+ try {
108
+ await super.getResourceProvider();
109
+ this.customerStatsbeatMeter.addBatchObservableCallback(this.itemSuccessCallback.bind(this), [
110
+ this.itemSuccessCountGauge,
111
+ ]);
112
+ this.customerStatsbeatMeter.addBatchObservableCallback(this.itemDropCallback.bind(this), [
113
+ this.itemDropCountGauge,
114
+ ]);
115
+ this.customerStatsbeatMeter.addBatchObservableCallback(this.itemRetryCallback.bind(this), [
116
+ this.itemRetryCountGauge,
117
+ ]);
118
+ }
119
+ catch (error) {
120
+ diag.debug("Call to get the resource provider failed for customer statsbeat metrics.");
121
+ }
122
+ }
123
+ // Observable gauge callbacks
124
+ itemSuccessCallback(observableResult) {
125
+ const counter = this.customerStatsbeatCounter;
126
+ const attributes = { ...this.customerProperties, telemetry_type: TelemetryType.UNKNOWN };
127
+ // For each { telemetry_type -> count } mapping, call observe, passing the count and attributes that include the telemetry_type
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);
134
+ }
135
+ }
136
+ itemDropCallback(observableResult) {
137
+ const counter = this.customerStatsbeatCounter;
138
+ const baseAttributes = {
139
+ ...this.customerProperties,
140
+ "drop.code": DropCode.UNKNOWN,
141
+ telemetry_type: TelemetryType.UNKNOWN,
142
+ };
143
+ // Iterate through the nested Map structure: telemetry_type -> drop.code -> reason -> count
144
+ for (const [telemetryType, dropCodeMap] of counter.totalItemDropCount.entries()) {
145
+ 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;
153
+ }
154
+ observableResult.observe(this.itemDropCountGauge, count, {
155
+ ...attributes,
156
+ });
157
+ // Reset the count to 0
158
+ reasonMap.set(reason, 0);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ itemRetryCallback(observableResult) {
164
+ const counter = this.customerStatsbeatCounter;
165
+ const baseAttributes = {
166
+ ...this.customerProperties,
167
+ "retry.code": RetryCode.UNKNOWN,
168
+ telemetry_type: TelemetryType.UNKNOWN,
169
+ };
170
+ // Iterate through the nested Map structure: telemetry_type -> retry.code -> reason -> count
171
+ for (const [telemetryType, retryCodeMap] of counter.totalItemRetryCount.entries()) {
172
+ for (const [retryCode, reasonMap] of retryCodeMap.entries()) {
173
+ for (const [reason, count] of reasonMap.entries()) {
174
+ const attributes = { ...baseAttributes };
175
+ attributes.telemetry_type = telemetryType;
176
+ attributes["retry.code"] = retryCode;
177
+ // Include retry.reason for all cases
178
+ if (reason) {
179
+ attributes["retry.reason"] = reason;
180
+ }
181
+ observableResult.observe(this.itemRetryCountGauge, count, {
182
+ ...attributes,
183
+ });
184
+ // Reset the count to 0
185
+ reasonMap.set(reason, 0);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ // Public methods to track metrics
191
+ /**
192
+ * Tracks succcessful items
193
+ * @param envelopes - Number of successful envelopes
194
+ * @param telemetry_type - The type of telemetry being tracked
195
+ */
196
+ countSuccessfulItems(envelopes) {
197
+ const counter = this.customerStatsbeatCounter;
198
+ let telemetry_type;
199
+ // Get the current count for this telemetry type, or 0 if it doesn't exist
200
+ for (const envelope of envelopes) {
201
+ telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);
202
+ const currentCount = counter.totalItemSuccessCount.get(telemetry_type) || 0;
203
+ counter.totalItemSuccessCount.set(telemetry_type, currentCount + 1);
204
+ }
205
+ }
206
+ /**
207
+ * Tracks dropped items
208
+ * @param envelopes - Number of envelopes dropped
209
+ * @param dropCode - The drop code indicating the reason for drop
210
+ * @param telemetry_type - The type of telemetry being tracked
211
+ * @param exceptionMessage - Optional exception message when dropCode is CLIENT_EXCEPTION
212
+ */
213
+ countDroppedItems(envelopes, dropCode, exceptionMessage) {
214
+ const counter = this.customerStatsbeatCounter;
215
+ let telemetry_type;
216
+ for (const envelope of envelopes) {
217
+ telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);
218
+ // Get or create the dropCode map for this telemetry_type
219
+ let dropCodeMap = counter.totalItemDropCount.get(telemetry_type);
220
+ if (!dropCodeMap) {
221
+ dropCodeMap = new Map();
222
+ counter.totalItemDropCount.set(telemetry_type, dropCodeMap);
223
+ }
224
+ // Get or create the reason map for this dropCode
225
+ let reasonMap = dropCodeMap.get(dropCode);
226
+ if (!reasonMap) {
227
+ reasonMap = new Map();
228
+ dropCodeMap.set(dropCode, reasonMap);
229
+ }
230
+ // 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);
235
+ }
236
+ }
237
+ /**
238
+ * Generates a low-cardinality, informative description for drop reasons
239
+ * @param dropCode - The drop code (enum value or status code number)
240
+ * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION
241
+ * @returns A descriptive reason string with low cardinality
242
+ */
243
+ getDropReason(dropCode, exceptionMessage) {
244
+ if (dropCode === DropCode.CLIENT_EXCEPTION) {
245
+ // For client exceptions, derive a low-cardinality reason from the exception message
246
+ if (exceptionMessage) {
247
+ return this.categorizeExceptionMessage(exceptionMessage);
248
+ }
249
+ return "unknown_exception";
250
+ }
251
+ // Handle status code drop codes (numeric values)
252
+ if (typeof dropCode === "number") {
253
+ return this.categorizeStatusCode(dropCode);
254
+ }
255
+ // Handle other enum drop codes
256
+ switch (dropCode) {
257
+ case DropCode.CLIENT_EXPIRED_DATA:
258
+ return "expired_data";
259
+ case DropCode.CLIENT_READONLY:
260
+ return "readonly_mode";
261
+ case DropCode.CLIENT_STALE_DATA:
262
+ return "stale_data";
263
+ case DropCode.CLIENT_PERSISTENCE_CAPACITY:
264
+ return "persistence_full";
265
+ case DropCode.NON_RETRYABLE_STATUS_CODE:
266
+ return "non_retryable_status";
267
+ case DropCode.UNKNOWN:
268
+ default:
269
+ return "unknown_reason";
270
+ }
271
+ }
272
+ /**
273
+ * Categorizes exception messages into low-cardinality groups
274
+ * @param exceptionMessage - The exception message to categorize
275
+ * @returns A low-cardinality category string
276
+ */
277
+ categorizeExceptionMessage(exceptionMessage) {
278
+ const message = exceptionMessage.toLowerCase();
279
+ 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";
292
+ }
293
+ if (message.includes("disk") || message.includes("storage") || message.includes("file")) {
294
+ return "storage_exception";
295
+ }
296
+ if (message.includes("memory") || message.includes("out of memory")) {
297
+ return "memory_exception";
298
+ }
299
+ return "other_exception";
300
+ }
301
+ /**
302
+ * Categorizes HTTP status codes into informative descriptions
303
+ * @param statusCode - The HTTP status code
304
+ * @returns A descriptive category string
305
+ */
306
+ categorizeStatusCode(statusCode) {
307
+ if (statusCode >= 400 && statusCode < 500) {
308
+ switch (statusCode) {
309
+ case 400:
310
+ return "bad_request";
311
+ case 401:
312
+ return "unauthorized";
313
+ case 403:
314
+ return "forbidden";
315
+ case 404:
316
+ return "not_found";
317
+ case 408:
318
+ return "request_timeout";
319
+ case 413:
320
+ return "payload_too_large";
321
+ case 429:
322
+ return "too_many_requests";
323
+ default:
324
+ return "client_error_4xx";
325
+ }
326
+ }
327
+ if (statusCode >= 500 && statusCode < 600) {
328
+ switch (statusCode) {
329
+ case 500:
330
+ return "internal_server_error";
331
+ case 502:
332
+ return "bad_gateway";
333
+ case 503:
334
+ return "service_unavailable";
335
+ case 504:
336
+ return "gateway_timeout";
337
+ default:
338
+ return "server_error_5xx";
339
+ }
340
+ }
341
+ return `status_${statusCode}`;
342
+ }
343
+ /**
344
+ * Tracks retried envelopes
345
+ * @param envelopes - Number of envelopes retried
346
+ * @param retryCode - The retry code indicating the reason for retry
347
+ * @param telemetry_type - The type of telemetry being tracked
348
+ * @param exceptionMessage - Optional exception message when retryCode is CLIENT_EXCEPTION
349
+ */
350
+ countRetryItems(envelopes, retryCode, exceptionMessage) {
351
+ const counter = this.customerStatsbeatCounter;
352
+ let telemetry_type;
353
+ for (const envelope of envelopes) {
354
+ telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);
355
+ // Get or create the retryCode map for this telemetry_type
356
+ let retryCodeMap = counter.totalItemRetryCount.get(telemetry_type);
357
+ if (!retryCodeMap) {
358
+ retryCodeMap = new Map();
359
+ counter.totalItemRetryCount.set(telemetry_type, retryCodeMap);
360
+ }
361
+ // Get or create the reason map for this retryCode
362
+ let reasonMap = retryCodeMap.get(retryCode);
363
+ if (!reasonMap) {
364
+ reasonMap = new Map();
365
+ retryCodeMap.set(retryCode, reasonMap);
366
+ }
367
+ // Generate a low-cardinality, informative reason description
368
+ const reason = this.getRetryReason(retryCode, exceptionMessage);
369
+ // Update the count for this reason
370
+ const currentCount = reasonMap.get(reason) || 0;
371
+ reasonMap.set(reason, currentCount + 1);
372
+ }
373
+ }
374
+ /**
375
+ * Generates a low-cardinality, informative description for retry reasons
376
+ * @param retryCode - The retry code (enum value or status code number)
377
+ * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION
378
+ * @returns A descriptive reason string with low cardinality
379
+ */
380
+ getRetryReason(retryCode, exceptionMessage) {
381
+ if (retryCode === RetryCode.CLIENT_EXCEPTION) {
382
+ // For client exceptions, derive a low-cardinality reason from the exception message
383
+ if (exceptionMessage) {
384
+ return this.categorizeExceptionMessage(exceptionMessage);
385
+ }
386
+ return "unknown_exception";
387
+ }
388
+ // Handle status code retry codes (numeric values)
389
+ if (typeof retryCode === "number") {
390
+ return this.categorizeStatusCode(retryCode);
391
+ }
392
+ // Handle other enum retry codes
393
+ switch (retryCode) {
394
+ case RetryCode.CLIENT_TIMEOUT:
395
+ return "client_timeout";
396
+ case RetryCode.RETRYABLE_STATUS_CODE:
397
+ return "retryable_status";
398
+ case RetryCode.UNKNOWN:
399
+ default:
400
+ return "unknown_reason";
401
+ }
402
+ }
403
+ /**
404
+ * Check if a metric name corresponds to a performance counter
405
+ * @param metricName - The name of the metric to check
406
+ * @returns true if the metric name is a performance counter, false otherwise
407
+ */
408
+ isPerformanceCounterMetric(metricName) {
409
+ return Object.values(BreezePerformanceCounterNames).includes(metricName);
410
+ }
411
+ /**
412
+ * Extract telemetry type from an envelope based on its baseType
413
+ * @param envelope - The envelope to extract telemetry type from
414
+ * @returns The corresponding telemetry type
415
+ */
416
+ getTelemetryTypeFromEnvelope(envelope) {
417
+ if (envelope.data && envelope.data.baseType) {
418
+ switch (envelope.data.baseType) {
419
+ case "MessageData":
420
+ return TelemetryType.TRACE;
421
+ case "AvailabilityData":
422
+ return TelemetryType.AVAILABILITY;
423
+ case "TelemetryEventData":
424
+ return TelemetryType.CUSTOM_EVENT;
425
+ case "TelemetryExceptionData":
426
+ return TelemetryType.EXCEPTION;
427
+ case "PageViewData":
428
+ return TelemetryType.PAGE_VIEW;
429
+ case "RemoteDependencyData":
430
+ return TelemetryType.DEPENDENCY;
431
+ case "RequestData":
432
+ return TelemetryType.REQUEST;
433
+ case "MetricData": {
434
+ const metricsData = envelope.data.baseData;
435
+ if (metricsData && metricsData.metrics && metricsData.metrics.length > 0) {
436
+ // Check if any of the metrics are performance counters
437
+ const hasPerformanceCounter = metricsData.metrics.some((metric) => this.isPerformanceCounterMetric(metric.name));
438
+ return hasPerformanceCounter
439
+ ? TelemetryType.PERFORMANCE_COUNTER
440
+ : TelemetryType.CUSTOM_METRIC;
441
+ }
442
+ return TelemetryType.CUSTOM_METRIC;
443
+ }
444
+ default:
445
+ return TelemetryType.UNKNOWN;
446
+ }
447
+ }
448
+ return TelemetryType.UNKNOWN;
449
+ }
450
+ /**
451
+ * Checks if the given error is a timeout-related error
452
+ * @param error - The error to check
453
+ * @returns true if the error is timeout-related, false otherwise
454
+ */
455
+ isTimeoutError(error) {
456
+ // Check for various timeout error codes that indicate client timeouts
457
+ const timeoutErrorCodes = [
458
+ "ETIMEDOUT", // Connection timed out
459
+ "ESOCKETTIMEDOUT", // Socket timeout
460
+ "ECONNRESET", // Connection reset (often due to timeout)
461
+ "ENOTFOUND", // DNS lookup failed/timeout
462
+ ];
463
+ if (error && error.code && timeoutErrorCodes.includes(error.code)) {
464
+ return true;
465
+ }
466
+ // Also check if the error message contains timeout-related keywords
467
+ if (error && error.message) {
468
+ const timeoutKeywords = ["timeout", "timed out", "connection reset"];
469
+ const errorMessage = error.message.toLowerCase();
470
+ return timeoutKeywords.some((keyword) => errorMessage.includes(keyword));
471
+ }
472
+ return false;
473
+ }
474
+ }
475
+ //# sourceMappingURL=customerStatsbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customerStatsbeat.js","sourceRoot":"","sources":["../../../../src/export/statsbeat/customerStatsbeat.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 { CustomerStatsbeatProperties, StatsbeatOptions } from \"./types.js\";\nimport { CustomerStatsbeat, DropCode, RetryCode } from \"./types.js\";\nimport { CustomStatsbeatCounter, 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 } from \"../../generated/index.js\";\nimport type { TelemetryItem as Envelope } from \"../../generated/index.js\";\n\n/**\n * Class that handles customer-facing statsbeat 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 statsbeat metrics\n * is exported every 15 minutes, regardless of the number of exporters or senders.\n */\nexport class CustomerStatsbeatMetrics extends StatsbeatMetrics {\n private static _instance: CustomerStatsbeatMetrics | undefined;\n\n private statsCollectionInterval: number = 900000; // 15 minutes\n private customerStatsbeatMeter: Meter;\n private customerStatsbeatMeterProvider: MeterProvider;\n private customerStatsbeatExporter: AzureMonitorStatsbeatExporter;\n private customerStatsbeatCounter: CustomerStatsbeat;\n private customerStatsbeatMetricReader: 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 statsbeat properties\n private customerProperties: CustomerStatsbeatProperties;\n\n private constructor(options: StatsbeatOptions) {\n super();\n const exporterConfig: AzureMonitorExporterOptions = {\n connectionString: `InstrumentationKey=${options.instrumentationKey};IngestionEndpoint=${options.endpointUrl}`,\n };\n\n this.customerStatsbeatExporter = new AzureMonitorStatsbeatExporter(exporterConfig);\n // Exports Customer Statsbeat every 15 minutes\n const customerMetricReaderOptions: PeriodicExportingMetricReaderOptions = {\n exporter: this.customerStatsbeatExporter,\n exportIntervalMillis: options.networkCollectionInterval || this.statsCollectionInterval,\n };\n this.customerStatsbeatMetricReader = new PeriodicExportingMetricReader(\n customerMetricReaderOptions,\n );\n this.customerStatsbeatMeterProvider = new MeterProvider({\n readers: [this.customerStatsbeatMetricReader],\n });\n\n this.customerStatsbeatMeter = this.customerStatsbeatMeterProvider.getMeter(\n \"Azure Monitor Customer Statsbeat\",\n );\n\n this.language = STATSBEAT_LANGUAGE;\n this.version = ai.packageVersion;\n\n this.itemSuccessCountGauge = this.customerStatsbeatMeter.createObservableGauge(\n CustomStatsbeatCounter.ITEM_SUCCESS_COUNT,\n );\n this.itemDropCountGauge = this.customerStatsbeatMeter.createObservableGauge(\n CustomStatsbeatCounter.ITEM_DROP_COUNT,\n );\n this.itemRetryCountGauge = this.customerStatsbeatMeter.createObservableGauge(\n CustomStatsbeatCounter.ITEM_RETRY_COUNT,\n );\n\n if (!this.isInitialized) {\n this.initialize();\n }\n this.isInitialized = true;\n\n // Initialize the single customer statsbeat counter\n this.customerStatsbeatCounter = new CustomerStatsbeat();\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 CustomerStatsbeatMetrics\n * @param options - Configuration options for customer statsbeat metrics\n * @returns The singleton instance\n */\n public static getInstance(options: StatsbeatOptions): CustomerStatsbeatMetrics {\n if (!CustomerStatsbeatMetrics._instance) {\n CustomerStatsbeatMetrics._instance = new CustomerStatsbeatMetrics(options);\n }\n return CustomerStatsbeatMetrics._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 (CustomerStatsbeatMetrics._instance) {\n const shutdownPromise = CustomerStatsbeatMetrics._instance.shutdown();\n CustomerStatsbeatMetrics._instance = undefined;\n return shutdownPromise;\n }\n return undefined;\n }\n\n /**\n * Shuts down the customer statsbeat metrics provider\n * @returns Promise<void>\n */\n public shutdown(): Promise<void> {\n return this.customerStatsbeatMeterProvider.shutdown();\n }\n\n /**\n * Initializes the customer statsbeat 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.customerStatsbeatMeter.addBatchObservableCallback(this.itemSuccessCallback.bind(this), [\n this.itemSuccessCountGauge,\n ]);\n this.customerStatsbeatMeter.addBatchObservableCallback(this.itemDropCallback.bind(this), [\n this.itemDropCountGauge,\n ]);\n this.customerStatsbeatMeter.addBatchObservableCallback(this.itemRetryCallback.bind(this), [\n this.itemRetryCountGauge,\n ]);\n } catch (error) {\n diag.debug(\"Call to get the resource provider failed for customer statsbeat metrics.\");\n }\n }\n\n // Observable gauge callbacks\n private itemSuccessCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerStatsbeat = this.customerStatsbeatCounter;\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 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 private itemDropCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerStatsbeat = this.customerStatsbeatCounter;\n const baseAttributes: CustomerStatsbeatProperties & {\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 -> count\n for (const [telemetryType, dropCodeMap] of counter.totalItemDropCount.entries()) {\n for (const [dropCode, reasonMap] of dropCodeMap.entries()) {\n for (const [reason, count] of reasonMap.entries()) {\n const attributes = { ...baseAttributes };\n attributes.telemetry_type = telemetryType;\n attributes[\"drop.code\"] = dropCode;\n\n // Include drop.reason for all case\n if (reason) {\n (attributes as any)[\"drop.reason\"] = reason;\n }\n\n observableResult.observe(this.itemDropCountGauge, count, {\n ...attributes,\n });\n\n // Reset the count to 0\n reasonMap.set(reason, 0);\n }\n }\n }\n }\n\n private itemRetryCallback(observableResult: BatchObservableResult): void {\n const counter: CustomerStatsbeat = this.customerStatsbeatCounter;\n const baseAttributes: CustomerStatsbeatProperties & {\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 observableResult.observe(this.itemRetryCountGauge, count, {\n ...attributes,\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: CustomerStatsbeat = this.customerStatsbeatCounter;\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 - Number of envelopes dropped\n * @param dropCode - The drop code indicating the reason for drop\n * @param telemetry_type - The type of telemetry being tracked\n * @param exceptionMessage - Optional exception message when dropCode is CLIENT_EXCEPTION\n */\n public countDroppedItems(\n envelopes: Envelope[],\n dropCode: DropCode | number,\n exceptionMessage?: string,\n ): void {\n const counter: CustomerStatsbeat = this.customerStatsbeatCounter;\n let telemetry_type: TelemetryType;\n\n for (const envelope of envelopes) {\n telemetry_type = this.getTelemetryTypeFromEnvelope(envelope);\n // Get or create the dropCode map for this telemetry_type\n let dropCodeMap = counter.totalItemDropCount.get(telemetry_type);\n if (!dropCodeMap) {\n dropCodeMap = new Map<DropCode | number, Map<string, 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, number>();\n dropCodeMap.set(dropCode, reasonMap);\n }\n\n // Generate a low-cardinality, informative reason description\n const reason = this.getDropReason(dropCode, exceptionMessage);\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 drop reasons\n * @param dropCode - The drop code (enum value or status code number)\n * @param exceptionMessage - Optional exception message for CLIENT_EXCEPTION\n * @returns A descriptive reason string with low cardinality\n */\n private getDropReason(dropCode: DropCode | number, exceptionMessage?: string): string {\n if (dropCode === DropCode.CLIENT_EXCEPTION) {\n // For client exceptions, derive a low-cardinality reason from the exception message\n if (exceptionMessage) {\n return this.categorizeExceptionMessage(exceptionMessage);\n }\n return \"unknown_exception\";\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_EXPIRED_DATA:\n return \"expired_data\";\n case DropCode.CLIENT_READONLY:\n return \"readonly_mode\";\n case DropCode.CLIENT_STALE_DATA:\n return \"stale_data\";\n case DropCode.CLIENT_PERSISTENCE_CAPACITY:\n return \"persistence_full\";\n case DropCode.NON_RETRYABLE_STATUS_CODE:\n return \"non_retryable_status\";\n case DropCode.UNKNOWN:\n default:\n return \"unknown_reason\";\n }\n }\n\n /**\n * Categorizes exception messages into low-cardinality groups\n * @param exceptionMessage - The exception message to categorize\n * @returns A low-cardinality category string\n */\n private categorizeExceptionMessage(exceptionMessage: string): string {\n const message = exceptionMessage.toLowerCase();\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return \"timeout_exception\";\n }\n if (message.includes(\"network\") || message.includes(\"connection\")) {\n return \"network_exception\";\n }\n if (\n message.includes(\"auth\") ||\n message.includes(\"unauthorized\") ||\n message.includes(\"forbidden\")\n ) {\n return \"auth_exception\";\n }\n if (message.includes(\"parsing\") || message.includes(\"parse\") || message.includes(\"invalid\")) {\n return \"parsing_exception\";\n }\n if (message.includes(\"disk\") || message.includes(\"storage\") || message.includes(\"file\")) {\n return \"storage_exception\";\n }\n if (message.includes(\"memory\") || message.includes(\"out of memory\")) {\n return \"memory_exception\";\n }\n\n return \"other_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 telemetry_type - The type of telemetry being tracked\n * @param exceptionMessage - Optional exception message when retryCode is CLIENT_EXCEPTION\n */\n public countRetryItems(\n envelopes: Envelope[],\n retryCode: RetryCode | number,\n exceptionMessage?: string,\n ): void {\n const counter: CustomerStatsbeat = this.customerStatsbeatCounter;\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);\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 * @returns A descriptive reason string with low cardinality\n */\n private getRetryReason(retryCode: RetryCode | number, exceptionMessage?: string): string {\n if (retryCode === RetryCode.CLIENT_EXCEPTION) {\n // For client exceptions, derive a low-cardinality reason from the exception message\n if (exceptionMessage) {\n return this.categorizeExceptionMessage(exceptionMessage);\n }\n return \"unknown_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 \"client_timeout\";\n case RetryCode.RETRYABLE_STATUS_CODE:\n return \"retryable_status\";\n case RetryCode.UNKNOWN:\n default:\n return \"unknown_reason\";\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 * 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"]}
@@ -13,13 +13,30 @@ import { getAttachType } from "../../utils/metricUtils.js";
13
13
  * @internal
14
14
  */
15
15
  export class LongIntervalStatsbeatMetrics extends StatsbeatMetrics {
16
+ static instance = null;
17
+ statsCollectionLongInterval = 86400000; // 1 day
18
+ // Custom dimensions
19
+ cikey;
20
+ runtimeVersion;
21
+ language;
22
+ version;
23
+ attach = getAttachType();
24
+ commonProperties;
25
+ attachProperties;
26
+ feature = 0;
27
+ instrumentation = 0;
28
+ longIntervalStatsbeatMeterProvider;
29
+ longIntervalAzureExporter;
30
+ longIntervalMetricReader;
31
+ longIntervalStatsbeatMeter;
32
+ // Network Attributes
33
+ connectionString;
34
+ // Observable Gauges
35
+ featureStatsbeatGauge;
36
+ attachStatsbeatGauge;
37
+ isInitialized = false;
16
38
  constructor(options) {
17
39
  super();
18
- this.statsCollectionLongInterval = 86400000; // 1 day
19
- this.attach = getAttachType();
20
- this.feature = 0;
21
- this.instrumentation = 0;
22
- this.isInitialized = false;
23
40
  this.connectionString = super.getConnectionString(options.endpointUrl);
24
41
  const exporterConfig = {
25
42
  connectionString: this.connectionString,
@@ -94,13 +111,21 @@ export class LongIntervalStatsbeatMetrics extends StatsbeatMetrics {
94
111
  let attributes;
95
112
  // Only send instrumentation statsbeat if value is greater than zero
96
113
  if (this.instrumentation > 0) {
97
- attributes = Object.assign(Object.assign({}, this.commonProperties), { feature: this.instrumentation, type: StatsbeatFeatureType.INSTRUMENTATION });
98
- observableResult.observe(this.featureStatsbeatGauge, 1, Object.assign({}, attributes));
114
+ attributes = {
115
+ ...this.commonProperties,
116
+ feature: this.instrumentation,
117
+ type: StatsbeatFeatureType.INSTRUMENTATION,
118
+ };
119
+ observableResult.observe(this.featureStatsbeatGauge, 1, { ...attributes });
99
120
  }
100
121
  // Only send feature statsbeat if value is greater than zero
101
122
  if (this.feature > 0) {
102
- attributes = Object.assign(Object.assign({}, this.commonProperties), { feature: this.feature, type: StatsbeatFeatureType.FEATURE });
103
- observableResult.observe(this.featureStatsbeatGauge, 1, Object.assign({}, attributes));
123
+ attributes = {
124
+ ...this.commonProperties,
125
+ feature: this.feature,
126
+ type: StatsbeatFeatureType.FEATURE,
127
+ };
128
+ observableResult.observe(this.featureStatsbeatGauge, 1, { ...attributes });
104
129
  }
105
130
  }
106
131
  setFeatures() {
@@ -116,7 +141,7 @@ export class LongIntervalStatsbeatMetrics extends StatsbeatMetrics {
116
141
  }
117
142
  }
118
143
  attachCallback(observableResult) {
119
- const attributes = Object.assign(Object.assign({}, this.commonProperties), this.attachProperties);
144
+ const attributes = { ...this.commonProperties, ...this.attachProperties };
120
145
  observableResult.observe(1, attributes);
121
146
  }
122
147
  shutdown() {
@@ -133,5 +158,4 @@ export class LongIntervalStatsbeatMetrics extends StatsbeatMetrics {
133
158
  return LongIntervalStatsbeatMetrics.instance;
134
159
  }
135
160
  }
136
- LongIntervalStatsbeatMetrics.instance = null;
137
161
  //# sourceMappingURL=longIntervalStatsbeatMetrics.js.map