or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdplatform.mdsamplers.mdspan-exporters.mdspan-processors.mdtracer-provider.md

span-exporters.mddocs/

0

# Span Exporters

1

2

Span exporters handle the final output of span data, sending it to various destinations such as console output, in-memory storage, or external tracing systems. OpenTelemetry SDK Trace Base provides built-in exporters and interfaces for creating custom exporters.

3

4

## Capabilities

5

6

### Core SpanExporter Interface

7

8

All span exporters implement the base SpanExporter interface for consistent export behavior.

9

10

```typescript { .api }

11

/**

12

* Interface for span export implementations

13

*/

14

interface SpanExporter {

15

/**

16

* Export a batch of spans

17

* @param spans - Array of ReadableSpan objects to export

18

* @param resultCallback - Callback to invoke when export completes

19

*/

20

export(

21

spans: ReadableSpan[],

22

resultCallback: (result: ExportResult) => void

23

): void;

24

25

/**

26

* Shutdown the exporter and release resources

27

* @returns Promise that resolves when shutdown is complete

28

*/

29

shutdown(): Promise<void>;

30

31

/**

32

* Force flush any pending export operations (optional)

33

* @returns Promise that resolves when flush is complete

34

*/

35

forceFlush?(): Promise<void>;

36

}

37

38

/**

39

* Result of an export operation

40

*/

41

interface ExportResult {

42

code: ExportResultCode;

43

error?: Error;

44

}

45

46

/**

47

* Export result codes indicating success or failure

48

*/

49

enum ExportResultCode {

50

SUCCESS = 0,

51

FAILED = 1

52

}

53

```

54

55

### ReadableSpan Interface

56

57

Spans provided to exporters implement the ReadableSpan interface with complete span data.

58

59

```typescript { .api }

60

/**

61

* Interface representing a completed span for export

62

*/

63

interface ReadableSpan {

64

/** Name of the span */

65

readonly name: string;

66

67

/** Kind of the span (CLIENT, SERVER, etc.) */

68

readonly kind: SpanKind;

69

70

/** Function that returns the span context */

71

readonly spanContext: () => SpanContext;

72

73

/** Parent span context if this span has a parent */

74

readonly parentSpanContext?: SpanContext;

75

76

/** High-resolution start time */

77

readonly startTime: HrTime;

78

79

/** High-resolution end time */

80

readonly endTime: HrTime;

81

82

/** Status of the span (OK, ERROR, UNSET) */

83

readonly status: SpanStatus;

84

85

/** Attributes attached to the span */

86

readonly attributes: Attributes;

87

88

/** Links to other spans */

89

readonly links: Link[];

90

91

/** Events that occurred during the span */

92

readonly events: TimedEvent[];

93

94

/** Calculated duration of the span */

95

readonly duration: HrTime;

96

97

/** Whether the span has ended */

98

readonly ended: boolean;

99

100

/** Resource associated with the span */

101

readonly resource: Resource;

102

103

/** Instrumentation scope that created the span */

104

readonly instrumentationScope: InstrumentationScope;

105

106

/** Number of attributes dropped due to limits */

107

readonly droppedAttributesCount: number;

108

109

/** Number of events dropped due to limits */

110

readonly droppedEventsCount: number;

111

112

/** Number of links dropped due to limits */

113

readonly droppedLinksCount: number;

114

}

115

116

/**

117

* Timed event within a span

118

*/

119

interface TimedEvent {

120

/** High-resolution time when event occurred */

121

time: HrTime;

122

123

/** Name of the event */

124

name: string;

125

126

/** Attributes associated with the event */

127

attributes?: Attributes;

128

129

/** Number of attributes dropped from the event */

130

droppedAttributesCount?: number;

131

}

132

```

133

134

### ConsoleSpanExporter

135

136

Exporter that outputs span information to the console, useful for development and debugging.

137

138

```typescript { .api }

139

/**

140

* Span exporter that prints spans to console for diagnostic purposes

141

*/

142

class ConsoleSpanExporter implements SpanExporter {

143

/**

144

* Export spans by printing them to console

145

* @param spans - Array of spans to print

146

* @param resultCallback - Called with SUCCESS after printing

147

*/

148

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;

149

150

/**

151

* Shutdown the exporter (no-op for console exporter)

152

* @returns Promise that resolves immediately

153

*/

154

shutdown(): Promise<void>;

155

156

/**

157

* Force flush (no-op for console exporter)

158

* @returns Promise that resolves immediately

159

*/

160

forceFlush(): Promise<void>;

161

}

162

```

163

164

**Output Format:**

165

The console exporter outputs structured span information including:

166

- Span name, trace ID, span ID, and parent span ID

167

- Start time, end time, and duration

168

- Span kind and status

169

- Attributes, events, and links

170

- Resource and instrumentation scope information

171

172

**Usage Examples:**

173

174

```typescript

175

import { BasicTracerProvider, BatchSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';

176

177

// Basic console output setup

178

const provider = new BasicTracerProvider({

179

spanProcessors: [

180

new BatchSpanProcessor(new ConsoleSpanExporter())

181

]

182

});

183

184

// Development setup with immediate console output

185

const devProvider = new BasicTracerProvider({

186

spanProcessors: [

187

new SimpleSpanProcessor(new ConsoleSpanExporter())

188

]

189

});

190

191

// Conditional console output

192

const provider = new BasicTracerProvider({

193

spanProcessors: [

194

...(process.env.DEBUG === 'true' ? [

195

new SimpleSpanProcessor(new ConsoleSpanExporter())

196

] : []),

197

new BatchSpanProcessor(new ConsoleSpanExporter()) // Replace with external exporter as needed

198

]

199

});

200

```

201

202

### InMemorySpanExporter

203

204

Exporter that stores spans in memory, useful for testing, debugging, and collecting metrics.

205

206

```typescript { .api }

207

/**

208

* Span exporter that stores spans in memory for testing purposes

209

*/

210

class InMemorySpanExporter implements SpanExporter {

211

/**

212

* Export spans by storing them in memory

213

* @param spans - Array of spans to store

214

* @param resultCallback - Called with SUCCESS after storing

215

*/

216

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;

217

218

/**

219

* Shutdown the exporter and mark as stopped

220

* @returns Promise that resolves immediately

221

*/

222

shutdown(): Promise<void>;

223

224

/**

225

* Force flush (no-op for in-memory exporter)

226

* @returns Promise that resolves immediately

227

*/

228

forceFlush(): Promise<void>;

229

230

/**

231

* Clear all stored spans

232

*/

233

reset(): void;

234

235

/**

236

* Retrieve all stored spans

237

* @returns Array of all spans that have been exported

238

*/

239

getFinishedSpans(): ReadableSpan[];

240

241

/** Whether the exporter has been stopped */

242

protected _stopped: boolean;

243

}

244

```

245

246

**Usage Examples:**

247

248

```typescript

249

import { BasicTracerProvider, BatchSpanProcessor, InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';

250

251

// Basic in-memory storage

252

const exporter = new InMemorySpanExporter();

253

const provider = new BasicTracerProvider({

254

spanProcessors: [

255

new BatchSpanProcessor(exporter)

256

]

257

});

258

259

// Create some spans

260

const tracer = provider.getTracer('test-service');

261

const span1 = tracer.startSpan('operation1');

262

span1.end();

263

const span2 = tracer.startSpan('operation2');

264

span2.end();

265

266

// Force flush to ensure spans are exported

267

await provider.forceFlush();

268

269

// Retrieve stored spans for analysis

270

const spans = exporter.getFinishedSpans();

271

console.log(`Captured ${spans.length} spans`);

272

spans.forEach(span => {

273

console.log(`Span: ${span.name}, Duration: ${span.duration}`);

274

});

275

276

// Clear stored spans

277

exporter.reset();

278

console.log(`Spans after reset: ${exporter.getFinishedSpans().length}`);

279

```

280

281

### Testing with InMemorySpanExporter

282

283

The in-memory exporter is particularly useful for testing tracing behavior.

284

285

**Usage Examples:**

286

287

```typescript

288

import { BasicTracerProvider, BatchSpanProcessor, InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';

289

290

describe('Tracing Tests', () => {

291

let provider: BasicTracerProvider;

292

let exporter: InMemorySpanExporter;

293

let tracer: Tracer;

294

295

beforeEach(() => {

296

exporter = new InMemorySpanExporter();

297

provider = new BasicTracerProvider({

298

spanProcessors: [new BatchSpanProcessor(exporter)]

299

});

300

tracer = provider.getTracer('test-tracer');

301

});

302

303

afterEach(async () => {

304

await provider.shutdown();

305

});

306

307

it('should capture span attributes', async () => {

308

const span = tracer.startSpan('test-operation');

309

span.setAttribute('user.id', '12345');

310

span.setAttribute('operation.type', 'read');

311

span.end();

312

313

await provider.forceFlush();

314

315

const spans = exporter.getFinishedSpans();

316

expect(spans).toHaveLength(1);

317

expect(spans[0].attributes['user.id']).toBe('12345');

318

expect(spans[0].attributes['operation.type']).toBe('read');

319

});

320

321

it('should capture span events', async () => {

322

const span = tracer.startSpan('test-operation');

323

span.addEvent('processing-started');

324

span.addEvent('data-loaded', { recordCount: 100 });

325

span.end();

326

327

await provider.forceFlush();

328

329

const spans = exporter.getFinishedSpans();

330

expect(spans[0].events).toHaveLength(2);

331

expect(spans[0].events[0].name).toBe('processing-started');

332

expect(spans[0].events[1].name).toBe('data-loaded');

333

expect(spans[0].events[1].attributes?.recordCount).toBe(100);

334

});

335

336

it('should track span hierarchy', async () => {

337

const parentSpan = tracer.startSpan('parent-operation');

338

const childSpan = tracer.startSpan('child-operation', {

339

parent: parentSpan.spanContext()

340

});

341

342

childSpan.end();

343

parentSpan.end();

344

345

await provider.forceFlush();

346

347

const spans = exporter.getFinishedSpans();

348

expect(spans).toHaveLength(2);

349

350

const child = spans.find(s => s.name === 'child-operation');

351

const parent = spans.find(s => s.name === 'parent-operation');

352

353

expect(child?.parentSpanContext?.spanId).toBe(parent?.spanContext().spanId);

354

});

355

});

356

```

357

358

### Custom Exporter Implementation

359

360

You can create custom exporters by implementing the SpanExporter interface.

361

362

**Usage Examples:**

363

364

```typescript

365

import { SpanExporter, ReadableSpan, ExportResult, ExportResultCode } from '@opentelemetry/sdk-trace-base';

366

367

// Custom exporter that sends spans to a webhook

368

class WebhookSpanExporter implements SpanExporter {

369

constructor(private webhookUrl: string, private apiKey: string) {}

370

371

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {

372

const payload = {

373

spans: spans.map(span => ({

374

name: span.name,

375

traceId: span.spanContext().traceId,

376

spanId: span.spanContext().spanId,

377

parentSpanId: span.parentSpanContext?.spanId,

378

startTime: span.startTime,

379

endTime: span.endTime,

380

attributes: span.attributes,

381

status: span.status

382

}))

383

};

384

385

fetch(this.webhookUrl, {

386

method: 'POST',

387

headers: {

388

'Content-Type': 'application/json',

389

'Authorization': `Bearer ${this.apiKey}`

390

},

391

body: JSON.stringify(payload)

392

})

393

.then(response => {

394

if (response.ok) {

395

resultCallback({ code: ExportResultCode.SUCCESS });

396

} else {

397

resultCallback({

398

code: ExportResultCode.FAILED,

399

error: new Error(`HTTP ${response.status}`)

400

});

401

}

402

})

403

.catch(error => {

404

resultCallback({ code: ExportResultCode.FAILED, error });

405

});

406

}

407

408

async shutdown(): Promise<void> {

409

// Cleanup resources if needed

410

}

411

412

async forceFlush(): Promise<void> {

413

// Force any pending operations if needed

414

}

415

}

416

417

// Custom exporter that filters spans

418

class FilteredSpanExporter implements SpanExporter {

419

constructor(

420

private baseExporter: SpanExporter,

421

private filter: (span: ReadableSpan) => boolean

422

) {}

423

424

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {

425

const filteredSpans = spans.filter(this.filter);

426

427

if (filteredSpans.length === 0) {

428

resultCallback({ code: ExportResultCode.SUCCESS });

429

return;

430

}

431

432

this.baseExporter.export(filteredSpans, resultCallback);

433

}

434

435

async shutdown(): Promise<void> {

436

return this.baseExporter.shutdown();

437

}

438

439

async forceFlush(): Promise<void> {

440

return this.baseExporter.forceFlush?.();

441

}

442

}

443

444

// Use custom exporters

445

const provider = new BasicTracerProvider({

446

spanProcessors: [

447

new BatchSpanProcessor(

448

new WebhookSpanExporter('https://my-api.com/traces', 'api-key-123')

449

),

450

new BatchSpanProcessor(

451

new FilteredSpanExporter(

452

new ConsoleSpanExporter(),

453

span => span.status.code === 2 // ERROR status code

454

)

455

)

456

]

457

});

458

```

459

460

### Error Handling

461

462

Exporters should handle errors gracefully and report them through the result callback.

463

464

**Usage Examples:**

465

466

```typescript

467

class RobustSpanExporter implements SpanExporter {

468

constructor(private backendUrl: string, private retryAttempts = 3) {}

469

470

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {

471

this.exportWithRetry(spans, this.retryAttempts, resultCallback);

472

}

473

474

private async exportWithRetry(

475

spans: ReadableSpan[],

476

attemptsLeft: number,

477

resultCallback: (result: ExportResult) => void

478

): Promise<void> {

479

try {

480

const response = await fetch(this.backendUrl, {

481

method: 'POST',

482

headers: { 'Content-Type': 'application/json' },

483

body: JSON.stringify({ spans })

484

});

485

486

if (response.ok) {

487

resultCallback({ code: ExportResultCode.SUCCESS });

488

} else if (response.status >= 500 && attemptsLeft > 1) {

489

// Retry on server errors

490

setTimeout(() => {

491

this.exportWithRetry(spans, attemptsLeft - 1, resultCallback);

492

}, 1000);

493

} else {

494

resultCallback({

495

code: ExportResultCode.FAILED,

496

error: new Error(`Export failed: ${response.status}`)

497

});

498

}

499

} catch (error) {

500

if (attemptsLeft > 1) {

501

setTimeout(() => {

502

this.exportWithRetry(spans, attemptsLeft - 1, resultCallback);

503

}, 1000);

504

} else {

505

resultCallback({

506

code: ExportResultCode.FAILED,

507

error: error as Error

508

});

509

}

510

}

511

}

512

513

async shutdown(): Promise<void> {

514

// Cleanup resources

515

}

516

}

517

```