SigNoz Cloud - This page is relevant for SigNoz Cloud editions.
Self-Host - This page is relevant for self-hosted SigNoz editions.

Send Metrics from Java Application using OpenTelemetry

This guide shows you how to send custom and JVM runtime metrics from your Java application to SigNoz using the OpenTelemetry Java agent.

Prerequisites

  • Java 8 or later (Java 17+ recommended for enhanced JVM metrics)
  • A SigNoz Cloud account or self-hosted SigNoz instance

Send metrics to SigNoz

Step 1. Download the OpenTelemetry Java agent

wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

Step 2. Set environment variables

export OTEL_SERVICE_NAME="<service-name>"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_METRIC_EXPORT_INTERVAL="60000"
export OTEL_TRACES_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"

Replace the following:

  • <region>: Your SigNoz Cloud region (us, eu, or in). See endpoints.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service-name>: A descriptive name for your service (e.g., payment-service).

Step 3. Add OpenTelemetry API dependency

Add the OpenTelemetry API to your project. The agent provides the implementation at runtime.

pom.xml
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-api</artifactId>
  <version>1.57.0</version>
</dependency>
Info

The version above is current as of this writing. Check the OpenTelemetry Java releases for the latest versions.

Step 4. Add custom metrics to your application

Use GlobalOpenTelemetry.getMeter() to create metrics. The agent configures the MeterProvider automatically.

OrderService.java
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;

public class OrderService {
    private static final Meter meter = GlobalOpenTelemetry.getMeter("order-service");
    private static final LongCounter ordersCounter = meter
            .counterBuilder("orders.processed")
            .setDescription("Total number of orders processed")
            .build();

    public void processOrder(String orderId) {
        // Your business logic here
        ordersCounter.add(1);
    }
}
Info

This example shows a Counter, which only increases. OpenTelemetry supports other metric types like UpDownCounter, Histogram, and Observable Gauge. See Custom Metrics Examples for complete examples of each type.

Step 5. Run your application

java -javaagent:./opentelemetry-javaagent.jar -jar your-app.jar
Info

The Java agent automatically generates HTTP metrics (http.server.request.duration and http.client.request.duration) for instrumented web frameworks like Spring Boot, JAX-RS, and Servlet containers. See the HTTP metrics semantic conventions for details.

Validate

Once your application is running with the Java agent:

  1. Trigger some application activity to generate metrics.
  2. In SigNoz, go to Metrics > Metrics Explorer.
  3. Search for your custom metric (e.g., orders.processed).
  4. JVM metrics like jvm.memory.used and jvm.cpu.time are collected automatically by the agent. See JVM Runtime Metrics for the full list.

JVM Runtime Metrics

The Java agent automatically collects JVM runtime metrics with zero configuration. These metrics are exported alongside any custom metrics you define, providing visibility into memory usage, garbage collection, threads, and CPU utilization.

Exported Metrics

CategoryMetricDescription
Memoryjvm.memory.usedCurrent memory usage by memory pool
jvm.memory.committedCommitted memory by memory pool
jvm.memory.limitMaximum memory available by memory pool
jvm.memory.used_after_last_gcMemory used after the last garbage collection
GCjvm.gc.durationTime spent in garbage collection (histogram)
Threadsjvm.thread.countNumber of executing threads
Classesjvm.class.loadedNumber of classes loaded since JVM start
jvm.class.unloadedTotal number of classes unloaded
jvm.class.countNumber of classes currently loaded
CPUjvm.cpu.timeCPU time used by the process
jvm.cpu.countNumber of available processors
jvm.cpu.recent_utilizationRecent CPU utilization

Visualize Runtime Metrics

Import the pre-built JVM dashboard to visualize these metrics:

  1. Download the JVM dashboard JSON
  2. In SigNoz, go to DashboardsNew DashboardImport JSON
  3. Paste the JSON content or upload the file

See the JVM Dashboard Template for details on available panels.

Custom Metrics Examples

OpenTelemetry provides four metric instrument types. Each example shows how to create and use the instrument.

Counter

A value that only increases (e.g., total requests, orders processed).

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;

Meter meter = GlobalOpenTelemetry.getMeter("my-service");

LongCounter requestCounter = meter
        .counterBuilder("http.requests.total")
        .setDescription("Total number of HTTP requests")
        .build();

// Increment the counter
requestCounter.add(1);

UpDownCounter

A value that can increase or decrease (e.g., queue size, active connections).

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;

Meter meter = GlobalOpenTelemetry.getMeter("my-service");

LongUpDownCounter activeRequests = meter
        .upDownCounterBuilder("http.requests.active")
        .setDescription("Number of active requests")
        .build();

// Increment when request starts
activeRequests.add(1);

// Decrement when request ends
activeRequests.add(-1);

Histogram

A distribution of values (e.g., request duration, response size).

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;

Meter meter = GlobalOpenTelemetry.getMeter("my-service");

DoubleHistogram requestDuration = meter
        .histogramBuilder("http.request.duration")
        .setDescription("HTTP request duration")
        .setUnit("ms")
        .build();

// Record a duration value
long startTime = System.currentTimeMillis();
// ... process request ...
requestDuration.record(System.currentTimeMillis() - startTime);

Gauge

A point-in-time value via callback (e.g., temperature, memory usage).

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableDoubleGauge;

Meter meter = GlobalOpenTelemetry.getMeter("my-service");

ObservableDoubleGauge memoryGauge = meter
        .gaugeBuilder("app.memory.usage")
        .setDescription("Current memory usage")
        .setUnit("By")
        .buildWithCallback(measurement -> {
            Runtime runtime = Runtime.getRuntime();
            measurement.record(runtime.totalMemory() - runtime.freeMemory());
        });

Troubleshooting

Metrics not appearing?

Check environment variables:

echo $OTEL_EXPORTER_OTLP_ENDPOINT
echo $OTEL_METRICS_EXPORTER

Enable debug logging:

OTEL_LOG_LEVEL=debug java -javaagent:./opentelemetry-javaagent.jar -jar app.jar

Verify agent is attached: Look for this log line on startup:

[otel.javaagent] INFO io.opentelemetry.javaagent.tooling.VersionLogger - opentelemetry-javaagent - version: X.X.X

GlobalOpenTelemetry.getMeter() returns no-op?

Make sure the Java agent is attached. Without the agent, GlobalOpenTelemetry returns a no-op implementation.

# Correct - agent flag before -jar
java -javaagent:./agent.jar -jar app.jar

# Wrong - agent flag after -jar is ignored
java -jar app.jar -javaagent:./agent.jar

Metrics delayed or not updating?

OTEL_METRIC_EXPORT_INTERVAL controls how often metrics are exported in milliseconds. The default is 60000 (60 seconds), so metrics may take up to a minute to appear.

To reduce the interval for testing:

export OTEL_METRIC_EXPORT_INTERVAL=10000  # 10 seconds

Authentication errors

If you see "Unauthorized" or "403 Forbidden":

  • Verify your ingestion key is correct in OTEL_EXPORTER_OTLP_HEADERS
  • Ensure the header format is exactly: signoz-ingestion-key=<your-key> (no extra spaces)

Setup OpenTelemetry Collector (Optional)

What is the OpenTelemetry Collector?

Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.

Why use it?

  • Cleaning up data - Filter out noisy metrics you don't care about, or remove sensitive info before it leaves your servers.
  • Keeping your app lightweight - Let the Collector handle batching, retries, and compression instead of your application code.
  • Adding context automatically - The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
  • Future flexibility - Want to send data to multiple backends later? The Collector makes that easy without changing your app.

See Switch from direct export to Collector for step-by-step instructions to convert your setup.

For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.

Next Steps

Last updated: January 23, 2026

Edit on GitHub

Was this page helpful?