UNPKG

@jayeshsadhwani/telemetry-sdk

Version:

A lightweight, configurable telemetry tracking library for JavaScript/TypeScript applications with comprehensive error handling, event batching, and graceful degradation

628 lines (461 loc) 23.1 kB
# Telemetry SDK A lightweight, configurable telemetry tracking library for JavaScript/TypeScript applications with comprehensive error handling, event batching, and graceful degradation. ## 📦 Installation ```bash npm install @jayeshsadhwani/telemetry-sdk # or yarn add @jayeshsadhwani/telemetry-sdk # or pnpm add @jayeshsadhwani/telemetry-sdk ``` ## 🚀 Quick Start ### Next.js Applications Create a client component for telemetry initialization: ```tsx "use client"; import { useEffect } from "react"; import { initTelemetry } from "@jayeshsadhwani/telemetry-sdk"; function TelemetryProvider() { useEffect(() => { let telemetry = initTelemetry({ hyperlookApiKey: "your-api-key", // Replace with your Hyperlook API key }); return () => { telemetry.destroy(); }; }, []); } export default TelemetryProvider; ``` Then add it to your `layout.{tsx,jsx,ts,js}`: ```tsx import TelemetryProvider from "./TelemetryProvider"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body> <TelemetryProvider /> {children} </body> </html> ); } ``` That's it! The telemetry SDK will automatically start collecting data once the component mounts. **🆕 Early Initialization**: The SDK now automatically captures network requests made before initialization, ensuring no requests are missed! ### React Applications ```tsx // In your main App component or entry point import { useEffect } from "react"; import { initTelemetry } from "@jayeshsadhwani/telemetry-sdk"; function App() { useEffect(() => { // Initialize telemetry once when the app starts const telemetry = initTelemetry({ hyperlookApiKey: "your-api-key", // Replace with your Hyperlook API key enableClicks: true, enableLogs: true, enableNetwork: true, enablePerformance: true, }); // Optional: Identify the user telemetry.identify("user-123", { name: "John Doe", email: "john@example.com", }); // The SDK automatically sets up shutdown handlers, so no cleanup needed }, []); return <div>{/* Your app content */}</div>; } export default App; ``` ### Basic Usage (Any JavaScript/TypeScript Application) ```typescript import { initTelemetry } from "@jayeshsadhwani/telemetry-sdk"; // Initialize with default configuration const telemetry = initTelemetry({ hyperlookApiKey: "your-api-key", // Replace with your Hyperlook API key enableClicks: true, enableLogs: true, enableNetwork: true, enablePerformance: true, enableCustomEvents: true, // Enable custom events }); // Identify a user (optional) telemetry.identify("user-123", { name: "John Doe", email: "john@example.com", }); // The SDK automatically starts collecting telemetry data // Network requests made before initialization are automatically captured and processed ``` ## 🎯 Advanced Configuration ```typescript import { initTelemetry } from "@jayeshsadhwani/telemetry-sdk"; const telemetry = initTelemetry({ hyperlookApiKey: "your-api-key", // Replace with your Hyperlook API key // Event batching configuration batchSize: 50, // Number of events to batch before sending flushInterval: 30000, // Flush interval in milliseconds (30 seconds) // Retry configuration maxRetries: 3, // Maximum number of retry attempts retryDelay: 1000, // Delay between retries in milliseconds // Sampling configuration samplingRate: 0.1, // Only capture 10% of events (0.0 to 1.0) // Plugin configuration enablePageViews: true, // Track page view events (page_hit) enableClicks: true, // Track user clicks enableLogs: true, // Track console logs enableNetwork: true, // Track HTTP requests enablePerformance: true, // Track performance metrics enableCustomEvents: true, // Enable custom events plugin // Logging configuration logging: { level: "INFO", // Log level: ERROR, WARN, INFO, DEBUG, SILENT enableConsole: true, // Enable console logging enableTimestamp: true, // Include timestamps in logs prefix: "[MyApp]", // Custom log prefix }, }); ``` ## 🔧 API Reference ### `initTelemetry(config?: TelemetryConfig): TelemetryManager` Initializes the telemetry SDK with the provided configuration. **Automatically sets up shutdown handlers** to ensure events are flushed when the application closes. #### Early Initialization The SDK automatically sets up network interceptors immediately when the `TelemetryManager` is constructed, ensuring that network requests made before the SDK is fully initialized are captured and processed once the SDK is ready. This prevents any requests from being missed, even if they occur during the initial page load or before your application code runs. **How it works:** 1. Network interceptors (fetch/XHR) are patched immediately in the `TelemetryManager` constructor 2. Requests made before initialization are queued in memory 3. When the SDK initialization completes, queued requests are processed and sent to your telemetry endpoint 4. All subsequent requests are captured in real-time This feature works automatically - no additional configuration required! #### Automatic Shutdown Handling The SDK automatically registers shutdown handlers when initialized: **Browser Environment:** - `beforeunload` event: Flushes events when the page is about to unload - `pagehide` event: Flushes events when the page is hidden (mobile browsers, tab switching) - `visibilitychange` event: Flushes events when the page becomes hidden (with 1-second delay to avoid unnecessary flushes) **Node.js Environment:** - `SIGTERM` signal: Graceful shutdown when the process receives termination signal - `SIGINT` signal: Graceful shutdown when the process receives interrupt signal (Ctrl+C) - `uncaughtException`: Shutdown on uncaught exceptions - `unhandledRejection`: Shutdown on unhandled promise rejections - `exit` event: Force cleanup when the process exits This ensures that telemetry data is not lost even if developers forget to manually call `shutdown()`. #### Configuration Options | Option | Type | Default | Description | | -------------------- | -------------- | -------------- | -------------------------------------------------------------------------------------------- | | `hyperlookApiKey` | `string` | Required | Your Hyperlook API key | | `batchSize` | `number` | `50` | Number of events to batch before sending | | `flushInterval` | `number` | `30000` | Flush interval in milliseconds | | `maxRetries` | `number` | `3` | Maximum number of retry attempts | | `retryDelay` | `number` | `1000` | Delay between retries in milliseconds | | `samplingRate` | `number` | `1.0` | Sampling rate (0.0 to 1.0) | | `enablePageViews` | `boolean` | `true` | Enable page view tracking (page_hit) | | `enableClicks` | `boolean` | `true` | Enable click event tracking | | `enableLogs` | `boolean` | `true` | Enable console log tracking | | `enableNetwork` | `boolean` | `true` | Enable network request tracking (automatically captures requests made before initialization) | | `enablePerformance` | `boolean` | `true` | Enable performance metrics tracking | | `enableCustomEvents` | `boolean` | `false` | Enable custom events plugin | | `sessionId` | `string` | Auto-generated | Custom session ID for tracking | | `userId` | `string` | `undefined` | Initial user ID for identification | | `logging` | `LoggerConfig` | `{}` | Logging configuration | ### TelemetryManager Methods #### `capture(event: TelemetryEvent): void` Captures a custom telemetry event. ```typescript telemetry.capture({ eventType: "custom", eventName: "user_action", payload: { action: "button_click", buttonId: "submit-form", userId: "12345", }, timestamp: new Date().toISOString(), }); ``` #### `shutdown(): Promise<void>` Gracefully shuts down the telemetry manager, flushing any remaining events. ```typescript // Before your app closes await telemetry.shutdown(); ``` **Note:** The SDK automatically sets up shutdown handlers when initialized, so events will be flushed even if you don't manually call `shutdown()`. This includes: - Browser: `beforeunload` and `pagehide` events - Node.js: `SIGTERM`, `SIGINT`, `uncaughtException`, and `unhandledRejection` events #### `destroy(): void` Immediately destroys the telemetry manager without flushing events. ```typescript // Emergency cleanup telemetry.destroy(); ``` #### `retryFailedEvents(): Promise<void>` Retries sending failed events. ```typescript // Retry failed events when network is restored await telemetry.retryFailedEvents(); ``` #### `identify(userId: string, traits?: Record<string, unknown>): void` Identifies a user with the given user ID and optional traits. This creates an "identify" event and sets the user ID for all subsequent events. ```typescript // Identify a user with traits telemetry.identify("user-123", { name: "John Doe", email: "john@example.com", plan: "premium", signupDate: "2024-01-15", }); // Identify without traits telemetry.identify("user-456"); ``` #### `getSessionId(): string` Returns the current session ID. ```typescript const sessionId = telemetry.getSessionId(); console.log("Current session:", sessionId); ``` #### `getUserId(): string | undefined` Returns the current user ID if set. ```typescript const userId = telemetry.getUserId(); console.log("Current user:", userId); ``` #### `getCustomEventsPlugin(): CustomEventsPlugin | undefined` Returns the custom events plugin if enabled, allowing you to capture custom events. ```typescript const customPlugin = telemetry.getCustomEventsPlugin(); if (customPlugin) { customPlugin.captureCustomEvent("ecommerce", "purchase", { productId: "prod_123", amount: 99.99, }); } ``` #### Monitoring Methods ```typescript // Get counts of different event types const failedCount = telemetry.getFailedEventsCount(); const queuedCount = telemetry.getQueuedEventsCount(); const bufferedCount = telemetry.getBufferedEventsCount(); console.log( `Failed: ${failedCount}, Queued: ${queuedCount}, Buffered: ${bufferedCount}` ); ``` ## 🎯 Custom Events & User Identification ### User Identification The SDK supports user identification through the `identify()` method, which creates an "identify" event and sets the user ID for all subsequent events. ```typescript // Initialize with custom events enabled const telemetry = initTelemetry({ hyperlookApiKey: "your-api-key", enableCustomEvents: true, sessionId: "custom-session-123", // Optional: provide custom session ID userId: "user-456", // Optional: provide initial user ID }); // Identify a user with traits telemetry.identify("user-789", { name: "John Doe", email: "john@example.com", plan: "premium", signupDate: "2024-01-15", }); ``` ### Custom Events When `enableCustomEvents` is enabled, you can capture custom events using the CustomEventsPlugin: ```typescript const customPlugin = telemetry.getCustomEventsPlugin(); if (customPlugin) { // Capture a custom event with type, name, and payload customPlugin.captureCustomEvent("ecommerce", "product_viewed", { productId: "prod_123", productName: "Wireless Headphones", category: "Electronics", price: 99.99, currency: "USD", }); // Capture a pre-built event object const customEvent = { eventType: "analytics", eventName: "page_view", payload: { page: "/dashboard", referrer: document.referrer, }, timestamp: new Date().toISOString(), }; customPlugin.captureEvent(customEvent); } ``` ### Session Tracking All events automatically include session tracking. The session ID is either provided in the configuration or auto-generated: ```typescript // Events automatically include session and user context telemetry.capture({ eventType: "interaction", eventName: "button_click", payload: { buttonId: "submit-form", page: "/contact", }, timestamp: new Date().toISOString(), }); // The above event will automatically include: // - sessionId: "custom-session-123" (or auto-generated) // - userId: "user-789" (if identified) ``` ## 🚀 Features - **Event Batching**: Efficiently batches events for optimal network performance - **Error Boundaries**: Comprehensive error handling with graceful degradation - **Plugin Architecture**: Modular design with built-in plugins for common use cases - **Type Safety**: Full TypeScript support with strict type checking - **Environment Detection**: Automatic detection of browser vs Node.js environments - **Performance Monitoring**: Built-in performance metrics collection - **Network Monitoring**: Automatic HTTP request tracking - **Console Logging**: Intercepts and tracks console logs with XSS protection - **User Interactions**: Captures click events and user interactions - **User Identification**: Track user identity with traits and session management - **Custom Events**: Capture user-defined events with flexible payloads - **Session Tracking**: Automatic session ID generation and tracking - **Input Validation**: Comprehensive validation and sanitization of all user data - **Automatic Shutdown**: Ensures events are flushed when the application closes (browser unload, Node.js process termination) ## 🔄 TelemetryTracker Compatibility The SDK is designed to be fully compatible with the TelemetryTracker format. All events are sent to Hyperlook in the exact same format and structure as your TelemetryTracker implementation: ### Event Format Compatibility All events include the same properties as TelemetryTracker: - `event_id`: Auto-generated unique event ID - `user_id`: User identification (if provided) - `session_id`: Session tracking - `event_type`: Event category (page, network, error, etc.) - `event_name`: Specific event name - `properties`: Event-specific data - `user_properties`: User-specific data - `page_url`: Current page URL - `page_title`: Page title - `referrer`: Referrer URL - `user_agent`: Browser user agent - `timestamp`: ISO timestamp ### API Key Configuration The SDK requires a Hyperlook API key to be provided: - **Required**: Must provide `hyperlookApiKey` in configuration - **Security**: No default API key is used for security reasons - **Configuration**: Set via `hyperlookApiKey` parameter in `initTelemetry()` ### Event Type Mapping | TelemetryTracker Event | SDK Event | Properties | | ----------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `PAGE_HIT` | `page_hit` | viewport, characterSet, language, cookieEnabled, onLine, platform, userAgent, referrer, url, title | | `XHR_COMPLETE` | `xhr_complete` | url, method, queryParams, responseStatus, responseStatusText, responseHeaders, responseBody, duration, startTime, endTime, isSupabaseQuery | | `FETCH_COMPLETE` | `fetch_complete` | url, method, queryParams, responseStatus, responseStatusText, responseHeaders, responseBody, duration, startTime, endTime, isSupabaseQuery | | `SUPABASE_XHR_COMPLETE` | `supabase_xhr_complete` | url, method, queryParams, responseStatus, responseStatusText, responseHeaders, responseBody, duration, startTime, endTime, isSupabaseQuery | | `SUPABASE_FETCH_COMPLETE` | `supabase_fetch_complete` | url, method, queryParams, responseStatus, responseStatusText, responseHeaders, responseBody, duration, startTime, endTime, isSupabaseQuery | | `SUPABASE_XHR_ERROR` | `supabase_xhr_error` | url, method, queryParams, error, duration, startTime, endTime, isSupabaseQuery | | `SUPABASE_FETCH_ERROR` | `supabase_fetch_error` | url, method, queryParams, error, duration, startTime, endTime, isSupabaseQuery | | `JAVASCRIPT_ERROR` | `javascript_error` | message, filename, lineno, colno, error, stack | | `UNHANDLED_PROMISE_REJECTION` | `unhandled_promise_rejection` | reason, promise | ## 📊 Event Types The SDK automatically captures various types of events: ### Automatic Events - **Page View Events**: Automatic page_hit events when someone first visits a page or navigates to a new page (React/Next.js client-side routing) - **Click Events**: User interactions with DOM elements - **Network Events**: HTTP requests and responses (fetch, XHR) - **Performance Events**: Page load metrics, Core Web Vitals, long tasks - **Log Events**: Console logs (log, warn, error, info, debug) - **Error Events**: JavaScript errors and exceptions ### Custom Events Enables capturing custom events with user-defined types, names, and payloads. Must be enabled via `enableCustomEvents: true` in the configuration. ```typescript const customPlugin = telemetry.getCustomEventsPlugin(); if (customPlugin) { customPlugin.captureCustomEvent("ecommerce", "purchase", { productId: "prod_123", amount: 99.99, }); } ``` ## 🔌 Plugin System The SDK uses a plugin architecture for extensibility. Built-in plugins include: ### PageViewPlugin Automatically captures page_hit events when someone first visits a page and on client-side navigation in React/Next.js applications. Includes viewport information, browser details, and page metadata. Now supports: - **Initial page loads**: Captures page view when the page first loads - **React Router navigation**: Safely tracks URL and title changes without interfering with routing - **Next.js client-side routing**: Automatically detects and tracks navigation using safe polling - **Browser navigation**: Captures back/forward button usage - **Duplicate prevention**: Avoids sending multiple events for the same URL/title combination - **Navigation flag**: Includes `isNavigation` flag to distinguish initial loads from navigation ### ClickPlugin Tracks user click events with element information. ### LogPlugin Intercepts and tracks console.log, console.error, etc. ### NetworkPlugin Monitors fetch and XMLHttpRequest calls with automatic Supabase detection and differentiation. **Features:** - **Automatic Supabase Detection**: Automatically detects requests to Supabase endpoints (URLs containing "supabase.co" or "supabase.com") - **Event Differentiation**: Sends separate event types for Supabase vs regular network requests - **Complete Request Tracking**: Captures both successful and failed requests - **Rich Metadata**: Includes response status, headers, body, timing, and query parameters **Event Types:** - **Supabase Events**: `supabase_fetch_complete`, `supabase_fetch_error`, `supabase_xhr_complete`, `supabase_xhr_error` - **Regular Network Events**: `fetch_complete`, `fetch_error`, `xhr_complete`, `xhr_error` **Event Properties:** - `url`: Request URL - `method`: HTTP method (GET, POST, etc.) - `queryParams`: URL query parameters - `responseStatus`: HTTP status code - `responseStatusText`: HTTP status text - `responseHeaders`: Response headers - `responseBody`: Response body (when available) - `duration`: Request duration in milliseconds - `startTime`: Request start timestamp - `endTime`: Request end timestamp - `isSupabaseQuery`: Boolean flag indicating if it's a Supabase request - `error`: Error message (for failed requests) ### ErrorPlugin Captures JavaScript errors and unhandled promise rejections. ### PerformancePlugin Collects performance metrics including Web Vitals. ### CustomEventsPlugin Enables capturing custom events with user-defined types, names, and payloads. Must be enabled via `enableCustomEvents: true` in the configuration. ```typescript const customPlugin = telemetry.getCustomEventsPlugin(); if (customPlugin) { customPlugin.captureCustomEvent("ecommerce", "purchase", { productId: "prod_123", amount: 99.99, }); } ``` ## 🛡️ Error Handling The SDK includes comprehensive error handling: ### Graceful Degradation If initialization fails, the SDK returns a no-op manager that won't crash your application. ### Plugin Error Isolation Each plugin has its own error boundaries. If one plugin fails, others continue working. ### Event-Level Error Handling Individual event failures don't affect the processing of other events. ### Automatic Retry Failed network requests are automatically retried with exponential backoff. ## 🌍 Environment Support The SDK automatically detects the environment and enables appropriate features: - **Browser**: All plugins available - **Node.js**: Only LogPlugin available (console tracking) - **React Native**: Limited plugin support based on available APIs ## 📈 Performance Considerations ### Event Batching Events are automatically batched to reduce network overhead. ### Sampling Use sampling to reduce data volume in high-traffic applications. ### Memory Management Failed events are automatically cleaned up to prevent memory leaks. ## 🔒 Privacy & Security - No personally identifiable information is collected by default - All data is sent to your specified endpoint - No data is stored locally beyond the current session - Failed events are automatically cleaned up ## 📄 License ISC ## 📞 Support For issues and questions, please open an issue on GitHub.