Skip to content

Commit

Permalink
[exporter/loki] Document the migration from the Loki Exporter to the …
Browse files Browse the repository at this point in the history
…Loki V3 OTLP endpoint (open-telemetry#34918)

**Description:** <Describe what has changed.>

Document how to migrate from the now deprecated OTel Collector Loki
Exporter to the new Loki V3 OTLP endpoint and new OTel log format

---------

Co-authored-by: Curtis Robert <crobert@splunk.com>
Co-authored-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
  • Loading branch information
3 people authored and f7o committed Sep 12, 2024
1 parent d36224d commit d7b4b60
Showing 1 changed file with 197 additions and 2 deletions.
199 changes: 197 additions & 2 deletions exporter/lokiexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,204 @@ Exports data via HTTP to [Loki](https://grafana.com/docs/loki/latest/).

## Deprecation notice

This component is **deprecated**: Loki now supports native [OTLP ingestion](https://grafana.com/docs/loki/latest/send-data/otel/) starting from v3. Grafana Cloud also supports [OTLP native ingestion](https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp) for logs. This component will be removed in September 2024.
This component is **deprecated**: Loki now supports native [OTLP ingestion](https://grafana.com/docs/loki/latest/send-data/otel/) starting from v3. Grafana Cloud also supports [OTLP native ingestion](https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp) for logs. This component will be removed in November 2024.

## Getting Started

### Benefits of the new Loki OpenTelemetry log format

The new format for OpenTelemetry logs introduced in Loki V3 brings the following benefits:

* Native support for the structure of OpenTelemetry logs enabling simpler querying (no more JSON parsing).
* Simplified client configuration to send OpenTelemetry data using the standard OTLP protocol.

### Loki log message format changes for OpenTelemetry logs

See OpenTelemetry Logs Data Model specification [here](https://opentelemetry.io/docs/specs/otel/logs/data-model/).

| OpenTelemetry log field | Pre Loki V3 | Loki V3 through the Loki OTLP Endpoint |
| ----- | ----- | ----- |
| [`Timestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-timestamp) | `timestamp` | `timestamp` |
| [`ObservedTimestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-observedtimestamp) | Not available | `metadata[observed_timestamp]` |
| [`TraceId`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-traceid) | `traceid` field of the Loki JSON log message | `metadata[trace_id]` |
| [`SpanId`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-spanid) | `spanid` field of the Loki JSON log message | `metadata[span_id]` |
| [`TraceFlags`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-traceflags) | Not available | `metadata[flags]` |
| [`SeverityText`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext) | `severity` field of the JSON log message (e.g. `Information`) and `level` label (e.g. `ERROR`, `INFO`...), the `detected_level` label is also available | `metadata[severity_text]`, the `detected_level` label is also available |
| [`SeverityNumber`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber) | Not available | `metadata[severity_number]` |
| [`Body`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body) | `body` field of the Loki JSON log message | The Loki log message. `__line__`in LogQL functions (e.g. `line_format`)|
| [`InstrumentationScope`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-instrumentationscope) | `instrumentation_scope_name` field of the JSON log message | `metadata[scope_name]` |
| [`Attributes`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes) | JSON fields of the Loki log message | `metadata[xyz]` Where `xyz` is the `_` version of the OTel attribute name (e.g. `thread_name` Loki metadata for the `thread.name` OpenTelemetry attribute)|
| [`Resource`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-resource) | `service.name`, `service.namespace`, and `service.instance.id` are promoted as the following labels: `job=[${service.namespace}/]${service.name}`, instance=${service.instance.id}, exporter="OTLP"`. Other resource attributes are stored as JSON fields of the Loki log message with the prefix `resources_` (e.g. `resources_k8s_namespace_name`) | Default list of resource attributes promoted as Loki labels: `cloud.availability_zone`, `cloud.region`, `container.name`, `deployment.environment`, `k8s.cluster.name`, `k8s.container.name`, `k8s.cronjob.name`, `k8s.daemonset.name`, `k8s.deployment.name`, `k8s.job.name`, `k8s.namespace.name`, `k8s.pod.name`, `k8s.replicaset.name` `k8s.statefulset.name`, `service.instance.id`, `service.name`, `service.namespace`. <br/>Other resource attributes are by default promoted as Loki message metadata.<br/> ℹ️ The list of promoted resource attributes is configurable using Loki’s distributor config parameter `default_resource_attributes_as_index_labels` when using self managed Loki ([here](https://grafana.com/docs/loki/latest/configure/\#distributor)) or opening a support request when using Grafana Cloud |

ℹ️ Additional conversion rules from OpenTelemetry Logs to Loki

* All special characters, including `.` in attribute and resource attribute names, are converted into `_` when they are mapped as Loki labels or metadata.
* OTel attribute values with complex data types (i.e. arrays, nested structures) are converted into JSON strings

### Migration instructions

#### Instrumentation migration

No changes are needed in the instrumentation layer. OpenTelemetry logs sources like OpenTelemetry SDKs or the [OpenTelemetry Collector File Log Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/filelogreceiver) don’t have to be modified.

#### Logs collection migration

Replace the OpenTelemetry Collector Loki Exporter by the [OpenTelemetry Collector OTLP HTTP Exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlphttpexporter) as OpenTelemetry logs should now be exported as is to the Loki OTLP endpoint.


OpenTelemetry Collector configuration migration

```yaml
========================
= BEFORE MIGRATION =
========================

extensions:
basicauth/loki:
client_auth:
username: <<username>>
password: <<password>>

exporters:
loki:
auth:
authenticator: basicauth/loki
endpoint: https://loki.example.com:3100/loki/api/v1/push

service:
extensions: [basicauth/loki]
pipelines:
logs:
receivers: [...]
processors: [...]
exporters: [loki, ...]


========================
= AFTER MIGRATION =
========================

extensions:
basicauth/loki:
client_auth:
username: <<username>>
password: <<password>>

exporters:
otlphttp/loki:
auth:
authenticator: basicauth/loki
endpoint: http://loki.example.com:3100/otlp/v1/logs

service:
extensions: [basicauth/loki]
pipelines:
logs:
receivers: [...]
processors: [...]
exporters: [otlphttp/loki, ...]
```
* When using Grafana Cloud, the [Grafana Cloud OTLP endpoint](https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/) should be used instead of the Loki OTLP endpoint. The connection details of the Grafana Cloud OTLP endpoint, OTLP HTTP URL and credentials are available using the Grafana Cloud "OpenTelemetry Collector" connection tile.
* The promotion of OpenTelemetry attributes and resource attributes to Loki labels using the `loki.attribute.labels` and `loki.resource.labels` hints is replaced by the list of promoted attributes managed centrally in Loki.
* The default list of resource attributes promoted as labels (see above) should be sufficient for most use cases.
* ℹ️ Changes can be made to this list using the Loki distributor configuration parameter `default_resource_attributes_as_index_labels` ([here](https://grafana.com/docs/loki/latest/configure/\#distributor)) for self managed instances and opening a support ticket for Grafana Cloud.

#### LogQL queries migration

##### From `job` and `instance` to `service_name`, `service_namespace`, and `service_instance_id`

The Loki labels `job` and `instance` are no longer generated and are replaced by the `service_name`, `service_namespace`, and `service_instance_id` labels.

Example:

```
BEFORE
{job="ecommerce/frontend", instance="instance-1234567890"}
AFTER
{service_name="frontend", service_namespace="ecommerce", service_instance_id="instance-1234567890"}
```

##### From `| json | an_attribute=...` to `{an_attribute=...}` or `| an_attribute=...`

OTel log attributes, resource attributes, and fields are no longer stored in the JSON message but are stored as:
* Loki message labels for promoted resource attributes (see list above),
* Loki message metadata for other resource attributes, log attributes, and log fields.

LogQL statements `| json | an_attribute=...` must be converted to:

* Promoted resource attributes: `{an_attribute=...}`
* For other resource attributes, log attributes, and log fields: `| an_attribute=...`

Example:

```
BEFORE
{exporter="OTLP", job="frontend"} | json | resources_deployment_environment="production"
AFTER
{service_name="frontend"} | deployment_environment="production"
```

##### From `| json | traceid=...` and `| json | spanid=...` to `| trace_id=...` and `| span_id=...`

The log fields `SpanID` and `TraceId` were stored as the JSON fields `spanid` and `traceid`; they are now stored as `metadata[span_id]` and `metadata[trace_id]`, LogQL queries must be changed accordingly.

`TraceID` filters like `| json | traceid=<<traceId>> ...` and `|= <<traceId>> ...` must be converted to `| trace_id=<<traceId>> ...` where `<<traceId>>` and <<spanId>> are the values you search for.
Similarly, `SpanID` filters like `| json | spanid=<<spanid>> ...` and `|=<<spanid>> ...` must be converted to `| span_id=<<spanid>> ...`.

Example:

```
BEFORE
{exporter="OTLP", job="/frontend"} |= "00960a472ea5b87954ca07902d66f914"
AFTER
{service_name="frontend"} | trace_id="00960a472ea5b87954ca07902d66f914"
```

##### From `line_format {{.body}}` to `line_format {{__line__}}`

The `{{.body}}` element of the JSON payload that used to hold the OTel log message body is now the message of the Loki log line and should be referenced as `{{__line__}}` in `line_format` calls.

Example:

```
BEFORE
{exporter="OTLP", job="frontend"} | json | line_format `[{{.level}}] {{.body}}`

AFTER
{service_name="frontend"} | line_format `[{{.detected_level}}] {{__line__}}`
```

#### Grafana visualizations migration

##### Tempo data source: Trace to Logs

To enable the "trace to logs" navigation from Tempo to Loki, navigate to the Grafana Tempo data source configuration screen, in the "Trace to logs" section, select "use custom query" and specify the query:

```
{service_name="${__span.tags["service.name"]}"} | trace_id="${__span.traceId}"
```

##### Loki data source: Log to Trace

To enable the "logs to trace" navigation from Loki to Tempo, navigate to the Grafana Loki data source configuration screen, in the "Derived fields" section, update or create a derived field with:
* Name: `Trace ID`
* Type: `Label` (this `Label` name is missleading because it also supports Loki message metadata)
* Label: `trace_id`
* Internal link: activated
* Select the Tempo data source on which "trace to logs" is configured


### See Also

* [Loki documentation / Ingesting OpenTelemetry logs](https://grafana.com/docs/loki/latest/send-data/otel/)

<hr/>
<hr/>

## Getting Started with the deprecated OpenTelemetry Collector Loki Exporter

The following settings are required:

Expand Down

0 comments on commit d7b4b60

Please sign in to comment.