or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context.mdindex.mdplatform.mdpropagation.mdtiming.mdtrace-state.mdutilities.mdvalidation.md

utilities.mddocs/

0

# Utility Functions

1

2

Common utility functions including deep object merging, timeout handling, URL pattern matching, and configuration parsing that support various SDK operations.

3

4

## Capabilities

5

6

### Object Utilities

7

8

Deep object merging with circular reference protection and array handling.

9

10

```typescript { .api }

11

/**

12

* Deep merge multiple objects with circular reference protection

13

* @param args - Objects to merge (later objects override earlier ones)

14

* @returns Merged object with all properties combined

15

*/

16

function merge(...args: any[]): any;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { merge } from "@opentelemetry/core";

23

24

// Basic object merging

25

const config1 = {

26

server: {

27

port: 3000,

28

host: "localhost"

29

},

30

features: ["logging", "metrics"]

31

};

32

33

const config2 = {

34

server: {

35

port: 8080,

36

ssl: true

37

},

38

features: ["tracing"],

39

database: {

40

url: "postgresql://localhost:5432/app"

41

}

42

};

43

44

const mergedConfig = merge(config1, config2);

45

console.log(mergedConfig);

46

// Result: {

47

// server: {

48

// port: 8080, // Overridden

49

// host: "localhost", // Preserved

50

// ssl: true // Added

51

// },

52

// features: ["tracing"], // Arrays are replaced, not merged

53

// database: {

54

// url: "postgresql://localhost:5432/app"

55

// }

56

// }

57

58

// Merge with multiple sources

59

const defaults = {

60

timeout: 5000,

61

retries: 3,

62

headers: {

63

"User-Agent": "OpenTelemetry/1.0"

64

}

65

};

66

67

const userConfig = {

68

timeout: 10000,

69

headers: {

70

"Authorization": "Bearer token123"

71

}

72

};

73

74

const envConfig = {

75

retries: 5,

76

headers: {

77

"X-Environment": "production"

78

}

79

};

80

81

const finalConfig = merge(defaults, userConfig, envConfig);

82

console.log(finalConfig);

83

// Result: {

84

// timeout: 10000,

85

// retries: 5,

86

// headers: {

87

// "User-Agent": "OpenTelemetry/1.0",

88

// "Authorization": "Bearer token123",

89

// "X-Environment": "production"

90

// }

91

// }

92

93

// Handle circular references safely

94

const obj1 = { name: "parent" };

95

const obj2 = { child: obj1 };

96

obj1.parent = obj2; // Creates circular reference

97

98

const obj3 = { version: "1.0" };

99

const merged = merge(obj1, obj3); // Won't cause infinite recursion

100

101

// Use in configuration management

102

class ConfigManager {

103

private defaultConfig = {

104

tracing: {

105

enabled: true,

106

samplingRate: 1.0,

107

exporters: ["console"]

108

},

109

metrics: {

110

enabled: false,

111

interval: 30000

112

}

113

};

114

115

loadConfig(userConfig: any, envConfig: any = {}) {

116

return merge(this.defaultConfig, userConfig, envConfig);

117

}

118

}

119

```

120

121

### Timeout Utilities

122

123

Promise timeout handling with custom error types.

124

125

```typescript { .api }

126

/**

127

* Error thrown when operations exceed their timeout

128

*/

129

class TimeoutError extends Error {

130

constructor(message?: string);

131

}

132

133

/**

134

* Add timeout to a promise, rejecting with TimeoutError if exceeded

135

* @param promise - Promise to add timeout to

136

* @param timeout - Timeout in milliseconds

137

* @returns Promise that resolves with original result or rejects with TimeoutError

138

*/

139

function callWithTimeout<T>(promise: Promise<T>, timeout: number): Promise<T>;

140

```

141

142

**Usage Examples:**

143

144

```typescript

145

import { callWithTimeout, TimeoutError } from "@opentelemetry/core";

146

147

// Add timeout to network requests

148

async function fetchWithTimeout(url: string, timeoutMs: number = 5000) {

149

const fetchPromise = fetch(url);

150

151

try {

152

const response = await callWithTimeout(fetchPromise, timeoutMs);

153

return response;

154

} catch (error) {

155

if (error instanceof TimeoutError) {

156

console.error(`Request to ${url} timed out after ${timeoutMs}ms`);

157

throw new Error(`Network timeout: ${url}`);

158

}

159

throw error; // Re-throw other errors

160

}

161

}

162

163

// Use with export operations

164

async function exportWithTimeout<T>(exportFn: () => Promise<T>, timeout: number = 10000): Promise<T> {

165

try {

166

return await callWithTimeout(exportFn(), timeout);

167

} catch (error) {

168

if (error instanceof TimeoutError) {

169

console.error("Export operation timed out");

170

return Promise.reject(new Error("Export timeout"));

171

}

172

throw error;

173

}

174

}

175

176

// Database operations with timeout

177

class DatabaseClient {

178

async query(sql: string, timeout: number = 30000) {

179

const queryPromise = this.executeQuery(sql);

180

181

try {

182

return await callWithTimeout(queryPromise, timeout);

183

} catch (error) {

184

if (error instanceof TimeoutError) {

185

console.error(`Database query timed out: ${sql}`);

186

// Cancel the query if possible

187

this.cancelQuery();

188

throw new Error("Database query timeout");

189

}

190

throw error;

191

}

192

}

193

194

private async executeQuery(sql: string): Promise<any[]> {

195

// Database query implementation

196

return [];

197

}

198

199

private cancelQuery(): void {

200

// Query cancellation implementation

201

}

202

}

203

204

// Batch operations with individual timeouts

205

async function processBatchWithTimeout<T, R>(

206

items: T[],

207

processor: (item: T) => Promise<R>,

208

itemTimeout: number = 5000

209

): Promise<Array<R | Error>> {

210

const results = await Promise.allSettled(

211

items.map(item => callWithTimeout(processor(item), itemTimeout))

212

);

213

214

return results.map((result, index) => {

215

if (result.status === 'fulfilled') {

216

return result.value;

217

} else {

218

const error = result.reason;

219

if (error instanceof TimeoutError) {

220

console.warn(`Item ${index} timed out`);

221

return new Error(`Item ${index} timeout`);

222

}

223

return error;

224

}

225

});

226

}

227

228

// Retry with timeout

229

async function retryWithTimeout<T>(

230

operation: () => Promise<T>,

231

maxRetries: number = 3,

232

timeout: number = 5000,

233

backoff: number = 1000

234

): Promise<T> {

235

let lastError: Error;

236

237

for (let attempt = 1; attempt <= maxRetries; attempt++) {

238

try {

239

return await callWithTimeout(operation(), timeout);

240

} catch (error) {

241

lastError = error instanceof Error ? error : new Error(String(error));

242

243

if (error instanceof TimeoutError) {

244

console.warn(`Attempt ${attempt} timed out`);

245

} else {

246

console.warn(`Attempt ${attempt} failed:`, error.message);

247

}

248

249

if (attempt < maxRetries) {

250

await new Promise(resolve => setTimeout(resolve, backoff * attempt));

251

}

252

}

253

}

254

255

throw lastError!;

256

}

257

```

258

259

### URL Utilities

260

261

URL pattern matching and filtering for instrumentation configuration.

262

263

```typescript { .api }

264

/**

265

* Check if URL matches a string or regex pattern

266

* @param url - URL to test

267

* @param urlToMatch - String or RegExp pattern to match against

268

* @returns True if URL matches the pattern

269

*/

270

function urlMatches(url: string, urlToMatch: string | RegExp): boolean;

271

272

/**

273

* Check if URL should be ignored based on ignore patterns

274

* @param url - URL to test

275

* @param ignoredUrls - Array of string or RegExp patterns to ignore

276

* @returns True if URL should be ignored

277

*/

278

function isUrlIgnored(url: string, ignoredUrls?: Array<string | RegExp>): boolean;

279

```

280

281

**Usage Examples:**

282

283

```typescript

284

import { urlMatches, isUrlIgnored } from "@opentelemetry/core";

285

286

// Basic URL matching

287

console.log(urlMatches("https://api.example.com/users", "https://api.example.com")); // true

288

console.log(urlMatches("https://api.example.com/users", /\/users$/)); // true

289

console.log(urlMatches("https://other.com/users", "https://api.example.com")); // false

290

291

// Configure ignored URLs for instrumentation

292

const ignoredUrls = [

293

"https://internal.monitoring.com",

294

/\/health$/,

295

/\/metrics$/,

296

"http://localhost:3000/debug"

297

];

298

299

// Check if URLs should be ignored

300

console.log(isUrlIgnored("https://api.example.com/users", ignoredUrls)); // false

301

console.log(isUrlIgnored("https://api.example.com/health", ignoredUrls)); // true

302

console.log(isUrlIgnored("https://internal.monitoring.com/status", ignoredUrls)); // true

303

304

// Use in HTTP instrumentation

305

class HttpInstrumentation {

306

private ignoredUrls: Array<string | RegExp>;

307

308

constructor(config: { ignoredUrls?: Array<string | RegExp> } = {}) {

309

this.ignoredUrls = config.ignoredUrls || [];

310

}

311

312

shouldInstrument(url: string): boolean {

313

return !isUrlIgnored(url, this.ignoredUrls);

314

}

315

316

instrumentRequest(url: string, options: any) {

317

if (!this.shouldInstrument(url)) {

318

console.log(`Skipping instrumentation for ignored URL: ${url}`);

319

return this.makeUninstrumentedRequest(url, options);

320

}

321

322

console.log(`Instrumenting request to: ${url}`);

323

return this.makeInstrumentedRequest(url, options);

324

}

325

326

private makeInstrumentedRequest(url: string, options: any) {

327

// Create span and instrument the request

328

}

329

330

private makeUninstrumentedRequest(url: string, options: any) {

331

// Make request without instrumentation

332

}

333

}

334

335

// Advanced pattern matching

336

const complexIgnorePatterns = [

337

// Ignore all internal services

338

/^https?:\/\/.*\.internal\./,

339

340

// Ignore health and monitoring endpoints

341

/\/(health|ping|status|metrics|ready|live)$/,

342

343

// Ignore static assets

344

/\.(css|js|png|jpg|gif|ico|svg|woff|woff2)$/,

345

346

// Ignore specific domains

347

"https://analytics.google.com",

348

"https://www.googletagmanager.com",

349

350

// Ignore development servers

351

/^https?:\/\/localhost/,

352

/^https?:\/\/127\.0\.0\.1/

353

];

354

355

function shouldTraceUrl(url: string): boolean {

356

return !isUrlIgnored(url, complexIgnorePatterns);

357

}

358

359

// URL categorization

360

function categorizeUrl(url: string): string {

361

const categories = [

362

{ pattern: /\/api\//, category: "api" },

363

{ pattern: /\/(health|ping|status)$/, category: "health" },

364

{ pattern: /\/(metrics|telemetry)$/, category: "monitoring" },

365

{ pattern: /\/static\//, category: "static" },

366

{ pattern: /\/admin\//, category: "admin" }

367

];

368

369

for (const { pattern, category } of categories) {

370

if (urlMatches(url, pattern)) {

371

return category;

372

}

373

}

374

375

return "unknown";

376

}

377

378

// Use in middleware

379

function createTracingMiddleware(ignoredUrls: Array<string | RegExp> = []) {

380

return (req: any, res: any, next: any) => {

381

const url = req.url;

382

383

if (isUrlIgnored(url, ignoredUrls)) {

384

// Skip tracing for ignored URLs

385

req.skipTracing = true;

386

} else {

387

// Enable tracing

388

req.urlCategory = categorizeUrl(url);

389

req.shouldTrace = true;

390

}

391

392

next();

393

};

394

}

395

```

396

397

### Callback Utilities

398

399

One-time callback execution with promise-based result handling.

400

401

```typescript { .api }

402

/**

403

* Ensures a callback is only invoked once, with promise-based result handling

404

*/

405

class BindOnceFuture<

406

R,

407

This = unknown,

408

T extends (this: This, ...args: unknown[]) => R = () => R

409

> {

410

/**

411

* Create a new BindOnceFuture

412

* @param callback - Function to bind and call once

413

* @param that - Context ('this' value) for the callback

414

*/

415

constructor(callback: T, that: This);

416

417

/** Whether the callback has been called */

418

readonly isCalled: boolean;

419

420

/** Promise that resolves with the callback result */

421

readonly promise: Promise<R>;

422

423

/**

424

* Call the callback (only the first call will execute)

425

* @param args - Arguments to pass to the callback

426

* @returns Promise resolving to callback result

427

*/

428

call(...args: Parameters<T>): Promise<R>;

429

}

430

```

431

432

**Usage Examples:**

433

434

```typescript

435

import { BindOnceFuture } from "@opentelemetry/core";

436

437

// Create one-time initialization function

438

class ServiceManager {

439

private initFuture: BindOnceFuture<void, this, () => void>;

440

441

constructor() {

442

this.initFuture = new BindOnceFuture(this.doInitialization, this);

443

}

444

445

async initialize(): Promise<void> {

446

// This will only run once, regardless of how many times initialize() is called

447

return this.initFuture.call();

448

}

449

450

private doInitialization(): void {

451

console.log("Initializing service...");

452

// Expensive initialization logic here

453

}

454

455

async getService(): Promise<any> {

456

await this.initialize(); // Safe to call multiple times

457

return this.service;

458

}

459

}

460

461

// One-time resource cleanup

462

class ResourceManager {

463

private cleanupFuture: BindOnceFuture<Promise<void>, this, () => Promise<void>>;

464

465

constructor() {

466

this.cleanupFuture = new BindOnceFuture(this.doCleanup, this);

467

}

468

469

async cleanup(): Promise<void> {

470

return this.cleanupFuture.call();

471

}

472

473

private async doCleanup(): Promise<void> {

474

console.log("Cleaning up resources...");

475

// Cleanup logic that should only run once

476

await this.closeConnections();

477

await this.releaseResources();

478

}

479

480

private async closeConnections(): Promise<void> {

481

// Implementation

482

}

483

484

private async releaseResources(): Promise<void> {

485

// Implementation

486

}

487

}

488

489

// One-time configuration loading

490

class ConfigLoader {

491

private loadConfigFuture: BindOnceFuture<any, this, () => any>;

492

493

constructor() {

494

this.loadConfigFuture = new BindOnceFuture(this.loadConfiguration, this);

495

}

496

497

async getConfig(): Promise<any> {

498

return this.loadConfigFuture.call();

499

}

500

501

private loadConfiguration(): any {

502

console.log("Loading configuration...");

503

// Expensive config loading that should only happen once

504

return {

505

database: { url: "postgresql://localhost:5432/app" },

506

redis: { url: "redis://localhost:6379" },

507

features: ["tracing", "metrics"]

508

};

509

}

510

}

511

512

// Multiple callers, single execution

513

async function demonstrateUsage() {

514

const configLoader = new ConfigLoader();

515

516

// Multiple concurrent calls - only loads once

517

const [config1, config2, config3] = await Promise.all([

518

configLoader.getConfig(),

519

configLoader.getConfig(),

520

configLoader.getConfig()

521

]);

522

523

console.log("All configs are identical:",

524

config1 === config2 && config2 === config3); // true

525

526

// Check if already called

527

console.log("Config was loaded:", configLoader.loadConfigFuture?.isCalled); // true

528

}

529

530

// Error handling with BindOnceFuture

531

class ErrorProneInitializer {

532

private initFuture: BindOnceFuture<string, this, () => string>;

533

534

constructor() {

535

this.initFuture = new BindOnceFuture(this.riskyInitialization, this);

536

}

537

538

async initialize(): Promise<string> {

539

try {

540

return await this.initFuture.call();

541

} catch (error) {

542

console.error("Initialization failed:", error);

543

throw error;

544

}

545

}

546

547

private riskyInitialization(): string {

548

if (Math.random() < 0.5) {

549

throw new Error("Random initialization failure");

550

}

551

return "Successfully initialized";

552

}

553

}

554

555

// Use with different callback signatures

556

function createCounter(): BindOnceFuture<number, object, (start: number) => number> {

557

const context = {};

558

559

return new BindOnceFuture((start: number) => {

560

console.log(`Counter starting at ${start}`);

561

return start;

562

}, context);

563

}

564

565

const counter = createCounter();

566

counter.call(10).then(result => console.log("Counter result:", result)); // 10

567

counter.call(20).then(result => console.log("Counter result:", result)); // Still 10

568

```

569

570

### Configuration Utilities

571

572

Configuration parsing and type conversion utilities.

573

574

```typescript { .api }

575

/**

576

* Convert string value to DiagLogLevel enum value

577

* Returns undefined only for null/undefined input.

578

* For invalid string values, logs a warning and returns DiagLogLevel.INFO as fallback.

579

* @param value - String representation of log level

580

* @returns DiagLogLevel enum value or undefined if input is null/undefined

581

*/

582

function diagLogLevelFromString(value: string | undefined): DiagLogLevel | undefined;

583

```

584

585

**Usage Examples:**

586

587

```typescript

588

import { diagLogLevelFromString } from "@opentelemetry/core";

589

import { DiagLogLevel, diag } from "@opentelemetry/api";

590

591

// Parse log level from environment variable

592

const logLevelStr = process.env.OTEL_LOG_LEVEL || "info";

593

const logLevel = diagLogLevelFromString(logLevelStr);

594

595

if (logLevel !== undefined) {

596

diag.setLogger(console, logLevel);

597

console.log(`Log level set to: ${DiagLogLevel[logLevel]}`);

598

} else {

599

console.warn(`Invalid log level: ${logLevelStr}, using default`);

600

diag.setLogger(console, DiagLogLevel.INFO);

601

}

602

603

// Configuration parser with validation

604

class TelemetryConfig {

605

static parseConfig(env: Record<string, string | undefined>) {

606

return {

607

logLevel: diagLogLevelFromString(env.OTEL_LOG_LEVEL) ?? DiagLogLevel.INFO,

608

serviceName: env.OTEL_SERVICE_NAME ?? "unknown-service",

609

serviceVersion: env.OTEL_SERVICE_VERSION ?? "1.0.0",

610

environment: env.OTEL_ENVIRONMENT ?? "development"

611

};

612

}

613

614

static setDiagnostics(config: ReturnType<typeof TelemetryConfig.parseConfig>) {

615

diag.setLogger(console, config.logLevel);

616

617

// Log configuration at startup

618

diag.info("OpenTelemetry configuration:", {

619

serviceName: config.serviceName,

620

serviceVersion: config.serviceVersion,

621

environment: config.environment,

622

logLevel: DiagLogLevel[config.logLevel]

623

});

624

}

625

}

626

627

// Usage in application startup

628

const config = TelemetryConfig.parseConfig(process.env);

629

TelemetryConfig.setDiagnostics(config);

630

631

// Test different log level formats

632

const testLogLevels = [

633

"debug", "DEBUG", "Debug",

634

"info", "INFO", "Info",

635

"warn", "WARN", "Warn",

636

"error", "ERROR", "Error",

637

"none", "NONE", "None",

638

"all", "ALL", "All",

639

"invalid", "", undefined

640

];

641

642

testLogLevels.forEach(level => {

643

const parsed = diagLogLevelFromString(level);

644

console.log(`"${level}" -> ${parsed !== undefined ? DiagLogLevel[parsed] : 'undefined'}`);

645

});

646

647

// Dynamic log level adjustment

648

function setLogLevel(levelStr: string): boolean {

649

const level = diagLogLevelFromString(levelStr);

650

651

if (level !== undefined) {

652

diag.setLogger(console, level);

653

diag.info(`Log level changed to: ${DiagLogLevel[level]}`);

654

return true;

655

} else {

656

diag.warn(`Invalid log level: ${levelStr}`);

657

return false;

658

}

659

}

660

661

// Runtime configuration updates

662

function updateTelemetryConfig(updates: Record<string, string>) {

663

if (updates.OTEL_LOG_LEVEL) {

664

const success = setLogLevel(updates.OTEL_LOG_LEVEL);

665

if (!success) {

666

delete updates.OTEL_LOG_LEVEL; // Remove invalid value

667

}

668

}

669

670

// Apply other configuration updates

671

return updates;

672

}

673

```

674

675

### Internal Utilities

676

677

Internal utilities namespace for advanced SDK operations.

678

679

```typescript { .api }

680

/**

681

* Internal utilities namespace (use with caution)

682

*/

683

const internal: {

684

/** Internal export utilities */

685

_export: any;

686

};

687

```

688

689

**Usage Examples:**

690

691

```typescript

692

import { internal } from "@opentelemetry/core";

693

694

// Access internal export utilities (advanced usage)

695

// Note: Internal APIs are subject to change without notice

696

const exportUtils = internal._export;

697

698

// These are internal utilities used by other OpenTelemetry packages

699

// Use with caution as they may change without notice

700

console.log("Internal export utilities available:", !!exportUtils);

701

```

702

```