---
title: Set Up Server-Side Flag Evaluation Metrics
description: >-
  Configure the Datadog Agent and your application to emit and visualize flag
  evaluation metrics for server-side feature flags.
breadcrumbs: >-
  Docs > Feature Flags > Feature Flags Guides > Set Up Server-Side Flag
  Evaluation Metrics
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Set Up Server-Side Flag Evaluation Metrics

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com, us2.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ({% placeholder "user-datadog-site-name" /%}).
{% /alert %}

{% /callout %}

## Overview{% #overview %}

Flag evaluation metrics let you measure how often each variant of a feature flag is returned by your server-side application. Use these metrics to track flag adoption over time, verify targeting rules are working as expected, and graph flag evaluation data on dashboards.

{% alert level="warning" %}
The `feature_flag.evaluations` metric is experimental and may change or be removed in a future release.
{% /alert %}

## Prerequisites{% #prerequisites %}

Before setting up flag evaluation metrics, confirm the following:

- [Server-side feature flags](https://docs.datadoghq.com/feature_flags/server.md) are already configured.
- Datadog Agent 7.32.0 or later is running.
- `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true` is set on your application.
- Your server-side tracer meets the minimum version for flag evaluation metrics support:

| Language | Minimum tracer version |
| -------- | ---------------------- |
| .NET     | 3.44.0                 |
| Go       | 2.8.0                  |
| Java     | 1.62.0                 |
| Node.js  | 5.99.0                 |
| PHP      | 1.21.1                 |
| Python   | 4.7.0                  |
| Ruby     | 2.32.0                 |

## Step 1: Enable the Agent OTLP receiver{% #step-1-enable-the-agent-otlp-receiver %}

Flag evaluation metrics are emitted over OpenTelemetry (OTLP). The Datadog Agent includes an OTLP receiver that is off by default. For setup instructions, see [OTLP Ingestion by the Datadog Agent](https://docs.datadoghq.com/opentelemetry/setup/otlp_ingest_in_the_agent.md).

You only need to enable the protocol your application uses (gRPC on port 4317, or HTTP on port 4318).

{% alert level="info" %}
If you are running Agent v7.61.0 or later in Docker, set `HOST_PROC=/proc` on the Agent container to work around a known issue with the OTLP pipeline.
{% /alert %}

## Step 2: Configure your application{% #step-2-configure-your-application %}

For all tracers except Java, set the following environment variable in addition to the standard [server-side feature flag configuration](https://docs.datadoghq.com/feature_flags/server.md). For Java, `DD_METRICS_OTEL_ENABLED` has no effect; see the Java: Add the OpenTelemetry SDK dependencies section instead.

```bash
# Enable flag evaluation metrics
DD_METRICS_OTEL_ENABLED=true
```

### Java: Add the OpenTelemetry SDK dependencies{% #java-add-the-opentelemetry-sdk-dependencies %}

The Java provider records `feature_flag.evaluations` through the OpenTelemetry SDK and exports it over OTLP, so the `opentelemetry-sdk-metrics` and `opentelemetry-exporter-otlp` dependencies must be on your application's classpath. Add them alongside your [Java feature flag dependencies](https://docs.datadoghq.com/feature_flags/server/java.md#installation). Import the OpenTelemetry BOM so the OpenTelemetry API and SDK stay on the same version:

{% tab title="Gradle (Groovy)" %}
In the `build.gradle` file:

```groovy
dependencies {
    implementation platform('io.opentelemetry:opentelemetry-bom:1.47.0')
    implementation 'io.opentelemetry:opentelemetry-sdk-metrics'
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
}
```

{% /tab %}

{% tab title="Gradle (Kotlin)" %}
In the `build.gradle.kts` file:

```kotlin
dependencies {
    implementation(platform("io.opentelemetry:opentelemetry-bom:1.47.0"))
    implementation("io.opentelemetry:opentelemetry-sdk-metrics")
    implementation("io.opentelemetry:opentelemetry-exporter-otlp")
}
```

{% /tab %}

{% tab title="Maven" %}
In the `pom.xml` file:

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-bom</artifactId>
            <version>1.47.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-metrics</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
    </dependency>
</dependencies>
```

{% /tab %}

On the Java tracer, the provider starts its OTLP metrics exporter automatically when the OpenTelemetry SDK is on the classpath. If the dependencies are missing, no metrics are emitted and the tracer logs `OpenTelemetry SDK is not on the classpath`.

{% alert level="info" %}
In Spring Boot applications, Spring Boot's OpenTelemetry autoconfiguration also creates an `OpenTelemetrySdk` bean. If the OpenTelemetry SDK version it resolves does not match the OpenTelemetry API version on the classpath, startup fails with a `BeanCreationException` for the `openTelemetry` bean and `NoClassDefFoundError: io/opentelemetry/sdk/internal/ScopeConfigurator`. Importing the `opentelemetry-bom` as shown above keeps the API and SDK on the same version and resolves the error.
{% /alert %}

### Ruby: Add the OpenTelemetry metrics gems{% #ruby-add-the-opentelemetry-metrics-gems %}

For Ruby applications, add the OpenTelemetry metrics SDK and OTLP metrics exporter gems to your application bundle:

In the `Gemfile` file:

```ruby
gem "opentelemetry-metrics-sdk", ">= 0.8"
gem "opentelemetry-exporter-otlp-metrics", ">= 0.4"
```

Install the gems with `bundle install`. These gems provide the OpenTelemetry meter provider and OTLP metrics exporter. The Ruby tracer uses them when `DD_METRICS_OTEL_ENABLED=true` is set. If the gems are missing, the Ruby tracer does not emit `feature_flag.evaluations` metrics and logs `Failed to load OpenTelemetry metrics gems`.

### Endpoint configuration{% #endpoint-configuration %}

By default, most tracers send OTLP metrics to the Agent at `DD_AGENT_HOST` on port `4318` (HTTP). If your application already sets `DD_AGENT_HOST` to reach the Agent, no endpoint configuration is required.

Set an OTLP endpoint explicitly in any of these cases:

- The Agent is not reachable at `DD_AGENT_HOST` on the default OTLP port (for example, a remote Agent or a non-default port).
- You use the **Java** tracer. Its flag evaluation metrics exporter supports OTLP/HTTP only (gRPC is not supported) on port `4318`. The Java tracer does not derive the endpoint from `DD_AGENT_HOST` and defaults to `http://localhost:4318`. Set `OTEL_EXPORTER_OTLP_ENDPOINT` to the Agent's HTTP endpoint when the Agent is not on `localhost`.
- You use the **Python** tracer. The Python tracer defaults to gRPC on port `4317`, not HTTP. Enable the gRPC OTLP receiver on the Agent, or override the protocol to use HTTP instead:

```bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://<AGENT_HOST>:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

To set the endpoint, use the standard OpenTelemetry variable:

```bash
# Point OTLP data at the Datadog Agent (HTTP, port 4318)
OTEL_EXPORTER_OTLP_ENDPOINT=http://<AGENT_HOST>:4318

# Or use gRPC (port 4317). For most tracers, the default protocol is http/protobuf,
# so set the protocol explicitly when switching to gRPC:
# OTEL_EXPORTER_OTLP_ENDPOINT=http://<AGENT_HOST>:4317
# OTEL_EXPORTER_OTLP_PROTOCOL=grpc
```

Replace `<AGENT_HOST>` with the hostname or IP address of your Datadog Agent.

Docker Compose example:

In the `docker-compose.yml` file:

```yaml
services:
  datadog-agent:
    environment:
      - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317
      - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318
      - HOST_PROC=/proc  # If running Agent v7.61.0+ in Docker

  app-go:
    environment:
      - DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
      - DD_METRICS_OTEL_ENABLED=true
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://datadog-agent:4318
    depends_on:
      datadog-agent:
        condition: service_healthy
```

## Step 3: Verify metrics are flowing{% #step-3-verify-metrics-are-flowing %}

After deploying, confirm metrics are reaching Datadog:

1. Go to [Metrics Explorer](https://app.datadoghq.com/metric/explorer) and search for `feature_flag.evaluations`.
1. If the metric does not appear within a few minutes of your application evaluating flags, check:
   - The Agent OTLP receiver is enabled and the correct port is exposed.
   - `OTEL_EXPORTER_OTLP_ENDPOINT` points to the Agent, not a separate collector.
   - Your application is actively evaluating flags with a server SDK at runtime (the code path is being executed).

## Step 4: Enable metric retention{% #step-4-enable-metric-retention %}

By default, `feature_flag.evaluations` retains only one hour of data. To retain a longer history:

1. Go to [Metrics Summary](https://app.datadoghq.com/metric/summary) and search for `feature_flag.evaluations`.
1. Select the metric and enable **Historical Metrics**.

This is an opt-in setting and is not enabled automatically for OTLP metrics.

## Graph flag evaluations on a dashboard{% #graph-flag-evaluations-on-a-dashboard %}

Use the following query to graph flag evaluations by flag key and variant on a [dashboard](https://docs.datadoghq.com/dashboards.md):

```text
sum:feature_flag.evaluations{*} by {feature_flag.key,feature_flag.result.variant}
```

The `feature_flag.evaluations` metric is a counter with the following tags:

| Tag                                  | Description                                                                  |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| `feature_flag.key`                   | The flag key being evaluated                                                 |
| `feature_flag.result.variant`        | The variant returned by the evaluation                                       |
| `feature_flag.result.reason`         | The reason for the evaluation result                                         |
| `feature_flag.result.allocation_key` | The identifier for the evaluated targeting rules (emitted only when present) |
| `error.type`                         | The error type (emitted only on error evaluations)                           |

## Further reading{% #further-reading %}

- [Server-Side Feature Flags](https://docs.datadoghq.com/feature_flags/server.md)
- [Feature Flag Graphs](https://docs.datadoghq.com/feature_flags/concepts/flag_graphs.md)
- [Metrics](https://docs.datadoghq.com/metrics.md)
- [Dashboards](https://docs.datadoghq.com/dashboards.md)
