---
title: OpenTelemetry logging
description: OTEL logging collects and exports logs using the OpenTelemetry Protocol (OTLP). It's a part of the broader OpenTelemetry observability framework that provides a unified way to collect logs, traces, and metrics from applications. Learn more in Distributed tracing.
component: pingidm
version: 8.1
page_id: pingidm:monitoring-guide:opentelemetry-logging
canonical_url: https://docs.pingidentity.com/pingidm/8.1/monitoring-guide/opentelemetry-logging.html
keywords: ["Monitoring", "Distributed tracing", "OpenTelemetry logging"]
section_ids:
  benefits-of-otel-logging: Benefits of OTEL logging
  configure-opentelemetry-logging: Configuring OTEL logging
  otel-log-record-example: OTEL log records
  otel-log-record-fields: Log record fields
  otel-logging-http-inbound-requests: OTEL logging for HTTP inbound requests
---

# OpenTelemetry logging

|   |                                                                                                                                                                                                                                                              |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | [OpenTelemetry (OTEL)](https://opentelemetry.io/docs/what-is-opentelemetry/) logging is an [Evolving](../release-notes/appendix-interface-stability.html) feature in PingIDM. It's subject to change without notice, even in a minor or maintenance release. |

OTEL logging collects and exports logs using the [OpenTelemetry Protocol (OTLP)](https://github.com/open-telemetry/opentelemetry-proto/tree/main/docs/). It's a part of the broader OpenTelemetry observability framework that provides a unified way to collect logs, traces, and metrics from applications. Learn more in [Distributed tracing](distributed-tracing.html).

In PingIDM, logs are sent directly from IDM to an [OTEL collector](https://opentelemetry.io/docs/collector/) using JSON format and OTLP, rather than through traditional logging pipelines. Learn more in [Server logs](server-logs.html).

## Benefits of OTEL logging

OTEL logging makes it easier to:

* Correlate logs across Ping Identity products using trace IDs and span IDs. When a request flows through multiple systems, such as PingAM, IDM, and PingDS, you can track the entire journey using a single trace ID. Learn more in [Understand a trace object](distributed-tracing.html#understand-trace-object).

* Troubleshoot and pinpoint where issues occur in complex workflows. Trace IDs provide a high-level overview of a request, while span IDs show specific tasks within that trace.

* Standardize observability. You can use your preferred observability tools, such as Splunk, Datadog, or Grafana that support OTLP.

* Simplify setup because logs go directly to the OTEL collector and bypass previous processing steps.

* Enhance visibility to efficiently identify patterns, bottlenecks, or errors across distributed systems.

## Configuring OTEL logging

To configure and implement OpenTelemetry logging:

1. [Enable distributed tracing](distributed-tracing.html#enable-tracing) in IDM, which includes [trace](https://opentelemetry.io/docs/concepts/signals/traces/) and [span](https://opentelemetry.io/docs/concepts/signals/traces/#spans) IDs in the logs.

2. Start the OTEL collector. Learn more in the [OTEL Collector Quick Start](https://opentelemetry.io/docs/collector/quick-start/) and in [Visualize traces with a trace collector](distributed-tracing.html#viz-otel-traces).

3. [Configure the `OpenTelemetryAppender`](server-logs.html#logging-opentelemetry-appender). Learn more in [Log appenders](server-logs.html#log-appenders).

4. Initialize the OTEL `LoggerProvider`, which contains information about the OTEL collector endpoint where logs are sent and default settings for batched log processing.

   * Add the `LoggerProvider` configuration to the `trace.json` file created when you enabled distributed tracing in step 1:

     ```json
     {
        "logging": {
           "enabled": true,
           "exporter": {
              "type": "otlp",
              "config": {
                 "endpoint": "http://localhost:4318/v1/logs"
              }
           }
        }
     }
     ```

     The following example shows all available `LoggerProvider` configuration options:

     ```json
     {
        "logging": {
           "enabled": true,
           "exporter": {
              "type": "otlp",
              "config": {
                 "endpoint": "http://localhost:4318/v1/logs",
                 "headers": "<headers>",
                 "connectionTimeout": "10 seconds",
                 "timeout": "10 seconds",
                 "compressionMethod": "gzip",
                 "retries": {
                    "backoffMultiplier": 1.5,
                    "initialBackoff": "1 second",
                    "maxAttempts": 5,
                    "maxBackoff": "5 seconds"
                 }
              },
              "batch": {
                 "maxExportBatchSize": 512,
                 "maxQueueSize": 2048,
                 "scheduleDelay": "5 seconds",
                 "exporterTimeout": "30 seconds"
              }
           }
        }
     }
     ```

     > **Collapse: OpenTelemetry logging configuration properties**
     >
     > * enabled: boolean, optional
     >
     >   Set to `true` to enable OpenTelemetry logging.
     >
     >   Default: `false`
     >
     > * resourceAttributes: object, optional
     >
     >   A map of additional resource attributes for processing logs. Find more information in the OpenTelemetry documentation on [Semantic Attributes with SDK-provided Default Value](https://opentelemetry.io/docs/specs/semconv/resource/#semantic-attributes-with-sdk-provided-default-value).
     >
     >   For example, if there are multiple Ping Identity Platform instances in a deployment, you could set the `"service.instance.id"` resource attribute differently for each one to distinguish between them:
     >
     >   ```json
     >   {
     >     "resourceAttributes": {
     >       "service.instance.id": "idm-server-1"
     >     }
     >   }
     >   ```
     >
     > * exporter: object, optional
     >
     >   Configuration for the exporter, which pushes logs to the OpenTelemetry service:
     >
     >   * type: string, optional
     >
     >     Set to `otlp` for OpenTelemetry Protocol (OTLP) support. This is currently the only supported protocol.
     >
     >     Default: `otlp`
     >
     >   * config: object, optional
     >
     >     Endpoint and timeout configuration:
     >
     >     * `compressionMethod`: *enumeration, optional*
     >
     >       Method used to compress log data; either `gzip` or `none`.
     >
     >       Default: `gzip`
     >
     >     * `connectionTimeout`: *duration, optional*
     >
     >       Time out a connection to the endpoint after this duration.
     >
     >       Default: 10 seconds.
     >
     >     * `endpoint`: *string, optional*
     >
     >       The endpoint to publish traces to.
     >
     >       For HTTPS, IDM trusts the default JVM CAs. To override this, set the `-Djavax.net.ssl.trustStore` and associated JVM settings when starting IDM. Learn more about the optional settings in the [Java Secure Socket Extension (JSSE) Reference Guide](https://docs.oracle.com/en/java/javase/21/security/java-secure-socket-extension-jsse-reference-guide.html).
     >
     >       |   |                                                                              |
     >       | - | ---------------------------------------------------------------------------- |
     >       |   | IDM doesn't support TLS configuration for the tracing endpoint at this time. |
     >
     >       Default: `http://localhost:4318/v1/traces`
     >
     >     * `headers`: *object, optional*
     >
     >       Map of additional headers to include in the export span request.
     >
     >       The following example sets the authorization header, `Authorization: Bearer ${bearer.token}`:
     >
     >       ```none
     >       "headers": { "Authorization": "Bearer ${bearer.token}" }
     >       ```
     >
     >     * `retries`: *object, optional*
     >
     >       Defines a retry policy for the export span requests.
     >
     >       Default: Enabled
     >
     >       * `backoffMultiplier`: *number, optional* Multiplier for the backoff wait time before retries.
     >
     >         Default: 1.5
     >
     >       * `enabled`: *boolean, optional*
     >
     >         Retry failed requests.
     >
     >         Default: `true`
     >
     >       * `initialBackoff`: *duration, optional*
     >
     >         How long to wait before the first retry.
     >
     >         Default: 1 second
     >
     >       * `maxAttempts`: *number, optional*
     >
     >         Maximum number of retries.
     >
     >         Default: 5
     >
     >       * `maxBackoff`: *duration, optional*
     >
     >         Maximum wait time between retries.
     >
     >         Default: 5 seconds
     >
     >     * `timeout`: *duration, optional*
     >
     >       Time out a request to publish data to the endpoint after this duration.
     >
     >       Default: 10 seconds.
     >
     >   * `batch`: *object, optional*
     >
     >     Enable and configure batch processing for log data.
     >
     >     * `enabled`: *boolean, optional*
     >
     >       Leave batch processing enabled in deployment.
     >
     >       Default: `true`
     >
     >     * `exporterTimeout`: *duration, optional*
     >
     >       Time out a data exporter after this duration.
     >
     >       Default: 30 seconds
     >
     >     * `exportUnsampledSpans`: *boolean, optional*
     >
     >       Whether to report on unsampled spans.
     >
     >       Default: `false`
     >
     >     * `maxExportBatchSize`: *number, optional*
     >
     >       Maximum number of spans in a batch.
     >
     >       Default: 512
     >
     >     * `maxQueueSize`: *number, optional*
     >
     >       Maximum number of spans to queue before dropping them.
     >
     >       Default: 2048
     >
     >     * `scheduleDelay`: *duration, optional*
     >
     >       Maximum interval between sending batches of log data.
     >
     >       Default: 5 seconds

5. At the end of the `resolver/boot.properties` file, add the location of the `trace.json` file:

   ```
   openidm.tracing.config.path=trace/trace.json
   ```

6. [Start IDM](../install-guide/chap-install.html#run-openidm).

7. Go to your OTEL collector and view the logs sent from IDM.

### OTEL log records

In the OTEL collector, you can view log records in the OTLP format to find the trace and span IDs to follow a request across multiple systems.

This is an example of an IDM log record in the OTEL collector:

```text
Trace ID:
Span ID:
Flags: 0
LogRecord #238
ObservedTimestamp: 2025-11-14 18:23:29.219669 +0000 UTC
Timestamp: 2025-11-14 18:23:29.219661 +0000 UTC
SeverityText: DEBUG
SeverityNumber: Debug(5)
Body: Str(Bundle xstream not matched by org.forgerock.*)
Attributes:
 -> thread.name: Str(HealthCheck Bundles Started)
```

#### Log record fields

| Field Name          | Description                                                                                                                                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `LogRecord`         | Represents the recording of an event. In OpenTelemetry, a log record contains two kinds of fields:- Named top-level fields of specific type and meaning

- Resource and attributes fields of arbitrary value and type |
| `ObservedTimestamp` | Time when the event was observed.                                                                                                                                                                                     |
| `Timestamp`         | Time when the event occurred.                                                                                                                                                                                         |
| `SeverityText`      | The severity text (also known as log level).                                                                                                                                                                          |
| `SeverityNumber`    | Numerical value of the severity.                                                                                                                                                                                      |
| `Body`              | The body of the log record.                                                                                                                                                                                           |
| `Attributes`        | Additional information about the event.                                                                                                                                                                               |
| `Trace ID`          | Request trace ID.                                                                                                                                                                                                     |
| `Span ID`           | Request span ID.                                                                                                                                                                                                      |
| `Flags`             | A binary encoding that contains trace information.                                                                                                                                                                    |

Learn more about [Log Record fields and descriptions](https://opentelemetry.io/docs/concepts/signals/logs/#log-record).

### OTEL logging for HTTP inbound requests

When tracing is enabled, incoming HTTP requests automatically generate trace and span IDs. These IDs are included in log records sent to the OTEL collector. The trace and span ID fields are blank if the request doesn't come from an HTTP inbound request.

For an HTTP inbound request, here's an example scenario to find the trace and span IDs in the OTEL collector logs:

1. Sign on to the [IDM admin user interface (UI)](../setup-guide/chap-ui.html).

2. Go to the OTEL collector and view the span for the sign-on request that contains the trace ID:

   ```text
   Span #7 (1)
   Trace ID: 4cafb0e638a8c2ba58a8288e0e12470fd (2)
   Parent ID:
   ID: 6bdc24e07a2c9754
   Name: GET /openidm
   Kind: Server
   Start time: 2025-11-14 18:26:24.104794 +0000 UTC
   End time: 2025-11-14 18:26:24.110788 +0000 UTC
   Status code: Unset
   Status message:
   Attributes:
    -> http.request.method: Str(GET)
    -> url.path: Str(/openidm/info/version)
    -> url.full: Str(https://localhost:8443/openidm/info/version)
    -> network.protocol.name: Str(http)
    -> forgerock.transaction.id: Str(3caddd39-2942-4ccd-b527-9dfbd1f6353-1380)
    -> {"resource": "{service.instance.id": "5f481198b-5fc4-416b-a8d5-1d2cd5581f64", "service.name": "otelcol-contrib", "service.version": "0.128.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"}
   2025-11-14T18:26:27.590Z info Metrics {"resource": "{service.instance.id": "5f481198b-5fc4-416b-a8d5-1d2cd5581f64", "service.name": "otelcol-contrib", "service.version": "0.128.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 27, "data points": 28}
   2025-11-14T18:26:27.591Z info Resource@Metrics #0
   Resource SchemaURL:
   Resource attributes:
    -> service.name: Str(otelcol-contrib)
    -> service.instance.id: Str(5f481198b-5fc4-416b-a8d5-1d2cd5581f64)
    -> server.port: Str(8888)
   ```

   |       |                                  |
   | ----- | -------------------------------- |
   | **1** | Span for the sign-on request     |
   | **2** | Associated trace ID for the span |

3. Find the matching trace ID in the log records to see the associated span ID:

   ```text
   LogRecord #23
   ObservedTimestamp: 2025-11-14 18:26:24.107832 +0000 UTC
   Timestamp: 2025-11-14 18:26:24.107823 +0000 UTC
   SeverityText: DEBUG
   SeverityNumber: Debug(5)
   Body: Str(Invoking read access)
   Attributes:
    -> thread.name: Str(qtp1868624459-218)
   Trace ID: 4cafb0e638a8c2ba58a8288e0e12470fd (1)
   Span ID: 6bdc24e07a2c9754 (2)
   Flags: 1
   ```

   |       |                                     |
   | ----- | ----------------------------------- |
   | **1** | Matching trace ID in the log record |
   | **2** | Matching associated span ID         |

4. View the log record body and attributes for more details about the sign-on request.

   In this example, the `Body` field for `LogRecord #23` shows the string `(Invoking read access)` because the user requested read access to the admin dashboard. This inbound HTTP request generated the trace and span IDs in the previous steps:

   ```text
   LogRecord #23
   ObservedTimestamp: 2025-11-14 18:26:24.107832 +0000 UTC
   Timestamp: 2025-11-14 18:26:24.107823 +0000 UTC
   SeverityText: DEBUG
   SeverityNumber: Debug(5)
   Body: Str(Invoking read access) (1)
   Attributes:
    -> thread.name: Str(qtp1868624459-218)
   Trace ID: 4cafb0e638a8c2ba58a8288e0e12470fd
   Span ID: 6bdc24e07a2c9754
   Flags: 1
   ```

   |       |                                                                          |
   | ----- | ------------------------------------------------------------------------ |
   | **1** | This string shows the user requested read access to the admin dashboard. |
