---
title: Java, manual instrumentation
description: Build GenAI spans by hand in Java with the OpenTelemetry SDK and send them to Coralogix AI Center using the new semantic conventions.
url: https://coralogix.com/docs/user-guides/ai/otel-integration/code-examples/java-manual/
product: ai
feature: AI App Catalog
feature_id: ai-app-catalog
feature_identifier: ai/ai-app-catalog
last_updated: '2026-09-03'
---

# Java: manual instrumentation

No auto-instrumentation exists for OpenAI in Java. Manual spans with new semconv.

Before you start

These examples export to a local OpenTelemetry Collector over OTLP/gRPC. Deploy the collector first. See [Code examples → Deploy an OpenTelemetry Collector](https://coralogix.com/docs/user-guides/ai/otel-integration/code-examples.md#prerequisites-deploy-an-opentelemetry-collector).

## Dependencies (`pom.xml`)

```
<dependencies>

    <dependency>

        <groupId>com.openai</groupId>

        <artifactId>openai-java</artifactId>

        <version>4.35.0</version>

    </dependency>

    <dependency>

        <groupId>io.opentelemetry</groupId>

        <artifactId>opentelemetry-sdk</artifactId>

    </dependency>

    <dependency>

        <groupId>io.opentelemetry</groupId>

        <artifactId>opentelemetry-exporter-otlp</artifactId>

    </dependency>

</dependencies>

<dependencyManagement>

    <dependencies>

        <dependency>

            <groupId>io.opentelemetry</groupId>

            <artifactId>opentelemetry-bom</artifactId>

            <version>1.59.0</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

    </dependencies>

</dependencyManagement>
```

## Environment variables

```
export OPENAI_API_KEY="sk-..."

export CX_OTEL_ENDPOINT="http://<COLLECTOR_HOST>:4317"
```

## Script

```
package com.example;



import com.openai.client.OpenAIClient;

import com.openai.client.okhttp.OpenAIOkHttpClient;

import com.openai.models.*;

// --- OTel imports ---

import io.opentelemetry.api.common.*;

import io.opentelemetry.api.trace.*;

import io.opentelemetry.context.Scope;

import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;

import io.opentelemetry.sdk.OpenTelemetrySdk;

import io.opentelemetry.sdk.resources.Resource;

import io.opentelemetry.sdk.trace.SdkTracerProvider;

import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;



public class GenAiManualInstrumentation {

    public static void main(String[] args) {

        // --- OTel setup: configure OTLP exporter, resource, and tracer ---

        var cxEndpoint = System.getenv().getOrDefault("CX_OTEL_ENDPOINT",

                "http://<COLLECTOR_HOST>:4317");



        var exporter = OtlpGrpcSpanExporter.builder()

                .setEndpoint(cxEndpoint).build();

        var resource = Resource.getDefault().merge(Resource.create(

                Attributes.builder()

                    .put("service.name", "java-genai-demo")

                    .put("cx.application.name", "my-genai-app")

                    .put("cx.subsystem.name", "my-service").build()));

        var tracerProvider = SdkTracerProvider.builder()

                .addSpanProcessor(BatchSpanProcessor.builder(exporter).build())

                .setResource(resource).build();

        var otel = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).build();

        var tracer = otel.getTracer("genai-manual");



        // --- Your app logic ---

        var openAi = OpenAIOkHttpClient.fromEnv();

        var model = "gpt-4o-mini";

        var systemMsg = "You are a concise assistant.";

        var userMsg = "Explain distributed tracing in one sentence.";



        // --- OTel: build gen_ai.input.messages in new semconv format ---

        var inputJson = "[" +

            "{\"role\":\"system\",\"parts\":[{\"type\":\"text\",\"content\":\"" + systemMsg + "\"}]}," +

            "{\"role\":\"user\",\"parts\":[{\"type\":\"text\",\"content\":\"" + userMsg + "\"}]}]";



        // OTel: create a GenAI span with required attributes

        Span span = tracer.spanBuilder("chat " + model)

                .setSpanKind(SpanKind.CLIENT)

                .setAttribute("gen_ai.operation.name", "chat")

                .setAttribute("gen_ai.provider.name", "openai")

                .setAttribute("gen_ai.request.model", model)

                .setAttribute("gen_ai.input.messages", inputJson)

                .startSpan();



        try (Scope ignored = span.makeCurrent()) {

            var params = ChatCompletionCreateParams.builder()

                    .model(ChatModel.GPT_4O_MINI)

                    .maxCompletionTokens(100)

                    .addSystemMessage(systemMsg)

                    .addUserMessage(userMsg).build();

            var completion = openAi.chat().completions().create(params);

            var text = completion.choices().get(0).message().content().orElse("");



            span.setAttribute("gen_ai.response.model", completion.model());

            span.setAttribute("gen_ai.response.id", completion.id());

            span.setAttribute("gen_ai.usage.input_tokens",

                    completion.usage().map(u -> u.promptTokens()).orElse(0L));

            span.setAttribute("gen_ai.usage.output_tokens",

                    completion.usage().map(u -> u.completionTokens()).orElse(0L));

            span.setAttribute("gen_ai.output.messages",

                    "[{\"role\":\"assistant\",\"parts\":[{\"type\":\"text\",\"content\":\"" +

                    text.replace("\"", "\\\"") + "\"}]}]");

            System.out.println("Response: " + text);

        } catch (Exception e) {

            span.setStatus(StatusCode.ERROR, e.getMessage());

            span.recordException(e);

        } finally {

            span.end();

        }

        tracerProvider.forceFlush();

        tracerProvider.shutdown();

    }

}
```

## Next steps

Look up which open-source library to use for your provider in [Compatibility matrix](https://coralogix.com/docs/user-guides/ai/otel-integration/providers.md).
