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

# Opensearch

> OpenTelemetry exporter for Opensearch

# Opensearch Exporter

![Status](https://img.shields.io/badge/status-alpha-red)

**Available in:** `contrib`

**Maintainers:** [@ps48](https://github.com/ps48)

**Source:** [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/opensearchexporter)

## Supported Telemetry

![Logs](https://img.shields.io/badge/logs-alpha-blue) ![Traces](https://img.shields.io/badge/traces-alpha-orange)

## Overview

OpenSearch exporter supports sending OpenTelemetry signals as documents to [OpenSearch](https://www.opensearch.org).

The documents are sent using [observability catalog](https://github.com/opensearch-project/opensearch-catalog/tree/main/schema/observability) schema.

## Configuration options

### Indexing Options

The Observability indices would follow the recommended pattern for immutable data stream ingestion using
the [data\_stream](https://opensearch.org/docs/latest/dashboards/im-dashboards/datastream) concepts.
Index pattern will follow the next naming template `ss4o_{type}-{dataset}-{namespace}`

* `dataset` (default=`default`) a user-provided label to classify source of telemetry. It is used to construct the name of the destination index or data stream.
* `namespace` (default=`namespace`) a user-provided label to group telemetry. It is used to construct the name of the destination index or data stream.

#### Dynamic Indexing

The OpenSearch exporter supports dynamic index names for both logs and traces using placeholders. You can use any attribute or context key as a placeholder to construct index names dynamically per record.

> Caution: In practice, OpenSearch clusters can become unstable or even break down when index counts reach a very high level. Introducing attributes with high cardinality resulting in many separate indices can significantly impact the stability of your target cluster.

**Configuration Options:**

* `logs_index` - Custom index name pattern for logs
* `logs_index_fallback` - Fallback value when placeholder is missing (default: `unknown`)
* `logs_index_time_format` - Time suffix format for logs
* `traces_index` - Custom index name pattern for traces
* `traces_index_fallback` - Fallback value when placeholder is missing (default: `unknown`)
* `traces_index_time_format` - Time suffix format for traces

**Placeholder Syntax:**

* Placeholder: `%{key}`
  * Example: `otel-logs-%{service.name}` or `otel-traces-%{service.name}-%{env}`
  * Multiple placeholders are supported per index name
  * The value is looked up from item attributes (log/span), scope attributes, and resource attributes (in that precedence order)
  * If the key is missing, the fallback value is used
  * Generated index names must adhere to [OpenSearch index naming restrictions](https://docs.opensearch.org/latest/api-reference/index-apis/create-index/#index-naming-restrictions)

**Time Suffix Format:**

Both logs and traces support time-formatted suffixes using `*_time_format` options. The time suffix only supports UTC.

* **Valid tokens** (case-sensitive):
  * `yyyy` (4-digit year), `yy` (2-digit year)
  * `MM` (2-digit month), `dd` (2-digit day)
  * `HH` (2-digit hour, 24h), `mm` (2-digit minute), `ss` (2-digit second)
* **Allowed separators**: `-`, `.`, `_`, `+`
* **Examples:** `yyyy.MM.dd` → `2024.06.07`, `yyyy-MM` → `2024-06`, `yyMMdd` → `240607`

**Default Behavior:**

If custom index names are not set, the exporter uses default patterns:

* Logs: `ss4o_logs-{dataset}-{namespace}`
* Traces: `ss4o_traces-{dataset}-{namespace}`

##### Example Configuration

```yaml theme={null}
exporters:
  opensearch:
    http:
      endpoint: http://opensearch.example.com:9200
    # Logs configuration
    logs_index: "otel-logs-%{service.name}-%{env}"
    logs_index_fallback: "default"
    logs_index_time_format: "yyyy.MM.dd"
    # Traces configuration
    traces_index: "otel-traces-%{service.name}-%{deployment.environment}"
    traces_index_fallback: "unknown"
    traces_index_time_format: "yyyy.MM.dd"
    sending_queue:
      batch:
```

This configuration will create:

* Log indexes like: `otel-logs-myservice-prod-2024.06.07`
* Trace indexes like: `otel-traces-myservice-production-2024.06.07`

If any placeholder key is missing, the fallback value is used e.g.:

* `otel-logs-myservice-default-2024.06.07`).
* `otel-traces-unknown-production-2024.06.07`

### OpenSearch document mapping

The mapping mode can be controlled via the scope attribute `opensearch.mapping.mode`.

The OpenSearch exporter supports several document schemas and preprocessing behaviors, which may be configured through the following settings:

* `mapping`:
  * `mode` (default=`ss4o`): Configures the field mappings. Supported modes are:
    * `ss4o`: Exports logs in the [Simple Schema for Observability](https://opensearch.org/docs/latest/observing-your-data/ss4o/) standard.
    * `ecs`: Maps fields defined in the OpenTelemetry Semantic Conventions to the [Elastic Common Schema](https://www.elastic.co/guide/en/ecs/current/index.html)
    * `flatten_attributes`: Uses the ECS mapping but flattens all resource and log attributes in the record to the top-level.
    * `bodymap`: uses the "body" of a log record as the exact content of the OpenSearch document, without any transformation. This mapping mode is intended for use cases where the client wishes to have complete control over the OpenSearch document structure.
    * `otel-v1`: exports logs and traces using the Data Prepper OTel v1 schema, compatible with OpenSearch Observability dashboards.
  * `timestamp_field`: (optional) Field to store the timestamp in. If not set, uses the default `@timestamp`.
  * `unix_timestamp`: (optional) Whether to store the timestamp in epoch milliseconds.
  * `dedup`: (optional) removes fields from the document, that have duplicate keys. The filtering only keeps the last value for a key.
  * `dedot`: (optional) convert dotted keys into nested JSON objects.

#### SS4O mapping mode

The default [`Simple Schema for Observability`](https://docs.opensearch.org/latest/observing-your-data/ss4o/) mapping mode.

In `ss4o` mapping mode, the OpenSearch exporter stores documents using the SS4O schema, which is designed for observability data in OpenSearch. Documents use standardized field names and structure to facilitate integration with OpenSearch dashboards and tools.

| Signal | Supported            |
| ------ | -------------------- |
| Logs   | :white\_check\_mark: |
| Traces | :white\_check\_mark: |

#### ECS mapping mode

> \[!WARNING]
> The ECS mapping mode is currently undergoing changes, and its behaviour is unstable.

In `ecs` mapping mode, the OpenSearch exporter maps fields from [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/) to [Elastic Common Schema (ECS)](https://www.elastic.co/guide/en/ecs/current/index.html) where possible. This mode may be used for compatibility with dashboards and tools that expect ECS.

| Signal | `ecs`                |
| ------ | -------------------- |
| Logs   | :white\_check\_mark: |
| Traces | :no\_entry\_sign:    |

#### Flatten attributes mapping mode

> \[!WARNING]
> The Flatten attributes mapping mode is currently undergoing changes, and its behaviour is unstable.

In `flatten_attributes` mapping mode, the OpenSearch exporter uses the ECS mapping but flattens all resource and log attributes in the record to the top-level of the document.

| Signal | `flatten_attributes` |
| ------ | -------------------- |
| Logs   | :white\_check\_mark: |
| Traces | :no\_entry\_sign:    |

#### Bodymap mapping mode

In `bodymap` mapping mode, the OpenSearch exporter supports only logs and uses the "body" of a log record as the exact content of the OpenSearch document, without any transformation. This mapping mode is intended for use cases where the client wishes to have complete control over the OpenSearch document structure.

The bodymap mapping mode only supports log records where the body is of type Map. If the log body is not a Map, encoding will fail with an error. This ensures that only structured map data can be used as the document content in bodymap mode.

| Signal | `bodymap`            |
| ------ | -------------------- |
| Logs   | :white\_check\_mark: |
| Traces | :no\_entry\_sign:    |

#### OTel v1 mapping mode

In `otel-v1` mapping mode, the OpenSearch exporter produces documents compatible with [Data Prepper's](https://github.com/opensearch-project/data-prepper) OTel v1 index schemas. This enables interoperability with OpenSearch Observability dashboards that consume Data Prepper indices.

Default index names:

* Traces: `otel-v1-apm-span`
* Logs: `otel-v1-logs`

These defaults can be overridden using `traces_index` and `logs_index` configuration options.

| Signal | `otel-v1`            |
| ------ | -------------------- |
| Logs   | :white\_check\_mark: |
| Traces | :white\_check\_mark: |

Schema references:

* [otel-v1-apm-span index template](https://github.com/opensearch-project/data-prepper/blob/main/data-prepper-plugins/opensearch/src/main/resources/index-template/otel-v1-apm-span-index-standard-template.json)
* [logs-otel-v1 index template](https://github.com/opensearch-project/data-prepper/blob/main/data-prepper-plugins/opensearch/src/main/resources/index-template/logs-otel-v1-index-standard-template.json)

> \[!NOTE]
> The exporter emits nanosecond-precision timestamps in the document body, but to materialize them as `date_nanos` (and apply the rest of the recommended field mappings) you must install the matching index templates before indexing begins. Without a template OpenSearch's dynamic mapping will infer `date` (millisecond precision) for timestamp fields. Install the templates out-of-band for now; see also the schema references above.

##### Example Configuration

```yaml theme={null}
exporters:
  opensearch:
    http:
      endpoint: http://opensearch.example.com:9200
    mapping:
      mode: "otel-v1"
    sending_queue:
      batch:
```

### HTTP Connection Options

OpenSearch export supports standard [HTTP client settings](https://github.com/open-telemetry/opentelemetry-collector/tree/main/config/confighttp#client-configuration).

* `http.endpoint` (required) `<url>:<port>` of OpenSearch node to send data to.

### TLS settings

Supports standard TLS settings as part of HTTP settings. See [TLS Configuration/Client Settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md#client-configuration).

### Retry Options

* `retry_on_failure`: See [retry\_on\_failure](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md)

### Sending Queue Options

* `sending_queue`: See [sending\_queue](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md)

### Timeout Options

* `timeout` : See [timeout](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md)

### Bulk Indexer Options

* `bulk_action` (optional): the [action](https://opensearch.org/docs/2.9/api-reference/document-apis/bulk/) for ingesting data. Only `create` and `index` are allowed here.
* `pipeline` (optional): the ID of an [ingest pipeline](https://opensearch.org/docs/latest/ingest-pipelines/) to apply when indexing documents. When set, all documents sent via the bulk API will be processed by the specified pipeline before being indexed. The ingest pipeline must exist in the cluster and there must be at least one node with the `ingest` node role assigned.

## Example

```yaml theme={null}
extensions:
  basicauth/client:
    client_auth:
      username: username
      password: password

exporters:
  opensearch/trace:
    http:
      endpoint: https://opensearch.example.com:9200
      auth:
        authenticator: basicauth/client
    sending_queue:
      batch:

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [opensearch/trace]
```

## Configuration

### Example Configuration

```yaml theme={null}
extensions:
  basicauth/sample_basic_auth:
    username: test
    password: testtoo

opensearch:
  http:
    endpoint: https://opensearch.example.com:9200

opensearch/default:

opensearch/empty_namespace:
  dataset: ngnix
  namespace: ""
  http:
    endpoint: https://opensearch.example.com:9200

opensearch/empty_dataset:
  dataset: ""
  namespace: eu
  http:
    endpoint: https://opensearch.example.com:9200

opensearch/invalid_bulk_action:
  bulk_action: "delete"
  http:
    endpoint: https://opensearch.example.com:9200

opensearch/trace:
  dataset: ngnix
  namespace: eu
  http:
    tls:
      insecure: false
    endpoint: https://opensearch.example.com:9200
    timeout: 2m
    headers:
      myheader: test
    auth:
      authenticator: sample_basic_auth
  timeout:
  retry_on_failure:
    enabled: true
    initial_interval: 100000000
    randomization_factor: 0.5

opensearch/dynamic_log_indexing:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: "yyyy.MM.dd"

opensearch/log_index_time_format_valid:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: "yyyy.MM.dd"

opensearch/log_index_time_format_empty:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: ""

opensearch/log_index_time_format_invalid:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: "invalid_format!"

opensearch/log_index_time_format_whitespace:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: "   "

opensearch/log_index_time_format_special_chars:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: "otel-logs-%{service.name}"
  logs_index_fallback: "default-service"
  logs_index_time_format: "yyyy/MM/dd@!#"

opensearch/traces_index_valid:
  http:
    endpoint: https://opensearch.example.com:9200
  traces_index: "otel-traces-%{service.name}"
  traces_index_fallback: "default-service"
  traces_index_time_format: "yyyy.MM.dd"

opensearch/traces_index_time_format_valid:
  http:
    endpoint: https://opensearch.example.com:9200
  traces_index: "otel-traces-%{service.name}"
  traces_index_fallback: "default-service"
  traces_index_time_format: "yyyy.MM.dd"

opensearch/traces_index_time_format_empty:
  http:
    endpoint: https://opensearch.example.com:9200
  traces_index: "otel-traces-%{service.name}"
  traces_index_fallback: "default-service"
  traces_index_time_format: ""

opensearch/traces_index_time_format_invalid:
  http:
    endpoint: https://opensearch.example.com:9200
  traces_index: "otel-traces-%{service.name}"
  traces_index_fallback: "default-service"
  traces_index_time_format: "invalid_format!"

opensearch/pipeline:
  http:
    endpoint: https://opensearch.example.com:9200
  pipeline: "my-pipeline"

opensearch/sending_queue_with_batch:
  http:
    endpoint: https://opensearch.example.com:9200
  logs_index: otel-logs
  logs_index_time_format: "yyyy-MM-dd"
  sending_queue:
    batch:

opensearch/otel_v1:
  http:
    endpoint: https://opensearch.example.com:9200
  mapping:
    mode: "otel-v1"

opensearch/otel_v1_with_dataset:
  dataset: ngnix
  http:
    endpoint: https://opensearch.example.com:9200
  mapping:
    mode: "otel-v1"
```

***

*Last generated: 2026-07-06*
