Skip to main content

Documentation Index

Fetch the complete documentation index at: https://arize-ax.mintlify.dev/docs/llms.txt

Use this file to discover all available pages before exploring further.

LangChain4j is the Java/Kotlin port of LangChain — a framework for composing LLM calls, tools, retrieval, and agents on the JVM. Arize AX captures every LangChain4j chat-model call via the com.arize:openinference-instrumentation-langchain4j artifact, attached as a model listener.

Prerequisites

  • Java 17+ (Java 11 also works, but the latest LangChain4j and OpenTelemetry releases test against 17+)
  • An Arize AX account (sign up)
  • An OPENAI_API_KEY from the OpenAI Platform

Launch Arize

  1. Sign in to your Arize AX account.
  2. From Space Settings, copy your Space ID and API Key. You will set them as ARIZE_SPACE_ID and ARIZE_API_KEY below.

Install

Add the dependencies to build.gradle:
plugins {
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    // OpenInference instrumentation + semantic conventions
    implementation 'com.arize:openinference-instrumentation-langchain4j:0.1.8'
    implementation 'com.arize:openinference-semantic-conventions:0.1.12'

    // LangChain4j core + OpenAI provider
    implementation 'dev.langchain4j:langchain4j:1.6.0'
    implementation 'dev.langchain4j:langchain4j-open-ai:1.6.0'

    // OpenTelemetry SDK + OTLP exporter
    implementation 'io.opentelemetry:opentelemetry-sdk:1.50.0'
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp:1.50.0'
}

application {
    mainClass = 'example.Main'
}

Configure credentials

export ARIZE_SPACE_ID="<your-space-id>"
export ARIZE_API_KEY="<your-api-key>"
export ARIZE_PROJECT_NAME="langchain4j-tracing-example"
export OPENAI_API_KEY="<your-openai-api-key>"

Setup tracing

Java doesn’t separate setup from runtime the way Python or TypeScript do — both happen in the same main method. Put the OpenTelemetry SDK initialization at the top, then use it for the rest of the program:
// src/main/java/example/Main.java
package example;

import com.arize.instrumentation.langchain4j.LangChain4jInstrumentor;
import com.arize.instrumentation.langchain4j.LangChain4jModelListener;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
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;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args) {
        String apiKey  = System.getenv("ARIZE_API_KEY");
        String spaceId = System.getenv("ARIZE_SPACE_ID");
        String project = System.getenv().getOrDefault(
            "ARIZE_PROJECT_NAME", "langchain4j-tracing-example");

        // Resource: service name + Arize project name (the latter is what
        // makes the trace appear under the right project in Arize).
        Resource resource = Resource.getDefault().merge(Resource.create(
            Attributes.of(
                AttributeKey.stringKey("service.name"), "langchain4j",
                // openinference.project.name = the literal value of
                // SemanticResourceAttributes.SEMRESATTRS_PROJECT_NAME.
                AttributeKey.stringKey("openinference.project.name"),
                project)));

        // OTLP gRPC exporter pointed at Arize.
        OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder()
            .setEndpoint("https://otlp.arize.com:443")
            .setHeaders(() -> Map.of(
                "authorization",   apiKey,
                "arize-space-id",  spaceId,
                "arize-interface", "java"))
            .build();

        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
            .addSpanProcessor(BatchSpanProcessor.builder(exporter)
                .setScheduleDelay(Duration.ofSeconds(1))
                .build())
            .setResource(resource)
            .build();

        OpenTelemetrySdk.builder()
            .setTracerProvider(tracerProvider)
            .setPropagators(ContextPropagators.create(
                W3CTraceContextPropagator.getInstance()))
            .buildAndRegisterGlobal();

        System.out.println("Arize AX tracing initialized for LangChain4j.");

        // The instrumentor returns a listener; attach it to every
        // ChatModel you build. Listeners are NOT auto-registered.
        LangChain4jInstrumentor instrumentor =
            LangChain4jInstrumentor.instrument();
        LangChain4jModelListener listener =
            instrumentor.createModelListener();

        ChatModel model = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-5")
            .timeout(Duration.ofMinutes(3))
            .listeners(List.of(listener))
            .build();

        String response = model.chat(
            "Why is the ocean salty? Answer in two sentences.");
        System.out.println(response);

        // Force flush + shutdown — without this, the JVM may exit before
        // the BatchSpanProcessor delivers its queue and spans get dropped.
        tracerProvider.forceFlush().join(10, TimeUnit.SECONDS);
        tracerProvider.shutdown().join(10, TimeUnit.SECONDS);
    }
}

Run LangChain4j

gradle run

Expected output

Arize AX tracing initialized for LangChain4j.
The ocean is salty because rivers continuously dissolve mineral salts from rocks and soil and carry them to the sea, where they accumulate over millions of years. Water leaves the ocean through evaporation but the salts remain, steadily concentrating until reaching today's roughly 3.5% salinity.

Verify in Arize

  1. Open your Arize AX space and select project langchain4j-tracing-example.
  2. You should see a new trace within ~30–60 seconds (Arize’s Java OTLP ingest is slightly slower than the Python path) containing a generate LLM span with the prompt, response, model name (gpt-5), and token-usage attributes attached.
  3. If no traces appear, see Troubleshooting.

Troubleshooting

  • No traces in Arize. Confirm ARIZE_SPACE_ID and ARIZE_API_KEY are set in the same shell that runs gradle run. The OTLP exporter logs at FINE level — to surface delivery errors, add java.util.logging.Logger.getLogger("io.opentelemetry").setLevel(Level.FINE) before initialization, or wire a SLF4J implementation. To confirm spans are being produced locally before troubleshooting export, add SimpleSpanProcessor.create(LoggingSpanExporter.create()) as an extra processor — it prints every span to stderr.
  • No spans, but LangChain4jInstrumentor.instrument() ran. The instrumentor is not auto-global. Each ChatModel (or EmbeddingModel, StreamingChatModel, etc.) must explicitly attach the listener: .listeners(List.of(instrumentor.createModelListener())). Models built without it produce no spans.
  • 401 from OpenAI. Verify OPENAI_API_KEY is set and has access to gpt-5. Swap modelName("gpt-5") for a model your key can call.
  • request timed out. GPT-5 reasoning can exceed LangChain4j’s default 60s timeout. Bump it: OpenAiChatModel.builder()...timeout(Duration.ofMinutes(3))....
  • Spans dropped at JVM exit. BatchSpanProcessor exports asynchronously. Always tracerProvider.forceFlush().join(...) and tracerProvider.shutdown().join(...) before main returns.
  • SLF4J(W): No SLF4J providers were found. Harmless — the OpenAI HTTP client looks for an SLF4J implementation. Add implementation 'org.slf4j:slf4j-simple:2.0.9' to build.gradle to silence it (and get HTTP request logs).

Resources

LangChain4j Documentation

OpenInference LangChain4j Instrumentor (Maven Central)

OpenInference LangChain4j Source

LangChain4j Example (with tools + multi-turn)