# Using OpenTelemetry

This guide explains how your Quarkus application can utilize OpenTelemetry (OTel) to provide distributed tracing for interactive web applications.

OpenTelemetry Metrics and Logging are not yet supported.

| Quarkus now supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at OpenTelemetry SDK Autoconfigure with the `quarkus.*` prefix. Extensions and the libraries they provide, are directly instrumented in Quarkus. The use of the OpenTelemetry Agent is not needed nor recommended due to context propagation issues between imperative and reactive libraries.

## Prerequisites

To complete this guide, you need:

- Roughly 15 minutes

- An IDE

- JDK 11+ installed with `JAVA_HOME` configured appropriately

- Apache Maven 3.9.3

- Docker and Docker Compose or Podman, and Docker Compose

- Optionally the Quarkus CLI if you want to use it

- Optionally Mandrel or GraalVM installed and configured appropriately if you want to build a native executable (or Docker if you use a native container build)

## Architecture

In this guide, we create a straightforward REST application to demonstrate distributed tracing.

## Solution

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can skip right to the completed example.

Clone the Git repository: `git clone https://github.com/quarkusio/quarkus-quickstarts.git`, or download an archive.

The solution is located in the `opentelemetry-quickstart` directory.

## Creating the Maven project

First, we need a new project. Create a new project with the following command:

CLI

    quarkus create app org.acme:opentelemetry-quickstart \
        --extension='resteasy-reactive,quarkus-opentelemetry' \
        --no-code
    cd opentelemetry-quickstart

To create a Gradle project, add the `--gradle` or `--gradle-kotlin-dsl` option.

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

Maven

    mvn io.quarkus.platform:quarkus-maven-plugin:3.3.0:create \
        -DprojectGroupId=org.acme \
        -DprojectArtifactId=opentelemetry-quickstart \
        -Dextensions='resteasy-reactive,quarkus-opentelemetry' \
        -DnoCode
    cd opentelemetry-quickstart

To create a Gradle project, add the `-DbuildTool=gradle` or `-DbuildTool=gradle-kotlin-dsl` option.

For Windows users:

- If using cmd, (don’t use backward slash `\` and put everything on the same line)

- If using Powershell, wrap `-D` parameters in double quotes e.g. `"-DprojectArtifactId=opentelemetry-quickstart"`

This command generates the Maven project and imports the `quarkus-opentelemetry` extension, which includes the default OpenTelemetry support, and a gRPC span exporter for OTLP.

If you already have your Quarkus project configured, you can add the `quarkus-opentelemetry` extension to your project by running the following command in your project base directory:

CLI

    quarkus extension add 'opentelemetry'

Maven

    ./mvnw quarkus:add-extension -Dextensions='opentelemetry'

Gradle

    ./gradlew addExtension --extensions='opentelemetry'

This will add the following to your build file:

pom.xml

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-opentelemetry</artifactId>
    </dependency>

build.gradle

    implementation("io.quarkus:quarkus-opentelemetry")

### Examine the Jakarta REST resource

Create a `src/main/java/org/acme/opentelemetry/TracedResource.java` file with the following content:

    package org.acme.opentelemetry;

    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;
    import org.jboss.logging.Logger;

    @Path("/hello")
    public class TracedResource {

        private static final Logger LOG = Logger.getLogger(TracedResource.class);

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() {
            LOG.info("hello");
            return "hello";
        }
    }

Notice that there is no tracing specific code included in the application. By default, requests sent to this endpoint will be traced without any required code changes.

### Create the configuration

There are no mandatory configurations for the extension to work.

If you need to change any of the default property values, here is an example on how to configure the default OTLP gRPC Exporter within the application, using the `src/main/resources/application.properties` file:

    quarkus.application.name=myservice (1)
    quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317 (2)
    quarkus.otel.exporter.otlp.traces.headers=authorization=Bearer my_secret (3)
    quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n  (4)

    # Alternative to the console log
    quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" (5)

1 | All spans created from the application will include an OpenTelemetry `Resource` indicating the span was created by the `myservice` application. If not set, it will default to the artifact id.
2 | gRPC endpoint to send spans. If not set, it will default to `http://localhost:4317`.
3 | Optional gRPC headers commonly used for authentication
4 | Add tracing information into log messages.
5 | You can also only put the trace info into the access log. In this case you must omit the info in the console log format.

| All configurations have been updated from `quarkus.opentelemetry.*` → `quarkus.otel.*` The legacy configurations are now deprecated but will still work during a transition period.

## Run the application

The first step is to configure and start the OpenTelemetry Collector to receive, process and export telemetry data to Jaeger that will display the captured traces.

| Jaeger-all-in-one includes the Jaeger agent, an OTel collector, and the query service/UI. You do not need to install a separated collector. You can directly send the trace data to Jaeger (after enabling OTLP receivers there, see e.g. this blog entry for details).

Start the OpenTelemetry Collector and Jaeger system via the following `docker-compose.yml` file that you can launch via `docker-compose up -d`:

    version: "2"
    services:

      # Jaeger
      jaeger-all-in-one:
        image: jaegertracing/all-in-one:latest
        ports:
          - "16686:16686" # Jaeger UI
          - "14268:14268" # Receive legacy OpenTracing traces, optional
          - "4317:4317"   # OTLP gRPC receiver
          - "4318:4318"   # OTLP HTTP receiver, not yet used by Quarkus, optional
          - "14250:14250" # Receive from external otel-collector, optional
        environment:
          - COLLECTOR_OTLP_ENABLED=true

You should remove the optional ports you don’t need them.

Now we are ready to run our application. If using `application.properties` to configure the tracer:

CLI

    quarkus dev

Maven

    ./mvnw quarkus:dev

Gradle

    ./gradlew --console=plain quarkusDev

or if configuring the OTLP gRPC endpoint via JVM arguments:

CLI

    quarkus dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"

Maven

    ./mvnw quarkus:dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"

Gradle

    ./gradlew --console=plain quarkusDev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"

With the OpenTelemetry Collector, the Jaeger system and the application running, you can make a request to the provided endpoint:

    $ curl http://localhost:8080/hello
    hello

When the first request has been submitted, you will be able to see the tracing information in the logs:

    10:49:02 INFO  traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, rest-client, resteasy, smallrye-context-propagation, vertx]
    10:49:03 INFO  traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello
    10:49:03 INFO  traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello

Then visit the Jaeger UI to see the tracing information.

Hit `CTRL+C` or type `q` to stop the application.

### JDBC

The JDBC instrumentation will add a span for each JDBC queries done by your application, to enable it, add the following dependency to your build file:

pom.xml

    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-jdbc</artifactId>
    </dependency>

build.gradle

    implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc")

As it uses a dedicated JDBC datasource wrapper, you must enable telemetry for your datasource:

    # enable tracing
    quarkus.datasource.jdbc.telemetry=true

    # configure datasource
    quarkus.datasource.db-kind=postgresql
    quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase

## Additional configuration

Some use cases will require custom configuration of OpenTelemetry. These sections will outline what is necessary to properly configure it.

### ID Generator

The OpenTelemetry extension will use by default a random ID Generator when creating the trace and span identifier.

Some vendor-specific protocols need a custom ID Generator, you can override the default one by creating a producer. The OpenTelemetry extension will detect the `IdGenerator` CDI bean and will use it when configuring the tracer producer.

    @Singleton
    public class CustomConfiguration {

        /** Creates a custom IdGenerator for OpenTelemetry */
        @Produces
        @Singleton
        public IdGenerator idGenerator() {
            return AwsXrayIdGenerator.getInstance();
        }
    }

### Propagators

OpenTelemetry propagates cross-cutting concerns through propagators that will share an underlying `Context` for storing state and accessing data across the lifespan of a distributed transaction.

By default, the OpenTelemetry extension enables the W3C Trace Context and the W3C Baggage propagators, you can however choose any of the supported OpenTelemetry propagators by setting the `propagators` config that is described in the OpenTelemetry Configuration Reference.

#### Additional Propagators

- The `b3`, `b3multi`, `jaeger` and `ottrace` propagators will need the trace-propagators extension to be added as a dependency to your project.

pom.xml

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-extension-trace-propagators</artifactId>
    </dependency>

build.gradle

    implementation("io.opentelemetry:opentelemetry-extension-trace-propagators")

- The `xray` propagator will need the aws extension to be added as a dependency to your project.

pom.xml

    <dependency>
        <groupId>io.opentelemetry.contrib</groupId>
        <artifactId>opentelemetry-aws-xray-propagator</artifactId>
    </dependency>

build.gradle

    implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")

### Resource

A resource is a representation of the entity that is producing telemetry, it adds attributes to the exported trace to characterize who is producing the trace.

You can add attributes by setting the `resource-attributes` tracer config that is described in the OpenTelemetry Configuration Reference. Since this property can be overridden at runtime, the OpenTelemetry extension will pick up its value following the order of precedence that is described in the Quarkus Configuration Reference.

If by any means you need to use a custom resource or one that is provided by one of the OpenTelemetry SDK Extensions you can create multiple resource producers. The OpenTelemetry extension will detect the `Resource` CDI beans and will merge them when configuring the tracer producer.

    @ApplicationScoped
    public class CustomConfiguration {

        @Produces
        @ApplicationScoped
        public Resource osResource() {
            return OsResource.get();
        }

        @Produces
        @ApplicationScoped
        public Resource ecsResource() {
            return EcsResource.get();
        }
    }

### Sampler

A sampler decides whether a trace should be sampled and exported, controlling noise and overhead by reducing the number of sample of traces collected and sent to the collector.

You can set a built-in sampler simply by setting the desired sampler config described in the OpenTelemetry Configuration Reference.

| Quarkus 3.0 introduced breaking changes on the configuration. Sampler related property names and values change to comply with the latest Java OpenTelemetry SDK. During a transition period it will be possible to set the new configuration values in the old property because we are mapping `quarkus.opentelemetry.tracer.sampler` → `quarkus.otel.traces.sampler`. If the sampler is parent based, there is no need to set, the now dropped property, `quarkus.opentelemetry.tracer.sampler.parent-based`. The values you need to set on `quarkus.opentelemetry.tracer.sampler` are now:
Old Sampler config value | New Sampler config value | New Sampler config value (Parent based)
--- | --- | ---
`on` | `always_on` | `parentbased_always_on`
`off` | `always_off` | `parentbased_always_off`
`ratio` | `traceidratio` | `parentbased_traceidratio`

If you need to use a custom sampler there are now 2 different ways:

#### Sampler CDI Producer

You can create a sampler CDI producer. The Quarkus OpenTelemetry extension will detect the `Sampler` CDI bean and will use it when configuring the Tracer.

    @Singleton
    public class CustomConfiguration {

        /** Creates a custom sampler for OpenTelemetry */
        @Produces
        @Singleton
        public Sampler sampler() {
            return JaegerRemoteSampler.builder()
            .setServiceName("my-service")
            .build();
        }
    }

#### OTel Sampler SPI

This will use the SPI hooks available with the OTel Autoconfiguration. You can create a simple Sampler class:

    public class CustomSPISampler implements Sampler {
        @Override
        public SamplingResult shouldSample(Context context,
                String s,
                String s1,
                SpanKind spanKind,
                Attributes attributes,
                List<LinkData> list) {
            // Do some sampling here
            return Sampler.alwaysOn().shouldSample(context, s, s1, spanKind, attributes, list);
        }

        @Override
        public String getDescription() {
            return "custom-spi-sampler-description";
        }
    }

Then a Sampler Provider:

    public class CustomSPISamplerProvider implements ConfigurableSamplerProvider {
        @Override
        public Sampler createSampler(ConfigProperties configProperties) {
            return new CustomSPISampler();
        }

        @Override
        public String getName() {
            return "custom-spi-sampler";
        }
    }

Write the SPI loader text file at `resources/META-INF/services` with name `io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider` containing the full qualified name of the `CustomSPISamplerProvider` class.

Then activate on the configuration:

    quarkus.otel.traces.sampler=custom-spi-sampler

As you can see, CDI is much simpler to work with.

## Additional instrumentation

Some Quarkus extensions will require additional code to ensure traces are propagated to subsequent execution. These sections will outline what is necessary to propagate traces across process boundaries.

The instrumentation documented in this section has been tested with Quarkus and works in both standard and native mode.

### CDI

Annotating a method in any CDI aware bean with the `io.opentelemetry.instrumentation.annotations.WithSpan` annotation will create a new Span and establish any required relationships with the current Trace context.

Method parameters can be annotated with the `io.opentelemetry.instrumentation.annotations.SpanAttribute` annotation to indicate which method parameters should be part of the Trace.

Example:

    @ApplicationScoped
    class SpanBean {
        @WithSpan
        void span() {

        }

        @WithSpan("name")
        void spanName() {

        }

        @WithSpan(kind = SERVER)
        void spanKind() {

        }

        @WithSpan
        void spanArgs(@SpanAttribute(value = "arg") String arg) {

        }
    }

### Available OpenTelemetry CDI injections

As per MicroProfile Telemetry Tracing specification, Quarkus supports the CDI injections of the following classes:

- `io.opentelemetry.api.OpenTelemetry`

- `io.opentelemetry.api.trace.Tracer`

- `io.opentelemetry.api.trace.Span`

- `io.opentelemetry.api.baggage.Baggage`

You can inject these classes in any CDI enabled bean. For instance, the `Tracer` is particularly useful to start custom spans:

    @Inject
    Tracer tracer;

    ...

    public void tracedWork() {
        Span span = tracer.spanBuilder("My custom span")
            .setAttribute("attr", "attr.value")
            .setParent(Context.current().with(Span.current()))
            .setSpanKind(SpanKind.INTERNAL)
            .startSpan();

        // traced work

        span.end();
    }

### SmallRye Reactive Messaging - Kafka

When using the SmallRye Reactive Messaging extension for Kafka, we are able to propagate the span into the Kafka Record with:

    TracingMetadata tm = TracingMetadata.withPrevious(Context.current());
    Message out = Message.of(...).withMetadata(tm);

The above creates a `TracingMetadata` object we can add to the `Message` being produced, which retrieves the OpenTelemetry `Context` to extract the current span for propagation.

## Exporters

Quarkus OpenTelemetry defaults to the standard OTLP exporter defined in OpenTelemetry.

Additional exporters will be available in the Quarkiverse quarkus-opentelemetry-exporter project.

## OpenTelemetry Configuration Reference

Quarkus supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at OpenTelemetry SDK Autoconfigure adding the usual `quarkus.*` prefix.

Quarkus OpenTelemetry configuration properties now have the `quarkus.otel.*` prefix.

The legacy properties with prefix `quarkus.opentelemetry.*` are currently being mapped to the new ones as a default, during a transition period. See Default column in the details below.

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property | Type | Default
--- | --- | ---
`quarkus.otel.traces.exporter` List of exporters supported by Quarkus. List of exporters to be used for tracing, separated by commas. Has one of the values on `ExporterType` `otlp`, `cdi`, `none` or the full qualified name of a class implementing `io.opentelemetry.sdk.trace.export.SpanExporter` Default on Quarkus is `ExporterType.Constants#CDI_VALUE`. Environment variable: `QUARKUS_OTEL_TRACES_EXPORTER` | list of string | `cdi`
`quarkus.otel.traces.sampler` The sampler to use for tracing. Has one of the values on `SamplerType` `always_on`, `always_off`, `traceidratio`, `parentbased_always_on`, `parentbased_always_off`, `parentbased_traceidratio` or the Sampler SPI name. This will use the OTel SPI hooks for the `io.opentelemetry.sdk.trace.samplers.Sampler` implementation set in the provider: `io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider`. Fallbacks to the legacy property `quarkus.opentelemetry.tracer.sampler.sampler.name` or defaults to `SamplerType.Constants#PARENT_BASED_ALWAYS_ON`. Environment variable: `QUARKUS_OTEL_TRACES_SAMPLER` | string | `parentbased_always_on`
`quarkus.otel.traces.eusp.enabled` Enable the `io.quarkus.opentelemetry.runtime.exporter.otlp.EndUserSpanProcessor`. The `io.quarkus.opentelemetry.runtime.exporter.otlp.EndUserSpanProcessor` adds the `io.opentelemetry.semconv.trace.attributes.SemanticAttributes.ENDUSER_ID` and `io.opentelemetry.semconv.trace.attributes.SemanticAttributes.ENDUSER_ROLE` to the Span. Environment variable: `QUARKUS_OTEL_TRACES_EUSP_ENABLED` | boolean | `false`
`quarkus.otel.metrics.exporter` No Metrics exporter for now Environment variable: `QUARKUS_OTEL_METRICS_EXPORTER` | list of string | `none`
`quarkus.otel.logs.exporter` No Log exporter for now. Environment variable: `QUARKUS_OTEL_LOGS_EXPORTER` | list of string | `none`
`quarkus.otel.propagators` The propagators to be used. Use a comma-separated list for multiple propagators. Has values from `PropagatorType` or the full qualified name of a class implementing `io.opentelemetry.context.propagation.TextMapPropagator`. Default is `PropagatorType.Constants#TRACE_CONTEXT`,`PropagatorType.Constants#BAGGAGE` (W3C). Environment variable: `QUARKUS_OTEL_PROPAGATORS` | list of string | `tracecontext,baggage`
`quarkus.otel.sdk.disabled` If true, disable the OpenTelemetry SDK. Runtime configuration. Defaults to `false`. Environment variable: `QUARKUS_OTEL_SDK_DISABLED` | boolean | `false`
`quarkus.otel.traces.suppress-non-application-uris` Suppress non-application uris from trace collection. This will suppress tracing of `/q` endpoints. Providing a custom `io.opentelemetry.sdk.trace.samplers.Sampler` CDI Bean will ignore this setting. This is a Quarkus specific property. Suppressing non-application uris is enabled by default. Fallbacks to the legacy property `quarkus.opentelemetry.tracer.suppress-non-application-uris` or defaults to `true`. Environment variable: `QUARKUS_OTEL_TRACES_SUPPRESS_NON_APPLICATION_URIS` | boolean | `true`
`quarkus.otel.traces.include-static-resources` Include static resources from trace collection. This is a Quarkus specific property. Include static resources is disabled by default. Providing a custom `io.opentelemetry.sdk.trace.samplers.Sampler` CDI Bean will ignore this setting. Fallbacks to the legacy property `quarkus.opentelemetry.tracer.include-static-resources` or defaults to `false`. Environment variable: `QUARKUS_OTEL_TRACES_INCLUDE_STATIC_RESOURCES` | boolean | `false`
`quarkus.otel.traces.sampler.arg` An argument to the configured tracer if supported, for example a ratio. Fallbacks to the legacy property `quarkus.opentelemetry.tracer.sampler.ratio` or defaults to `1.0`. Environment variable: `QUARKUS_OTEL_TRACES_SAMPLER_ARG` | double | `1.0d`
`quarkus.otel.attribute.value.length.limit` The maximum length of attribute values. Applies to spans and logs. By default, there is no limit. Environment variable: `QUARKUS_OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` | string |
`quarkus.otel.attribute.count.limit` The maximum number of attributes. Applies to spans, span events, span links, and logs. Default is `128`. Environment variable: `QUARKUS_OTEL_ATTRIBUTE_COUNT_LIMIT` | int | `128`
`quarkus.otel.span.attribute.value.length.limit` The maximum length of span attribute values. Takes precedence over `otel.attribute.value.length.limit`. By default, there is no limit. Environment variable: `QUARKUS_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` | int |
`quarkus.otel.span.attribute.count.limit` The maximum number of attributes per span. Takes precedence over `otel.attribute.count.limit`. Default is `128`. Environment variable: `QUARKUS_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` | int | `128`
`quarkus.otel.span.event.count.limit` The maximum number of events per span. Default is `128`. Environment variable: `QUARKUS_OTEL_SPAN_EVENT_COUNT_LIMIT` | int | `128`
`quarkus.otel.span.link.count.limit` The maximum number of links per span. Default is `128`. Environment variable: `QUARKUS_OTEL_SPAN_LINK_COUNT_LIMIT` | int | `128`
`quarkus.otel.bsp.schedule.delay` The interval, in milliseconds, between two consecutive exports. Default is `5000`. Environment variable: `QUARKUS_OTEL_BSP_SCHEDULE_DELAY` | Duration | `5S`
`quarkus.otel.bsp.max.queue.size` The maximum queue size. Default is `2048`. Environment variable: `QUARKUS_OTEL_BSP_MAX_QUEUE_SIZE` | int | `2048`
`quarkus.otel.bsp.max.export.batch.size` The maximum batch size. Default is `512`. Environment variable: `QUARKUS_OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | int | `512`
`quarkus.otel.bsp.export.timeout` The maximum allowed time, in milliseconds, to export data. Default is `30s`. Environment variable: `QUARKUS_OTEL_BSP_EXPORT_TIMEOUT` | Duration | `30S`
`quarkus.otel.resource.attributes` Specify resource attributes in the following format: `key1=val1,key2=val2,key3=val3`. Environment variable: `QUARKUS_OTEL_RESOURCE_ATTRIBUTES` | list of string |
`quarkus.otel.service.name` Specify logical service name. Takes precedence over service.name defined with otel.resource.attributes and from quarkus.application.name. Defaults to `quarkus.application.name`. Environment variable: `QUARKUS_OTEL_SERVICE_NAME` | string | `${quarkus.application.name:unset}`
`quarkus.otel.experimental.resource.disabled-keys` Specify resource attribute keys that are filtered. Environment variable: `QUARKUS_OTEL_EXPERIMENTAL_RESOURCE_DISABLED_KEYS` | list of string |
`quarkus.otel.exporter.otlp.endpoint` Sets the OTLP endpoint to connect to. If unset, defaults to `OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI`. We are currently using just the traces, therefore `quarkus.otel.exporter.otlp.traces.endpoint` is recommended. Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT` | string | `http://localhost:4317/`
`quarkus.otel.exporter.otlp.traces.endpoint` OTLP Exporter specific. Will override `otel.exporter.otlp.endpoint`, if set. Fallbacks to the legacy property `quarkus.opentelemetry.tracer.exporter.otlp.endpoint<` or defaults to `OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI`. Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | string | `http://localhost:4317/`
`quarkus.otel.exporter.otlp.traces.headers` Key-value pairs to be used as headers associated with gRPC requests. The format is similar to the `OTEL_EXPORTER_OTLP_HEADERS` environment variable, a list of key-value pairs separated by the "=" character. i.e.: key1=value1,key2=value2 Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_HEADERS` | list of string |
`quarkus.otel.exporter.otlp.traces.compression` Sets the method used to compress payloads. If unset, compression is disabled. Currently supported compression methods include `gzip` and `none`. Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | `gzip`, `none` |
`quarkus.otel.exporter.otlp.traces.timeout` Sets the maximum time to wait for the collector to process an exported batch of spans. If unset, defaults to `OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS`s. Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | Duration | `10S`
`quarkus.otel.exporter.otlp.traces.protocol` OTLP defines the encoding of telemetry data and the protocol used to exchange data between the client and the server. Depending on the exporter, the available protocols will be different. Currently, only `grpc` and `http` are allowed. Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | string | `grpc`
`quarkus.otel.exporter.otlp.traces.key-cert.keys` Comma-separated list of the path to the key files (Pem format). Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_KEY_CERT_KEYS` | list of string |
`quarkus.otel.exporter.otlp.traces.key-cert.certs` Comma-separated list of the path to the certificate files (Pem format). Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_KEY_CERT_CERTS` | list of string |
`quarkus.otel.exporter.otlp.traces.trust-cert.certs` Comma-separated list of the trust certificate files (Pem format). Environment variable: `QUARKUS_OTEL_EXPORTER_OTLP_TRACES_TRUST_CERT_CERTS` | list of string |

| About the Duration format The format for durations uses the standard `java.time.Duration` format. You can learn more about it in the Duration#parse() javadoc. You can also provide duration values starting with a number. In this case, if the value consists only of a number, the converter treats the value as seconds. Otherwise, `PT` is implicitly prepended to the value to obtain a standard `java.time.Duration` format.