Cette page guide une première intégration : déclarer les artefacts, configurer l’exporter OTLP, démarrer le runtime auto-configuré, et observer un span dans le Collector. La cible est un projet Java 25 + Maven 4 qui consomme déjà Cassini (JAX-RS) et Vauban (CDI) — l’intégration HTTP et CDI est alors automatique.

Prérequis

  • Java 25 (Temurin) — sdk env depuis le repo (.sdkmanrc fourni).

  • Maven 3.9.16 — si mvn échoue avec « modelVersion 4.1.0 not supported », sdk use maven 3.9.16.

  • Un OpenTelemetry Collector accessible en HTTP (ou un récepteur compatible OTLP/HTTP-JSON). Pour les essais : docker run --rm -p 4318:4318 otel/opentelemetry-collector:latest.

Déclarer les artefacts

Ajouter au pom.xml consommateur :

<dependency>
    <groupId>io.vidocq.humboldt</groupId>
    <artifactId>humboldt-runtime</artifactId>
    <version>${humboldt.version}</version>
</dependency>

<!-- Optionnel — instrumentation automatique JAX-RS via Cassini -->
<dependency>
    <groupId>io.vidocq.humboldt</groupId>
    <artifactId>humboldt-rest</artifactId>
    <version>${humboldt.version}</version>
</dependency>

<!-- Optionnel — interceptor @WithSpan via CDI Vauban -->
<dependency>
    <groupId>io.vidocq.humboldt</groupId>
    <artifactId>humboldt-cdi</artifactId>
    <version>${humboldt.version}</version>
</dependency>

Le module humboldt-runtime agrège transitivement le SDK trace, metric, log, l’exporter OTLP HTTP, le propagator W3C et l’auto-config par variables d’environnement.

Déclarer le module dans module-info.java

module mon.application {
    requires io.vidocq.humboldt.api;
    requires io.vidocq.humboldt.runtime;

    // Optionnel selon l'intégration choisie
    requires io.vidocq.humboldt.cdi;
    requires io.vidocq.humboldt.rest;

    requires io.opentelemetry.api;
    requires io.opentelemetry.context;

    exports mon.application;
}

Configurer via variables d’environnement OTel

Humboldt lit les variables d’environnement standard OpenTelemetry (OTEL_*), avec repli sur les system properties équivalentes (otel.*). Voir Référence pour la table exhaustive.

export OTEL_SERVICE_NAME=mon-service
export OTEL_RESOURCE_ATTRIBUTES=service.namespace=billing,deployment.environment=prod
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=1.0

Auto-configurer le runtime

Au démarrage de l’application :

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.vidocq.humboldt.runtime.AutoConfiguredHumboldt;
import io.vidocq.humboldt.runtime.HumboldtAutoConfigure;

public final class Main {
    public static void main(String[] args) throws Exception {
        try (AutoConfiguredHumboldt sdk = HumboldtAutoConfigure.configure()) {
            GlobalOpenTelemetry.set(sdk);
            // Démarrer Chappe + Cassini + Vauban — l'instrumentation HTTP
            // et l'interceptor @WithSpan utiliseront automatiquement ce SDK.
            runApplication();
            sdk.flush().join(10, java.util.concurrent.TimeUnit.SECONDS);
        }
    }
}

AutoConfiguredHumboldt implémente io.opentelemetry.api.OpenTelemetry (interface standard OTel) et AutoCloseable : il expose getTracerProvider(), getMeterProvider(), getLogsBridge(), getPropagators() et coupe proprement les processors batch lors du close().

Émettre une première trace

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public final class CheckoutService {

    private static final Tracer TRACER =
        GlobalOpenTelemetry.getTracer("mon.application", "1.0.0");

    public Receipt checkout(Cart cart) {
        Span span = TRACER.spanBuilder("checkout")
            .setAttribute("cart.size", cart.size())
            .startSpan();
        try (Scope ignored = span.makeCurrent()) {
            return doCheckout(cart);
        } catch (RuntimeException ex) {
            span.recordException(ex);
            span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR);
            throw ex;
        } finally {
            span.end();
        }
    }
}

Variante CDI (avec humboldt-cdi) :

import io.opentelemetry.instrumentation.annotations.WithSpan;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class CheckoutService {

    @WithSpan("checkout")
    public Receipt checkout(Cart cart) {
        return doCheckout(cart);
    }
}

Lancer un Collector local

docker run --rm \
  -p 4318:4318 \
  -v $(pwd)/otel-collector.yaml:/etc/otelcol/config.yaml \
  otel/opentelemetry-collector:latest

Un fichier otel-collector.yaml minimal :

receivers:
  otlp:
    protocols:
      http:
exporters:
  logging:
    loglevel: debug
service:
  pipelines:
    traces:  { receivers: [otlp], exporters: [logging] }
    metrics: { receivers: [otlp], exporters: [logging] }
    logs:    { receivers: [otlp], exporters: [logging] }

À l’exécution, les spans checkout apparaîtront dans la console du Collector avec le bon service.name et l’attribut cart.size.

Build et vérification

./mvnw -ntp install -DskipTests
./mvnw test

L’invariant à observer dans les logs : les threads exportateurs sont des virtual threads (HttpClient-virtual, humboldt-batch-worker). Aucune classe io.opentelemetry.sdk. ne doit apparaître dans le classpath applicatif — seul io.opentelemetry.api. est consommé.

Et après ?

  • Pour comprendre le vocabulaire OTel (Span, Resource, Scope, baggage), voir Concepts.

  • Pour les patterns d’instrumentation manuelle, métriques et logs : Cas d’usage.

  • Pour la table complète des clés OTEL_* / MP_TELEMETRY_* : Référence.