---
title: "Emit contextualized JSON logs with Java | OpenTelemetry documentation"
description: "Integrate OpenTelemetry logging with popular Java logging frameworks to emit contextualized JSON logs."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Emit contextualized JSON logs with Java

Most popular logging frameworks, such as [Log4j](https://logging.apache.org/log4j/2.x/index.html) or [SLF4J/Logback](https://logback.qos.ch/) in Java, support emitting JSON formatted logs.

To integrate OpenTelemetry with logging libraries, specify the resource attributes you want available in the log line.

To keep your architecture simple, add only the attributes you need to filter and correlate logs, such as `service.name`, `service.namespace`, `deployment.environment`, and `service.instance.id`.

## Example application

The following example uses [Spring Boot](https://spring.io/projects/spring-boot) to enrich logs with OpenTelemetry attributes and emit them formatted in JSON through stdout.

> Note
> 
> You can find the full code of the example in the [Docker LGTM repository](https://github.com/grafana/docker-otel-lgtm/tree/main/examples/java/json-logging-logback).

Create a Spring Boot application 3.3.0+ using the default logging instrumentation with the Logback library and the stdout output.

### Instrument the application

Instrument the Spring Boot application with the OpenTelemetry Java Agent by following the setup flow defined by the Grafana Cloud integration for Java:

1. Open the Grafana Cloud home page in a web browser.
2. Navigate to **Connections &gt; Add new Connection**.
3. Select **Java OpenTelemetry** and follow the setup instructions.

Enrich Logback logs with OpenTelemetry resource attributes and log attributes using:

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
export OTEL_INSTRUMENTATION_COMMON_MDC_RESOURCE_ATTRIBUTES=service.namespace,service.name,service.instance.id,service.version,deployment.environment
```

Change the Logback SpringBoot configuration to emit JSON logs with OpenTelemetry contextualization and add the following `logback-spring.xml` under `src/main/resources`:

xml ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```xml
<!-- tested with Logback 1.5.0 and Spring Boot 3.3.0 -->
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <root level="INFO">
        <appender-ref ref="CONSOLE_JSON"/>
    </root>

    <appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.JsonEncoder">
            <withFormattedMessage>true</withFormattedMessage>
            <withMessage>false</withMessage>
            <withArguments>false</withArguments>
            <withSequenceNumber>false</withSequenceNumber>
            <withNanoseconds>false</withNanoseconds>
        </encoder>
    </appender>
</configuration>
```

### Deploy the application

Deploy the Spring Boot application on Kubernetes and verify that the application outputs logs to the container stdout stream with JSON formatting and OpenTelemetry contextualization, for example `kubectl logs my_pod_name | jq` outputs:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "timestamp": 1727346005788,
  "level": "INFO",
  "threadName": "http-nio-8080-exec-5",
  "loggerName": "com.grafana.example.RollController",
  "context": {
    "name": "default",
    "birthdate": 1727345887787,
    "properties": {
    }
  },
  "mdc": {
    "trace_id": "97a39974ba2dfc9275e4d31dc2730ee4",
    "trace_flags": "01",
    "service.name": "dice",
    "service.instance.id": "0fb18318-06c0-4893-9a8b-353c00b45227",
    "service.version": "1.1",
    "span_id": "08d6d83d645e3ad2",
    "service.namespace": "shop",
    "deployment.environment": "staging"
  },
  "formattedMessage": "Anonymous player is rolling the dice: 6",
  "throwable": null
}
```

### Configure OpenTelemetry Collector

Configure the OpenTelemetry Collector instance with the file log receiver:

1. Add a [file log receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/filelogreceiver).
2. Add the [container parser](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/docs/operators/container.md) operator.
3. Add the [json parser](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/docs/operators/json_parser.md) operator.
   
   - Map `body` to `attributes.formattedMessage`
   - Map `timestamp.parse_from` to `attributes.timestamp`
   - Map `severity.parse_from` to `attributes.level`
   - Map `trace.trace_id.parse_from` to `attributes.mdc.trace_id`
   - Map `trace.span_id.parse_from` to `attributes.mdc.span_id`
   - Map `trace.trace_flags.parse_from` to `attributes.mdc.trace_flags`
4. Add the [move](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/docs/operators/move.md) operator.
   
   - Move top level fields, such as `threadName`, to their corresponding OTel attributes, such as [`thread.name`](https://opentelemetry.io/docs/specs/semconv/attributes-registry/thread/)
   - Move entries from the `mdc` field to the resource entry of the log entry
5. Remove all unnecessary fields using the [attributes](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/docs/operators/remove.md) operator.

You can find the full configuration in the [reference OTel collector configuration](https://github.com/grafana/docker-otel-lgtm/blob/main/examples/java/json-logging-logback/k8s/collector-configmap.yaml).
