Skip to main content
This page covers instrumenting a Java application with the OpenTelemetry SDK to send logs and traces to Bronto over OTLP/HTTP via a local OTel Collector. Logs are bridged via the Logback or Log4j2 appender — no log statement changes needed. Traces are added by configuring a tracer provider alongside the log provider.
If you don’t have a Collector and want to export directly from your application to Bronto, see Direct export to Bronto at the bottom of this page.

Prerequisites

Install dependencies

Use the OpenTelemetry BOM to manage all SDK versions in one place and avoid version conflicts.
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.opentelemetry</groupId>
      <artifactId>opentelemetry-bom</artifactId>
      <!-- check https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-bom for latest -->
      <version>LATEST</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
  </dependency>
  <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
  </dependency>
  <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
  </dependency>
  <!-- check https://mvnrepository.com/artifact/io.opentelemetry.instrumentation/opentelemetry-logback-appender-1.0 for latest -->
  <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-logback-appender-1.0</artifactId>
    <version>LATEST</version>
  </dependency>
</dependencies>
PackagePurpose
opentelemetry-apiCore OTel API
opentelemetry-sdkSDK — SdkLoggerProvider, processors
opentelemetry-exporter-otlpOTLP/HTTP exporter
opentelemetry-logback-appender-1.0Bridges Logback into OTel
Using Log4j2 instead of Logback? Replace the appender dependency with opentelemetry-log4j-appender-2.17 from io.opentelemetry.instrumentation.

Configure the log bridge

Add the OpenTelemetryAppender to your logback.xml. Every log record Logback handles will be forwarded to the OTel pipeline.
logback.xml
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="OpenTelemetry"
            class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
    <captureExperimentalAttributes>true</captureExperimentalAttributes>
    <captureCodeAttributes>true</captureCodeAttributes>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="OpenTelemetry" />
  </root>
</configuration>
captureExperimentalAttributes includes thread name and logger name as OTel attributes. captureCodeAttributes includes the source file, class, method, and line number.

Configure the OTLP exporter

Create an OpenTelemetrySdk instance with a SdkLoggerProvider and install it as the global instance. Call OpenTelemetryAppender.install() to wire the Logback appender to the SDK.
OtelConfig.java
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ResourceAttributes;

public class OtelConfig {
    public static void configure() {
        OtlpHttpLogRecordExporter exporter = OtlpHttpLogRecordExporter.builder()
            .setEndpoint("http://localhost:4318/v1/logs")
            .build();

        Resource resource = Resource.getDefault().merge(
            Resource.create(Attributes.of(
                ResourceAttributes.SERVICE_NAME, "my-service",
                ResourceAttributes.SERVICE_NAMESPACE, "my-team",
                ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "production"
            ))
        );

        SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
            .setResource(resource)
            .addLogRecordProcessor(BatchLogRecordProcessor.builder(exporter).build())
            .build();

        OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder()
            .setLoggerProvider(loggerProvider)
            .buildAndRegisterGlobal();

        OpenTelemetryAppender.install(openTelemetry);
    }
}
Call OtelConfig.configure() once at application startup, before the first log statement.

Set resource attributes

Resource attributes are attached to every log record exported from this process. Two attributes drive how Bronto organises incoming logs:
OTel attributeBronto conceptDescription
service.nameDatasetGroups logs from one service
service.namespaceCollectionGroups related services or a team’s services
These are set via Resource.create() in the SDK configuration above.

Complete example

Main.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        OtelConfig.configure();

        logger.info("Application started");
        logger.warn("Low disk space, free_gb={}", 2.1);
        logger.error("Database connection failed", new RuntimeException("timeout"));
    }
}
Existing SLF4J / Logback log statements require no changes.

Verify log collection

After running your application, open the Search page in Bronto. Filter by the dataset name you set in service.name — your log records should appear within a few seconds. If no logs appear, check:
  • The OTel Collector is running and reachable at the configured endpoint.
  • The Collector’s pipeline includes a logs pipeline with an otlp receiver and the Bronto exporter — see Connect Open Telemetry to Bronto.
  • OpenTelemetryAppender.install() is called before the first log statement.
  • BatchLogRecordProcessor exports on a background thread — for short-lived programs, add a shutdown hook: loggerProvider.shutdown().

Traces

The OpenTelemetry Java Agent provides zero-code auto-instrumentation for Spring, Hibernate, gRPC, Kafka, JDBC, and many more frameworks. Download the jar and attach it at startup with -javaagent:opentelemetry-javaagent.jar — no SDK code changes are needed. It is the recommended starting point for most Java applications.

Configure the tracer provider

No additional packages are needed — opentelemetry-sdk and opentelemetry-exporter-otlp already include tracing support. Create a SdkTracerProvider and combine it with the SdkLoggerProvider in the same OpenTelemetrySdk builder:
OtelConfig.java
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;

SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .setResource(resource)
    .addSpanProcessor(BatchSpanProcessor.builder(
        OtlpHttpSpanExporter.builder()
            .setEndpoint("http://localhost:4318/v1/traces")
            .build()
    ).build())
    .build();

OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder()
    .setLoggerProvider(loggerProvider)
    .setTracerProvider(tracerProvider)
    .buildAndRegisterGlobal();

OpenTelemetryAppender.install(openTelemetry);
The shared resource ensures service.name and service.namespace are identical on both logs and traces.

Creating spans

Get a tracer from the global instance and use it to create spans:
Tracer tracer = GlobalOpenTelemetry.getTracer("my-service");

Span span = tracer.spanBuilder("process-payment")
    .startSpan();
try (Scope scope = span.makeCurrent()) {
    span.setAttribute("payment.amount", 99.99);
    logger.info("Processing payment");  // trace_id and span_id injected automatically
} finally {
    span.end();
}
Any Logback or Log4j2 statement inside an active span will automatically have trace_id and span_id attached.

Direct export to Bronto

If you are not using an OTel Collector, export directly to Bronto by replacing the exporter configurations with the Bronto OTLP endpoints and your API key:
OtlpHttpLogRecordExporter logExporter = OtlpHttpLogRecordExporter.builder()
    .setEndpoint("https://ingestion.eu.bronto.io/v1/logs") // or ingestion.us.bronto.io
    .addHeader("x-bronto-api-key", "<YOUR_API_KEY>")
    .build();

OtlpHttpSpanExporter traceExporter = OtlpHttpSpanExporter.builder()
    .setEndpoint("https://ingestion.eu.bronto.io/v1/traces") // or ingestion.us.bronto.io
    .addHeader("x-bronto-api-key", "<YOUR_API_KEY>")
    .build();
RegionLogs endpointTraces endpoint
EUhttps://ingestion.eu.bronto.io/v1/logshttps://ingestion.eu.bronto.io/v1/traces
UShttps://ingestion.us.bronto.io/v1/logshttps://ingestion.us.bronto.io/v1/traces
See API Keys for how to create a key with ingestion permissions. No other changes to the rest of the setup are required.