[PR #22529] [MERGED] feat(otel): introduce an environment variable to control the export interval of otel metrics #114050

Closed
opened 2026-05-18 14:39:36 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/22529
Author: @lorenzophys
Created: 3/10/2026
Status: Merged
Merged: 3/11/2026
Merged by: @tjbck

Base: devHead: metrics-export


📝 Commits (1)

  • 47cab96 feat(otel): introduce an environment variable to control the export interval of otel metrics

📊 Changes

2 files changed (+6 additions, -4 deletions)

View changed files

📝 backend/open_webui/env.py (+3 -0)
📝 backend/open_webui/utils/telemetry/metrics.py (+3 -4)

📄 Description

Description

Introduced an environment variable OTEL_METRICS_EXPORT_INTERVAL_MILLIS to control the export interval of the otel metrics. While it keeps the current default value it will allow us to control the amount of datapoints per minute.

This is useful whenever one is charged based on metrics DPM. For example Grafana Cloud recommends staying below 1 DPM not to incur in higher cost.

Currently open-webui defaults to 6DPM, which is way above 1 DPM.

Added

  • The OTEL_METRICS_EXPORT_INTERVAL_MILLIS environment variable.

Breaking Changes

  • None since the default value of the variable remains unchanged.

Testing

docker-compose.yaml:

services:
  open-webui:
    image: oui:otel-scrape-interval
    container_name: open-webui
    ports:
      - "8080:8080"
    environment:
      - OLLAMA_BASE_URL=http://host.docker.internal:11434
      # OpenTelemetry Configuration
      - ENABLE_OTEL=true
      - ENABLE_OTEL_METRICS=true
      - OTEL_METRICS_EXPORT_INTERVAL_MILLIS=60000 # 1DPM
      - OTEL_EXPORTER_OTLP_INSECURE=true
      - OTEL_SERVICE_NAME=open-webui
      - OTEL_TRACES_EXPORTER=none
      - OTEL_LOGS_EXPORTER=none
    volumes:
      - open-webui:/app/backend/data
    restart: always
    networks:
      - openwebui-network

  # otel-collector sidecar - shares open-webui network namespace, logs metrics to stdout
  otel-collector:
    image: otel/opentelemetry-collector:latest
    container_name: otel-collector
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
      - ./metrics.json:/tmp/metrics/metrics.json
    network_mode: "service:open-webui"
    restart: always
    depends_on:
      - open-webui

networks:
  openwebui-network:
    driver: bridge

volumes:
  open-webui:

otel-collector-config.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

exporters:
  file:
    path: /tmp/metrics/metrics.json
    format: json

service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [file]

Run:

docker compose up -d

Then look at ./metrics.json and compare the timestamps

{"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"1"}]}}]}]}]}
{"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"1"}]}}]}]}]}
{"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"1"}]}}]}]}]}

A python script to convert the unix timestamp to a human readable format (I just hardcoded timeUnixNano)

from datetime import datetime, UTC

timestamps = [
    "1773146611176150917",
    "1773146671197690570",
    "1773146731210840244"
]

for ts_nano in timestamps:
    # Convert nanoseconds to seconds
    ts_seconds = int(ts_nano) / 1e9
    dt = datetime.fromtimestamp(ts_seconds, UTC)
    print(f"{ts_nano}{dt}")

Result

1773146611176150917 → 2026-03-10 12:43:31.176151+00:00
1773146671197690570 → 2026-03-10 12:44:31.197691+00:00
1773146731210840244 → 2026-03-10 12:45:31.210840+00:00

Contributor License Agreement

Note

Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/22529 **Author:** [@lorenzophys](https://github.com/lorenzophys) **Created:** 3/10/2026 **Status:** ✅ Merged **Merged:** 3/11/2026 **Merged by:** [@tjbck](https://github.com/tjbck) **Base:** `dev` ← **Head:** `metrics-export` --- ### 📝 Commits (1) - [`47cab96`](https://github.com/open-webui/open-webui/commit/47cab96c54e25fb141c376f92304bdc2018bebb5) feat(otel): introduce an environment variable to control the export interval of otel metrics ### 📊 Changes **2 files changed** (+6 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/env.py` (+3 -0) 📝 `backend/open_webui/utils/telemetry/metrics.py` (+3 -4) </details> ### 📄 Description # Description Introduced an environment variable `OTEL_METRICS_EXPORT_INTERVAL_MILLIS` to control the export interval of the otel metrics. While it keeps the current default value it will allow us to control the amount of datapoints per minute. This is useful whenever one is charged based on metrics DPM. For example [Grafana Cloud](https://grafana.com/docs/learning-paths/billing-usage/learn-cost-calculations/#scrape-interval-impact-on-costs) recommends staying below 1 DPM not to incur in higher cost. Currently open-webui defaults to 6DPM, which is way above 1 DPM. ### Added - The `OTEL_METRICS_EXPORT_INTERVAL_MILLIS` environment variable. ### Breaking Changes - None since the default value of the variable remains unchanged. --- ## Testing `docker-compose.yaml`: ```yaml services: open-webui: image: oui:otel-scrape-interval container_name: open-webui ports: - "8080:8080" environment: - OLLAMA_BASE_URL=http://host.docker.internal:11434 # OpenTelemetry Configuration - ENABLE_OTEL=true - ENABLE_OTEL_METRICS=true - OTEL_METRICS_EXPORT_INTERVAL_MILLIS=60000 # 1DPM - OTEL_EXPORTER_OTLP_INSECURE=true - OTEL_SERVICE_NAME=open-webui - OTEL_TRACES_EXPORTER=none - OTEL_LOGS_EXPORTER=none volumes: - open-webui:/app/backend/data restart: always networks: - openwebui-network # otel-collector sidecar - shares open-webui network namespace, logs metrics to stdout otel-collector: image: otel/opentelemetry-collector:latest container_name: otel-collector command: ["--config=/etc/otel-collector-config.yaml"] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./metrics.json:/tmp/metrics/metrics.json network_mode: "service:open-webui" restart: always depends_on: - open-webui networks: openwebui-network: driver: bridge volumes: open-webui: ``` `otel-collector-config.yaml` ```yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 exporters: file: path: /tmp/metrics/metrics.json format: json service: pipelines: metrics: receivers: [otlp] exporters: [file] ``` Run: ```bash docker compose up -d ``` Then look at `./metrics.json` and compare the timestamps ```json {"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146611176150917","asInt":"1"}]}}]}]}]} {"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146671197690570","asInt":"1"}]}}]}]}]} {"resourceMetrics":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.40.0"}},{"key":"service.name","value":{"stringValue":"open-webui"}}]},"scopeMetrics":[{"scope":{"name":"open_webui.utils.telemetry.metrics"},"metrics":[{"name":"webui.users.total","description":"Total number of registered users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"1"}]}},{"name":"webui.users.active","description":"Number of currently active users","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"0"}]}},{"name":"webui.users.active.today","description":"Number of users active since midnight today","unit":"users","gauge":{"dataPoints":[{"timeUnixNano":"1773146731210840244","asInt":"1"}]}}]}]}]} ``` A python script to convert the unix timestamp to a human readable format (I just hardcoded `timeUnixNano`) ```python from datetime import datetime, UTC timestamps = [ "1773146611176150917", "1773146671197690570", "1773146731210840244" ] for ts_nano in timestamps: # Convert nanoseconds to seconds ts_seconds = int(ts_nano) / 1e9 dt = datetime.fromtimestamp(ts_seconds, UTC) print(f"{ts_nano} → {dt}") ``` Result ``` 1773146611176150917 → 2026-03-10 12:43:31.176151+00:00 1773146671197690570 → 2026-03-10 12:44:31.197691+00:00 1773146731210840244 → 2026-03-10 12:45:31.210840+00:00 ``` ### Contributor License Agreement <!-- 🚨 DO NOT DELETE THE TEXT BELOW 🚨 Keep the "Contributor License Agreement" confirmation text intact. Deleting it will trigger the CLA-Bot to INVALIDATE your PR. Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA. --> - [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. > [!NOTE] > Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-18 14:39:36 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#114050