# Send Metrics from Java Application using OpenTelemetry

> Source: <https://signoz.io/docs/metrics-management/send-metrics/applications/opentelemetry-java>
> Published: 2026-06-23 00:00:00+00:00

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+](https://github.com/open-telemetry/opentelemetry-java#requirements) - 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_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE="delta"
export OTEL_TRACES_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
```

Replace the following:

`<region>`

: Your SigNoz Cloud region. See[endpoints](https://signoz.io/docs/ingestion/signoz-cloud/overview/#endpoint).`<your-ingestion-key>`

: Your SigNoz[ingestion key](https://signoz.io/docs/ingestion/signoz-cloud/keys/).`<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.

```
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-api</artifactId>
  <version>1.57.0</version>
</dependency>
implementation 'io.opentelemetry:opentelemetry-api:1.57.0'
```

The version above is current as of this writing. Check the [OpenTelemetry Java releases](https://github.com/open-telemetry/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.

``` python
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);
    }
}
```

This example shows a Counter, which only increases. OpenTelemetry supports other metric types like UpDownCounter, Histogram, and Observable Gauge. See [Custom Metrics Examples](#custom-metrics-examples) for complete examples of each type.

Step 5. Run your application

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

Step 1. Add the agent to your Dockerfile

```
FROM eclipse-temurin:17-jre
WORKDIR /app

COPY target/<my-app>.jar app.jar

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

ENTRYPOINT ["java", "-javaagent:/app/opentelemetry-javaagent.jar", "-jar", "app.jar"]
```

Step 2. Add environment variables to your deployment

```
env:
- name: OTEL_SERVICE_NAME
  value: '<service-name>'
- name: OTEL_EXPORTER_OTLP_ENDPOINT
  value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_EXPORTER_OTLP_HEADERS
  value: 'signoz-ingestion-key=<your-ingestion-key>'
- name: OTEL_METRICS_EXPORTER
  value: 'otlp'
- name: OTEL_METRIC_EXPORT_INTERVAL
  value: '60000'
- name: OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
  value: 'delta'
- name: OTEL_TRACES_EXPORTER
  value: 'none'
- name: OTEL_LOGS_EXPORTER
  value: 'none'
```

Replace `<region>`

, `<your-ingestion-key>`

, and `<service-name>`

with your values.

Step 3. Add OpenTelemetry API and custom metrics

Follow Step 3 and Step 4 from the VM tab to add the dependency and custom metrics code.

Step 1. Download the OpenTelemetry Java agent

```
Invoke-WebRequest -Uri "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar" -OutFile "opentelemetry-javaagent.jar"
```

Step 2. Set environment variables

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

Replace `<region>`

, `<your-ingestion-key>`

, and `<service-name>`

with your values.

Step 3. Add OpenTelemetry API and custom metrics

Follow Step 3 and Step 4 from the VM tab to add the dependency and custom metrics code.

Step 4. Run your application

```
java -javaagent:.\opentelemetry-javaagent.jar -jar your-app.jar
```

Step 1. Add the agent to your Dockerfile

```
FROM eclipse-temurin:17-jre
WORKDIR /app

COPY target/<my-app>.jar app.jar

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

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

ENTRYPOINT ["java", "-javaagent:/app/opentelemetry-javaagent.jar", "-jar", "app.jar"]
```

Replace `<my-app>.jar`

, `<region>`

, `<your-ingestion-key>`

, and `<service-name>`

with your values.

Step 2. Add OpenTelemetry API and custom metrics

Follow Step 3 and Step 4 from the VM tab to add the dependency and custom metrics code.

Step 3. Build and run

```
docker build -t my-java-app .
docker run -p 8080:8080 my-java-app
```

Or pass environment variables at runtime:

```
docker run -p 8080:8080 \
  -e OTEL_SERVICE_NAME="my-service" \
  -e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
  -e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<key>" \
  -e OTEL_METRICS_EXPORTER="otlp" \
  -e OTEL_METRIC_EXPORT_INTERVAL="60000" \
  -e OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE="delta" \
  -e OTEL_TRACES_EXPORTER="none" \
  -e OTEL_LOGS_EXPORTER="none" \
  my-java-app
```

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](https://opentelemetry.io/docs/specs/semconv/http/http-metrics/) for details.

Validate

Once your application is running with the Java agent:

- Trigger some application activity to generate metrics.
- In SigNoz, go to
**Metrics**>** Metrics Explorer**. - Search for your custom metric (e.g.,
`orders.processed`

). - JVM metrics like
`jvm.memory.used`

and`jvm.cpu.time`

are collected automatically by the agent. See[JVM Runtime Metrics](#jvm-runtime-metrics)for the full list.

JVM Runtime Metrics

[JVM Runtime Metrics](#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

| Category | Metric | Description |
|---|---|---|
Memory | `jvm.memory.used` | Current memory usage by memory pool |
`jvm.memory.committed` | Committed memory by memory pool | |
`jvm.memory.limit` | Maximum memory available by memory pool | |
`jvm.memory.used_after_last_gc` | Memory used after the last garbage collection | |
GC | `jvm.gc.duration` | Time spent in garbage collection (histogram) |
Threads | `jvm.thread.count` | Number of executing threads |
Classes | `jvm.class.loaded` | Number of classes loaded since JVM start |
`jvm.class.unloaded` | Total number of classes unloaded | |
`jvm.class.count` | Number of classes currently loaded | |
CPU | `jvm.cpu.time` | CPU time used by the process |
`jvm.cpu.count` | Number of available processors | |
`jvm.cpu.recent_utilization` | Recent CPU utilization |

Visualize Runtime Metrics

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

- Download the
[JVM dashboard JSON](https://github.com/SigNoz/dashboards/tree/main/jvm) - In SigNoz, go to
**Dashboards**→** New Dashboard**→** Import JSON** - Paste the JSON content or upload the file

See the [JVM Dashboard Template](https://signoz.io/docs/dashboards/dashboard-templates/jvm-metrics/) for details on available panels.

Custom Metrics Examples

[Custom Metrics Examples](#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).

``` python
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).

``` python
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).

``` python
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).

``` python
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

[Troubleshooting](#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)

[Setup OpenTelemetry Collector (Optional)](#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](https://signoz.io/docs/opentelemetry-collection-agents/opentelemetry-collector/switch-to-collector/) for step-by-step instructions to convert your setup.

For more details, see [Why use the OpenTelemetry Collector?](https://signoz.io/docs/opentelemetry-collection-agents/opentelemetry-collector/why-to-use-collector/) and the [Collector configuration guide](https://signoz.io/docs/opentelemetry-collection-agents/opentelemetry-collector/configuration/).

Next Steps

[Create Dashboards](https://signoz.io/docs/userguide/manage-dashboards/)to visualize your custom metrics.[Set up Alerts](https://signoz.io/docs/setup-alerts-notification/)on your metrics.- Instrument your Java application with
[traces](https://signoz.io/docs/instrumentation/java/opentelemetry-java/)and[logs](https://signoz.io/docs/logs-management/send-logs/java-logs/)for complete observability.
