Manual Instrumentation in NodeJS

Why Manual Instrumentation

Manual instrumentation allows developers to gain fine-grained control over the monitoring and observability of their applications by adding specific traces and metrics that automatic instrumentation might miss.

When to Use Manual Spans

  • Detailed Business Logic Tracking: For tracing specific business processes and operations.
  • Performance Bottlenecks: To identify and analyze performance issues in critical sections of your application.
  • Error Handling and Debugging: For tracking and recording detailed error information and exceptions.

When to Use Custom Metrics

  • Performance Monitoring: For tracking specific performance metrics like response times or processing durations.
  • Resource Utilization: To monitor and manage resource usage, such as memory and CPU consumption.
  • Business KPIs: For capturing key performance indicators relevant to your business logic.

Set Up Custom Spans

Install Dependencies

If you haven't already, install the necessary OpenTelemetry libraries:

npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions

Acquiring a Tracer

Acquire a tracer to create spans:

import { trace } from '@opentelemetry/api'
const tracer = trace.getTracer('your-service-name')

Adding Custom Spans

Example of adding a custom span in a function:

async function yourFunction() {
  return tracer.startActiveSpan('your-span-name', async (span) => {
    try {
      // Start a new active span named 'your-span-name'
      span.addEvent('Event description') // Add an event with a description to the span
      span.setAttribute('attributeKey', 'attributeValue') // Set an attribute on the span
      span.setStatus({ code: SpanStatusCode.OK }) // Set the status of the span to OK
    } catch (error) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }) // Set the status to ERROR if an exception occurs
      span.recordException(error) // Record the exception in the span
      throw error
    } finally {
      span.end() // End the span
    }
  })
}

Running the Application

Run your application normally, ensuring the tracing configuration is loaded first.

Verifying the Manual Spans

Check SigNoz for the manually created spans and verify the attributes and events are correctly logged.

For more detailed instructions with a sample app, refer to the complete article in the series: Manual Instrumentation for Traces - OpenTelemetry NodeJS

Set Up Custom Metrics

Setup Metric Collection

Create a telemetry.js file and configure the MeterProvider with an OTLPMetricExporter:

import { metrics } from '@opentelemetry/api'
import {
  MeterProvider,
  PeriodicExportingMetricReader,
  ConsoleMetricExporter,
} from '@opentelemetry/sdk-metrics'
import { Resource } from '@opentelemetry/resources'
import {
  SEMRESATTRS_SERVICE_NAME,
  SEMRESATTRS_SERVICE_VERSION,
} from '@opentelemetry/semantic-conventions'
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'

// Define resource with service name
const resource = Resource.default().merge(
  new Resource({
    [SEMRESATTRS_SERVICE_NAME]: 'your-service-name',
  })
)

// Configure OTLP metrics exporter
const otlpMetricsExporter = new OTLPMetricExporter({
  url: 'http://otel-collector:4318/v1/metrics', // ensure the port is correct
})

const metricReader = new PeriodicExportingMetricReader({
  exporter: otlpMetricsExporter,

  // Default is 60000ms (60 seconds). Set to 10 seconds for demonstrative purposes only.
  exportIntervalMillis: 10000,
})

// Initialize MeterProvider
const myServiceMeterProvider = new MeterProvider({
  resource: resource,
  readers: [metricReader],
})

// Set this MeterProvider to be global to the app being instrumented.
metrics.setGlobalMeterProvider(myServiceMeterProvider)
  • Here, your-service-name is the name you give to your service, which helps identify it in SigNoz. Replace your-service-name with a unique name that describes the service's function.
  • otel-collector refers to the OpenTelemetry Collector endpoint where your application will send its metrics. Make sure this URL points to the correct location of your OpenTelemetry Collector instance.

Collect Metrics in Business Logic

Modify your business logic to measure and record metrics.

import { performance } from 'perf_hooks'

const meter = metrics.getMeter('your-service-name')

async function yourFunction(parameter) {
  const startTime = performance.now()
  const yourMetric = meter.createHistogram('your-metric-name', {
    description: 'Description of your metric',
  })

  // Your business logic here

  const duration = performance.now() - startTime
  yourMetric.record(duration, { attributeKey: 'attributeValue' })
}
  • Replace your-service-name with the name you give to your service.
  • your-metric-name is the name you give to the metric you are creating. It should be a unique and descriptive name that reflects what the metric is measuring.
  • The Description of your metric provides additional context about what the metric represents, helping others understand its purpose.

Running the Application

Run your application ensuring the telemetry configuration is loaded first.

Verifying and Visualizing Metrics

  1. Ensure your application is running and sending metrics.
  2. Use your observability platform's dashboard to visualize the metrics.
  3. Create a visualization to display the recorded metrics.

For more detailed instructions, refer to the complete article with a sample app in the series: Setting up Custom Metrics - OpenTelemetry NodeJS