Using Opentelemetry instrumentation and tracing to mitigate performance bottlenecks.

Using Opentelemetry instrumentation and tracing to mitigate performance bottlenecks.

A Comprehensive Guide to Instrumentation and Tracing

In the dynamic landscape of web development, optimizing the performance of applications is not just a goal but a necessity. When it comes to NestJS Apollo GraphQL applications, incorporating Opentelemetry instrumentation and tracing can significantly enhance your ability to identify and mitigate performance bottlenecks. In this guide, we'll walk through the steps to set up instrumentation within a NestJS Apollo GraphQL web application, allowing you to gain valuable insights into the request flow and performance characteristics of your application.

Use jaeger UI, Nest JS, Apollo GraphQL, Docker

We're enhancing our NestJS Apollo GraphQL app with Opentelemetry for detailed tracing. Jaeger UI visualizes this data, showing performance insights. Docker ensures a consistent environment for easy development and debugging. This setup streamlines building and optimizing high-performance apps.

Setting up instrumentation in a NestJS Apollo GraphQL web application

Setting Up NestJS Apollo GraphQL

create a NestJS app and install Apollo graphQL

npm i -g @nestjs/cli
nest new nestjs-api
cd nestjs-api
npm install
npm install @nestjs/graphql @nestjs/apollo @apollo/server graphql

Installing Opentelemetry Packages

Install packages

npm install @opentelemetry/semantic-conventions 
npm install @opentelemetry/exporter-trace-otlp-http 
npm install @opentelemetry/instrumentation 
npm install @opentelemetry/instrumentation-express
npm install @opentelemetry/instrumentation-graphq
npm install @opentelemetry/instrumentation-http
npm install @opentelemetry/sdk-trace-base 
npm install @opentelemetry/sdk-trace-node 
npm install @opentelemetry/resources
npm install @opentelemetry/api
npm install @opentelemetry/core

installing these packages is crucial for setting up instrumentation and tracing in your NestJS Apollo GraphQL application. These packages provide standardized conventions for telemetry data, enable exporting trace data to compatible backends like Jaeger, offer core instrumentation functionality, and provide specific instrumentation for Express.js and GraphQL, ensuring comprehensive visibility into the performance and request flow of your application.

Configuring Tracing withtracer.ts

Create tracer.ts file to configure tracing

// Import required symbols
import { Resource } from '@opentelemetry/resources';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

const provider = new NodeTracerProvider({
resource: new Resource({
      'service.name': 'nestjs-api',
    }),
});
const oltpExporter = new OTLPTraceExporter();
provider.addSpanProcessor(new SimpleSpanProcessor(oltpExporter));
provider.register();

// Register server-related instrumentation
registerInstrumentations({
    tracerProvider: provider,
    instrumentations: [
      new HttpInstrumentation(), //HTTP instrumentation
      new ExpressInstrumentation(), // Express instrumentation
      new GraphQLInstrumentation(), //GraphQL instrumentation
        //..Other instrumentation (DB instrumentation etc..)
    ],
});

we created a file named tracer.ts to set up tracing for our NestJS Apollo GraphQL application using Opentelemetry. Inside this file, we bring in important tools like NodeTracerProvider and OTLPTraceExporter. We use NodeTracerProvider to define our app's name and OTLPTraceExporter to export tracing data. After that, we link them together and enable tracing for parts of our app like HTTP requests, Express framework, and GraphQL queries. This helps us keep a close eye on how our app performs and how requests move through it, useful for improving its speed and reliability.

Enabling Instrumentation inmain.ts

enable the instrumentation in the main.ts or entry point of the application (Before booting up the app)

import './tracing'; //import the tracing for profiling and optimization
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    cors: true,
  });
  await app.listen(3000);
  app.enableShutdownHooks();
}
bootstrap();

activate Opentelemetry's instrumentation in the main.ts file, the entry point of our NestJS Apollo GraphQL application. By importing the tracing setup, we equip the app with tracing for profiling and optimization. Then, using NestFactory, we create the app instance from AppModule, configure it with options like enabling CORS, and start it on port 3000. Lastly, we enable shutdown hooks for smooth application termination.

Visualizing Traces with Jaeger UI

Run jaeger UI to visualize the traces

Use docker command to run the jaeger

docker run --rm --name jaeger -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest

Finally run your application, open few GraphQL endpoints (Queries and mutation)

and access http://localhost:16686/search to view the tracings for proper understaffing of the request flow.

Select your service (nestjs-api)

Query the traces

Expand and view traces

Finally, run your NestJS Apollo GraphQL application and perform various GraphQL queries and mutations. Access the Jaeger UI at http://localhost:16686/search to view the captured traces and understand the request flow of your application.

Query the traces to analyze the performance of individual requests. Expand and view traces to identify potential bottlenecks and optimize critical sections of your application.