arrow

Unlocking the Power of OpenTelemetry: A Comprehensive Guide

Abhishek Porwal

Abhishek Porwal

|
Mar 5, 2025
|
book

7 mins read

cover-image

In today’s complex, microservices-driven world, understanding how applications perform is more critical than ever. Enter OpenTelemetry, an open-source observability framework that simplifies capturing and correlating telemetry data across distributed systems. In this blog, we’ll explore what OpenTelemetry is, how it works, and why it’s a game-changer—with real-world examples to make things more relatable.

What is OpenTelemetry?

Imagine you’re managing a delivery service with hundreds of drivers, each taking unique routes. To ensure timely deliveries, you need real-time updates on driver locations, road conditions, and package statuses. OpenTelemetry does something similar for your applications—it collects data about their health and performance, helping you stay in control.

OpenTelemetry is an open-source observability framework that collects telemetry data—metrics, logs, and traces—from cloud-native applications. Governed by the Cloud Native Computing Foundation (CNCF), it’s designed to simplify monitoring for distributed systems.

Key Features of OpenTelemetry

  1. Cross-Language Support: Use it with Java, Python, JavaScript, Go, .NET, and more.

  2. Unified Data Model: Collect metrics, logs, and traces with a single API.

  3. Interoperability: Works with tools like Prometheus, Grafana, and Jaeger.

  4. Auto-Instrumentation: Add monitoring to your app without writing extra code.

Why OpenTelemetry? A Real-Life Scenario

Imagine you run an e-commerce platform. During a sale, users complain about slow checkout times. Without observability, you’d be guessing the cause—a slow database, a buggy API, or overloaded servers. With OpenTelemetry, you’d have detailed traces showing:

  • A specific API call causing the delay

  • How long it takes for the database to respond

  • Which microservice is the bottleneck

This clear view helps you fix the issue faster and keeps your customers happy.

Benefits of OpenTelemetry

1. End-to-End Observability

Picture this: a user clicks “Buy Now,” and OpenTelemetry tracks that request from the frontend through APIs, databases, and back—pinpointing slow spots instantly.

2. Vendor Agnostic

Say goodbye to being locked into a single monitoring tool. Instrument your code once, and send data to any backend you prefer, from Prometheus to Datadog.

3. Developer-Friendly

Forget juggling multiple libraries for logs, traces, and metrics. With OpenTelemetry’s unified APIs, you can spend more time coding and less time configuring.

Let’s Get Practical: Implementing OpenTelemetry

Step 1: Set Up a Basic Node.js Application

  1. Initialize the Project:
mkdir opentelemetry-demo cd opentelemetry-demo npm init -y
  1. Install Express:
npm install express
  1. Create the Application File: Create a file named app.js with the following content:
const express = require('express'); const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello, OpenTelemetry!'); }); app.listen(port, () => { console.log(`App listening at http://localhost:${port}`); });

Step 2: Install OpenTelemetry Packages

Install the necessary OpenTelemetry packages:

npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node

Step 3: Configure OpenTelemetry

  1. Create a Tracing Configuration File: Create a file named tracing.js with the following content:
const { NodeSDK } = require('@opentelemetry/sdk-node'); const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-base'); const sdk = new NodeSDK({ // for demostration purpose I am just using console exporter here // so that we get span in console when we make any request traceExporter: new ConsoleSpanExporter(), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();

Span: A Span represents a unit of work. They can be thought of as the work being done during an operation’s execution. You can read more about it https://medium.com/dzerolabs/observability-journey-understanding-logs-events-traces-and-spans-836524d63172

  1. Integrate Tracing into Our Application: In your main application file (e.g., app.js), we have to first import tracing.js for initializing it
require('./tracing'); // Initialize tracing const express = require('express'); const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello, OpenTelemetry!'); }); app.get('/products', (req, res) => { res.send('products fetching'); }); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); });

In above example we uses ConsoleSpanExporter to get the spans in console. Now when you make a request to /products route. it will show different state of a call. Let say you make a call to /products route. it will generate spans for 'GET /products' , 'request handler - /products', 'middleware-expressInit’ 'middleware - query', with all the details like timestamp, duration, status_code etc. Here we are using only 1 service but it is more useful when your product have micro-services talking to each other for a operation to be completely done.

Spans Generated for each step in console -

  1. Parent Span - /products

{ resource: { attributes: { 'service.name': 'unknown_service:node', 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.30.1', 'process.pid': 83799, 'process.executable.name': 'node', 'process.executable.path': '/path/to/node', 'process.command_args': [ '/path/to/node', '/path/to/app.js' ], 'process.runtime.version': '23.4.0', 'process.runtime.name': 'nodejs', 'process.runtime.description': 'Node.js', 'process.command': '/path/to/app.js', 'process.owner': 'user', 'host.name': 'host-name.local', 'host.arch': 'arm64', 'host.id': '8c0CB8DB-47FF-5BC2-A89D-EF1D94077FFE' // using representation of host id for demostration only } }, instrumentationScope: { name: '@opentelemetry/instrumentation-http', version: '0.57.1', schemaUrl: undefined }, traceId: '843f7b03dc67f95b775f3e5e63e8e191', parentId: undefined, traceState: undefined, name: 'GET /products', // genrated when execution come to /products endpoint id: 'f07cbc8ef59a1f6d', kind: 1, timestamp: 1740487705464000, duration: 14133.75, attributes: { 'http.url': 'http://localhost:3000/products', 'http.host': 'localhost:3000', 'net.host.name': 'localhost', 'http.method': 'GET', 'http.scheme': 'http', 'http.target': '/products', 'http.user_agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36', 'http.flavor': '1.1', 'net.transport': 'ip_tcp', 'net.host.ip': '::1', 'net.host.port': 3000, 'net.peer.ip': '::1', 'net.peer.port': 63507, 'http.status_code': 304, 'http.status_text': 'NOT MODIFIED', 'http.route': '/products' }, status: { code: 0 }, events: [], links: [] }
  1. Child Span - request handler - /products

{ resource: { attributes: { 'service.name': 'unknown_service:node', 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.30.1', 'process.pid': 83799, 'process.executable.name': 'node', 'process.executable.path': '/path/to/node', 'process.command_args': [ '/path/to/node', '/path/to/app.js' ], 'process.runtime.version': '23.4.0', 'process.runtime.name': 'nodejs', 'process.runtime.description': 'Node.js', 'process.command': '/path/to/app.js', 'process.owner': 'user', 'host.name': 'host-name.local', 'host.arch': 'arm64', 'host.id': '8c0CB8DB-47FF-5BC2-A89D-EF1D94077FFE' // using representation of host id for demostration only } }, instrumentationScope: { name: '@opentelemetry/instrumentation-express', version: '0.47.0', schemaUrl: undefined }, traceId: '843f7b03dc67f95b775f3e5e63e8e191', parentId: 'f07cbc8ef59a1f6d', traceState: undefined, name: 'request handler - /products', id: '31f73561dab0b8f7', kind: 0, timestamp: 1740487705471000, duration: 23.75, attributes: { 'http.route': '/products', 'express.name': '/products', 'express.type': 'request_handler' }, status: { code: 0 }, events: [], links: [] }
  1. Child Span - middleware - expressInit

{ resource: { attributes: { 'service.name': 'unknown_service:node', 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.30.1', 'process.pid': 83799, 'process.executable.name': 'node', 'process.executable.path': '/path/to/node', 'process.command_args': [ '/path/to/node', '/path/to/app.js' ], 'process.runtime.version': '23.4.0', 'process.runtime.name': 'nodejs', 'process.runtime.description': 'Node.js', 'process.command': '/path/to/app.js', 'process.owner': 'user', 'host.name': 'host-name.local', 'host.arch': 'arm64', 'host.id': '8c0CB8DB-47FF-5BC2-A89D-EF1D94077FFE' // using representation of host id for demostration only } }, instrumentationScope: { name: '@opentelemetry/instrumentation-express', version: '0.47.0', schemaUrl: undefined }, traceId: '843f7b03dc67f95b775f3e5e63e8e191', parentId: 'f07cbc8ef59a1f6d', traceState: undefined, name: 'middleware - expressInit', id: '11b1672790af89b6', kind: 0, timestamp: 1740487705471000, duration: 411.75, attributes: { 'http.route': '/', 'express.name': 'expressInit', 'express.type': 'middleware' }, status: { code: 0 }, events: [], links: [] }
  1. Child Span - 'middleware - query'

{ resource: { attributes: { 'service.name': 'unknown_service:node', 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.30.1', 'process.pid': 83799, 'process.executable.name': 'node', 'process.executable.path': '/path/to/node', 'process.command_args': [ '/path/to/node', '/path/to/app.js' ], 'process.runtime.version': '23.4.0', 'process.runtime.name': 'nodejs', 'process.runtime.description': 'Node.js', 'process.command': '/path/to/app.js', 'process.owner': 'user', 'host.name': 'host-name.local', 'host.arch': 'arm64', 'host.id': '8c0CB8DB-47FF-5BC2-A89D-EF1D94077FFE' // using representation of host id for demostration only } }, instrumentationScope: { name: '@opentelemetry/instrumentation-express', version: '0.47.0', schemaUrl: undefined }, traceId: '843f7b03dc67f95b775f3e5e63e8e191', parentId: 'f07cbc8ef59a1f6d', traceState: undefined, name: 'middleware - query', id: 'd874c9995a60bfc3', kind: 0, timestamp: 1740487705469000, duration: 894.458, attributes: { 'http.route': '/', 'express.name': 'query', 'express.type': 'middleware' }, status: { code: 0 }, events: [], links: [] }

You can access the source code here. Don't forget to follow me! 😊

Common Use Cases

  1. Distributed Tracing: Track requests across microservices to find performance bottlenecks.

  2. Performance Monitoring: Measure how your application uses CPU, memory, and other resources.

  3. Error Diagnosis: Correlate logs and traces to debug faster.

The Future of OpenTelemetry

With its growing community and vendor-agnostic approach, OpenTelemetry is set to become the backbone of observability for modern applications. Its support for logs, metrics, and traces under one roof is transforming how developers monitor their systems.

Wrapping Up

OpenTelemetry isn’t just another tool; it’s a revolution in observability. Whether you’re debugging a slow API or scaling a microservices architecture, it offers the insights you need to act fast. Start small, experiment, and watch as it transforms your approach to monitoring and performance optimization.

Why wait? Dive into OpenTelemetry today and see the difference firsthand!

For more information, check out the documentation: https://opentelemetry.io/docs/

A curated list of OpenTelemetry Resources: https://github.com/magsther/awesome-opentelemetry

Logfire is a great example of OpenTelemetry in use: https://pydantic.dev/logfire.

Reach out to us

Premium Development Services are just a click away!