11# SDK Telemetry Hooks
22
33> ** Issue #362 ** : Add opt-in telemetry hooks for error and performance monitoring
4+ > ** Issue #903 ** : Add SDK metrics export in Prometheus format
45
56## Overview
67
@@ -17,6 +18,7 @@ All hooks are **fire-and-forget** — exceptions within hooks do not propagate t
1718- ✅ Full TypeScript type safety
1819- ✅ Zero dependencies
1920- ✅ Opt-in (no performance impact when not configured)
21+ - ✅ Prometheus-format metrics export (see [ Prometheus Metrics Export] ( #prometheus-metrics-export ) )
2022
2123## Installation
2224
@@ -62,6 +64,88 @@ client.setTelemetryHooks({
6264client .clearTelemetryHooks ();
6365```
6466
67+ ## Prometheus Metrics Export
68+
69+ > ** Issue #903 ** : Add SDK metrics export in Prometheus format
70+
71+ The SDK can export the metrics it collects through the telemetry hooks in the
72+ [ Prometheus text exposition format] ( https://prometheus.io/docs/instrumenting/exposition_formats/ ) ,
73+ so they can be scraped by a Prometheus server or any compatible agent.
74+
75+ ### Enabling Metrics Collection
76+
77+ Metrics are collected from the same ` onCallStart ` / ` onCallEnd ` / ` onError `
78+ events used by the telemetry hooks. Register the built-in metrics collector to
79+ start recording them:
80+
81+ ``` typescript
82+ import { createPrometheusMetrics } from " @stellar-split/sdk" ;
83+
84+ const metrics = createPrometheusMetrics ();
85+
86+ client .setTelemetryHooks ({
87+ onError: metrics .onError ,
88+ onCallStart: metrics .onCallStart ,
89+ onCallEnd: metrics .onCallEnd ,
90+ });
91+ ```
92+
93+ ### Exposing the Metrics Endpoint
94+
95+ Call ` metrics.export() ` to obtain the current snapshot rendered in Prometheus
96+ text format. Serve it from any HTTP handler (Express, Fastify, a serverless
97+ function, etc.):
98+
99+ ``` typescript
100+ import express from " express" ;
101+
102+ const app = express ();
103+
104+ app .get (" /metrics" , (_req , res ) => {
105+ res .set (" Content-Type" , " text/plain; version=0.0.4; charset=utf-8" );
106+ res .send (metrics .export ());
107+ });
108+
109+ app .listen (9464 );
110+ ```
111+
112+ ### Exported Metrics
113+
114+ | Metric | Type | Labels | Description |
115+ | --- | --- | --- | --- |
116+ | ` stellar_split_sdk_calls_total ` | counter | ` method ` , ` success ` | Total number of SDK calls, split by outcome |
117+ | ` stellar_split_sdk_call_duration_seconds ` | histogram | ` method ` | Duration of SDK calls in seconds |
118+ | ` stellar_split_sdk_errors_total ` | counter | ` method ` | Total number of SDK errors |
119+ | ` stellar_split_sdk_in_flight_calls ` | gauge | ` method ` | SDK calls currently in progress |
120+
121+ Example output:
122+
123+ ```
124+ # HELP stellar_split_sdk_calls_total Total number of SDK calls.
125+ # TYPE stellar_split_sdk_calls_total counter
126+ stellar_split_sdk_calls_total{method="createInvoice",success="true"} 12
127+ stellar_split_sdk_calls_total{method="createInvoice",success="false"} 1
128+ # HELP stellar_split_sdk_call_duration_seconds Duration of SDK calls in seconds.
129+ # TYPE stellar_split_sdk_call_duration_seconds histogram
130+ stellar_split_sdk_call_duration_seconds_bucket{method="createInvoice",le="0.1"} 8
131+ stellar_split_sdk_call_duration_seconds_bucket{method="createInvoice",le="0.5"} 12
132+ stellar_split_sdk_call_duration_seconds_bucket{method="createInvoice",le="+Inf"} 13
133+ stellar_split_sdk_call_duration_seconds_sum{method="createInvoice"} 1.842
134+ stellar_split_sdk_call_duration_seconds_count{method="createInvoice"} 13
135+ # HELP stellar_split_sdk_errors_total Total number of SDK errors.
136+ # TYPE stellar_split_sdk_errors_total counter
137+ stellar_split_sdk_errors_total{method="createInvoice"} 1
138+ # HELP stellar_split_sdk_in_flight_calls SDK calls currently in progress.
139+ # TYPE stellar_split_sdk_in_flight_calls gauge
140+ stellar_split_sdk_in_flight_calls{method="createInvoice"} 0
141+ ```
142+
143+ ### Resetting Metrics
144+
145+ ``` typescript
146+ metrics .reset ();
147+ ```
148+
65149## Hook Signatures
66150
67151### ` onError `
@@ -308,112 +392,4 @@ Console output:
308392
309393- ** Zero overhead when not configured** : Hooks have no performance impact when not registered
310394- ** Minimal overhead when configured** : Hook execution is synchronous and fast
311- - ** Fire-and-forget** : Hook errors never block SDK operations
312- - ** No memory leaks** : Hooks are properly cleaned up when cleared
313-
314- ## TypeScript Support
315-
316- All hook types are fully typed for IDE autocomplete and type safety:
317-
318- ``` typescript
319- import type {
320- TelemetryHooks ,
321- TelemetryErrorContext ,
322- TelemetryCallStartParams ,
323- TelemetryCallEndParams ,
324- } from " @stellar-split/sdk" ;
325-
326- const hooks: TelemetryHooks = {
327- onError : (error , context ) => {
328- // `error` is typed as StellarSplitError
329- // `context` is typed as TelemetryErrorContext
330- console .log (error .code , context .method );
331- },
332- onCallStart : (params ) => {
333- // `params` is typed as TelemetryCallStartParams
334- console .log (params .method , params .timestamp );
335- },
336- onCallEnd : (params ) => {
337- // `params` is typed as TelemetryCallEndParams
338- console .log (params .success , params .durationMs );
339- },
340- };
341- ```
342-
343- ## Best Practices
344-
345- 1 . ** Keep hooks lightweight** : Avoid heavy computation in hooks
346- 2 . ** Use async operations carefully** : If you need to make async calls, don't await them in hooks
347- 3 . ** Handle hook errors gracefully** : Expect hooks to fail occasionally (network issues, etc.)
348- 4 . ** Sanitize sensitive data** : The SDK provides basic sanitization, but you may want additional filtering
349- 5 . ** Test your hooks** : Ensure your monitoring code doesn't introduce bugs
350-
351- ## Troubleshooting
352-
353- ### Hook not being called
354-
355- Ensure the hook is registered before making SDK calls:
356-
357- ``` typescript
358- client .setTelemetryHooks ({ onError });
359- await client .createInvoice (params ); // Hook will be called
360- ```
361-
362- ### Hook exceptions appearing in console
363-
364- This is expected fire-and-forget behavior. Fix the exception in your hook code:
365-
366- ``` typescript
367- client .setTelemetryHooks ({
368- onError : (error , context ) => {
369- try {
370- // Your monitoring code
371- sendToSentry (error );
372- } catch (err ) {
373- // Handle gracefully
374- console .warn (" Failed to send error to Sentry:" , err );
375- }
376- },
377- });
378- ```
379-
380- ## Migration Guide
381-
382- If you were using custom error handling before:
383-
384- ``` typescript
385- // Before
386- try {
387- await client .createInvoice (params );
388- } catch (error ) {
389- trackError (error );
390- throw error ;
391- }
392- ```
393-
394- ``` typescript
395- // After
396- client .setTelemetryHooks ({
397- onError : (error , context ) => trackError (error , context ),
398- });
399-
400- await client .createInvoice (params ); // Error tracking happens automatically
401- ```
402-
403- ## API Reference
404-
405- ### ` client.setTelemetryHooks(hooks: TelemetryHooks): void `
406-
407- Register telemetry hooks. Replaces any previously registered hooks.
408-
409- ### ` client.clearTelemetryHooks(): void `
410-
411- Remove all registered telemetry hooks.
412-
413- ## Related Issues
414-
415- - [ #362 : Add SDK telemetry hooks for error and performance monitoring] ( https://github.com/Stellar-split/split-sdk/issues/362 )
416-
417- ## License
418-
419- MIT
395+ - ** Fire-and-forget** : Hook errors
0 commit comments