Resource library

QA How-To

Monitor Selenium Grid with OpenTelemetry: Selenium Grid OpenTelemetry Monitoring Setup

Complete this selenium grid opentelemetry monitoring setup with Docker Compose, traces, metrics, dashboards, alerts, and practical verification in 2026.

24 min read | 2,448 words

TL;DR

Run Selenium Grid with OTLP trace export enabled, route spans through the OpenTelemetry Collector to Jaeger, and scrape Grid status into Prometheus. Use Grafana for dashboards and alert on queue pressure, capacity exhaustion, and missing telemetry.

Key Takeaways

  • Send Selenium Grid spans to an OpenTelemetry Collector with the official SE_OTEL environment variables.
  • Use Jaeger to follow a WebDriver request across Grid components and identify slow or failed operations.
  • Scrape Grid's GraphQL endpoint with the Collector to produce practical capacity and queue metrics.
  • Keep trace collection and metric collection separate so each signal has a clear purpose and failure path.
  • Verify the telemetry pipeline with health endpoints, a real Remote WebDriver session, trace search, and PromQL queries.
  • Alert on sustained queue pressure, missing scrape targets, and low free-slot capacity instead of noisy single samples.

A selenium grid opentelemetry monitoring setup should tell you why sessions wait or fail, not merely confirm that port 4444 responds. You will export Grid's built-in distributed traces through an OpenTelemetry Collector, inspect them in Jaeger, collect operational metrics in Prometheus, and query both signals from Grafana.

This tutorial is the observability implementation for the Selenium Grid cloud scaling complete guide. Read the pillar first if you still need to choose between static nodes, dynamic Docker nodes, Kubernetes, and a managed provider.

The lab uses pinned Selenium 4.44.0 images and OpenTelemetry Collector 0.156.0. The monitoring design stays version-agnostic, but pinning images makes the exercise repeatable. Recheck release notes and rerun every verification before promoting newer tags.

What You Will Build

You will build a local monitoring stack with these parts:

  • Selenium Hub plus one Chrome node.
  • OpenTelemetry Collector receiving OTLP over gRPC on port 4317.
  • Jaeger storing and visualizing Selenium distributed traces.
  • A Collector HTTP check receiver polling Grid status data.
  • Prometheus storing Collector and Grid-derived metrics.
  • Grafana querying both Prometheus and Jaeger.
  • A Python Remote WebDriver smoke test that creates traceable Grid traffic.
  • Prometheus alert rules for queue pressure, lost telemetry, and capacity exhaustion.

The signal path is intentionally explicit:

Selenium Hub and Node -> OTLP/gRPC -> OpenTelemetry Collector -> Jaeger
Selenium Grid status -> Collector HTTP check -> Prometheus -> Grafana
Selenium Grid GraphQL -> Python exporter -> Prometheus -> Grafana

Grid already instruments server requests with OpenTelemetry. You do not add tracing calls to Selenium source code. The Collector receives, batches, and routes those spans. Metrics answer recurring operational questions, while traces explain one request in detail.

Prerequisites

Install Docker Engine 26 or newer and Docker Compose v2.34 or newer. Docker Desktop works for this local lab. You also need Python 3.11 or newer, curl, and about 4 CPU cores plus 8 GB of memory. Actual production sizing must come from representative workload measurements.

Check the tools:

docker version
docker compose version
python3 --version
curl --version

Create an empty working directory outside your application repository:

mkdir selenium-grid-otel
cd selenium-grid-otel
mkdir -p otel prometheus grafana/provisioning/datasources tests

This tutorial pins these images:

Component Image Purpose
Selenium Hub selenium/hub:4.44.0-20260505 Routes sessions and coordinates Grid
Chrome node selenium/node-chrome:4.44.0-20260505 Runs browser sessions
Collector otel/opentelemetry-collector-contrib:0.156.0 Receives and routes telemetry
Jaeger jaegertracing/jaeger:2.15.0 Stores and queries traces
Prometheus prom/prometheus:v3.7.3 Stores and evaluates metrics
Grafana grafana/grafana:12.2.1 Visualizes traces and metrics

If a pinned image is unavailable for your CPU architecture, choose a current compatible release and keep all Selenium images on exactly the same tag. Do not mix Hub and node versions.

Step 1: Configure the Selenium Grid OpenTelemetry Monitoring Setup

Create compose.yaml:

name: selenium-grid-otel

services:
  jaeger:
    image: jaegertracing/jaeger:2.15.0
    command: ["--set=receivers.otlp.protocols.grpc.endpoint=0.0.0.0:4317"]
    ports:
      - "16686:16686"

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.156.0
    command: ["--config=/etc/otelcol-contrib/config.yaml"]
    volumes:
      - ./otel/collector.yaml:/etc/otelcol-contrib/config.yaml:ro
    ports:
      - "4317:4317"
      - "8888:8888"
      - "8889:8889"
      - "13133:13133"
    depends_on:
      - jaeger

  selenium-hub:
    image: selenium/hub:4.44.0-20260505
    environment:
      SE_ENABLE_TRACING: "true"
      SE_OTEL_TRACES_EXPORTER: otlp
      SE_OTEL_EXPORTER_ENDPOINT: http://otel-collector:4317
      SE_OTEL_SERVICE_NAME: selenium-hub
    ports:
      - "4442:4442"
      - "4443:4443"
      - "4444:4444"
    depends_on:
      - otel-collector

  chrome:
    image: selenium/node-chrome:4.44.0-20260505
    shm_size: 2gb
    environment:
      SE_EVENT_BUS_HOST: selenium-hub
      SE_EVENT_BUS_PUBLISH_PORT: "4442"
      SE_EVENT_BUS_SUBSCRIBE_PORT: "4443"
      SE_NODE_MAX_SESSIONS: "1"
      SE_ENABLE_TRACING: "true"
      SE_OTEL_TRACES_EXPORTER: otlp
      SE_OTEL_EXPORTER_ENDPOINT: http://otel-collector:4317
      SE_OTEL_SERVICE_NAME: selenium-node-chrome
    depends_on:
      - selenium-hub

  prometheus:
    image: prom/prometheus:v3.7.3
    command: ["--config.file=/etc/prometheus/prometheus.yml"]
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro
    ports:
      - "9090:9090"
    depends_on:
      - otel-collector

  grafana:
    image: grafana/grafana:12.2.1
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: admin
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    ports:
      - "3000:3000"
    depends_on:
      - prometheus
      - jaeger

SE_OTEL_TRACES_EXPORTER=otlp selects OTLP export. SE_OTEL_EXPORTER_ENDPOINT uses the Compose service name, not localhost, because each component has its own network namespace. Explicit service names make traces easy to filter.

Verify the step: validate the Compose model before creating containers:

docker compose config --quiet

No output and exit code 0 means the YAML and variable structure are valid. Do not start it yet because the mounted configuration files do not exist.

Step 2: Configure the OpenTelemetry Collector

Create otel/collector.yaml:

extensions:
  health_check:
    endpoint: 0.0.0.0:13133

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
  httpcheck/grid:
    targets:
      - endpoint: http://selenium-hub:4444/status
        method: GET
        collection_interval: 15s

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 256
    spike_limit_mib: 64
  batch:
    timeout: 5s
    send_batch_size: 512
  resource/grid:
    attributes:
      - key: deployment.environment.name
        value: local-lab
        action: upsert

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: selenium_grid
  debug:
    verbosity: basic

service:
  extensions: [health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resource/grid, batch]
      exporters: [otlp/jaeger, debug]
    metrics:
      receivers: [httpcheck/grid]
      processors: [memory_limiter, resource/grid, batch]
      exporters: [prometheus]

The OTLP receiver accepts Selenium spans. The batch processor reduces export overhead, while the memory limiter prevents unbounded Collector growth. The HTTP check proves Grid availability but does not expose queue depth or free slots. You will add those operational metrics in Step 5.

The debug exporter is useful during setup because the Collector log confirms span arrival. Remove it in a busy production environment after the pipeline is proven.

Verify the step: use the Collector image to validate the mounted file:

docker run --rm \
  -v "$PWD/otel/collector.yaml:/etc/otelcol-contrib/config.yaml:ro" \
  otel/opentelemetry-collector-contrib:0.156.0 \
  validate --config=/etc/otelcol-contrib/config.yaml

Expect configuration is valid. A component-not-found error usually means you selected the core Collector image instead of the contrib distribution.

Step 3: Configure Prometheus and Grafana Data Sources

Create prometheus/prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - /etc/prometheus/alerts.yml

scrape_configs:
  - job_name: otel-collector
    static_configs:
      - targets: ["otel-collector:8888"]
  - job_name: selenium-grid
    static_configs:
      - targets: ["otel-collector:8889"]

Create an initially empty prometheus/alerts.yml:

groups: []

Create grafana/provisioning/datasources/datasources.yaml:

apiVersion: 1

datasources:
  - name: Prometheus
    uid: prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
  - name: Jaeger
    uid: jaeger
    type: jaeger
    access: proxy
    url: http://jaeger:16686

Grafana reaches backends through Compose DNS. Publishing ports 9090, 16686, and 3000 is for your browser and command-line verification, not service-to-service communication. In production, protect every UI with authentication and network controls.

Verify the step: render the final model and confirm every bind-mounted file resolves:

docker compose config > /tmp/selenium-grid-otel-compose.txt
docker compose up -d
docker compose ps

All six services should be running. The Chrome node can take several seconds to register. Use docker compose logs --tail=50 otel-collector if the Collector restarts.

Step 4: Prove Grid Health and Generate a Trace

Wait for Grid readiness:

until curl -fsS http://localhost:4444/status | grep -q '"ready": true'; do
  sleep 2
done
curl -fsS http://localhost:13133/

The first command checks Grid's public status contract. The second checks the Collector health extension. Both must pass before a test can prove the full path.

Create tests/test_grid_trace.py:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

GRID_URL = "http://localhost:4444"

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.set_capability("se:name", "otel-smoke-test")

driver = webdriver.Remote(command_executor=GRID_URL, options=options)
try:
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    WebDriverWait(driver, 10).until(
        lambda current: current.find_element(By.NAME, "my-text").is_displayed()
    )
    driver.find_element(By.NAME, "my-text").send_keys("OpenTelemetry")
    driver.find_element(By.CSS_SELECTOR, "button").click()
    assert driver.find_element(By.ID, "message").text == "Received!"
    print(f"session={driver.session_id} status=passed")
finally:
    driver.quit()

Install Selenium into a virtual environment and run the test:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install "selenium>=4.39,<5"
python tests/test_grid_trace.py

The finally block always releases the Grid slot. A leaked session can distort queue and capacity monitoring, so cleanup is part of observability correctness.

Verify the step: expect status=passed, then open http://localhost:16686. Select selenium-hub or selenium-node-chrome, click Find Traces, and open a recent trace. You should see timed spans and attributes for the session request. Also run:

docker compose logs otel-collector | grep -E 'Traces|ResourceSpans' | tail

Recent trace export messages prove Selenium reached the Collector. Jaeger search proves the Collector reached the backend.

Step 5: Add Grid Capacity and Queue Metrics

Grid's /status endpoint is a readiness contract, not a complete capacity API. For this lab, use Grid GraphQL to obtain sessionQueueSize, sessionCount, maxSession, and node availability. In production, run a small exporter or Collector extension that converts these fields into stable Prometheus metrics.

First inspect the source data:

curl -fsS -X POST http://localhost:4444/graphql \
  -H 'Content-Type: application/json' \
  --data '{"query":"{ grid { sessionQueueSize sessionCount maxSession nodeCount nodesInfo { status maxSession sessionCount } } }"}'

Expected output contains a data.grid object. With one idle Chrome node, nodeCount is 1, sessionCount is 0, and the queue is empty. Exact formatting can vary.

For a paste-ready local exporter, create tests/grid_metrics.py:

import json
import urllib.request
from http.server import BaseHTTPRequestHandler, HTTPServer

QUERY = b'{"query":"{ grid { sessionQueueSize sessionCount maxSession nodeCount } }"}'
GRID = "http://selenium-hub:4444/graphql"

def read_grid():
    request = urllib.request.Request(
        GRID, data=QUERY, headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(request, timeout=5) as response:
        return json.load(response)["data"]["grid"]

class MetricsHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != "/metrics":
            self.send_response(404)
            self.end_headers()
            return
        try:
            grid = read_grid()
            values = {
                "queue_size": grid["sessionQueueSize"],
                "sessions_active": grid["sessionCount"],
                "sessions_max": grid["maxSession"],
                "nodes": grid["nodeCount"],
            }
            lines = [f"selenium_grid_{key} {value}" for key, value in values.items()]
            body = ("\n".join(lines) + "\n").encode()
            self.send_response(200)
            self.send_header("Content-Type", "text/plain; version=0.0.4")
            self.end_headers()
            self.wfile.write(body)
        except Exception as error:
            self.send_response(503)
            self.end_headers()
            self.wfile.write(str(error).encode())

    def log_message(self, format, *args):
        return

HTTPServer(("0.0.0.0", 8000), MetricsHandler).serve_forever()

Add this service to compose.yaml:

  grid-metrics:
    image: python:3.13-slim
    command: ["python", "/app/grid_metrics.py"]
    volumes:
      - ./tests/grid_metrics.py:/app/grid_metrics.py:ro
    depends_on:
      - selenium-hub

Add a Prometheus scrape job and restart affected services:

  - job_name: grid-graphql-exporter
    static_configs:
      - targets: ["grid-metrics:8000"]
docker compose up -d grid-metrics prometheus

Verify the step: query Prometheus's HTTP API:

curl -fsSG http://localhost:9090/api/v1/query \
  --data-urlencode 'query=selenium_grid_nodes'
curl -fsSG http://localhost:9090/api/v1/query \
  --data-urlencode 'query=selenium_grid_sessions_max-selenium_grid_sessions_active'

The first result should report one node. The second should report one free slot while the lab is idle. This exporter uses Python's standard library and runs as written, but production code should add unit tests, authentication, bounded retries, structured logs, and metric HELP and TYPE declarations.

Step 6: Build a Useful Grafana Dashboard

Open http://localhost:3000 and sign in with admin and admin. Grafana will prompt you to change the password. Create a dashboard with these PromQL queries:

Panel PromQL Meaning
Active sessions selenium_grid_sessions_active Current Grid workload
Free slots selenium_grid_sessions_max - selenium_grid_sessions_active Immediate headroom
Queue size selenium_grid_queue_size Requests waiting for a slot
Registered nodes selenium_grid_nodes Available infrastructure
Grid HTTP availability selenium_grid_httpcheck_status{http_status_class=\"2xx\"} Collector's status check
Collector refused spans rate(otelcol_receiver_refused_spans_total[5m]) Telemetry backpressure
Failed span exports rate(otelcol_exporter_send_failed_spans_total[5m]) Backend delivery failures

Use stat panels for active sessions, free slots, queue, and nodes. Use time series panels for Collector failure rates. A single snapshot hides queue spikes and intermittent export failures, so keep enough retention to cover your debugging window.

Add Jaeger as an Explore data source and filter by service.name. From a slow test period, open a trace and compare its span timing with queue and free-slot panels. This correlation distinguishes infrastructure waiting from slow application interactions.

Verify the step: run the smoke test again. The active-session panel should briefly rise, then return to zero. Jaeger should show another recent trace. If metrics move but no trace appears, inspect the traces pipeline. If traces appear but panels stay flat, inspect exporter and Prometheus targets.

Step 7: Add Selenium Grid OpenTelemetry Monitoring Setup Alerts

Replace prometheus/alerts.yml with:

groups:
  - name: selenium-grid
    rules:
      - alert: SeleniumGridQueueGrowing
        expr: selenium_grid_queue_size > 0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: Selenium Grid has queued sessions
          description: Session requests have waited for at least five minutes.

      - alert: SeleniumGridNoFreeSlots
        expr: selenium_grid_sessions_max - selenium_grid_sessions_active <= 0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: Selenium Grid has no free browser slots
          description: All registered capacity has remained occupied for ten minutes.

      - alert: SeleniumGridMetricsMissing
        expr: up{job="grid-graphql-exporter"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: Selenium Grid metric exporter is unavailable
          description: Prometheus cannot scrape Grid operational metrics.

      - alert: SeleniumTraceExportFailures
        expr: rate(otelcol_exporter_send_failed_spans_total[5m]) > 0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: OpenTelemetry Collector cannot export Selenium spans
          description: Trace export failures have continued for five minutes.

The for duration prevents one transient sample from paging the team. Tune thresholds against your service-level objectives. A short queue can be expected during a burst, while a sustained queue with no free slots indicates insufficient capacity, a stuck session, or a node problem.

Reload Prometheus:

docker compose restart prometheus
curl -fsS http://localhost:9090/api/v1/rules

Verify the step: the API response should list the selenium-grid group with four rules. Stop the exporter with docker compose stop grid-metrics, wait longer than two minutes, and confirm SeleniumGridMetricsMissing becomes pending and then firing at http://localhost:9090/alerts. Start it again after the exercise.

Step 8: Harden and Operate the Monitoring Pipeline

The lab publishes every UI to localhost and uses default Grafana credentials. Production needs a different security and reliability posture. Put Grafana and Jaeger behind your identity-aware proxy. Do not publish OTLP, Collector administration, Prometheus, or Grid ports to an untrusted network. Enable TLS and authenticated OTLP when telemetry crosses trust boundaries.

Apply these operating controls:

  • Pin immutable image versions and upgrade in staging.
  • Give the Collector CPU and memory limits based on measured telemetry volume.
  • Keep memory_limiter before batch in each pipeline.
  • Use retry-capable exporters and persistent queues when losing traces is unacceptable.
  • Define retention and storage separately for metrics and traces.
  • Limit high-cardinality attributes. Do not turn session IDs into Prometheus labels.
  • Redact credentials, tokens, URLs with secrets, and user data before export.
  • Monitor the Collector with its own otelcol_* metrics.
  • Run synthetic WebDriver sessions to verify the complete path, not only process health.

For elastic capacity, instrument the system before changing its scaling policy. The Selenium Grid Kubernetes autoscaling tutorial shows how to add and remove capacity. If you use disposable browser containers on a Docker host, follow the Selenium Grid dynamic Docker node tutorial. Pair trace evidence with video recording for failed Selenium sessions when a visual browser failure cannot be explained by server spans alone.

Verify the step: restart the complete stack and rerun acceptance checks:

docker compose down
docker compose up -d
python tests/test_grid_trace.py
curl -fsS http://localhost:4444/status
curl -fsS http://localhost:13133/
curl -fsS http://localhost:9090/-/ready

Then confirm a fresh trace, current metrics, loaded alert rules, and zero unexpected container restarts. This is the minimum upgrade gate for the lab.

Troubleshooting

Problem: Grid logs Failed to export spans -> Confirm every Grid component has SE_OTEL_TRACES_EXPORTER=otlp and points to http://otel-collector:4317. Inside a container, localhost:4317 means that same container, not the Collector. Check docker compose logs otel-collector.

Problem: Collector reports an unknown receiver or exporter -> Use otel/opentelemetry-collector-contrib, not the smaller core distribution. Run the validate command from Step 2 and pin the same version in validation and Compose.

Problem: Jaeger has no Selenium service -> Generate a real Remote WebDriver session, expand the search window, and search both selenium-hub and selenium-node-chrome. Confirm the Collector debug exporter received ResourceSpans and its Jaeger exporter reports no failures.

Problem: Prometheus target is down -> Open http://localhost:9090/targets, identify the failed job, then use the Compose service name and internal port. Prometheus must scrape otel-collector:8889 and grid-metrics:8000, not host-published addresses.

Problem: GraphQL returns an error for a field -> Query a smaller selection and inspect the Grid GraphQL schema supported by your pinned Selenium release. Keep the exporter and Grid version tested together because schema fields are not a substitute for a versioned Prometheus contract.

Problem: Queue alert fires after tests finish -> Confirm every test calls driver.quit() in finally, inspect active sessions, and check node health. Increase the alert duration only after ruling out leaked sessions and genuine capacity pressure.

Where To Go Next

You now have a working telemetry path and an acceptance checklist. Use the complete Selenium Grid cloud scaling guide to place monitoring inside a broader capacity and reliability plan.

Choose your next implementation based on the constraint you observe:

Treat the dashboard as a shared diagnostic interface. Review it during incidents, record which panels answered real questions, and remove panels that do not lead to action.

Interview Questions and Answers

Q: Why does Selenium Grid need distributed tracing?

A WebDriver request can cross the Router, Session Queue, Distributor, Session Map, and Node. A trace preserves the request path and timing across those components. It helps separate queue delay, routing failure, node startup, and browser execution problems.

Q: What does the OpenTelemetry Collector do in this setup?

It receives OTLP spans from Grid, applies resource, memory, and batch processing, then exports traces to Jaeger. It also exposes its own metrics and can collect availability signals. This decouples Selenium from a specific observability backend.

Q: Why use both Prometheus and Jaeger?

Prometheus answers aggregate questions over time, such as queue growth, free capacity, and scrape health. Jaeger explains an individual request with spans and attributes. Metrics identify where and when to investigate, while traces provide request-level context.

Q: Which Grid signals should trigger alerts?

Alert on sustained queue depth, prolonged zero free slots, missing nodes, failed scrapes, and continued span export failures. Use durations to reduce noise. Set thresholds from expected demand and service objectives rather than copying lab values unchanged.

Q: Why should session IDs not be Prometheus labels?

Each session ID is nearly unique, so labeling metrics with it creates unbounded time-series cardinality. Put session IDs in traces or structured logs, where request-level identifiers belong. Keep metric labels bounded, such as environment, browser family, or Grid role.

Q: How do you verify observability after a Grid upgrade?

Run a synthetic Remote WebDriver session and assert Grid readiness, Collector health, trace arrival, metric freshness, and loaded alert rules. Compare service names and important attributes with the previous release. Test alert behavior safely in staging before production rollout.

Best Practices

  • Do use pinned, compatible component versions and a documented upgrade gate.
  • Do attach service.name and environment resource attributes to traces.
  • Do retain enough history to cover the delay between a test failure and investigation.
  • Do protect dashboards and ingestion endpoints with network controls and authentication.
  • Do measure queue duration and capacity over time, not only current values.
  • Do not expose Collector, Prometheus, Jaeger, or unauthenticated Grid endpoints publicly.
  • Do not put unique test or session values into metric labels.
  • Do not assume a healthy dashboard means browser sessions succeed. Run a synthetic test.
  • Do not alert on every transient queue sample. Require sustained conditions.
  • Do not let debug exporters flood production logs after setup is complete.

Conclusion

A useful selenium grid opentelemetry monitoring setup connects Grid's built-in OTLP traces to a Collector and trace backend, then pairs request detail with capacity, queue, availability, and Collector metrics. The result explains both broad Grid behavior and one failing session.

Start with the Compose lab, prove each boundary, and preserve the verification commands as an upgrade smoke test. Then harden access, storage, retention, cardinality, and alert thresholds for your production workload.

Interview Questions and Answers

How is Selenium Grid instrumented for distributed tracing?

Selenium Grid 4 uses OpenTelemetry instrumentation around server request processing. A request produces spans as it crosses Grid components, with events and attributes that describe the operation. Grid can export those spans over OTLP to a Collector or compatible trace backend.

What is the role of an OpenTelemetry Collector in Grid monitoring?

The Collector separates telemetry production from storage. It receives Grid spans, applies processors such as memory limiting, resource enrichment, and batching, then exports them to one or more backends. It also exposes self-metrics that reveal dropped or failed telemetry.

How do traces, metrics, and logs differ during a Grid incident?

Metrics show the scope and timing of a condition, such as a sustained queue or loss of nodes. Traces show the path and latency of one WebDriver request. Logs provide detailed component events and exceptions, ideally correlated with trace and span IDs.

How would you detect Selenium Grid capacity exhaustion?

Track active sessions, maximum sessions, free slots, queue size, and queue duration. Alert only when the queue or zero-free-slot condition persists beyond an expected burst window. Correlate the condition with node health and resource saturation before deciding to scale.

Why is metric cardinality important in Selenium monitoring?

Unique labels create a new time series for every value and can overwhelm Prometheus memory and storage. Session IDs, test names, and URLs are high-cardinality values. Keep them in traces or logs and reserve metric labels for small, controlled dimensions.

How would you test the observability pipeline itself?

Run a synthetic Remote WebDriver transaction with a known test name. Verify Grid and Collector health, find the new trace, query current metrics, and confirm alert rules are loaded. Periodically simulate a safe dependency failure in staging to prove that alerts fire and recover.

What security controls belong around Grid telemetry?

Restrict network access to Grid, OTLP, Prometheus, Jaeger, and Collector administration endpoints. Use TLS and authentication across trust boundaries, redact sensitive attributes, and protect dashboards with centralized identity. Treat telemetry as potentially sensitive operational data.

Frequently Asked Questions

Does Selenium Grid support OpenTelemetry natively?

Yes. Selenium Grid 4 includes OpenTelemetry-based tracing for server requests. Configure an OTLP exporter endpoint for each Grid component and send the spans to a Collector or compatible backend.

What is the OTLP endpoint for Selenium Grid in Docker Compose?

Use the Collector's Compose service name and OTLP port, such as `http://otel-collector:4317` for OTLP over gRPC. Do not use localhost unless the Collector runs in the same container.

Can Prometheus store Selenium Grid traces?

No. Prometheus stores time-series metrics, not distributed traces. Send Grid traces to Jaeger, Tempo, or another tracing backend, and use Prometheus for queue, capacity, availability, and Collector metrics.

Which Selenium Grid metrics should I monitor?

Monitor queued requests, active sessions, maximum sessions, free slots, registered and unavailable nodes, Grid readiness, scrape health, and Collector export failures. Add browser-specific breakdowns only with bounded labels.

Why are Selenium Grid traces missing from Jaeger?

Common causes are an incorrect container endpoint, an OTLP protocol mismatch, tracing variables missing from one Grid component, or a Collector export failure. Verify each boundary separately with Grid logs, Collector logs, and Jaeger service search.

Should I run an OpenTelemetry Collector in production?

A Collector is usually preferable because it centralizes batching, retries, resource attributes, filtering, and backend routing. Run it with measured resource limits, secure transport, self-monitoring, and a resilience design appropriate to your trace-loss tolerance.

How do I avoid high-cardinality Selenium monitoring metrics?

Keep unique values such as session ID, test name, URL, and build ID out of Prometheus labels. Store request-level identifiers in traces or logs and use bounded metric labels such as environment, Grid role, and browser family.

Related Guides