> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bronto.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Java logs, metrics, and traces to Bronto

> Instrument Java applications with the OpenTelemetry Java agent to send logs, metrics, and traces to Bronto over OTLP/HTTP, via an OTel Collector or directly.

The OpenTelemetry Java agent sends **logs, metrics, and traces** to Bronto with no code changes. Attach it at startup and it instruments Spring, Hibernate, JDBC, Kafka, gRPC, and hundreds of other libraries, bridges your existing Logback or Log4j output into the OTLP logs pipeline, and collects JVM metrics — all under one resource identity.

The application exports to an [OpenTelemetry Collector](/agent-setup/open-telemetry), which forwards to Bronto. If you don't run a Collector, [export directly to Bronto](#direct-export-to-bronto) instead — only the endpoint and an API key header change.

## Prerequisites

* Java 8 or later
* An OTel Collector reachable from your application, with `logs`, `metrics`, and `traces` pipelines forwarding to Bronto — see [Connect OpenTelemetry Collector to Bronto](/agent-setup/open-telemetry)

## Attach the agent

Download the agent jar:

```bash theme={"dark"}
curl -L -O https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```

Configure it through environment variables and attach it with `-javaagent`:

```bash theme={"dark"}
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_SERVICE_NAME="my-service"
export OTEL_RESOURCE_ATTRIBUTES="service.namespace=my-team,deployment.environment=production"

java -javaagent:./opentelemetry-javaagent.jar -jar myapp.jar
```

That's the whole setup. The agent exports all three signals over OTLP by default, and agent 2.x uses `http/protobuf`, appending the signal path (`/v1/logs`, `/v1/metrics`, `/v1/traces`) to the endpoint automatically.

Two resource attributes determine how Bronto organises your data:

| OTel attribute      | Bronto concept | Set with                   |
| ------------------- | -------------- | -------------------------- |
| `service.name`      | Dataset        | `OTEL_SERVICE_NAME`        |
| `service.namespace` | Collection     | `OTEL_RESOURCE_ATTRIBUTES` |

<Note>
  `http://localhost:4318` is the standard OTLP/HTTP address for a Collector on the same host. Use the address reachable from your application if the Collector runs in another container, pod, or host. No authentication is needed between the application and the Collector — the Collector holds the Bronto API key.
</Note>

<Note>
  Every setting has a system-property equivalent — `-Dotel.service.name=my-service` — if you would rather pass it on the command line. See [Agent configuration](https://opentelemetry.io/docs/zero-code/java/agent/configuration/) for the full list, including how to disable individual instrumentations.
</Note>

## Instrument your application

Existing SLF4J, Logback, and Log4j statements need no changes — the agent picks them up and attaches `trace_id` and `span_id` to any log emitted inside an active span, so you can jump from a log line to its trace in Bronto.

For spans and metrics around your own business logic, add the API dependency — not the SDK, which the agent already supplies:

<CodeGroup>
  ```xml Maven (pom.xml) theme={"dark"}
  <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    <!-- check https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api for latest -->
    <version>LATEST</version>
  </dependency>
  ```

  ```groovy Gradle (build.gradle) theme={"dark"}
  // check https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api for latest
  implementation 'io.opentelemetry:opentelemetry-api:LATEST'
  ```
</CodeGroup>

```java PaymentService.java theme={"dark"}
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PaymentService {
    private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
    private static final Tracer tracer = GlobalOpenTelemetry.getTracer("my-service");
    private static final LongCounter payments = GlobalOpenTelemetry.getMeter("my-service")
        .counterBuilder("app.payments")
        .build();

    public void processPayment(double amount) {
        Span span = tracer.spanBuilder("process-payment").startSpan();
        try (Scope scope = span.makeCurrent()) {
            span.setAttribute("payment.amount", amount);
            payments.add(1);
            logger.info("Processing payment");  // trace_id and span_id attached automatically
        } finally {
            span.end();
        }
    }
}
```

<Note>
  **Can't use the agent?** Where a javaagent isn't an option — GraalVM native images, for example — build the SDK in process with `opentelemetry-sdk-extension-autoconfigure`, which reads the same `OTEL_*` variables, and add `opentelemetry-logback-appender-1.0` (or `opentelemetry-log4j-appender-2.17`) plus `OpenTelemetryAppender.install(openTelemetry)` to bridge your logs. Spring Boot users can use the [OpenTelemetry Spring Boot starter](https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/) instead. See [Configure the SDK](https://opentelemetry.io/docs/languages/java/configuration/).
</Note>

## Verify

Run your application, then check each signal in Bronto, filtering by the `service.name` you set:

* **Logs** — the [Search](https://app.bronto.io/search) page, in the dataset named after your service
* **Metrics** — the Metric Explorer
* **Traces** — the Explore Traces page

If nothing arrives:

* Confirm the Collector is running and reachable at `OTEL_EXPORTER_OTLP_ENDPOINT`, and that its pipelines include an `otlp` receiver and the Bronto exporters.
* Port `4318` is OTLP/HTTP and `4317` is OTLP/gRPC. If you point at `4317`, also set `OTEL_EXPORTER_OTLP_PROTOCOL=grpc`.
* Run with `-Dotel.javaagent.debug=true` to log what the agent instruments and exports at startup.

For signal-specific reference material, see [Send Metrics to Bronto](/metrics/send-metrics) and [Send Traces to Bronto](/tracing/send-traces).

## Direct export to Bronto

Without a Collector, the agent exports straight to Bronto over OTLP/HTTP. Point the endpoint at your Bronto region and add your API key:

```bash theme={"dark"}
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingestion.eu.bronto.io"   # or ingestion.us.bronto.io
export OTEL_EXPORTER_OTLP_HEADERS="x-bronto-api-key=<YOUR_API_KEY>"
```

| Region | Base endpoint                    |
| ------ | -------------------------------- |
| EU     | `https://ingestion.eu.bronto.io` |
| US     | `https://ingestion.us.bronto.io` |

See [API Keys](/Account-Management/API-Keys) for how to create a key with ingestion permissions.

<Tip>
  Prefer the Collector for multi-service environments — it batches, filters, and enriches telemetry, and keeps the API key out of every application. Direct export suits single services and simple architectures.
</Tip>

## GenAI semantic conventions

If your application calls an LLM, OpenTelemetry's [GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md) define `gen_ai.*` span attributes for model, token usage, and prompt and response content.

The Java agent includes GenAI instrumentation for the OpenAI Java client and AWS SDK v2 Bedrock calls with no extra dependency. Those model calls produce a span carrying `gen_ai.provider.name`, `gen_ai.request.model`, and token-usage attributes in place of a plain HTTP client span.

<Note>
  GenAI coverage in the Java agent is newer and narrower than Python's — check the [agent's supported libraries list](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md) for your provider or framework, and use manual spans below where it isn't covered.
</Note>

### Capture prompts and responses

Content capture is off by default:

```bash theme={"dark"}
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
```

Depending on the instrumented client and agent version, captured content may be emitted as OTel log records rather than span attributes. It then travels through the logs pipeline, correlated by `trace_id`, and is not queryable on the span itself — to make it searchable alongside the span, emit it as a structured log record following [LLM Observability](/ai-features/llm-observability).

### Manual spans

Where the agent doesn't cover your provider, set the same attributes yourself:

```java theme={"dark"}
Span span = tracer.spanBuilder("chat gpt-4o-mini").startSpan();
try (Scope scope = span.makeCurrent()) {
    span.setAttribute("gen_ai.provider.name", "openai");
    span.setAttribute("gen_ai.request.model", "gpt-4o-mini");
    span.setAttribute("gen_ai.usage.input_tokens", 33);
    span.setAttribute("gen_ai.usage.output_tokens", 74);
    // Bronto flattens array attributes — this is searchable as finish_reasons.0
    span.setAttribute(
        AttributeKey.stringArrayKey("gen_ai.response.finish_reasons"),
        List.of("stop"));
} finally {
    span.end();
}
```

See the [GenAI span conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md) for the full attribute list.

For the recommended attribute set and Bronto search queries, see [LLM Observability](/ai-features/llm-observability).
