shutdown()

Gracefully shutdown the tracing client and flush all pending spans

Overview

shutdown() flushes pending spans and gracefully closes the tracer. Use it when your application is intentionally stopping tracing, such as in long-running servers, workers, signal handlers, or test teardown. Short-lived Node.js scripts do not need shutdown() solely to export their final spans because the SDK drains pending spans on normal process exit.

Signature

1async shutdown(): Promise<void>

Basic Usage

1import { RespanTelemetry } from '@respan/tracing';
2
3const respanAi = new RespanTelemetry({
4 apiKey: process.env.RESPAN_API_KEY,
5 appName: 'my-app'
6});
7
8await respanAi.initialize();
9
10await respanAi.withWorkflow(
11 { name: 'my_workflow' },
12 async () => {
13 return await processData();
14 }
15);
16
17// Close the tracer when this process is done tracing
18await respanAi.shutdown();
19console.log('Tracing shutdown complete');

Complete Application Lifecycle

1async function main() {
2 const respanAi = new RespanTelemetry({
3 apiKey: process.env.RESPAN_API_KEY,
4 appName: 'my-app'
5 });
6
7 try {
8 // Initialize
9 await respanAi.initialize();
10 console.log('Tracing initialized');
11
12 // Run your application
13 await respanAi.withWorkflow(
14 { name: 'main_workflow' },
15 async () => {
16 return await runApplication();
17 }
18 );
19 } catch (error) {
20 console.error('Application error:', error);
21 } finally {
22 // Always shutdown, even on error
23 await respanAi.shutdown();
24 console.log('Tracing shutdown complete');
25 }
26}
27
28main();

Graceful Shutdown on Signals

1const respanAi = new RespanTelemetry({
2 apiKey: process.env.RESPAN_API_KEY,
3 appName: 'my-server'
4});
5
6await respanAi.initialize();
7
8// Handle shutdown signals
9process.on('SIGTERM', async () => {
10 console.log('SIGTERM received, shutting down gracefully...');
11 await respanAi.shutdown();
12 process.exit(0);
13});
14
15process.on('SIGINT', async () => {
16 console.log('SIGINT received, shutting down gracefully...');
17 await respanAi.shutdown();
18 process.exit(0);
19});
20
21// Run application
22await runServer();

Express Application

1import express from 'express';
2
3const app = express();
4
5const respanAi = new RespanTelemetry({
6 apiKey: process.env.RESPAN_API_KEY,
7 appName: 'api-server'
8});
9
10await respanAi.initialize();
11
12app.get('/api/data', async (req, res) => {
13 await respanAi.withWorkflow(
14 { name: 'api_request' },
15 async () => {
16 const data = await fetchData();
17 res.json(data);
18 }
19 );
20});
21
22const server = app.listen(3000, () => {
23 console.log('Server running on port 3000');
24});
25
26// Graceful shutdown
27async function gracefulShutdown() {
28 console.log('Shutting down gracefully...');
29
30 // Stop accepting new requests
31 server.close(async () => {
32 console.log('HTTP server closed');
33
34 // Shutdown tracing
35 await respanAi.shutdown();
36 console.log('Tracing shutdown complete');
37
38 process.exit(0);
39 });
40
41 // Force exit after 10 seconds
42 setTimeout(() => {
43 console.error('Forced shutdown after timeout');
44 process.exit(1);
45 }, 10000);
46}
47
48process.on('SIGTERM', gracefulShutdown);
49process.on('SIGINT', gracefulShutdown);

Worker/Job Processing

1async function processJobs() {
2 const respanAi = new RespanTelemetry({
3 apiKey: process.env.RESPAN_API_KEY,
4 appName: 'job-worker'
5 });
6
7 await respanAi.initialize();
8
9 let shouldStop = false;
10
11 process.on('SIGTERM', () => {
12 console.log('Stopping job processing...');
13 shouldStop = true;
14 });
15
16 try {
17 while (!shouldStop) {
18 const job = await getNextJob();
19
20 if (job) {
21 await respanAi.withWorkflow(
22 { name: 'process_job' },
23 async () => {
24 return await processJob(job);
25 }
26 );
27 } else {
28 await new Promise(resolve => setTimeout(resolve, 1000));
29 }
30 }
31 } finally {
32 await respanAi.shutdown();
33 console.log('Worker shutdown complete');
34 }
35}
36
37processJobs();

CLI Application

1#!/usr/bin/env node
2import { program } from 'commander';
3
4async function runCLI() {
5 const respanAi = new RespanTelemetry({
6 apiKey: process.env.RESPAN_API_KEY,
7 appName: 'cli-tool'
8 });
9
10 await respanAi.initialize();
11
12 program
13 .command('process <file>')
14 .action(async (file) => {
15 try {
16 await respanAi.withWorkflow(
17 { name: 'cli_process' },
18 async () => {
19 return await processFile(file);
20 }
21 );
22 console.log('Processing complete');
23 } catch (error) {
24 console.error('Error:', error);
25 process.exit(1);
26 } finally {
27 await respanAi.shutdown();
28 }
29 });
30
31 await program.parseAsync();
32}
33
34runCLI();

Testing

1import { describe, it, beforeAll, afterAll } from '@jest/globals';
2
3describe('My Service', () => {
4 let respanAi: RespanTelemetry;
5
6 beforeAll(async () => {
7 respanAi = new RespanTelemetry({
8 apiKey: process.env.RESPAN_API_KEY,
9 appName: 'test-suite'
10 });
11 await respanAi.initialize();
12 });
13
14 afterAll(async () => {
15 // Shutdown after all tests
16 await respanAi.shutdown();
17 });
18
19 it('should process data', async () => {
20 await respanAi.withWorkflow(
21 { name: 'test_workflow' },
22 async () => {
23 const result = await processData();
24 expect(result).toBeDefined();
25 }
26 );
27 });
28});

What shutdown() Does

  1. Flushes all pending spans - Sends any buffered spans to Respan
  2. Closes the tracer - Gracefully closes the OpenTelemetry tracer
  3. Cleans up resources - Releases any held resources

shutdown() vs flush()

MethodPurpose
shutdown()Complete cleanup, sends pending data, and closes the tracer
flush()Sends pending data but keeps the tracer active for more spans
Automatic Node.js exit drainSends pending data on normal process exit without closing it manually

Best Practices

  • Use shutdown() for long-running applications, workers, explicit signal handling, and test teardown
  • Use in finally blocks when your code owns a terminal tracing lifecycle
  • Handle SIGTERM and SIGINT signals for graceful shutdown
  • Set a timeout for forced shutdown if graceful shutdown takes too long
  • In testing, shutdown after all tests complete
  • Never reuse the tracer after calling shutdown()
  • For serverless, you might prefer flush() over shutdown() if the environment is reused