0
# Validation & Error Handling
1
2
Attribute validation, sanitization, and comprehensive error handling infrastructure with global error handler support and logging integration.
3
4
## Capabilities
5
6
### Attribute Validation & Sanitization
7
8
Functions for validating and cleaning OpenTelemetry attributes according to specification requirements.
9
10
```typescript { .api }
11
/**
12
* Sanitize attributes object, removing invalid keys and values
13
* @param attributes - Raw attributes object to sanitize
14
* @returns Clean Attributes object with valid key-value pairs
15
*/
16
function sanitizeAttributes(attributes: unknown): Attributes;
17
18
/**
19
* Check if a value is a valid OpenTelemetry attribute value
20
* @param val - Value to validate
21
* @returns True if value is valid AttributeValue (string, number, boolean, or homogeneous array)
22
*/
23
function isAttributeValue(val: unknown): val is AttributeValue;
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import { sanitizeAttributes, isAttributeValue } from "@opentelemetry/core";
30
31
// Sanitize mixed attributes object
32
const rawAttributes = {
33
"user.id": "12345", // Valid string
34
"request.size": 1024, // Valid number
35
"cache.enabled": true, // Valid boolean
36
"tags": ["web", "api"], // Valid string array
37
"": "invalid-empty-key", // Invalid: empty key
38
"valid.key": undefined, // Invalid: undefined value
39
"another.key": { nested: "object" }, // Invalid: object value
40
"mixed.array": ["string", 123], // Invalid: mixed array types
41
"valid.null": null // Valid: null is allowed
42
};
43
44
const cleanAttributes = sanitizeAttributes(rawAttributes);
45
console.log(cleanAttributes);
46
// Result: {
47
// "user.id": "12345",
48
// "request.size": 1024,
49
// "cache.enabled": true,
50
// "tags": ["web", "api"],
51
// "valid.null": null
52
// }
53
54
// Validate individual values
55
console.log(isAttributeValue("string")); // true
56
console.log(isAttributeValue(42)); // true
57
console.log(isAttributeValue(true)); // true
58
console.log(isAttributeValue(null)); // true
59
console.log(isAttributeValue([1, 2, 3])); // true (homogeneous array)
60
console.log(isAttributeValue(["a", "b"])); // true (homogeneous array)
61
console.log(isAttributeValue([1, "mixed"])); // false (mixed array)
62
console.log(isAttributeValue({})); // false (object)
63
console.log(isAttributeValue(undefined)); // false (undefined)
64
65
// Use in span creation
66
function createSpanWithAttributes(name: string, rawAttrs: unknown) {
67
const cleanAttrs = sanitizeAttributes(rawAttrs);
68
const span = trace.getTracer("my-service").startSpan(name, {
69
attributes: cleanAttrs
70
});
71
return span;
72
}
73
74
// Safe attribute setting
75
function setSpanAttribute(span: Span, key: string, value: unknown) {
76
if (key && key.length > 0 && isAttributeValue(value)) {
77
span.setAttribute(key, value);
78
} else {
79
console.warn(`Invalid attribute: ${key}=${value}`);
80
}
81
}
82
```
83
84
### Error Handling Infrastructure
85
86
Global error handling system with pluggable error handlers and default logging implementation.
87
88
```typescript { .api }
89
/**
90
* Error handler function type
91
*/
92
type ErrorHandler = (ex: Exception) => void;
93
94
/**
95
* Set the global error handler for OpenTelemetry operations
96
* @param handler - Error handler function to use globally
97
*/
98
function setGlobalErrorHandler(handler: ErrorHandler): void;
99
100
/**
101
* Invoke the global error handler with an exception
102
* @param ex - Exception to handle
103
*/
104
function globalErrorHandler(ex: Exception): void;
105
106
/**
107
* Get a logging error handler that outputs errors using diag logger
108
* @returns ErrorHandler that logs errors with JSON serialization
109
*/
110
function loggingErrorHandler(): ErrorHandler;
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import {
117
setGlobalErrorHandler,
118
globalErrorHandler,
119
loggingErrorHandler
120
} from "@opentelemetry/core";
121
import { diag } from "@opentelemetry/api";
122
123
// Set up custom error handler
124
const customErrorHandler = (error: Exception) => {
125
console.error("OpenTelemetry Error:", error);
126
127
// Send to external error tracking service
128
if (typeof window !== 'undefined' && window.errorTracker) {
129
window.errorTracker.captureException(error);
130
}
131
132
// Log structured error information
133
const errorInfo = {
134
message: error.message || "Unknown error",
135
stack: error.stack,
136
timestamp: new Date().toISOString(),
137
source: "opentelemetry-core"
138
};
139
140
console.error("Error details:", JSON.stringify(errorInfo, null, 2));
141
};
142
143
// Set the global error handler
144
setGlobalErrorHandler(customErrorHandler);
145
146
// Use default logging error handler
147
const loggingHandler = loggingErrorHandler();
148
setGlobalErrorHandler(loggingHandler);
149
150
// Trigger error handling
151
try {
152
throw new Error("Something went wrong in telemetry");
153
} catch (error) {
154
globalErrorHandler(error); // Will use the configured handler
155
}
156
157
// Error handling in async operations
158
async function riskyTelemetryOperation() {
159
try {
160
// Some telemetry operation that might fail
161
await exportData();
162
} catch (error) {
163
// Use global error handler
164
globalErrorHandler(error);
165
166
// Still throw for caller to handle business logic
167
throw error;
168
}
169
}
170
171
// Safe error handler wrapper
172
function safeErrorHandler(handler: ErrorHandler): ErrorHandler {
173
return (error: Exception) => {
174
try {
175
handler(error);
176
} catch (handlerError) {
177
// Fallback to console if handler itself fails
178
console.error("Error handler failed:", handlerError);
179
console.error("Original error:", error);
180
}
181
};
182
}
183
184
setGlobalErrorHandler(safeErrorHandler(customErrorHandler));
185
```
186
187
### Export Result Types
188
189
Types and enums for representing the results of export operations.
190
191
```typescript { .api }
192
/**
193
* Result codes for export operations
194
*/
195
enum ExportResultCode {
196
/** Export operation succeeded */
197
SUCCESS,
198
/** Export operation failed */
199
FAILED
200
}
201
202
/**
203
* Result of an export operation
204
*/
205
interface ExportResult {
206
/** Result status code */
207
code: ExportResultCode;
208
/** Optional error information if export failed */
209
error?: Error;
210
}
211
```
212
213
**Usage Examples:**
214
215
```typescript
216
import { ExportResult, ExportResultCode } from "@opentelemetry/core";
217
218
// Create export results
219
const successResult: ExportResult = {
220
code: ExportResultCode.SUCCESS
221
};
222
223
const failureResult: ExportResult = {
224
code: ExportResultCode.FAILED,
225
error: new Error("Network timeout")
226
};
227
228
// Use in exporter implementations
229
class CustomExporter {
230
async export(data: any[]): Promise<ExportResult> {
231
try {
232
await this.sendData(data);
233
return { code: ExportResultCode.SUCCESS };
234
} catch (error) {
235
return {
236
code: ExportResultCode.FAILED,
237
error: error instanceof Error ? error : new Error(String(error))
238
};
239
}
240
}
241
242
private async sendData(data: any[]): Promise<void> {
243
// Implementation
244
}
245
}
246
247
// Handle export results
248
async function performExport(exporter: CustomExporter, data: any[]) {
249
const result = await exporter.export(data);
250
251
switch (result.code) {
252
case ExportResultCode.SUCCESS:
253
console.log("Export successful");
254
break;
255
256
case ExportResultCode.FAILED:
257
console.error("Export failed:", result.error?.message);
258
259
// Use global error handler for failed exports
260
if (result.error) {
261
globalErrorHandler(result.error);
262
}
263
264
// Implement retry logic
265
await scheduleRetry(data);
266
break;
267
}
268
}
269
270
// Batch export with error handling
271
async function batchExport(items: any[], batchSize: number = 100): Promise<ExportResult[]> {
272
const results: ExportResult[] = [];
273
274
for (let i = 0; i < items.length; i += batchSize) {
275
const batch = items.slice(i, i + batchSize);
276
277
try {
278
const result = await exportBatch(batch);
279
results.push(result);
280
281
if (result.code === ExportResultCode.FAILED) {
282
console.warn(`Batch ${i / batchSize + 1} failed:`, result.error?.message);
283
}
284
} catch (error) {
285
const failureResult: ExportResult = {
286
code: ExportResultCode.FAILED,
287
error: error instanceof Error ? error : new Error(String(error))
288
};
289
290
results.push(failureResult);
291
globalErrorHandler(failureResult.error!);
292
}
293
}
294
295
return results;
296
}
297
298
// Result aggregation
299
function aggregateResults(results: ExportResult[]): {
300
successCount: number;
301
failureCount: number;
302
errors: Error[];
303
} {
304
return results.reduce((acc, result) => {
305
if (result.code === ExportResultCode.SUCCESS) {
306
acc.successCount++;
307
} else {
308
acc.failureCount++;
309
if (result.error) {
310
acc.errors.push(result.error);
311
}
312
}
313
return acc;
314
}, { successCount: 0, failureCount: 0, errors: [] as Error[] });
315
}
316
```
317
318
### Instrumentation Scope Types
319
320
Types for defining instrumentation library metadata.
321
322
```typescript { .api }
323
/**
324
* An instrumentation scope consists of the name and optional version
325
* used to obtain a tracer or meter from a provider
326
*/
327
interface InstrumentationScope {
328
/** Instrumentation library name */
329
readonly name: string;
330
/** Optional instrumentation library version */
331
readonly version?: string;
332
/** Optional schema URL */
333
readonly schemaUrl?: string;
334
}
335
```
336
337
**Usage Examples:**
338
339
```typescript
340
import { InstrumentationScope } from "@opentelemetry/core";
341
342
// Define instrumentation scopes
343
const webServerScope: InstrumentationScope = {
344
name: "my-web-server",
345
version: "1.2.0",
346
schemaUrl: "https://opentelemetry.io/schemas/1.21.0"
347
};
348
349
const databaseScope: InstrumentationScope = {
350
name: "database-client",
351
version: "2.5.1"
352
};
353
354
// Use with tracer provider
355
const tracer = trace.getTracer(
356
webServerScope.name,
357
webServerScope.version,
358
{ schemaUrl: webServerScope.schemaUrl }
359
);
360
361
// Create spans with scope information
362
const span = tracer.startSpan("handle-request");
363
span.setAttribute("instrumentation.name", webServerScope.name);
364
span.setAttribute("instrumentation.version", webServerScope.version || "unknown");
365
366
// Validate scope information
367
function validateScope(scope: InstrumentationScope): boolean {
368
if (!scope.name || scope.name.trim().length === 0) {
369
console.error("Instrumentation scope must have a non-empty name");
370
return false;
371
}
372
373
if (scope.version && scope.version.trim().length === 0) {
374
console.warn("Empty version string provided for scope:", scope.name);
375
}
376
377
if (scope.schemaUrl && !isValidUrl(scope.schemaUrl)) {
378
console.warn("Invalid schema URL provided:", scope.schemaUrl);
379
}
380
381
return true;
382
}
383
384
// Helper function for URL validation
385
function isValidUrl(url: string): boolean {
386
try {
387
new URL(url);
388
return true;
389
} catch {
390
return false;
391
}
392
}
393
394
// Use scopes in instrumentation libraries
395
class MyInstrumentation {
396
private scope: InstrumentationScope;
397
398
constructor(scope: InstrumentationScope) {
399
if (!validateScope(scope)) {
400
throw new Error("Invalid instrumentation scope");
401
}
402
this.scope = scope;
403
}
404
405
createTracer() {
406
return trace.getTracer(
407
this.scope.name,
408
this.scope.version,
409
{ schemaUrl: this.scope.schemaUrl }
410
);
411
}
412
413
createMeter() {
414
return metrics.getMeter(
415
this.scope.name,
416
this.scope.version,
417
{ schemaUrl: this.scope.schemaUrl }
418
);
419
}
420
}
421
```