Cursor can generate OpenTelemetry tracing instrumentation across your microservices when given proper context about your architecture and tracing conventions. This tutorial shows how to use .cursorrules for consistent span naming, reference your tracing setup with @file, and prompt Cursor to add traces to existing endpoints without breaking your code.
Adding observability to microservices with Cursor
Adding distributed tracing to an existing codebase is repetitive but detail-sensitive work. Each service needs consistent span names, attribute conventions, and error recording. Cursor can automate this instrumentation if you provide the right context about your tracing setup and naming conventions. This tutorial walks through setting up a reusable tracing pattern that Cursor can apply across your services.
Prerequisites
- Cursor installed with a microservices project
- OpenTelemetry SDK installed in your project
- A tracing backend (Jaeger, Zipkin, or Grafana Tempo) for verification
- Basic understanding of distributed tracing concepts
Step-by-step guide
Generate a tracing setup module with Cursor
Generate a tracing setup module with Cursor
Use Cursor Chat (Cmd+L) to generate the initial OpenTelemetry configuration. This module initializes the tracer provider, sets up exporters, and creates a reusable tracer instance. Cursor needs to know your runtime, exporter type, and service name pattern.
1// Cursor Chat prompt (Cmd+L):2// Generate an OpenTelemetry tracing setup module for a3// Node.js Express service. Use OTLP/gRPC exporter, batch4// span processor, and the W3C trace context propagator.5// Service name should come from environment variable6// OTEL_SERVICE_NAME. Export a getTracer() function.78// Expected output:9import { NodeSDK } from '@opentelemetry/sdk-node';10import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';11import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';12import { trace } from '@opentelemetry/api';1314const sdk = new NodeSDK({15 traceExporter: new OTLPTraceExporter({16 url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4317',17 }),18 instrumentations: [getNodeAutoInstrumentations()],19 serviceName: process.env.OTEL_SERVICE_NAME || 'unknown-service',20});2122sdk.start();2324export function getTracer(name: string) {25 return trace.getTracer(name);26}Expected result: A reusable tracing setup module that initializes OpenTelemetry with your preferred configuration.
Add tracing rules to .cursor/rules
Add tracing rules to .cursor/rules
Create rules that define your span naming convention, required attributes, and error recording pattern. This ensures every trace Cursor generates follows the same format across all services.
1---2description: OpenTelemetry tracing conventions3globs: "src/**/*.ts"4alwaysApply: false5---67## Tracing Rules8- Import tracer: import { getTracer } from '@/tracing/setup'9- Span naming: '{service}.{operation}' e.g. 'order-service.createOrder'10- ALWAYS add these attributes to spans:11 - 'http.method' for HTTP handlers12 - 'http.route' for the URL pattern13 - 'user.id' when available from auth context14 - 'error' boolean attribute on failures15- Record exceptions with span.recordException(error)16- Set span status to ERROR on catch blocks17- Use startActiveSpan for automatic context propagation18- NEVER create spans inside tight loops (batch them)19- End spans in finally blocks to prevent leaksExpected result: Cursor follows consistent tracing conventions whenever it generates or modifies service code.
Add traces to an existing endpoint with Cmd+K
Add traces to an existing endpoint with Cmd+K
Open an existing route handler, select the function body, press Cmd+K, and ask Cursor to wrap it with tracing. Reference the tracing setup file so Cursor uses the correct import path.
1// Select the handler function, press Cmd+K:2// @src/tracing/setup.ts Add OpenTelemetry tracing to this3// handler. Use startActiveSpan with the naming convention4// 'order-service.getOrder'. Add http.method, http.route,5// and user.id attributes. Record errors.67// Cursor output:8import { getTracer } from '@/tracing/setup';9import { SpanStatusCode } from '@opentelemetry/api';1011const tracer = getTracer('order-service');1213app.get('/orders/:id', async (req, res) => {14 return tracer.startActiveSpan('order-service.getOrder', async (span) => {15 try {16 span.setAttribute('http.method', 'GET');17 span.setAttribute('http.route', '/orders/:id');18 span.setAttribute('user.id', req.user?.id || 'anonymous');1920 const order = await orderRepository.findById(req.params.id);21 if (!order) {22 span.setAttribute('error', true);23 return res.status(404).json({ error: 'Order not found' });24 }25 res.json(order);26 } catch (error) {27 span.recordException(error as Error);28 span.setStatus({ code: SpanStatusCode.ERROR });29 res.status(500).json({ error: 'Internal server error' });30 } finally {31 span.end();32 }33 });34});Pro tip: Always end spans in a finally block. Cursor sometimes forgets this, causing span leaks that distort your tracing data.
Expected result: The endpoint now creates a traced span with proper attributes, error recording, and cleanup.
Instrument multiple endpoints with Composer
Instrument multiple endpoints with Composer
Use Composer Agent mode (Cmd+I) to add tracing to all endpoints in a service at once. Reference the tracing setup and rules to ensure consistency.
1// Composer prompt (Cmd+I):2// @src/tracing/setup.ts @.cursor/rules/tracing.mdc3// @src/routes/ Add OpenTelemetry tracing to all route4// handlers in the routes directory. Follow the tracing5// rules for span naming, attributes, and error recording.6// Process one file at a time and show me the diff before7// proceeding to the next file.Expected result: Cursor instruments each route file with consistent tracing, waiting for approval between files.
Verify traces in your tracing backend
Verify traces in your tracing backend
Start your service and send a few test requests. Open your tracing backend (Jaeger, Grafana Tempo) to verify the spans appear with correct names and attributes. Use Cursor Chat to troubleshoot if traces are missing.
1// Terminal: Start the service with tracing enabled2OTEL_SERVICE_NAME=order-service \3OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \4npm run dev56// Send a test request7curl http://localhost:3000/orders/12389// If traces are missing, ask Cursor:10// Cmd+L: @src/tracing/setup.ts My traces are not appearing11// in Jaeger at localhost:16686. The service starts without12// errors. What could be wrong?Expected result: Traces appear in your backend with correct service name, span names, and attributes.
Complete working example
1import { NodeSDK } from '@opentelemetry/sdk-node';2import {3 getNodeAutoInstrumentations,4} from '@opentelemetry/auto-instrumentations-node';5import {6 OTLPTraceExporter,7} from '@opentelemetry/exporter-trace-otlp-grpc';8import {9 BatchSpanProcessor,10} from '@opentelemetry/sdk-trace-base';11import { trace, SpanStatusCode } from '@opentelemetry/api';12import { W3CTraceContextPropagator } from '@opentelemetry/core';1314const exporter = new OTLPTraceExporter({15 url:16 process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||17 'http://localhost:4317',18});1920const sdk = new NodeSDK({21 traceExporter: exporter,22 spanProcessor: new BatchSpanProcessor(exporter, {23 maxExportBatchSize: 512,24 scheduledDelayMillis: 5000,25 }),26 textMapPropagator: new W3CTraceContextPropagator(),27 instrumentations: [28 getNodeAutoInstrumentations({29 '@opentelemetry/instrumentation-fs': { enabled: false },30 }),31 ],32 serviceName:33 process.env.OTEL_SERVICE_NAME || 'unknown-service',34});3536sdk.start();3738process.on('SIGTERM', () => {39 sdk.shutdown().then(() => process.exit(0));40});4142export function getTracer(name: string) {43 return trace.getTracer(name, '1.0.0');44}4546export { SpanStatusCode };Common mistakes when adding tracing and observability with Cursor
Why it's a problem: Forgetting to end spans in finally blocks
How to avoid: Add 'End spans in finally blocks to prevent leaks' to your .cursor/rules and verify every Cursor-generated span has a finally block.
Why it's a problem: Creating spans inside loops
How to avoid: Add a rule: 'NEVER create spans inside tight loops. Create one span for the batch operation with item count as an attribute.'
Why it's a problem: Inconsistent span naming across services
How to avoid: Define the naming convention in .cursor/rules: 'Span naming: {service}.{operation}' and reference the rules in every tracing prompt.
Best practices
- Define span naming conventions in .cursor/rules for consistent instrumentation across services
- Always reference @src/tracing/setup.ts when prompting Cursor to add traces
- End all spans in finally blocks to prevent resource leaks
- Use startActiveSpan for automatic context propagation between functions
- Add standard HTTP attributes (method, route, status_code) to all request spans
- Disable file system instrumentation to reduce noise in traces
- Use Composer Agent mode to instrument multiple endpoints consistently in one session
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Generate an OpenTelemetry tracing setup for a Node.js Express microservice. Use OTLP/gRPC exporter with batch span processor. Include a getTracer function. Then show how to instrument a GET /orders/:id endpoint with proper span attributes (http.method, http.route, user.id), error recording with recordException, and span cleanup in a finally block.
In Cursor Composer (Cmd+I): @src/tracing/setup.ts @.cursor/rules/tracing.mdc @src/routes/orders.ts Add OpenTelemetry tracing to all handlers in this route file. Use the getTracer from our setup module. Follow the naming convention 'order-service.{handler}'. Add http.method, http.route, user.id attributes. Record exceptions. End spans in finally blocks.
Frequently asked questions
Does Cursor understand OpenTelemetry APIs?
Yes. Cursor's training data includes OpenTelemetry documentation and examples. However, the API changed significantly between versions, so specify your OpenTelemetry SDK version in .cursorrules to avoid outdated API usage.
Should I use auto-instrumentation or manual spans?
Use both. Auto-instrumentation covers HTTP, database, and gRPC calls automatically. Add manual spans for business-logic operations like 'processPayment' or 'validateOrder' that auto-instrumentation cannot detect.
How do I propagate trace context across services?
OpenTelemetry's W3C TraceContext propagator handles this automatically for HTTP calls. For message queues, you need to manually inject/extract context. Ask Cursor: 'Show how to propagate trace context through an SQS message.'
Will adding tracing to every endpoint slow my service?
The overhead is minimal (microseconds per span) when using a batch span processor. The BatchSpanProcessor buffers spans and sends them in bulk, adding negligible latency to request handling.
Can Cursor add tracing to Python or Go services too?
Yes. Update your .cursor/rules with the language-specific OpenTelemetry API patterns. For Python, reference the opentelemetry-python SDK. For Go, reference the go.opentelemetry.io/otel package.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation