@vtex/api 7.3.0 → 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [7.3.1] - 2026-01-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- OpenTelemetry context-based base attributes to DiagnosticsMetrics
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Merge request context attributes with custom metrics - custom metrics now automatically include request-scoped base attributes (account, route_id, status_code, etc.) using OpenTelemetry Context API
|
|
17
|
+
|
|
18
|
+
## [7.3.0] - 2025-12-16
|
|
19
|
+
### Changed
|
|
20
|
+
- Update temporality to delta in metrics client
|
|
21
|
+
- New deploy pipeline with npm publish flow
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Fix account attribute to diagnostics metrics
|
|
25
|
+
|
|
10
26
|
## [7.2.9-beta.1] - 2025-12-12
|
|
11
27
|
### Changed
|
|
12
28
|
- Increase maximum attributes limit for diagnostics metrics
|
|
@@ -7,6 +7,21 @@ import { Attributes } from '@opentelemetry/api';
|
|
|
7
7
|
* Uses a single histogram for all latency measurements with attributes to differentiate.
|
|
8
8
|
* This follows OpenTelemetry best practices and reduces metric cardinality.
|
|
9
9
|
*
|
|
10
|
+
* ## Base Attributes (Request Context)
|
|
11
|
+
*
|
|
12
|
+
* DiagnosticsMetrics supports automatic merging of request-scoped "base attributes"
|
|
13
|
+
* with custom attributes provided in each metric call. This is useful for automatically
|
|
14
|
+
* including request context (account, status_code, route_id, etc.) in all metrics
|
|
15
|
+
* recorded during a request lifecycle.
|
|
16
|
+
*
|
|
17
|
+
* Use `runWithBaseAttributes()` to set base attributes for a scope. All metric calls
|
|
18
|
+
* within that scope will automatically include these base attributes, merged with
|
|
19
|
+
* any custom attributes provided.
|
|
20
|
+
*
|
|
21
|
+
* **Important:** Base attributes take precedence over custom attributes. If a custom
|
|
22
|
+
* attribute has the same key as a base attribute, the custom attribute is silently
|
|
23
|
+
* dropped and the base attribute value is used.
|
|
24
|
+
*
|
|
10
25
|
* @example
|
|
11
26
|
* ```typescript
|
|
12
27
|
* const diagnosticsMetrics = new DiagnosticsMetrics()
|
|
@@ -25,6 +40,16 @@ import { Attributes } from '@opentelemetry/api';
|
|
|
25
40
|
*
|
|
26
41
|
* // Set a gauge value
|
|
27
42
|
* diagnosticsMetrics.setGauge('cache_items_current', 1024, { cache: 'pages' })
|
|
43
|
+
*
|
|
44
|
+
* // Using base attributes for request context
|
|
45
|
+
* await diagnosticsMetrics.runWithBaseAttributes(
|
|
46
|
+
* { 'vtex.account.name': 'mystore', status_code: 200 },
|
|
47
|
+
* async () => {
|
|
48
|
+
* // All metrics recorded here will include the base attributes
|
|
49
|
+
* diagnosticsMetrics.recordLatency(100, { operation: 'custom-op' })
|
|
50
|
+
* // Result: { 'vtex.account.name': 'mystore', status_code: 200, operation: 'custom-op' }
|
|
51
|
+
* }
|
|
52
|
+
* )
|
|
28
53
|
* ```
|
|
29
54
|
*/
|
|
30
55
|
export declare class DiagnosticsMetrics {
|
|
@@ -44,13 +69,69 @@ export declare class DiagnosticsMetrics {
|
|
|
44
69
|
* Called after metric client is initialized.
|
|
45
70
|
*/
|
|
46
71
|
private createLatencyHistogram;
|
|
72
|
+
/**
|
|
73
|
+
* Execute a function with base attributes set in the OpenTelemetry context.
|
|
74
|
+
* All metric calls within the function will automatically include these base attributes,
|
|
75
|
+
* merged with any custom attributes provided in each call.
|
|
76
|
+
*
|
|
77
|
+
* Base attributes take precedence over custom attributes when there are key conflicts.
|
|
78
|
+
* Conflicting custom attributes are silently dropped.
|
|
79
|
+
*
|
|
80
|
+
* @param baseAttributes Base attributes to include in all metrics within the scope
|
|
81
|
+
* @param fn Function to execute with the base attributes context
|
|
82
|
+
* @returns The return value of the function
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* // In a request middleware
|
|
87
|
+
* await diagnosticsMetrics.runWithBaseAttributes(
|
|
88
|
+
* {
|
|
89
|
+
* 'vtex.account.name': ctx.vtex.account,
|
|
90
|
+
* status_code: ctx.status,
|
|
91
|
+
* route_id: ctx.vtex.route.id,
|
|
92
|
+
* },
|
|
93
|
+
* async () => {
|
|
94
|
+
* await next()
|
|
95
|
+
* }
|
|
96
|
+
* )
|
|
97
|
+
*
|
|
98
|
+
* // In app code (custom attributes are merged with base)
|
|
99
|
+
* diagnosticsMetrics.recordLatency(elapsed, { operation: 'my-operation', status: 'success' })
|
|
100
|
+
* // Result includes both base attributes AND custom attributes
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
runWithBaseAttributes<T>(baseAttributes: Attributes, fn: () => T): T;
|
|
104
|
+
/**
|
|
105
|
+
* Get the base attributes from the current OpenTelemetry context.
|
|
106
|
+
* Returns undefined if no base attributes are set.
|
|
107
|
+
*/
|
|
108
|
+
private getBaseAttributes;
|
|
109
|
+
/**
|
|
110
|
+
* Merge base attributes from context with provided custom attributes.
|
|
111
|
+
* Base attributes take precedence over custom attributes when there are key conflicts.
|
|
112
|
+
*
|
|
113
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES before merging.
|
|
114
|
+
* Custom attributes with keys that conflict with base attributes are silently dropped.
|
|
115
|
+
* Base attributes are not limited.
|
|
116
|
+
*
|
|
117
|
+
* @param customAttributes Custom attributes provided by the caller
|
|
118
|
+
* @returns Merged attributes (base + non-conflicting limited custom) or undefined if both are empty
|
|
119
|
+
*/
|
|
120
|
+
private mergeAttributes;
|
|
47
121
|
/**
|
|
48
122
|
* Record a latency measurement using the single shared histogram.
|
|
49
123
|
* Accepts either an hrtime tuple from process.hrtime() or milliseconds as a number.
|
|
50
124
|
* Use attributes to differentiate between different operations.
|
|
51
125
|
*
|
|
126
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
127
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
128
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
129
|
+
* the custom attribute is silently dropped.
|
|
130
|
+
*
|
|
131
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
132
|
+
*
|
|
52
133
|
* @param value Either [seconds, nanoseconds] from process.hrtime() or milliseconds
|
|
53
|
-
* @param attributes
|
|
134
|
+
* @param attributes Custom attributes including 'operation' to identify the operation type (max 5 custom attributes)
|
|
54
135
|
*
|
|
55
136
|
* @example
|
|
56
137
|
* ```typescript
|
|
@@ -67,9 +148,16 @@ export declare class DiagnosticsMetrics {
|
|
|
67
148
|
* Increment a counter by a specific value.
|
|
68
149
|
* Multiple counters are stored by name since counters represent different types of events.
|
|
69
150
|
*
|
|
151
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
152
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
153
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
154
|
+
* the custom attribute is silently dropped.
|
|
155
|
+
*
|
|
156
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
157
|
+
*
|
|
70
158
|
* @param name Counter name (e.g., 'http_requests_total', 'cache_hits_total')
|
|
71
159
|
* @param value Amount to increment by (typically 1)
|
|
72
|
-
* @param attributes Optional attributes for the counter (max
|
|
160
|
+
* @param attributes Optional custom attributes for the counter (max 5 custom attributes, e.g., { method: 'GET', status: '2xx' })
|
|
73
161
|
*
|
|
74
162
|
* @example
|
|
75
163
|
* ```typescript
|
|
@@ -81,9 +169,16 @@ export declare class DiagnosticsMetrics {
|
|
|
81
169
|
* Set a gauge to a specific value (current state).
|
|
82
170
|
* Multiple gauges are stored by name since gauges represent different types of measurements.
|
|
83
171
|
*
|
|
172
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
173
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
174
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
175
|
+
* the custom attribute is silently dropped.
|
|
176
|
+
*
|
|
177
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
178
|
+
*
|
|
84
179
|
* @param name Gauge name (e.g., 'cache_items_current', 'memory_usage_bytes')
|
|
85
180
|
* @param value Current value
|
|
86
|
-
* @param attributes Optional attributes for the gauge (max
|
|
181
|
+
* @param attributes Optional custom attributes for the gauge (max 5 custom attributes, e.g., { cache: 'pages' })
|
|
87
182
|
*
|
|
88
183
|
* @example
|
|
89
184
|
* ```typescript
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DiagnosticsMetrics = void 0;
|
|
4
|
+
const api_1 = require("@opentelemetry/api");
|
|
4
5
|
const client_1 = require("../service/metrics/client");
|
|
5
6
|
const constants_1 = require("../constants");
|
|
6
7
|
/**
|
|
7
|
-
* Maximum number of attributes allowed per metric to control cardinality.
|
|
8
|
-
* This limit applies to
|
|
8
|
+
* Maximum number of custom attributes allowed per metric call to control cardinality.
|
|
9
|
+
* This limit applies only to custom attributes provided by callers (VTEX IO Apps).
|
|
10
|
+
* Base attributes (set via runWithBaseAttributes) are not counted toward this limit.
|
|
11
|
+
*
|
|
12
|
+
* Total attributes sent = base attributes + custom attributes (up to MAX_CUSTOM_ATTRIBUTES)
|
|
13
|
+
*/
|
|
14
|
+
const MAX_CUSTOM_ATTRIBUTES = 7;
|
|
15
|
+
/**
|
|
16
|
+
* Context key for storing base attributes in OpenTelemetry context.
|
|
17
|
+
* These attributes are automatically merged with custom attributes in all metric methods.
|
|
9
18
|
*/
|
|
10
|
-
const
|
|
19
|
+
const BASE_ATTRIBUTES_KEY = (0, api_1.createContextKey)('vtex.metrics.baseAttributes');
|
|
11
20
|
/**
|
|
12
21
|
* Converts an hrtime tuple [seconds, nanoseconds] to milliseconds.
|
|
13
22
|
*/
|
|
@@ -15,25 +24,27 @@ function hrtimeToMillis(hrtime) {
|
|
|
15
24
|
return (hrtime[0] * 1e3) + (hrtime[1] / 1e6);
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
|
-
* Limits the number of attributes to prevent high cardinality.
|
|
19
|
-
* Takes the first
|
|
27
|
+
* Limits the number of custom attributes to prevent high cardinality.
|
|
28
|
+
* Takes the first MAX_CUSTOM_ATTRIBUTES entries if the limit is exceeded.
|
|
20
29
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
30
|
+
* Note: This limit applies only to custom attributes. Base attributes are not limited.
|
|
31
|
+
*
|
|
32
|
+
* @param customAttributes Optional custom attributes object
|
|
33
|
+
* @returns Limited custom attributes object or undefined
|
|
23
34
|
*/
|
|
24
|
-
function
|
|
25
|
-
if (!
|
|
35
|
+
function limitCustomAttributes(customAttributes) {
|
|
36
|
+
if (!customAttributes) {
|
|
26
37
|
return undefined;
|
|
27
38
|
}
|
|
28
|
-
const entries = Object.entries(
|
|
29
|
-
if (entries.length <=
|
|
30
|
-
return
|
|
39
|
+
const entries = Object.entries(customAttributes);
|
|
40
|
+
if (entries.length <= MAX_CUSTOM_ATTRIBUTES) {
|
|
41
|
+
return customAttributes;
|
|
31
42
|
}
|
|
32
43
|
if (constants_1.LINKED) {
|
|
33
|
-
console.warn(`
|
|
34
|
-
`Consider reducing the number of attributes to avoid high cardinality
|
|
44
|
+
console.warn(`Custom attribute limit exceeded: ${entries.length} custom attributes provided, using only the first ${MAX_CUSTOM_ATTRIBUTES}. ` +
|
|
45
|
+
`Consider reducing the number of custom attributes to avoid high cardinality. `);
|
|
35
46
|
}
|
|
36
|
-
return Object.fromEntries(entries.slice(0,
|
|
47
|
+
return Object.fromEntries(entries.slice(0, MAX_CUSTOM_ATTRIBUTES));
|
|
37
48
|
}
|
|
38
49
|
/**
|
|
39
50
|
* DiagnosticsMetrics provides a high-level API for recording metrics using
|
|
@@ -43,6 +54,21 @@ function limitAttributes(attributes) {
|
|
|
43
54
|
* Uses a single histogram for all latency measurements with attributes to differentiate.
|
|
44
55
|
* This follows OpenTelemetry best practices and reduces metric cardinality.
|
|
45
56
|
*
|
|
57
|
+
* ## Base Attributes (Request Context)
|
|
58
|
+
*
|
|
59
|
+
* DiagnosticsMetrics supports automatic merging of request-scoped "base attributes"
|
|
60
|
+
* with custom attributes provided in each metric call. This is useful for automatically
|
|
61
|
+
* including request context (account, status_code, route_id, etc.) in all metrics
|
|
62
|
+
* recorded during a request lifecycle.
|
|
63
|
+
*
|
|
64
|
+
* Use `runWithBaseAttributes()` to set base attributes for a scope. All metric calls
|
|
65
|
+
* within that scope will automatically include these base attributes, merged with
|
|
66
|
+
* any custom attributes provided.
|
|
67
|
+
*
|
|
68
|
+
* **Important:** Base attributes take precedence over custom attributes. If a custom
|
|
69
|
+
* attribute has the same key as a base attribute, the custom attribute is silently
|
|
70
|
+
* dropped and the base attribute value is used.
|
|
71
|
+
*
|
|
46
72
|
* @example
|
|
47
73
|
* ```typescript
|
|
48
74
|
* const diagnosticsMetrics = new DiagnosticsMetrics()
|
|
@@ -61,6 +87,16 @@ function limitAttributes(attributes) {
|
|
|
61
87
|
*
|
|
62
88
|
* // Set a gauge value
|
|
63
89
|
* diagnosticsMetrics.setGauge('cache_items_current', 1024, { cache: 'pages' })
|
|
90
|
+
*
|
|
91
|
+
* // Using base attributes for request context
|
|
92
|
+
* await diagnosticsMetrics.runWithBaseAttributes(
|
|
93
|
+
* { 'vtex.account.name': 'mystore', status_code: 200 },
|
|
94
|
+
* async () => {
|
|
95
|
+
* // All metrics recorded here will include the base attributes
|
|
96
|
+
* diagnosticsMetrics.recordLatency(100, { operation: 'custom-op' })
|
|
97
|
+
* // Result: { 'vtex.account.name': 'mystore', status_code: 200, operation: 'custom-op' }
|
|
98
|
+
* }
|
|
99
|
+
* )
|
|
64
100
|
* ```
|
|
65
101
|
*/
|
|
66
102
|
class DiagnosticsMetrics {
|
|
@@ -110,13 +146,99 @@ class DiagnosticsMetrics {
|
|
|
110
146
|
unit: 'ms',
|
|
111
147
|
});
|
|
112
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Execute a function with base attributes set in the OpenTelemetry context.
|
|
151
|
+
* All metric calls within the function will automatically include these base attributes,
|
|
152
|
+
* merged with any custom attributes provided in each call.
|
|
153
|
+
*
|
|
154
|
+
* Base attributes take precedence over custom attributes when there are key conflicts.
|
|
155
|
+
* Conflicting custom attributes are silently dropped.
|
|
156
|
+
*
|
|
157
|
+
* @param baseAttributes Base attributes to include in all metrics within the scope
|
|
158
|
+
* @param fn Function to execute with the base attributes context
|
|
159
|
+
* @returns The return value of the function
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // In a request middleware
|
|
164
|
+
* await diagnosticsMetrics.runWithBaseAttributes(
|
|
165
|
+
* {
|
|
166
|
+
* 'vtex.account.name': ctx.vtex.account,
|
|
167
|
+
* status_code: ctx.status,
|
|
168
|
+
* route_id: ctx.vtex.route.id,
|
|
169
|
+
* },
|
|
170
|
+
* async () => {
|
|
171
|
+
* await next()
|
|
172
|
+
* }
|
|
173
|
+
* )
|
|
174
|
+
*
|
|
175
|
+
* // In app code (custom attributes are merged with base)
|
|
176
|
+
* diagnosticsMetrics.recordLatency(elapsed, { operation: 'my-operation', status: 'success' })
|
|
177
|
+
* // Result includes both base attributes AND custom attributes
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
runWithBaseAttributes(baseAttributes, fn) {
|
|
181
|
+
const currentContext = api_1.context.active();
|
|
182
|
+
const newContext = currentContext.setValue(BASE_ATTRIBUTES_KEY, baseAttributes);
|
|
183
|
+
return api_1.context.with(newContext, fn);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get the base attributes from the current OpenTelemetry context.
|
|
187
|
+
* Returns undefined if no base attributes are set.
|
|
188
|
+
*/
|
|
189
|
+
getBaseAttributes() {
|
|
190
|
+
return api_1.context.active().getValue(BASE_ATTRIBUTES_KEY);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Merge base attributes from context with provided custom attributes.
|
|
194
|
+
* Base attributes take precedence over custom attributes when there are key conflicts.
|
|
195
|
+
*
|
|
196
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES before merging.
|
|
197
|
+
* Custom attributes with keys that conflict with base attributes are silently dropped.
|
|
198
|
+
* Base attributes are not limited.
|
|
199
|
+
*
|
|
200
|
+
* @param customAttributes Custom attributes provided by the caller
|
|
201
|
+
* @returns Merged attributes (base + non-conflicting limited custom) or undefined if both are empty
|
|
202
|
+
*/
|
|
203
|
+
mergeAttributes(customAttributes) {
|
|
204
|
+
const baseAttributes = this.getBaseAttributes();
|
|
205
|
+
// Limit custom attributes before merging
|
|
206
|
+
const limitedCustomAttributes = limitCustomAttributes(customAttributes);
|
|
207
|
+
if (!baseAttributes && !limitedCustomAttributes) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (!baseAttributes) {
|
|
211
|
+
return limitedCustomAttributes;
|
|
212
|
+
}
|
|
213
|
+
if (!limitedCustomAttributes) {
|
|
214
|
+
return baseAttributes;
|
|
215
|
+
}
|
|
216
|
+
// Filter out custom attributes that conflict with base attributes (base takes precedence)
|
|
217
|
+
const baseKeys = new Set(Object.keys(baseAttributes));
|
|
218
|
+
const nonConflictingCustomAttributes = {};
|
|
219
|
+
for (const [key, value] of Object.entries(limitedCustomAttributes)) {
|
|
220
|
+
if (!baseKeys.has(key)) {
|
|
221
|
+
nonConflictingCustomAttributes[key] = value;
|
|
222
|
+
}
|
|
223
|
+
// Silently drop conflicting custom attributes - base attributes take precedence
|
|
224
|
+
}
|
|
225
|
+
// Merge: base attributes + non-conflicting custom attributes
|
|
226
|
+
return { ...baseAttributes, ...nonConflictingCustomAttributes };
|
|
227
|
+
}
|
|
113
228
|
/**
|
|
114
229
|
* Record a latency measurement using the single shared histogram.
|
|
115
230
|
* Accepts either an hrtime tuple from process.hrtime() or milliseconds as a number.
|
|
116
231
|
* Use attributes to differentiate between different operations.
|
|
117
232
|
*
|
|
233
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
234
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
235
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
236
|
+
* the custom attribute is silently dropped.
|
|
237
|
+
*
|
|
238
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
239
|
+
*
|
|
118
240
|
* @param value Either [seconds, nanoseconds] from process.hrtime() or milliseconds
|
|
119
|
-
* @param attributes
|
|
241
|
+
* @param attributes Custom attributes including 'operation' to identify the operation type (max 5 custom attributes)
|
|
120
242
|
*
|
|
121
243
|
* @example
|
|
122
244
|
* ```typescript
|
|
@@ -135,18 +257,25 @@ class DiagnosticsMetrics {
|
|
|
135
257
|
}
|
|
136
258
|
// Convert hrtime to milliseconds if needed
|
|
137
259
|
const milliseconds = Array.isArray(value) ? hrtimeToMillis(value) : value;
|
|
138
|
-
//
|
|
139
|
-
const
|
|
140
|
-
// Record to the single shared histogram with attributes
|
|
141
|
-
this.latencyHistogram.record(milliseconds,
|
|
260
|
+
// Merge base attributes from context with custom attributes (custom attrs are limited internally)
|
|
261
|
+
const mergedAttributes = this.mergeAttributes(attributes);
|
|
262
|
+
// Record to the single shared histogram with merged attributes
|
|
263
|
+
this.latencyHistogram.record(milliseconds, mergedAttributes);
|
|
142
264
|
}
|
|
143
265
|
/**
|
|
144
266
|
* Increment a counter by a specific value.
|
|
145
267
|
* Multiple counters are stored by name since counters represent different types of events.
|
|
146
268
|
*
|
|
269
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
270
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
271
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
272
|
+
* the custom attribute is silently dropped.
|
|
273
|
+
*
|
|
274
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
275
|
+
*
|
|
147
276
|
* @param name Counter name (e.g., 'http_requests_total', 'cache_hits_total')
|
|
148
277
|
* @param value Amount to increment by (typically 1)
|
|
149
|
-
* @param attributes Optional attributes for the counter (max
|
|
278
|
+
* @param attributes Optional custom attributes for the counter (max 5 custom attributes, e.g., { method: 'GET', status: '2xx' })
|
|
150
279
|
*
|
|
151
280
|
* @example
|
|
152
281
|
* ```typescript
|
|
@@ -166,18 +295,25 @@ class DiagnosticsMetrics {
|
|
|
166
295
|
});
|
|
167
296
|
this.counters.set(name, counter);
|
|
168
297
|
}
|
|
169
|
-
//
|
|
170
|
-
const
|
|
298
|
+
// Merge base attributes from context with custom attributes (custom attrs are limited internally)
|
|
299
|
+
const mergedAttributes = this.mergeAttributes(attributes);
|
|
171
300
|
// Increment the counter
|
|
172
|
-
this.counters.get(name).add(value,
|
|
301
|
+
this.counters.get(name).add(value, mergedAttributes);
|
|
173
302
|
}
|
|
174
303
|
/**
|
|
175
304
|
* Set a gauge to a specific value (current state).
|
|
176
305
|
* Multiple gauges are stored by name since gauges represent different types of measurements.
|
|
177
306
|
*
|
|
307
|
+
* Base attributes from the current context (set via `runWithBaseAttributes`) are
|
|
308
|
+
* automatically merged with the provided custom attributes. Base attributes take
|
|
309
|
+
* precedence - if a custom attribute key conflicts with a base attribute key,
|
|
310
|
+
* the custom attribute is silently dropped.
|
|
311
|
+
*
|
|
312
|
+
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
|
|
313
|
+
*
|
|
178
314
|
* @param name Gauge name (e.g., 'cache_items_current', 'memory_usage_bytes')
|
|
179
315
|
* @param value Current value
|
|
180
|
-
* @param attributes Optional attributes for the gauge (max
|
|
316
|
+
* @param attributes Optional custom attributes for the gauge (max 5 custom attributes, e.g., { cache: 'pages' })
|
|
181
317
|
*
|
|
182
318
|
* @example
|
|
183
319
|
* ```typescript
|
|
@@ -197,10 +333,10 @@ class DiagnosticsMetrics {
|
|
|
197
333
|
});
|
|
198
334
|
this.gauges.set(name, gauge);
|
|
199
335
|
}
|
|
200
|
-
//
|
|
201
|
-
const
|
|
336
|
+
// Merge base attributes from context with custom attributes (custom attrs are limited internally)
|
|
337
|
+
const mergedAttributes = this.mergeAttributes(attributes);
|
|
202
338
|
// Set the gauge value
|
|
203
|
-
this.gauges.get(name).set(value,
|
|
339
|
+
this.gauges.get(name).set(value, mergedAttributes);
|
|
204
340
|
}
|
|
205
341
|
}
|
|
206
342
|
exports.DiagnosticsMetrics = DiagnosticsMetrics;
|
|
@@ -33,33 +33,54 @@ const logBillingInfo = ({ account, workspace, production, route: { id, type } },
|
|
|
33
33
|
'workspace': workspace,
|
|
34
34
|
});
|
|
35
35
|
async function timings(ctx, next) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
36
|
+
const { vtex: { route: { id, type } }, vtex } = ctx;
|
|
37
|
+
// Set base attributes for all metrics recorded during this request.
|
|
38
|
+
// This includes metrics recorded by VTEX IO apps during handler execution.
|
|
39
|
+
// These attributes will be automatically merged with custom attributes.
|
|
40
|
+
const baseAttributes = {
|
|
41
|
+
[constants_1.AttributeKeys.VTEX_ACCOUNT_NAME]: vtex.account,
|
|
42
|
+
component: 'http-handler',
|
|
43
|
+
route_id: id,
|
|
44
|
+
route_type: type,
|
|
45
|
+
};
|
|
46
|
+
// Wrap the request handling with base attributes context.
|
|
47
|
+
// All metrics recorded during next() will automatically include these attributes.
|
|
48
|
+
const executeWithBaseAttributes = async () => {
|
|
49
|
+
// Errors will be caught by the next middleware so we don't have to catch.
|
|
50
|
+
await next();
|
|
51
|
+
const { status: statusCode, timings: { total } } = ctx;
|
|
52
|
+
const totalMillis = (0, time_1.hrToMillis)(total);
|
|
53
|
+
console.log(log(ctx, totalMillis));
|
|
54
|
+
console.log(logBillingInfo(vtex, totalMillis));
|
|
55
|
+
const status = (0, status_1.statusLabel)(statusCode);
|
|
56
|
+
// Legacy metrics (backward compatibility)
|
|
57
|
+
// Only batch successful responses so metrics don't consider errors
|
|
58
|
+
metrics.batch(`http-handler-${id}`, status === 'success' ? total : undefined, { [status]: 1 });
|
|
59
|
+
// New diagnostics metrics with stable names and attributes
|
|
60
|
+
// Note: base attributes (account, route_id, route_type) are automatically merged
|
|
61
|
+
// We only need to provide the response-specific attributes here
|
|
62
|
+
if (global.diagnosticsMetrics) {
|
|
63
|
+
const responseAttributes = {
|
|
64
|
+
status_code: statusCode,
|
|
65
|
+
status,
|
|
66
|
+
};
|
|
67
|
+
// Record latency histogram (record all requests, not just successful ones)
|
|
68
|
+
global.diagnosticsMetrics.recordLatency(total, responseAttributes);
|
|
69
|
+
// Increment counter (status is an attribute, not in metric name)
|
|
70
|
+
global.diagnosticsMetrics.incrementCounter('http_handler_requests_total', 1, responseAttributes);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.warn('DiagnosticsMetrics not available. HTTP handler metrics not reported.');
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// If diagnosticsMetrics is available, run with base attributes context
|
|
77
|
+
// Otherwise, run without context (fallback for graceful degradation)
|
|
47
78
|
if (global.diagnosticsMetrics) {
|
|
48
|
-
|
|
49
|
-
[constants_1.AttributeKeys.VTEX_ACCOUNT_NAME]: vtex.account,
|
|
50
|
-
component: 'http-handler',
|
|
51
|
-
route_id: id,
|
|
52
|
-
route_type: type,
|
|
53
|
-
status_code: statusCode,
|
|
54
|
-
status,
|
|
55
|
-
};
|
|
56
|
-
// Record latency histogram (record all requests, not just successful ones)
|
|
57
|
-
global.diagnosticsMetrics.recordLatency(total, attributes);
|
|
58
|
-
// Increment counter (status is an attribute, not in metric name)
|
|
59
|
-
global.diagnosticsMetrics.incrementCounter('http_handler_requests_total', 1, attributes);
|
|
79
|
+
await global.diagnosticsMetrics.runWithBaseAttributes(baseAttributes, executeWithBaseAttributes);
|
|
60
80
|
}
|
|
61
81
|
else {
|
|
62
82
|
console.warn('DiagnosticsMetrics not available. HTTP handler metrics not reported.');
|
|
83
|
+
await executeWithBaseAttributes();
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
86
|
exports.timings = timings;
|