@vtex/diagnostics-web
v5.5.3
Published
Diagnostics library for Web applications
Downloads
2,448
Maintainers
Readme
OpenTelemetry Diagnostics Library for Web
A JavaScript library for browsers that simplifies telemetry collection using OpenTelemetry, providing unified interfaces for metrics, logs, events and traces in web applications.
Installation
This library is hosted on a private AWS CodeArtifact registry. To install it, follow these steps:
1. Configure the Scoped Registry
Create or update an .npmrc file in your project root:
@vtex:registry=https://main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/
//main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/:always-auth=true
//main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/:_authToken=${CODEARTIFACT_AUTH_TOKEN}2. Authenticate with AWS
You need a valid authorization token from AWS CodeArtifact. Run the following command (requires AWS CLI configured):
# Get the token
export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain main --domain-owner 053131491888 --region us-east-1 --query authorizationToken --output text)Note: Authorization tokens are valid for 12 hours.
3. Install the library
npm install @vtex/diagnostics-webCollector URLs
When configuring your exporters to send telemetry data to VTEX's OpenTelemetry Collector from a web application, use the following HTTP endpoints.
| Signal | Endpoint |
| :--- | :--- |
| Logs | https://stable.vtexobservability.com/v1/logs |
| Traces | https://stable.vtexobservability.com/v1/traces |
Quick Start
Here's how to set up basic telemetry for your web application:
import { NewTelemetryClient, Exporters, Types, Config } from '@vtex/diagnostics-web';
async function init() {
// Initialize the central telemetry client
const telemetryClient = await NewTelemetryClient(
"my-app",
"web-component",
"v1.0.0",
{}
);
// Create traces client with OTLP exporter
const tracesClient = await telemetryClient.newTracesClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.TRACES,
options: {
endpoint: 'http://localhost:4318/v1/traces',
},
}, 'otlp', Config.ExporterProfile.BACKOFFICE)
});
// Start tracing
const span = tracesClient.startSpan("my-operation", {});
try {
// Your business logic here
span.setAttributes({ 'my.attribute': 'value' });
// Create a child span for a sub-operation
const childSpan = tracesClient.startSpan("sub-operation", {});
// ... more logic
childSpan.end();
} catch (error) {
console.error(error);
throw error;
} finally {
span.end();
}
}
init().catch(console.error);Metrics Example
import { NewTelemetryClient, Exporters, Types, Config } from '@vtex/diagnostics-web';
async function setupMetrics() {
const telemetryClient = await NewTelemetryClient("metrics-client", "web-component", "v1.0.0", {});
const metricsClient = await telemetryClient.newMetricsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.METRICS,
options: {
endpoint: 'http://localhost:4318/v1/metrics'
}
}, 'otlp', Config.ExporterProfile.BACKOFFICE)
});
// Create a counter
const requestCounter = metricsClient.counter("http_requests_total");
requestCounter.add(1, { method: "GET", path: "/api" });
// Create a histogram
const responseTimeHistogram = metricsClient.histogram("response_time_seconds");
responseTimeHistogram.record(0.35, { method: "GET", path: "/api" });
// Create a gauge
const activeConnectionsGauge = metricsClient.gauge("active_connections");
activeConnectionsGauge.set(42, { pool: "main" });
return { metricsClient, requestCounter, responseTimeHistogram, activeConnectionsGauge };
}Logging Example
import { NewTelemetryClient, Exporters, Types, Config } from '@vtex/diagnostics-web';
async function setupLogging() {
const telemetryClient = await NewTelemetryClient("logs-client", "web-component", "v1.0.0", {});
const logsClient = await telemetryClient.newLogsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.LOGS,
options: {
endpoint: 'http://localhost:4318/v1/logs'
}
}, 'otlp', Config.ExporterProfile.BACKOFFICE)
});
// Log different levels
logsClient.info("Application started", { version: "1.0.0" });
logsClient.warn("Resource usage high", { memory: "85%" });
try {
// Some operation that might fail
throw new Error("API connection failed");
} catch (err) {
logsClient.error("Operation failed", { error: err.message });
}
return logsClient;
}Log Sampling
The diagnostics library allows for dynamic, per-log sampling by adding a sampleRate attribute. When this attribute is present, the OpenTelemetry Collector can be configured to sample logs based on the provided percentage, allowing you to control log volume at the source.
This is particularly useful for high-volume logs where you only need a statistical sample for analysis.
How It Works
- When you create a log, you can optionally add a
sampleRateattribute with a value between 0 and 100. - A
sampleRateof10means you want to keep approximately 10% of these logs. A value of100means keep all, and0means drop all. - If the
sampleRateattribute is not explicitly included in a log, the sampling decision will default to the rules defined in the OpenTelemetry Collector's configuration. - The sampler processor collector uses this attribute (or its default configuration) to decide whether to keep or drop the log.
- The final sampling rate applied to a log can be observed in the
otelcol.sample_rateattribute of the log.
Example
// This log for a high-traffic endpoint will only be kept 5% of the time.
logsClient.info('High-traffic endpoint was accessed', {
'http.target': '/api/health-check',
'sampleRate': 5
});
// Critical error logs can be always kept by setting sampleRate to 100.
logsClient.error('Critical payment failure', {
'payment.transactionId': 'txn_123',
'error.code': 'E-500',
'sampleRate': 100
});Events Example
Events are structured logs that do not have a severity level. They are useful for capturing meaningful occurrences in your application.
import { NewTelemetryClient, Exporters, Types, Config } from '@vtex/diagnostics-web';
async function setupEvents() {
const telemetryClient = await NewTelemetryClient("events-client", "web-component", "v1.0.0", {});
const eventsClient = await telemetryClient.newEventsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.LOGS, // Events use the LOGS telemetry type
options: {
endpoint: 'http://localhost:4318/v1/logs'
}
}, 'otlp', Config.ExporterProfile.BACKOFFICE)
});
// Send an event
eventsClient.event("user_login", { userId: "123", method: "google" });
eventsClient.event("add_to_cart", { productId: "abc", quantity: 2 });
return eventsClient;
}Automatic Instrumentation
The library supports automatic instrumentations for various important metrics and events in web applications:
import { NewTelemetryClient, Types } from '@vtex/diagnostics-web';
async function setupInstrumentation() {
const telemetryClient = await NewTelemetryClient("my-app", "web-component", "v1.0.0", {});
// Configure default instrumentations
telemetryClient.useDefaultInstrumentations({
instrumentationLevel: Types.InstrumentationLevel.COMPREHENSIVE,
});
// This will automatically enable collection of:
// - Web Vitals (CLS, FID, LCP, etc.)
// - Exception tracking (uncaught errors, promise rejections)
// - User interactions
// - Resource loading
// - Long tasks
}React Integration
Telemetry Provider
// TelemetryProvider.jsx
'use client';
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { NewTelemetryClient, Exporters, Types, Config } from "@vtex/diagnostics-web";
type TelemetryContextType = {
tracesClient: any | null;
logsClient: any | null;
metricsClient: any | null;
eventsClient: any | null;
isLoaded: boolean;
};
export const TelemetryContext = createContext<TelemetryContextType>({
tracesClient: null,
logsClient: null,
metricsClient: null,
eventsClient: null,
isLoaded: false,
});
export function TelemetryProvider({ children }: { children: ReactNode }) {
const [clients, setClients] = useState<TelemetryContextType>({
tracesClient: null,
logsClient: null,
metricsClient: null,
eventsClient: null,
isLoaded: false,
});
useEffect(() => {
const initTelemetry = async () => {
try {
const telemetryClient = await NewTelemetryClient("my-react-app", "ui", "v1.0.0", {});
const tracesClient = await telemetryClient.newTracesClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.TRACES,
options: {
endpoint: 'http://localhost:4318/v1/traces',
}
}, 'otlp', Config.ExporterProfile.DEBUG)
});
const logsClient = await telemetryClient.newLogsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.LOGS,
options: {
endpoint: 'http://localhost:4318/v1/logs'
}
}, 'otlp', Config.ExporterProfile.DEBUG)
});
const metricsClient = await telemetryClient.newMetricsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.METRICS,
options: {
endpoint: 'http://localhost:4318/v1/metrics'
}
}, 'otlp', Config.ExporterProfile.DEBUG)
});
const eventsClient = await telemetryClient.newEventsClient({
exporter: Exporters.CreateExporter({
telemetryType: Types.TelemetryType.LOGS,
options: {
endpoint: 'http://localhost:4318/v1/logs'
}
}, 'otlp', Config.ExporterProfile.DEBUG)
});
telemetryClient.useDefaultInstrumentations({
instrumentationLevel: Types.InstrumentationLevel.COMPREHENSIVE,
});
setClients({
tracesClient,
logsClient,
metricsClient,
eventsClient,
isLoaded: true,
});
} catch (error) {
console.error('Failed to initialize telemetry:', error);
}
};
initTelemetry();
}, []);
return (
<TelemetryContext.Provider value={clients}>
{children}
</TelemetryContext.Provider>
);
}
export function useTelemetry() {
return useContext(TelemetryContext);
}Using the hook in components
// MyComponent.jsx
import { useTelemetry } from './TelemetryProvider';
export default function MyComponent() {
const { tracesClient, logsClient, eventsClient, isLoaded } = useTelemetry();
const handleClick = () => {
if (!isLoaded) return;
const span = tracesClient.startSpan("button_click", {});
try {
// Click logic
logsClient.info("Button clicked", { component: "MyComponent" });
eventsClient.event("button_clicked", { component: "MyComponent" });
// More operations...
} catch (error) {
logsClient.error("Error handling click", { error: error.message });
} finally {
span.end();
}
};
return (
<button onClick={handleClick}>Click Me</button>
);
}Advanced Configuration
Exporter Profiles
The library offers different configuration profiles to optimize behavior for your environment:
import { Config } from '@vtex/diagnostics-web';
// Available in Config.ExporterProfile:
// - DEBUG: High verbosity for development
// - BACKOFFICE: For administrative applications
// - STOREFRONT: Optimized for high traffic (store frontends)
const exporter = Exporters.CreateExporter(config, 'otlp', Config.ExporterProfile.DEBUG);Instrumentation Levels
Control the level of automatic instrumentation based on your needs:
import { Types } from '@vtex/diagnostics-web';
// Available levels:
// - MINIMAL: Minimal instrumentation (lowest overhead)
// - COMPREHENSIVE: Full instrumentation (Web Vitals, exceptions, interactions, etc.)
telemetryClient.useDefaultInstrumentations({
instrumentationLevel: Types.InstrumentationLevel.COMPREHENSIVE,
});Custom Sampling
import { Samplers } from '@vtex/diagnostics-web';
// Create a composed sampler
const customSampler = new Samplers.Traces.ComposedSampler([
new Samplers.Traces.DebugSampler(), // Sample spans with debug flag
new Samplers.Traces.MetadataSampler() // Sample based on metadata
]);
// Use the custom sampler
const tracesClient = await telemetryClient.newTracesClient({
exporter: /* exporter configuration */,
sampler: customSampler
});Processors
The library supports processors that can modify or enrich telemetry data:
import { Processors } from '@vtex/diagnostics-web';
// Use processor to add VTEX attributes
const tracesClient = await telemetryClient.newTracesClient({
exporter: /* exporter configuration */,
processors: [new Processors.VTEXAttrs.SpanProcessor()]
});
const logsClient = await telemetryClient.newLogsClient({
exporter: /* exporter configuration */,
processors: [new Processors.VTEXAttrs.LogProcessor()]
});Available Guides
For more information and detailed setup instructions, see our guides:
- GitHub Actions Configuration: Detailed instructions on how to authenticate with AWS CodeArtifact and install the library in CI/CD environments.
- Attribute Enrichment Strategy: How to choose between Resource Attributes,
WithAttributes, andWithContextfor log enrichment.
Version Policy
This library follows Semantic Versioning. The current version is in package.json.
| Tag pattern | Trigger |
|---|---|
| js-web-v<semver> | dkcicd publishes @vtex/diagnostics-web to AWS CodeArtifact npm registry |
To bump the version:
cd libs/javascript
make bump-patch # bug fix
make bump-minor # new feature (backwards-compatible)
make bump-major # breaking changeAlways run make test-all from the repo root before bumping. Never hand-edit semconv/ generated output.
Contributing
See CONTRIBUTING.md for the full workflow, including adding semantic convention attributes and bumping versions.
Architecture
The library is structured in organized modules:
- config: Exporter configurations and version
- discovery: Resource discovery
- exporters: Exporter implementations (OTLP, stdout)
- instrumentation: Automatic instrumentations (Web Vitals, exceptions, etc.)
- logs: Logs client
- events: Events client
- metrics: Metrics client (counters, histograms, gauges)
- processors: Processors for data enrichment
- samplers: Sampling strategies
- telemetry: Main telemetry client
- traces: Traces client and spans
- types: Type definitions
- utils: Various utilities
