@vtex/api
Version:
VTEX I/O API client
190 lines (189 loc) • 8.4 kB
TypeScript
import { Attributes } from '@opentelemetry/api';
/**
* DiagnosticsMetrics provides a high-level API for recording metrics using
* the @vtex/diagnostics-nodejs library. It completely abstracts instrument
* management, bucket configuration, and lifecycle.
*
* Uses a single histogram for all latency measurements with attributes to differentiate.
* This follows OpenTelemetry best practices and reduces metric cardinality.
*
* ## Base Attributes (Request Context)
*
* DiagnosticsMetrics supports automatic merging of request-scoped "base attributes"
* with custom attributes provided in each metric call. This is useful for automatically
* including request context (account, status_code, route_id, etc.) in all metrics
* recorded during a request lifecycle.
*
* Use `runWithBaseAttributes()` to set base attributes for a scope. All metric calls
* within that scope will automatically include these base attributes, merged with
* any custom attributes provided.
*
* **Important:** Base attributes take precedence over custom attributes. If a custom
* attribute has the same key as a base attribute, the custom attribute is silently
* dropped and the base attribute value is used.
*
* @example
* ```typescript
* const diagnosticsMetrics = new DiagnosticsMetrics()
* diagnosticsMetrics.initMetricClient()
*
* // Record latency with operation type in attributes
* const start = process.hrtime()
* // ... do work ...
* diagnosticsMetrics.recordLatency(process.hrtime(start), { operation: 'api-call', status: '2xx' })
*
* // Or from milliseconds
* diagnosticsMetrics.recordLatency(42.5, { operation: 'db-query', status: 'success' })
*
* // Increment a counter
* diagnosticsMetrics.incrementCounter('http_requests_total', 1, { method: 'GET', status: '2xx' })
*
* // Set a gauge value
* diagnosticsMetrics.setGauge('cache_items_current', 1024, { cache: 'pages' })
*
* // Using base attributes for request context
* await diagnosticsMetrics.runWithBaseAttributes(
* { 'vtex.account.name': 'mystore', status_code: 200 },
* async () => {
* // All metrics recorded here will include the base attributes
* diagnosticsMetrics.recordLatency(100, { operation: 'custom-op' })
* // Result: { 'vtex.account.name': 'mystore', status_code: 200, operation: 'custom-op' }
* }
* )
* ```
*/
export declare class DiagnosticsMetrics {
private metricsClient;
private clientInitPromise;
private latencyHistogram;
private counters;
private gauges;
constructor();
/**
* Initialize the metrics client with timeout handling.
* Called automatically in constructor.
*/
private initMetricClient;
/**
* Create the single shared histogram for all latency measurements.
* Called after metric client is initialized.
*/
private createLatencyHistogram;
/**
* Execute a function with base attributes set in the OpenTelemetry context.
* All metric calls within the function will automatically include these base attributes,
* merged with any custom attributes provided in each call.
*
* Base attributes take precedence over custom attributes when there are key conflicts.
* Conflicting custom attributes are silently dropped.
*
* @param baseAttributes Base attributes to include in all metrics within the scope
* @param fn Function to execute with the base attributes context
* @returns The return value of the function
*
* @example
* ```typescript
* // In a request middleware
* await diagnosticsMetrics.runWithBaseAttributes(
* {
* 'vtex.account.name': ctx.vtex.account,
* status_code: ctx.status,
* route_id: ctx.vtex.route.id,
* },
* async () => {
* await next()
* }
* )
*
* // In app code (custom attributes are merged with base)
* diagnosticsMetrics.recordLatency(elapsed, { operation: 'my-operation', status: 'success' })
* // Result includes both base attributes AND custom attributes
* ```
*/
runWithBaseAttributes<T>(baseAttributes: Attributes, fn: () => T): T;
/**
* Get the base attributes from the current OpenTelemetry context.
* Returns undefined if no base attributes are set.
*/
private getBaseAttributes;
/**
* Merge base attributes from context with provided custom attributes.
* Base attributes take precedence over custom attributes when there are key conflicts.
*
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES before merging.
* Custom attributes with keys that conflict with base attributes are silently dropped.
* Base attributes are not limited.
*
* @param customAttributes Custom attributes provided by the caller
* @returns Merged attributes (base + non-conflicting limited custom) or undefined if both are empty
*/
private mergeAttributes;
/**
* Record a latency measurement using the single shared histogram.
* Accepts either an hrtime tuple from process.hrtime() or milliseconds as a number.
* Use attributes to differentiate between different operations.
*
* Base attributes from the current context (set via `runWithBaseAttributes`) are
* automatically merged with the provided custom attributes. Base attributes take
* precedence - if a custom attribute key conflicts with a base attribute key,
* the custom attribute is silently dropped.
*
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
*
* @param value Either [seconds, nanoseconds] from process.hrtime() or milliseconds
* @param attributes Custom attributes including 'operation' to identify the operation type (max 5 custom attributes)
*
* @example
* ```typescript
* const start = process.hrtime()
* // ... do work ...
* diagnosticsMetrics.recordLatency(process.hrtime(start), { operation: 'api-call', status: '2xx' })
*
* // Or with milliseconds
* diagnosticsMetrics.recordLatency(42.5, { operation: 'db-query', status: 'success' })
* ```
*/
recordLatency(value: [number, number] | number, attributes?: Attributes): void;
/**
* Increment a counter by a specific value.
* Multiple counters are stored by name since counters represent different types of events.
*
* Base attributes from the current context (set via `runWithBaseAttributes`) are
* automatically merged with the provided custom attributes. Base attributes take
* precedence - if a custom attribute key conflicts with a base attribute key,
* the custom attribute is silently dropped.
*
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
*
* @param name Counter name (e.g., 'http_requests_total', 'cache_hits_total')
* @param value Amount to increment by (typically 1)
* @param attributes Optional custom attributes for the counter (max 5 custom attributes, e.g., { method: 'GET', status: '2xx' })
*
* @example
* ```typescript
* diagnosticsMetrics.incrementCounter('http_requests_total', 1, { method: 'GET', status: '2xx' })
* ```
*/
incrementCounter(name: string, value: number, attributes?: Attributes): void;
/**
* Set a gauge to a specific value (current state).
* Multiple gauges are stored by name since gauges represent different types of measurements.
*
* Base attributes from the current context (set via `runWithBaseAttributes`) are
* automatically merged with the provided custom attributes. Base attributes take
* precedence - if a custom attribute key conflicts with a base attribute key,
* the custom attribute is silently dropped.
*
* Custom attributes are limited to MAX_CUSTOM_ATTRIBUTES (5). Base attributes are not limited.
*
* @param name Gauge name (e.g., 'cache_items_current', 'memory_usage_bytes')
* @param value Current value
* @param attributes Optional custom attributes for the gauge (max 5 custom attributes, e.g., { cache: 'pages' })
*
* @example
* ```typescript
* diagnosticsMetrics.setGauge('cache_items_current', 1024, { cache: 'pages' })
* ```
*/
setGauge(name: string, value: number, attributes?: Attributes): void;
}