0
# Hook Functions
1
2
Hook functions provide custom logic for filtering requests and adding attributes during HTTP instrumentation lifecycle events. These functions allow fine-grained control over which requests are instrumented and what telemetry data is collected.
3
4
## Capabilities
5
6
### Request Filtering Functions
7
8
Functions used to determine whether incoming or outgoing requests should be instrumented.
9
10
```typescript { .api }
11
/**
12
* Function to determine if an incoming request should be ignored by instrumentation
13
* @param request - The incoming HTTP request
14
* @returns true to ignore the request, false to instrument it
15
*/
16
interface IgnoreIncomingRequestFunction {
17
(request: IncomingMessage): boolean;
18
}
19
20
/**
21
* Function to determine if an outgoing request should be ignored by instrumentation
22
* @param request - The outgoing HTTP request options
23
* @returns true to ignore the request, false to instrument it
24
*/
25
interface IgnoreOutgoingRequestFunction {
26
(request: RequestOptions): boolean;
27
}
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
34
import type { IncomingMessage, RequestOptions } from "http";
35
36
const instrumentation = new HttpInstrumentation({
37
// Ignore specific incoming request patterns
38
ignoreIncomingRequestHook: (request: IncomingMessage) => {
39
const url = request.url || '';
40
const userAgent = request.headers['user-agent'] || '';
41
42
// Ignore health checks, metrics endpoints, and bot traffic
43
return url.startsWith('/health') ||
44
url.startsWith('/metrics') ||
45
url.startsWith('/_internal') ||
46
userAgent.includes('bot') ||
47
userAgent.includes('crawler');
48
},
49
50
// Ignore specific outgoing request patterns
51
ignoreOutgoingRequestHook: (request: RequestOptions) => {
52
const hostname = request.hostname || request.host || '';
53
const path = request.path || '';
54
55
// Ignore internal services and health checks
56
return hostname.includes('internal.company.com') ||
57
hostname === 'localhost' ||
58
hostname === '127.0.0.1' ||
59
path.includes('/health');
60
}
61
});
62
```
63
64
### Custom Attribute Functions
65
66
Functions used to add custom attributes to spans at different points in the request lifecycle.
67
68
```typescript { .api }
69
/**
70
* Function for adding custom attributes to spans after both request and response are processed
71
* @param span - The current span
72
* @param request - The HTTP request (ClientRequest for outgoing, IncomingMessage for incoming)
73
* @param response - The HTTP response (IncomingMessage for outgoing, ServerResponse for incoming)
74
*/
75
interface HttpCustomAttributeFunction {
76
(
77
span: Span,
78
request: ClientRequest | IncomingMessage,
79
response: IncomingMessage | ServerResponse
80
): void;
81
}
82
83
/**
84
* Function for adding custom attributes to spans during request processing
85
* @param span - The current span
86
* @param request - The HTTP request (ClientRequest for outgoing, IncomingMessage for incoming)
87
*/
88
interface HttpRequestCustomAttributeFunction {
89
(span: Span, request: ClientRequest | IncomingMessage): void;
90
}
91
92
/**
93
* Function for adding custom attributes to spans during response processing
94
* @param span - The current span
95
* @param response - The HTTP response (IncomingMessage for outgoing, ServerResponse for incoming)
96
*/
97
interface HttpResponseCustomAttributeFunction {
98
(span: Span, response: IncomingMessage | ServerResponse): void;
99
}
100
```
101
102
**Usage Examples:**
103
104
```typescript
105
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
106
import type { Span } from "@opentelemetry/api";
107
import type { ClientRequest, IncomingMessage, ServerResponse } from "http";
108
109
const instrumentation = new HttpInstrumentation({
110
// Add custom attributes during request processing
111
requestHook: (span: Span, request: ClientRequest | IncomingMessage) => {
112
// Common attributes for both incoming and outgoing requests
113
const userAgent = request.headers['user-agent'];
114
const contentType = request.headers['content-type'];
115
const requestId = request.headers['x-request-id'];
116
117
if (userAgent) {
118
span.setAttribute('http.user_agent.original', userAgent);
119
span.setAttribute('http.user_agent.is_mobile', userAgent.includes('Mobile'));
120
}
121
122
if (contentType) {
123
span.setAttribute('http.request.content_type', contentType);
124
}
125
126
if (requestId) {
127
span.setAttribute('http.request.id', requestId);
128
}
129
130
// Differentiate between incoming and outgoing requests
131
if ('method' in request) {
132
// This is an IncomingMessage (incoming request)
133
span.setAttribute('http.direction', 'incoming');
134
span.setAttribute('http.client.ip', request.socket.remoteAddress || '');
135
} else {
136
// This is a ClientRequest (outgoing request)
137
span.setAttribute('http.direction', 'outgoing');
138
}
139
},
140
141
// Add custom attributes during response processing
142
responseHook: (span: Span, response: IncomingMessage | ServerResponse) => {
143
const contentLength = response.headers['content-length'];
144
const cacheControl = response.headers['cache-control'];
145
const server = response.headers['server'];
146
147
if (contentLength) {
148
span.setAttribute('http.response.body.size', parseInt(contentLength));
149
}
150
151
if (cacheControl) {
152
span.setAttribute('http.response.cache_control', cacheControl);
153
}
154
155
if (server) {
156
span.setAttribute('http.response.server', server);
157
}
158
159
// Add custom performance categorization
160
if ('statusCode' in response && response.statusCode) {
161
const category = response.statusCode < 300 ? 'success' :
162
response.statusCode < 400 ? 'redirect' :
163
response.statusCode < 500 ? 'client_error' : 'server_error';
164
span.setAttribute('http.response.category', category);
165
}
166
},
167
168
// Add comprehensive attributes after complete request/response cycle
169
applyCustomAttributesOnSpan: (
170
span: Span,
171
request: ClientRequest | IncomingMessage,
172
response: IncomingMessage | ServerResponse
173
) => {
174
// Calculate request processing time
175
const endTime = Date.now();
176
const startTime = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000;
177
const duration = endTime - startTime;
178
179
// Add performance categories
180
span.setAttribute('http.duration.category',
181
duration < 50 ? 'very_fast' :
182
duration < 200 ? 'fast' :
183
duration < 1000 ? 'moderate' :
184
duration < 5000 ? 'slow' : 'very_slow'
185
);
186
187
// Add size categories for responses
188
const contentLength = response.headers['content-length'];
189
if (contentLength) {
190
const size = parseInt(contentLength);
191
span.setAttribute('http.response.size.category',
192
size < 1024 ? 'small' :
193
size < 102400 ? 'medium' :
194
size < 1048576 ? 'large' : 'very_large'
195
);
196
}
197
}
198
});
199
```
200
201
### Span Start Attribute Functions
202
203
Functions used to add custom attributes before spans are created, allowing attributes to be set at span initialization.
204
205
```typescript { .api }
206
/**
207
* Function for adding custom attributes before an incoming request span is started
208
* @param request - The incoming HTTP request
209
* @returns Object containing attributes to add to the span
210
*/
211
interface StartIncomingSpanCustomAttributeFunction {
212
(request: IncomingMessage): Attributes;
213
}
214
215
/**
216
* Function for adding custom attributes before an outgoing request span is started
217
* @param request - The outgoing HTTP request options
218
* @returns Object containing attributes to add to the span
219
*/
220
interface StartOutgoingSpanCustomAttributeFunction {
221
(request: RequestOptions): Attributes;
222
}
223
```
224
225
**Usage Examples:**
226
227
```typescript
228
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
229
import type { Attributes } from "@opentelemetry/api";
230
import type { IncomingMessage, RequestOptions } from "http";
231
232
const instrumentation = new HttpInstrumentation({
233
// Add attributes at the start of incoming request spans
234
startIncomingSpanHook: (request: IncomingMessage): Attributes => {
235
const attributes: Attributes = {};
236
237
// Extract tenant/customer information
238
const tenantId = request.headers['x-tenant-id'];
239
const customerId = request.headers['x-customer-id'];
240
const apiVersion = request.headers['api-version'];
241
242
if (tenantId) {
243
attributes['tenant.id'] = tenantId;
244
}
245
246
if (customerId) {
247
attributes['customer.id'] = customerId;
248
}
249
250
if (apiVersion) {
251
attributes['api.version'] = apiVersion;
252
}
253
254
// Add request classification
255
const url = request.url || '';
256
if (url.startsWith('/api/v1/')) {
257
attributes['api.type'] = 'rest';
258
attributes['api.version'] = 'v1';
259
} else if (url.startsWith('/graphql')) {
260
attributes['api.type'] = 'graphql';
261
} else if (url.startsWith('/webhook')) {
262
attributes['api.type'] = 'webhook';
263
}
264
265
return attributes;
266
},
267
268
// Add attributes at the start of outgoing request spans
269
startOutgoingSpanHook: (request: RequestOptions): Attributes => {
270
const attributes: Attributes = {};
271
const hostname = request.hostname || request.host || '';
272
const path = request.path || '';
273
274
// Service identification
275
if (hostname.includes('api.stripe.com')) {
276
attributes['service.name'] = 'stripe';
277
attributes['service.type'] = 'payment';
278
} else if (hostname.includes('api.github.com')) {
279
attributes['service.name'] = 'github';
280
attributes['service.type'] = 'code_repository';
281
} else if (hostname.includes('amazonaws.com')) {
282
attributes['service.name'] = 'aws';
283
attributes['service.type'] = 'cloud_service';
284
}
285
286
// Operation classification
287
const method = request.method?.toUpperCase() || 'GET';
288
if (method === 'GET') {
289
attributes['operation.type'] = 'read';
290
} else if (method === 'POST' || method === 'PUT') {
291
attributes['operation.type'] = 'write';
292
} else if (method === 'DELETE') {
293
attributes['operation.type'] = 'delete';
294
}
295
296
// Add timeout information if specified
297
if (request.timeout) {
298
attributes['http.client.timeout'] = request.timeout;
299
}
300
301
return attributes;
302
}
303
});
304
```
305
306
## Advanced Hook Patterns
307
308
### Conditional Attribute Addition
309
310
```typescript
311
const instrumentation = new HttpInstrumentation({
312
requestHook: (span, request) => {
313
// Only add attributes for certain content types
314
const contentType = request.headers['content-type'] || '';
315
316
if (contentType.includes('application/json')) {
317
span.setAttribute('request.format', 'json');
318
319
// Add content length for JSON requests
320
const contentLength = request.headers['content-length'];
321
if (contentLength) {
322
span.setAttribute('request.json.size', parseInt(contentLength));
323
}
324
} else if (contentType.includes('multipart/form-data')) {
325
span.setAttribute('request.format', 'multipart');
326
} else if (contentType.includes('application/x-www-form-urlencoded')) {
327
span.setAttribute('request.format', 'form');
328
}
329
}
330
});
331
```
332
333
### Error Context Enhancement
334
335
```typescript
336
const instrumentation = new HttpInstrumentation({
337
applyCustomAttributesOnSpan: (span, request, response) => {
338
// Enhanced error context for failed requests
339
if ('statusCode' in response && response.statusCode && response.statusCode >= 400) {
340
span.setAttribute('error.type', 'http_error');
341
span.setAttribute('error.status_code', response.statusCode);
342
343
// Add more context for specific error ranges
344
if (response.statusCode >= 500) {
345
span.setAttribute('error.category', 'server_error');
346
span.setAttribute('error.severity', 'high');
347
} else if (response.statusCode >= 400) {
348
span.setAttribute('error.category', 'client_error');
349
span.setAttribute('error.severity', 'medium');
350
}
351
352
// Add request details for debugging
353
if ('url' in request) {
354
span.setAttribute('error.request.url', request.url || '');
355
}
356
if ('method' in request) {
357
span.setAttribute('error.request.method', request.method || '');
358
}
359
}
360
}
361
});
362
```