0
# Utilities
1
2
Utility functions for safe execution, function wrapping detection, and error handling in instrumentation code.
3
4
## Capabilities
5
6
### Safe Execution Functions
7
8
Functions for executing code safely with error handling and recovery mechanisms.
9
10
```typescript { .api }
11
/**
12
* Execute function safely with error handling
13
* @param execute Function to execute
14
* @param onFinish Callback executed after completion (success or failure)
15
* @param preventThrowingError If true, prevents rethrowing errors
16
* @returns Result of execution
17
*/
18
function safeExecuteInTheMiddle<T>(
19
execute: () => T,
20
onFinish: (e: Error | undefined, result: T | undefined) => void,
21
preventThrowingError?: boolean
22
): T;
23
24
/**
25
* Execute async function safely with error handling
26
* @param execute Async function to execute
27
* @param onFinish Callback executed after completion (success or failure)
28
* @param preventThrowingError If true, prevents rethrowing errors
29
* @returns Promise with result of execution
30
*/
31
function safeExecuteInTheMiddleAsync<T>(
32
execute: () => T,
33
onFinish: (e: Error | undefined, result: T | undefined) => void,
34
preventThrowingError?: boolean
35
): Promise<T>;
36
```
37
38
**Usage Examples:**
39
40
```typescript
41
import { safeExecuteInTheMiddle, safeExecuteInTheMiddleAsync } from "@opentelemetry/instrumentation";
42
43
// Synchronous safe execution
44
const result = safeExecuteInTheMiddle(
45
() => {
46
// Potentially risky operation
47
return JSON.parse(someJsonString);
48
},
49
(error, result) => {
50
if (error) {
51
console.error('JSON parsing failed:', error);
52
// Log or handle error
53
} else {
54
console.log('JSON parsed successfully:', result);
55
}
56
},
57
true // Prevent throwing error, handle gracefully
58
);
59
60
// Asynchronous safe execution
61
const asyncResult = await safeExecuteInTheMiddleAsync(
62
async () => {
63
// Potentially risky async operation
64
const response = await fetch('https://api.example.com/data');
65
return response.json();
66
},
67
(error, result) => {
68
if (error) {
69
console.error('API request failed:', error);
70
// Could emit metrics or traces here
71
} else {
72
console.log('API request successful');
73
}
74
}
75
);
76
77
// Use in instrumentation context
78
class HttpInstrumentation extends InstrumentationBase {
79
private _patchRequest(original: Function) {
80
const instrumentation = this;
81
82
return function(this: any, ...args: any[]) {
83
return safeExecuteInTheMiddle(
84
() => {
85
// Create span and add instrumentation
86
const span = instrumentation.tracer.startSpan('http.request');
87
const result = original.apply(this, args);
88
span.end();
89
return result;
90
},
91
(error, result) => {
92
if (error) {
93
instrumentation._diag.error('Failed to instrument HTTP request:', error);
94
}
95
},
96
true // Don't break original functionality on instrumentation errors
97
);
98
};
99
}
100
}
101
```
102
103
### Function Wrapping Detection
104
105
Utility to check if a function has been wrapped by the shimmer library.
106
107
```typescript { .api }
108
/**
109
* Check if a function has been wrapped by shimmer
110
* @param func Function to check
111
* @returns True if function is wrapped
112
*/
113
function isWrapped(func: unknown): func is ShimWrapped;
114
115
/**
116
* Interface for wrapped functions with shimmer metadata
117
*/
118
interface ShimWrapped extends Function {
119
/** Indicates the function is wrapped */
120
__wrapped: boolean;
121
/** Unwrap function to restore original */
122
__unwrap: Function;
123
/** Reference to original function */
124
__original: Function;
125
}
126
```
127
128
**Usage Examples:**
129
130
```typescript
131
import { isWrapped } from "@opentelemetry/instrumentation";
132
import * as shimmer from "shimmer";
133
134
class InstrumentationExample extends InstrumentationBase {
135
enable() {
136
const http = require('http');
137
138
// Check if already wrapped to avoid double-wrapping
139
if (!isWrapped(http.request)) {
140
shimmer.wrap(http, 'request', this._wrapRequest.bind(this));
141
} else {
142
this._diag.debug('http.request is already wrapped');
143
}
144
}
145
146
disable() {
147
const http = require('http');
148
149
// Only unwrap if it's actually wrapped
150
if (isWrapped(http.request)) {
151
shimmer.unwrap(http, 'request');
152
}
153
}
154
155
private _inspectWrapping() {
156
const http = require('http');
157
158
if (isWrapped(http.request)) {
159
const wrapped = http.request as ShimWrapped;
160
console.log('Function is wrapped:', wrapped.__wrapped);
161
console.log('Original function available:', typeof wrapped.__original);
162
console.log('Unwrap function available:', typeof wrapped.__unwrap);
163
164
// Could access original if needed
165
const originalRequest = wrapped.__original;
166
}
167
}
168
169
private _conditionalWrap(target: any, property: string, wrapper: Function) {
170
// Defensive wrapping pattern
171
if (target && typeof target[property] === 'function') {
172
if (!isWrapped(target[property])) {
173
shimmer.wrap(target, property, wrapper);
174
this._diag.debug(`Wrapped ${property}`);
175
} else {
176
this._diag.debug(`${property} already wrapped, skipping`);
177
}
178
} else {
179
this._diag.warn(`Cannot wrap ${property}: not a function or target invalid`);
180
}
181
}
182
}
183
```
184
185
### Advanced Safe Execution Patterns
186
187
Advanced patterns for instrumentation error handling and recovery.
188
189
**Usage Examples:**
190
191
```typescript
192
import { safeExecuteInTheMiddle } from "@opentelemetry/instrumentation";
193
194
class RobustInstrumentation extends InstrumentationBase {
195
private _metrics = {
196
instrumentationErrors: this.meter.createCounter('instrumentation_errors_total'),
197
executionTime: this.meter.createHistogram('instrumentation_execution_duration')
198
};
199
200
private _safeWrap(target: any, property: string, wrapper: Function) {
201
return safeExecuteInTheMiddle(
202
() => {
203
const original = target[property];
204
if (typeof original !== 'function') {
205
throw new Error(`${property} is not a function`);
206
}
207
208
target[property] = wrapper(original);
209
return target[property];
210
},
211
(error, result) => {
212
if (error) {
213
this._metrics.instrumentationErrors.add(1, {
214
operation: 'wrap',
215
target: property,
216
error: error.message
217
});
218
this._diag.error(`Failed to wrap ${property}:`, error);
219
} else {
220
this._diag.debug(`Successfully wrapped ${property}`);
221
}
222
},
223
true // Don't throw, allow application to continue
224
);
225
}
226
227
private _createTimedWrapper(name: string, original: Function) {
228
const instrumentation = this;
229
230
return function(this: any, ...args: any[]) {
231
const startTime = Date.now();
232
233
return safeExecuteInTheMiddle(
234
() => {
235
// Execute original with instrumentation
236
const span = instrumentation.tracer.startSpan(`${name}.call`);
237
try {
238
const result = original.apply(this, args);
239
span.setStatus({ code: 1 }); // OK
240
return result;
241
} catch (error) {
242
span.recordException(error as Error);
243
span.setStatus({ code: 2, message: (error as Error).message }); // ERROR
244
throw error;
245
} finally {
246
span.end();
247
}
248
},
249
(error, result) => {
250
const duration = Date.now() - startTime;
251
instrumentation._metrics.executionTime.record(duration, {
252
operation: name,
253
success: error ? 'false' : 'true'
254
});
255
256
if (error) {
257
instrumentation._diag.error(`Error in ${name}:`, error);
258
}
259
}
260
);
261
};
262
}
263
}
264
```
265
266
### Utility Composition
267
268
Combining safe execution with wrapping detection for robust instrumentation.
269
270
**Usage Example:**
271
272
```typescript
273
import { safeExecuteInTheMiddle, isWrapped } from "@opentelemetry/instrumentation";
274
275
class CompositeInstrumentation extends InstrumentationBase {
276
enable() {
277
this._instrumentModuleSafely('http', ['request', 'get']);
278
this._instrumentModuleSafely('https', ['request', 'get']);
279
}
280
281
disable() {
282
this._uninstrumentModuleSafely('http', ['request', 'get']);
283
this._uninstrumentModuleSafely('https', ['request', 'get']);
284
}
285
286
private _instrumentModuleSafely(moduleName: string, methods: string[]) {
287
safeExecuteInTheMiddle(
288
() => {
289
const module = require(moduleName);
290
291
methods.forEach(method => {
292
if (module[method] && !isWrapped(module[method])) {
293
shimmer.wrap(module, method, this._createWrapper(method));
294
this._diag.debug(`Wrapped ${moduleName}.${method}`);
295
} else if (isWrapped(module[method])) {
296
this._diag.debug(`${moduleName}.${method} already wrapped`);
297
} else {
298
this._diag.warn(`${moduleName}.${method} not found or not a function`);
299
}
300
});
301
302
return module;
303
},
304
(error) => {
305
if (error) {
306
this._diag.error(`Failed to instrument ${moduleName}:`, error);
307
}
308
},
309
true // Don't fail application startup on instrumentation errors
310
);
311
}
312
313
private _uninstrumentModuleSafely(moduleName: string, methods: string[]) {
314
safeExecuteInTheMiddle(
315
() => {
316
const module = require(moduleName);
317
318
methods.forEach(method => {
319
if (isWrapped(module[method])) {
320
shimmer.unwrap(module, method);
321
this._diag.debug(`Unwrapped ${moduleName}.${method}`);
322
}
323
});
324
325
return module;
326
},
327
(error) => {
328
if (error) {
329
this._diag.error(`Failed to uninstrument ${moduleName}:`, error);
330
}
331
},
332
true
333
);
334
}
335
336
private _createWrapper(methodName: string) {
337
return (original: Function) => {
338
const instrumentation = this;
339
340
return function(this: any, ...args: any[]) {
341
return safeExecuteInTheMiddleAsync(
342
async () => {
343
const span = instrumentation.tracer.startSpan(`${methodName}.call`);
344
try {
345
const result = await original.apply(this, args);
346
return result;
347
} finally {
348
span.end();
349
}
350
},
351
(error) => {
352
if (error) {
353
instrumentation._diag.error(`Error in wrapped ${methodName}:`, error);
354
}
355
}
356
);
357
};
358
};
359
}
360
}