This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Grid

Want to run tests in parallel across multiple machines? Then, Grid is for you.

Selenium Grid allows the execution of WebDriver scripts on remote machines (virtual or real) by routing commands sent by the client to remote browser instances. It aims to provide an easy way to run tests in parallel on multiple machines.

Selenium Grid allows us to run tests in parallel on multiple machines, and to manage different browser versions and browser configurations centrally (instead of in each individual test).

Selenium Grid is not a silver bullet. It solves a subset of common delegation and distribution problems, but will for example not manage your infrastructure, and might not suit your specific needs.

Purposes and main functionalities

  • Central entry point for all tests
  • Management and control of the nodes / environment where the browsers run
  • Scaling
  • Running tests in parallel
  • Cross-platform testing
  • Load balancing

1 - Components

Check the different Grid components to understand how to use them.
Grid Components

Selenium Grid 4 Components

Router

The Router takes care of forwarding the request to the correct component.

It is the entry point of the Grid, all external requests will be received by it. The Router behaves differently depending on the request. If it is a new session request, the Router will add it to the New Session Queue. The Distributor regularly checks if there is a free slot. If so, the first matching request is removed from the New Session Queue. will receive the event and poll the New Session Queue to get the new session request. If the request belongs to an existing session, the Router will send the session id to the Session Map, and the Session Map will return the Node where the session is running. After this, the Router will forward the request to the Node.

The Router aims to balance the load in the Grid by sending the requests to the component that is able to handle them better, without overloading any component that is not needed in the process.

Distributor

The Distributor is aware of all the Nodes and their capabilities. Its main role is to receive a new session request and find a suitable Node where the session can be created. After the session is created, the Distributor stores in the Session Map the relation between the session id and Node where the session is being executed.

Node

A Node can be present several times in a Grid. Each Node takes care of managing the slots for the available browsers of the machine where it is running.

The Node registers itself to the Distributor through the Event Bus, and its configuration is sent as part of the registration message.

By default, the Node auto-registers all browser drivers available on the path of the machine where it runs. It also creates one slot per available CPU for Chromium based browsers and Firefox. For Safari and Internet Explorer, only one slot is created. Through a specific configuration, it can run sessions in Docker containers or relay commands. You can see more configuration details in the next section.

A Node only executes the received commands, it does not evaluate, make judgments, or control anything. The machines where the Node is running does not need to have the same operating system as the other components. For example, A Windows Node might have the capability of offering Internet Explorer as a browser option, whereas this would not be possible on Linux or Mac.

Session Map

The Session Map is a data store that keeps the information of the session id and the Node where the session is running. It serves as a support for the Router in the process of forwarding a request to the Node. The Router will ask the Session Map for the Node associated to a session id.

New Session Queue

New Session Queue holds all the new session requests in a FIFO order. It has configurable parameters for setting the request timeout and request retry interval.

The Router adds the new session request to the New Session Queue and waits for the response. The New Session Queue regularly checks if any request in the queue has timed out, if so the request is rejected and removed immediately.

The Distributor regularly checks if a slot is available. If so, the Distributor requests the New Session Queue for the first matching request. The Distributor then attempts to create a new session.

Once the requested capabilities match the capabilities of any of the free Node slots, the Distributor attempts to get the available slot. If all the slots are busy, the Distributor will ask the queue to add the request to the front of the queue. If request times out while retrying or adding to the front of the queue it is rejected.

After a session is created successfully, the Distributor sends the session information to the New Session Queue. The New Session Queue sends the response back to the client.

Event Bus

The Event Bus serves as a communication path between the Nodes, Distributor, New Session Queue, and Session Map. The Grid does most of its internal communication through messages, avoiding expensive HTTP calls. When starting the Grid in its fully distributed mode, the Event Bus is the first component that should be started.

2 - Setting up your own

Instructions, step by step, showing how to run a simple Selenium Grid.

Grid roles

Several components compose a Selenium Grid. Depending on your needs, you can start each one of them on its own, or a few at the same time by using a Grid role.

Standalone

Standalone is the union of all components, and to the user’s eyes, they are executed as one. A fully functional Grid of one is available after starting it in the Standalone mode.

Standalone is also the easiest mode to spin up a Selenium Grid. By default, the server will be listening on http://localhost:4444, and that’s the URL you should point your RemoteWebDriver tests. The server will detect the available drivers that it can use from the System PATH.

java -jar selenium-server-<version>.jar standalone

Hub and Node(s)

It enables the classic Hub & Node(s) setup. These roles are suitable for small and middle-sized Grids.

Hub

A Hub is the union of the following components:

  • Router
  • Distributor
  • Session Map
  • New Session Queue
  • Event Bus
java -jar selenium-server-<version>.jar hub

By default, the server will be listening on http://localhost:4444, and that’s the URL you should point your RemoteWebDriver tests.

Node(s)

One or more Nodes can be started in this setup, and the server will detect the available drivers that it can use from the System PATH.

java -jar selenium-server-<version>.jar node

Distributed

On Distributed mode, each component needs to be started on its own. This setup is more suitable for large Grids.

  1. Event Bus: serves as a communication path to other Grid components in subsequent steps.
java -jar selenium-server-<version>.jar  event-bus
  1. Session Map: responsible for mapping session IDs to the Node where the session is running.
java -jar selenium-server-<version>.jar sessions
  1. New Session Queue: adds the new session request to a queue, then the distributor processes it.
java -jar selenium-server-<version>.jar sessionqueue
  1. Distributor: Nodes register to it, and assigns a Node for a session request.
java -jar selenium-server-<version>.jar distributor --sessions http://localhost:5556 --sessionqueue http://localhost:5559 --bind-bus false
  1. Router: the Grid entrypoint, in charge of redirecting requests to the right component.
java -jar selenium-server-<version>.jar router --sessions http://localhost:5556 --distributor http://localhost:5553 --sessionqueue http://localhost:5559
  1. Node(s)
java -jar selenium-server-<version>.jar node 

Querying Selenium Grid

After starting a Grid, there are mainly two ways of querying its status, through the Grid UI or via an API call.

The Grid UI can be reached by opening your preferred browser and heading to http://localhost:4444.

API calls can be done through the http://localhost:4444/status endpoint or using GraphQL:

curl -X POST -H "Content-Type: application/json" --data '{ "query": "{grid{uri}}" }' -s http://localhost:4444/graphql | jq .

For simplicity, all command examples shown in this page assume that components are running locally. More detailed examples and usages can be found in the Configuring Components section.

Warning

Selenium Grid must be protected from external access using appropriate firewall permissions.

Failure to protect your Grid could result in one or more of the following occurring:

  • You provide open access to your Grid infrastructure
  • You allow third parties to access internal web applications and files
  • You allow third parties to run custom binaries

See this blog post on Detectify, which gives a good overview of how a publicly exposed Grid could be misused: Don’t Leave your Grid Wide Open

3 - Configuring Components

Here you can see how each Grid component can be configured individually based on common configuration values and component-specific configuration values.

3.1 - Config help

Get information about all the available options to configure Grid.

The help commands display information based on the current code implementation. Hence, it will provide accurate information in case the documentation is not updated. It is the easiest way to learn about Grid 4 configuration for any new version.

Info Command

The info command provides detailed docs on the following topics:

  • Configuring Selenium
  • Security
  • Session Map setup
  • Tracing

Config help

Quick config help and overview is provided by running:

java -jar selenium-server-<version>.jar info config

Security

To get details on setting up the Grid servers for secure communication and node registration:

java -jar selenium-server-<version>.jar info security

Session Map setup

By default, Grid uses a local session map to store session information. Grid supports additional storage options like Redis and JDBC - SQL supported databases. To set up different session storage, use the following command to get setup steps:

java -jar selenium-server-<version>.jar info sessionmap

Setting up tracing with OpenTelemetry and Jaeger

By default, tracing is enabled. To export traces and visualize them via Jaeger, use the following command for instructions:

java -jar selenium-server-<version>.jar info tracing

List the Selenium Grid commands

java -jar selenium-server-<version>.jar --config-help

It will show all the available commands and description for each one.

Component help commands

Pass –help config option after the Selenium role to get component-specific config information.

Standalone

java -jar selenium-server-<version>.jar standalone --help

Hub

java -jar selenium-server-<version>.jar hub --help

Sessions

java -jar selenium-server-<version>.jar sessions --help

New Session Queue

java -jar selenium-server-<version>.jar sessionqueue --help

Distributor

java -jar selenium-server-<version>.jar distributor --help

Router

java -jar selenium-server-<version>.jar router --help

Node

java -jar selenium-server-<version>.jar node --help

3.2 - CLI Options

All Grid components configuration CLI options in detail.

Different sections are available to configure a Grid. Each section has options can be configured through command line arguments.

A complete description of the component to section mapping can be seen below.

Note that this documentation could be outdated if an option was modified or added but has not been documented yet. In case you bump into this situation, please check the “Config help” section and feel free to send us a pull request updating this page.

Sections

StandaloneHubNodeDistributorRouterSessionsSessionQueue
Distributor
Docker
Events
Logging
Network
Node
Router
Relay
Server
SessionQueue
Sessions

Distributor

OptionTypeValue/ExampleDescription
--grid-modelstringorg.openqa.selenium.grid.distributor.GridModelFull classname of non-default grid model. This is used to store the states of all the registered Nodes.
--healthcheck-intervalint120How often, in seconds, will the health check run for all Nodes. This ensures the server can ping all the Nodes successfully.
--distributorurihttp://localhost:5553Url of the distributor.
--distributor-hoststringlocalhostHost on which the distributor is listening.
--distributor-implementationstringorg.openqa.selenium.grid.distributor.local.LocalDistributorFull class name of non-default distributor implementation
--distributor-portint5553Port on which the distributor is listening.
--reject-unsupported-capsbooleanfalseAllow the Distributor to reject a request immediately if the Grid does not support the requested capability. Rejecting requests immediately is suitable for a Grid setup that does not spin up Nodes on demand.
--slot-matcherstringorg.openqa.selenium.grid.data.DefaultSlotMatcherFull class name of non-default slot matcher to use. This is used to determine whether a Node can support a particular session.
--slot-selectorstringorg.openqa.selenium.grid.distributor.selector.DefaultSlotSelectorFull class name of non-default slot selector. This is used to select a slot in a Node once the Node has been matched.

Docker

OptionTypeValue/ExampleDescription
--docker-assets-pathstring/opt/selenium/assetsAbsolute path where assets will be stored
--docker-string[]selenium/standalone-firefox:latest '{"browserName": "firefox"}'Docker configs which map image name to stereotype capabilities (example `-D selenium/standalone-firefox:latest ‘{“browserName”: “firefox”}')
--docker-hoststringlocalhostHost name where the Docker daemon is running
--docker-portint2375Port where the Docker daemon is running
--docker-urlstringhttp://localhost:2375URL for connecting to the Docker daemon
--docker-video-imagestringselenium/video:latestDocker image to be used when video recording is enabled

Events

OptionTypeValue/ExampleDescription
--bind-busbooleanfalseWhether the connection string should be bound or connected.
When true, the component will be bound to the Event Bus (as in the Event Bus will also be started by the component, typically by the Distributor and the Hub).
When false, the component will connect to the Event Bus.
--events-implementationstringorg.openqa.selenium.events.zeromq.ZeroMqEventBusFull class name of non-default event bus implementation
--publish-eventsstringtcp://*:4442Connection string for publishing events to the event bus
--subscribe-eventsstringtcp://*:4443Connection string for subscribing to events from the event bus

Logging

OptionTypeValue/ExampleDescription
--http-logsbooleanfalseEnable http logging. Tracing should be enabled to log http logs.
--log-encodingstringUTF-8Log encoding
--logstringWindows path example :
'\path\to\file\gridlog.log'
or
'C:\path\path\to\file\gridlog.log'

Linux/Unix/MacOS path example :
'/path/to/file/gridlog.log'
File to write out logs. Ensure the file path is compatible with the operating system’s file path.
--log-levelstring“INFO”Log level. Default logging level is INFO. Log levels are described here https://docs.oracle.com/javase/7/docs/api/java/util/logging/Level.html
--plain-logsbooleantrueUse plain log lines
--structured-logsbooleanfalseUse structured logs
--tracingbooleantrueEnable trace collection

Network

OptionTypeValue/ExampleDescription
--relax-checksbooleanfalseRelax checks on origin header and content type of incoming requests, in contravention of strict W3C spec compliance.

Node

OptionTypeValue/ExampleDescription
--detect-driversbooleantrueAutodetect which drivers are available on the current system, and add them to the Node.
--driver-configurationstring[]display-name="Firefox Nightly" max-sessions=2 webdriver-path="/usr/local/bin/geckodriver" stereotype='{"browserName": "firefox", "browserVersion": "86", "moz:firefoxOptions": {"binary":"/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin"}}'List of configured drivers a Node supports. It is recommended to provide this type of configuration through a toml config file to improve readability
--driver-factorystring[]org.openqa.selenium.example.LynxDriverFactory '{"browserName": "lynx"}'Mapping of fully qualified class name to a browser configuration that this matches against.
--driver-implementationstring[]"firefox"Drivers that should be checked. If specified, will skip autoconfiguration.
--grid-urlstringhttps://grid.example.comPublic URL of the Grid as a whole (typically the address of the Hub or the Router)
--heartbeat-periodint60How often, in seconds, will the Node send heartbeat events to the Distributor to inform it that the Node is up.
--max-sessionsint8Maximum number of concurrent sessions. Default value is the number of available processors.
--override-max-sessionsbooleanfalseThe # of available processos is the recommended max sessions value (1 browser session per processor). Setting this flag to true allows the recommended max value to be overwritten. Session stability and reliability might suffer as the host could run out of resources.
--register-cycleint10How often, in seconds, the Node will try to register itself for the first time to the Distributor.
--register-periodint120How long, in seconds, will the Node try to register to the Distributor for the first time. After this period is completed, the Node will not attempt to register again.
--session-timeoutint300Let X be the session-timeout in seconds. The Node will automatically kill a session that has not had any activity in the last X seconds. This will release the slot for other tests.
--vnc-env-varstringSTART_XVFBEnvironment variable to check in order to determine if a vnc stream is available or not.

Relay

OptionTypeValue/ExampleDescription
--service-urlstringhttp://localhost:67853URL for connecting to the service
--service-hoststringlocalhostHost name where the service is running
--service-portint67853Port where the service is running
--service-status-endpointstring/statusOptional, endpoint to query the service status, an HTTP 200 response is expected

Router

OptionTypeValue/ExampleDescription
--passwordstringmyStrongPasswordPassword clients must use to connect to the server. Both this and the username need to be set in order to be used.
--usernamestringadminUser name clients must use to connect to the server. Both this and the password need to be set in order to be used.

Server

OptionTypeValue/ExampleDescription
--allow-corsbooleantrueWhether the Selenium server should allow web browser connections from any host
--hoststringlocalhostServer IP or hostname: usually determined automatically.
--https-certificatepath/path/to/cert.pemServer certificate for https. Get more detailed information by running “java -jar selenium-server.jar info security”
--https-private-keypath/path/to/key.pkcs8Private key for https. Get more detailed information by running “java -jar selenium-server.jar info security”
--max-threadsint24Maximum number of listener threads. Default value is: (available processors) * 3.
--portint4444Port to listen on. There is no default as this parameter is used by different components, for example, Router/Hub/Standalone will use 4444 and Node will use 5555.

SessionQueue

OptionTypeValue/ExampleDescription
--sessionqueueurihttp://localhost:1237Address of the session queue server.
-sessionqueue-hoststringlocalhostHost on which the session queue server is listening.
--sessionqueue-portint1234Port on which the session queue server is listening.
--session-request-timeoutint300Timeout in seconds. A new incoming session request is added to the queue. Requests sitting in the queue for longer than the configured time will timeout.
--session-retry-intervalint5Retry interval in seconds. If all slots are busy, new session request will be retried after the given interval.

Sessions

OptionTypeValue/ExampleDescription
--sessionsurihttp://localhost:1234Address of the session map server.
--sessions-hoststringlocalhostHost on which the session map server is listening.
--sessions-portint1234Port on which the session map server is listening.

Configuration examples

All the options mentioned above can be used when starting the Grid components. They are a good way of exploring the Grid options, and trying out values to find a suitable configuration.

We recommend the use of Toml files to configure a Grid. Configuration files improve readability, and you can also check them in source control.

When needed, you can combine a Toml file configuration with CLI arguments.

Command-line flags

To pass config options as command-line flags, identify the valid options for the component and follow the template below.

java -jar selenium-server-<version>.jar <component> --<option> value

Standalone, setting max sessions and main port

java -jar selenium-server-<version>.jar standalone --max-sessions 4 --port 4444

Hub, setting a new session request timeout, a main port, and disabling tracing

java -jar selenium-server-<version>.jar hub --session-request-timeout 500 --port 3333 --tracing false

Node, with 4 max sessions, with debug(fine) log, 7777 as port, and only with Firefox and Edge

java -jar selenium-server-<version>.jar node --max-sessions 4 --log-level "fine" --port 7777 --driver-implementation "firefox" --driver-implementation "edge"

Distributor, setting Session Map server url, Session Queue server url, and disabling bus

java -jar selenium-server-<version>.jar distributor --sessions http://localhost:5556 --sessionqueue http://localhost:5559 --bind-bus false

3.3 - Toml Options

Grid configuration examples using Toml files.

All the options shown in CLI options can be configured through a TOML file. This page shows configuration examples for the different Grid components.

Note that this documentation could be outdated if an option was modified or added but has not been documented yet. In case you bump into this situation, please check the “Config help” section and feel free to send us a pull request updating this page.

Overview

Selenium Grid uses TOML format for config files. The config file consists of sections and each section has options and its respective value(s).

Refer to the TOML documentation for detailed usage guidance. In case of parsing errors, validate the config using TOML linter.

The general configuration structure has the following pattern:

[section1]
option1="value"

[section2]
option2=["value1","value2"]
option3=true

Below are some examples of Grid components configured with a Toml file, the component can be started in the following way:

java -jar selenium-server-<version>.jar <component> --config /path/to/file/<file-name>.toml

Standalone

A Standalone server, running on port 4449, and a new session request timeout of 500 seconds.

[server]
port = 4449

[sessionqueue]
session-request-timeout = 500

Specific browsers and a limit of max sessions

A Standalone server or a Node which only has Firefox and Chrome enabled by default.

[node]
drivers = ["chrome", "firefox"]
max-sessions = 3

Configuring and customising drivers

Standalone or Node server with customised drivers, which allows things like having Firefox Beta or Nightly, and having different browser versions.

[node]
detect-drivers = false
[[node.driver-configuration]]
max-sessions = 100
display-name = "Firefox Nightly"
stereotype = "{\"browserName\": \"firefox\", \"browserVersion\": \"93\", \"platformName\": \"MAC\", \"moz:firefoxOptions\": {\"binary\": \"/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin\"}}"
[[node.driver-configuration]]
display-name = "Chrome Beta"
stereotype = "{\"browserName\": \"chrome\", \"browserVersion\": \"94\", \"platformName\": \"MAC\", \"goog:chromeOptions\": {\"binary\": \"/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta\"}}"
[[node.driver-configuration]]
display-name = "Chrome Dev"
stereotype = "{\"browserName\": \"chrome\", \"browserVersion\": \"95\", \"platformName\": \"MAC\", \"goog:chromeOptions\": {\"binary\": \"/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev\"}}"
webdriver-executable = '/path/to/chromedriver/95/chromedriver'

Standalone or Node with Docker

A Standalone or Node server that is able to run each new session in a Docker container. Disabling drivers detection, having maximum 2 concurrent sessions. Stereotypes configured need to be mapped to a Docker image, and the Docker daemon needs to be exposed via http/tcp.

[node]
detect-drivers = false
max-sessions = 2

[docker]
configs = 
    [
        "selenium/standalone-chrome:93.0", "{\"browserName\": \"chrome\", \"browserVersion\": \"91\"}", 
        "selenium/standalone-firefox:92.0", "{\"browserName\": \"firefox\", \"browserVersion\": \"92\"}"
    ]
url = "http://localhost:2375"
video-image = "selenium/video:latest"

Relaying commands to a service endpoint that supports WebDriver

It is useful to connect an external service that supports WebDriver to Selenium Grid. An example of such service could be a cloud provider or an Appium server. In this way, Grid can enable more coverage to platforms and versions not present locally.

The following is an en example of connecting an Appium server to Grid.

[server]
port = 5555

[node]
detect-drivers = false

[relay]
# Default Appium server endpoint
url = "http://localhost:4723/wd/hub"
status-endpoint = "/status"
# Stereotypes supported by the service
configs = [
  "1", "{\"browserName\": \"chrome\", \"platformName\": \"android\", \"appium:platformVersion\": \"11\"}"
]

Basic auth enabled

It is possible to protect a Grid with basic auth by configuring the Router/Hub/Standalone with a username and password. This user/password combination will be needed when loading the Grid UI or starting a new session.

[router]
username = "admin"
password = "myStrongPassword"

Here is a Java example showing how to start a session using the configured user and password.

URL gridUrl = new URL("http://admin:myStrongPassword@localhost:4444");
RemoteWebDriver webDriver = new RemoteWebDriver(gridUrl, new ChromeOptions());

4 - Advanced Features

To get all the details of the advanced features, understand how it works, and how to set up your own, please browse thorough the following sections.

4.1 - Observability

Table of Contents

Selenium Grid

Grid aids in scaling and distributing tests by executing tests on various browser and operating system combinations.

Observability

Observability has three pillars: traces, metrics and logs. Since Selenium Grid 4 is designed to be fully distributed, observability will make it easier to understand and debug the internals.

Distributed tracing

A single request or transaction spans multiple services and components. Tracing tracks the request lifecycle as each service executes the request. It is useful in debugging in an error scenario. Some key terms used in tracing context are:

Trace Tracing allows one to trace a request through multiple services, starting from its origin to its final destination. This request’s journey helps in debugging, monitoring the end-to-end flow, and identifying failures. A trace depicts the end-to-end request flow. Each trace has a unique id as its identifier.

Span Each trace is made up of timed operations called spans. A span has a start and end time and it represents operations done by a service. The granularity of span depends on how it is instrumented. Each span has a unique identifier. All spans within a trace have the same trace id.

Span Attributes Span attributes are key-value pairs which provide additional information about each span.

Events Events are timed-stamped logs within a span. They provide additional context to the existing spans. Events also contain key-value pairs as event attributes.

Event logging

Logging is essential to debug an application. Logging is often done in a human-readable format. But for machines to search and analyze the logs, it has to have a well-defined format. Structured logging is a common practice of recording logs consistently in a fixed format. It commonly contains fields like:

  • Timestamp
  • Logging level
  • Logger class
  • Log message (This is further broken down into fields relevant to the operation where the log was recorded)

Logs and events are closely related. Events encapsulate all the possible information available to do a single unit of work. Logs are essentially subsets of an event. At the crux, both aid in debugging. Refer following resources for detailed understanding:

  1. https://www.honeycomb.io/blog/how-are-structured-logs-different-from-events/
  2. https://charity.wtf/2019/02/05/logs-vs-structured-events/

Grid Observability

Selenium server is instrumented with tracing using OpenTelemetry. Every request to the server is traced from start to end. Each trace consists of a series of spans as a request is executed within the server. Most spans in the Selenium server consist of two events:

  1. Normal event - records all information about a unit of work and marks successful completion of the work.
  2. Error event - records all information till the error occurs and then records the error information. Marks an exception event.

Running Selenium server

  1. Standalone
  2. Hub and Node
  3. Fully Distributed
  4. Docker

Visualizing Traces

All spans, events and their respective attributes are part of a trace. Tracing works while running the server in all of the above-mentioned modes.

By default, tracing is enabled in the Selenium server. Selenium server exports the traces via two exporters:

  1. Console - Logs all traces and their included spans at FINE level. By default, Selenium server prints logs at INFO level and above. The log-level flag can be used to pass a logging level of choice while running the Selenium Grid jar/s.
java -jar selenium-server-4.0.0-<selenium-version>.jar standalone --log-level FINE
  1. Jaeger UI - OpenTelemetry provides the APIs and SDKs to instrument traces in the code. Whereas Jaeger is a tracing backend, that aids in collecting the tracing telemetry data and providing querying, filtering and visualizing features for the data.

Detailed instructions of visualizing traces using Jaeger UI can be obtained by running the command :

java -jar selenium-server-4.0.0-<selenium-version>.jar info tracing

A very good example and scripts to run the server and send traces to Jaeger

Leveraging event logs

Tracing has to be enabled for event logging as well, even if one does not wish to export traces to visualize them.
By default, tracing is enabled. No additional parameters need to be passed to see logs on the console. All events within a span are logged at FINE level. Error events are logged at WARN level.

All event logs have the following fields :

FieldField valueDescription
Event timeeventIdTimestamp of the event record in epoch nanoseconds.
Trace IdtracedIdEach trace is uniquely identified by a trace id.
Span IdspanIdEach span within a trace is uniquely identified by a span id.
Span KindspanKindSpan kind is a property of span indicating the type of span. It helps in understanding the nature of the unit of work done by the Span.
Event nameeventNameThis maps to the log message.
Event attributeseventAttributesThis forms the crux of the event logs, based on the operation executed, it has JSON formatted key-value pairs. This also includes a handler class attribute, to show the logger class.

Sample log

FINE [LoggingOptions$1.lambda$export$1] - {
  "traceId": "fc8aef1d44b3cc8bc09eb8e581c4a8eb",
  "spanId": "b7d3b9865d3ddd45",
  "spanKind": "INTERNAL",
  "eventTime": 1597819675128886121,
  "eventName": "Session request execution complete",
  "attributes": {
    "http.status_code": 200,
    "http.handler_class": "org.openqa.selenium.grid.router.HandleSession",
    "http.url": "\u002fsession\u002fdd35257f104bb43fdfb06242953f4c85",
    "http.method": "DELETE",
    "session.id": "dd35257f104bb43fdfb06242953f4c85"
  }
}

In addition to the above fields, based on OpenTelemetry specification error logs consist of :

FieldField valueDescription
Exception typeexception.typeThe class name of the exception.
Exception messageexception.messageReason for the exception.
Exception stacktraceexception.stacktracePrints the call stack at the point of time when the exception was thrown. Helps in understanding the origin of the exception.

Sample error log

WARN [LoggingOptions$1.lambda$export$1] - {
  "traceId": "7efa5ea57e02f89cdf8de586fe09f564",
  "spanId": "914df6bc9a1f6e2b",
  "spanKind": "INTERNAL",
  "eventTime": 1597820253450580272,
  "eventName": "exception",
  "attributes": {
    "exception.type": "org.openqa.selenium.ScriptTimeoutException",
    "exception.message": "Unable to execute request: java.sql.SQLSyntaxErrorException: Table 'mysql.sessions_mappa' doesn't exist ..." (full message will be printed),
    "exception.stacktrace": "org.openqa.selenium.ScriptTimeoutException: java.sql.SQLSyntaxErrorException: Table 'mysql.sessions_mappa' doesn't exist\nBuild info: version: '4.0.0-alpha-7', revision: 'Unknown'\nSystem info: host: 'XYZ-MacBook-Pro.local', ip: 'fe80:0:0:0:10d5:b63a:bdc6:1aff%en0', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.13.6', java.version: '11.0.7'\nDriver info: driver.version: unknown ...." (full stack will be printed),
    "http.handler_class": "org.openqa.selenium.grid.distributor.remote.RemoteDistributor",
    "http.url": "\u002fsession",
    "http.method": "POST"
  }
}

Note: Logs are pretty printed above for readability. Pretty printing for logs is turned off in Selenium server.

The steps above should set you up for seeing traces and logs.

References

  1. Understanding Tracing
  2. OpenTelemetry Tracing API Specification
  3. Selenium Wiki
  4. Structured logs vs events
  5. Jaeger framework

4.2 - GraphQL Query Support

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. It gives users the power to ask for exactly what they need and nothing more.

Enums

Enums represent possible sets of values for a field.

For example, the Node object has a field called status. The state is an enum (specifically, of type Status) because it may be UP , DRAINING or UNAVAILABLE.

Scalars

Scalars are primitive values: Int, Float, String, Boolean, or ID.

When calling the GraphQL API, you must specify nested subfield until you return only scalars.

Structure of the Schema

The structure of grid schema is as follows:

{
    session(id: "<session-id>") : {
        id,
        capabilities,
        startTime,
        uri,
        nodeId,
        nodeUri,
        sessionDurationMillis
        slot : {
            id,
            stereotype,
            lastStarted
        }
    }
    grid: {
        uri,
        totalSlots,
        nodeCount,
        maxSession,
        sessionCount,
        version,
        sessionQueueSize
    }
    sessionsInfo: {
        sessionQueueRequests,
        sessions: [
            {
                id,
                capabilities,
                startTime,
                uri,
                nodeId,
                nodeUri,
                sessionDurationMillis
                slot : {
                    id,
                    stereotype,
                    lastStarted
                }
            }
        ]
    }
    nodesInfo: {
        nodes : [
            {
                id,
                uri,
                status,
                maxSession,
                slotCount,
                sessions: [
                    {
                        id,
                        capabilities,
                        startTime,
                        uri,
                        nodeId,
                        nodeUri,
                        sessionDurationMillis
                        slot : {
                            id,
                            stereotype,
                            lastStarted
                        }
                    }
                ],
                sessionCount,
                stereotypes,
                version,
                osInfo: {
                    arch,
                    name,
                    version
                }
            }
        ]
    }
}

Querying GraphQL

The best way to query GraphQL is by using curl requests. GraphQL allows you to fetch only the data that you want, nothing more nothing less.

Some of the example GraphQL queries are given below. You can build your own queries as you like.

Querying the number of maxSession and sessionCount in the grid :

curl -X POST -H "Content-Type: application/json" --data '{"query": "{ grid { maxSession, sessionCount } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Generally on local machine the <LINK_TO_GRAPHQL_ENDPOINT> would be http://localhost:4444/graphql

Querying all details for session, node and the Grid :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ grid { uri, maxSession, sessionCount }, nodesInfo { nodes { id, uri, status, sessions { id, capabilities, startTime, uri, nodeId, nodeUri, sessionDurationMillis, slot { id, stereotype, lastStarted } }, slotCount, sessionCount }} }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query for getting the current session count in the Grid :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ grid { sessionCount } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query for getting the max session count in the Grid :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ grid { maxSession } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query for getting all session details for all nodes in the Grid :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ sessionsInfo { sessions { id, capabilities, startTime, uri, nodeId, nodeId, sessionDurationMillis } } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query to get slot information for all sessions in each Node in the Grid :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ sessionsInfo { sessions { id, slot { id, stereotype, lastStarted } } } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query to get session information for a given session:

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ session (id: "<session-id>") { id, capabilities, startTime, uri, nodeId, nodeUri, sessionDurationMillis, slot { id, stereotype, lastStarted } } } "}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Querying the capabilities of each node in the grid :

curl -X POST -H "Content-Type: application/json" --data '{"query": "{ nodesInfo { nodes { stereotypes } } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Querying the status of each node in the grid :

curl -X POST -H "Content-Type: application/json" --data '{"query": "{ nodesInfo { nodes { status } } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Querying the URI of each node and the grid :

curl -X POST -H "Content-Type: application/json" --data '{"query": "{ nodesInfo { nodes { uri } } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query for getting the current requests in the New Session Queue:

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ sessionsInfo { sessionQueueRequests } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

Query for getting the New Session Queue size :

curl -X POST -H "Content-Type: application/json" --data '{"query":"{ grid { sessionQueueSize } }"}' -s <LINK_TO_GRAPHQL_ENDPOINT>

4.3 - Grid Endpoints

Grid

Grid Status

Grid status provides the current state of the Grid. It consists of details about every registered Node. For every Node, the status includes information regarding Node availability, sessions, and slots.

cURL GET 'http://localhost:4444/status'

In the Standalone mode, the Grid URL is the Standalone server address.

In the Hub-Node mode, the Grid URL is the Hub server address.

In the fully distributed mode, the Grid URL is the Router server address.

Default URL for all the above modes is http://localhost:4444.

Distributor

Remove Node

To remove the Node from the Grid, use the cURL command enlisted below. It does not stop any ongoing session running on that Node. The Node continues running as it is unless explicitly killed. The Distributor is no longer aware of the Node and hence any matching new session request will not be forwarded to that Node.

In the Standalone mode, the Distributor URL is the Standalone server address.

In the Hub-Node mode, the Distributor URL is the Hub server address.

cURL --request DELETE 'http://localhost:4444/se/grid/distributor/node/<node-id>' --header 'X-REGISTRATION-SECRET: <secret> '

In the fully distributed mode, the URL is the Distributor server address.

cURL --request DELETE 'http://localhost:5553/se/grid/distributor/node/<node-id>' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request DELETE 'http://<Distributor-URL>/se/grid/distributor/node/<node-id>' --header 'X-REGISTRATION-SECRET;'

Drain Node

Node drain command is for graceful node shutdown. Draining a Node stops the Node after all the ongoing sessions are complete. However, it does not accept any new session requests.

In the Standalone mode, the Distributor URL is the Standalone server address.

In the Hub-Node mode, the Distributor URL is the Hub server address.

cURL --request POST 'http://localhost:4444/se/grid/distributor/node/<node-id>/drain' --header 'X-REGISTRATION-SECRET: <secret> '

In the fully distributed mode, the URL is the Distributor server address.

cURL --request POST 'http://localhost:5553/se/grid/distributor/node/<node-id>/drain' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request POST 'http://<Distributor-URL>/se/grid/distributor/node/<node-id>/drain' --header 'X-REGISTRATION-SECRET;'

Node

The endpoints in this section are applicable for Hub-Node mode and fully distributed Grid mode where the Node runs independently. The default Node URL is http://localhost:5555 in case of one Node. In case of multiple Nodes, use Grid status to get all Node details and locate the Node address.

Status

The Node status is essentially a health-check for the Node. Distributor pings the node status are regular intervals and updates the Grid Model accordingly. The status includes information regarding availability, sessions, and slots.

cURL --request GET 'http://localhost:5555/status'

Drain

Distributor passes the drain command to the appropriate node identified by the node-id. To drain the Node directly, use the cuRL command enlisted below. Both endpoints are valid and produce the same result. Drain finishes the ongoing sessions before stopping the Node.

cURL --request POST 'http://localhost:5555/se/grid/node/drain' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request POST 'http://<node-URL>/se/grid/node/drain' --header 'X-REGISTRATION-SECRET;'

Check session owner

To check if a session belongs to a Node, use the cURL command enlisted below.

cURL --request GET 'http://localhost:5555/se/grid/node/owner/<session-id>' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request GET 'http://<node-URL>/se/grid/node/owner/<session-id>' --header 'X-REGISTRATION-SECRET;'

It will return true if the session belongs to the Node else it will return false.

Delete session

Deleting the session terminates the WebDriver session, quits the driver and removes it from the active sessions map. Any request using the removed session-id or reusing the driver instance will throw an error.

cURL --request DELETE 'http://localhost:5555/se/grid/node/session/<session-id>' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request DELETE 'http://<node-URL>/se/grid/node/session/<session-id>' --header 'X-REGISTRATION-SECRET;'

New Session Queue

Clear New Session Queue

New Session Request Queue holds the new session requests. To clear the queue, use the cURL command enlisted below. Clearing the queue rejects all the requests in the queue. For each such request, the server returns an error response to the respective client. The result of the clear command is the total number of deleted requests.

In the Standalone mode, the Queue URL is the Standalone server address.

In the Hub-Node mode, the Queue URL is the Hub server address.

cURL --request DELETE 'http://localhost:4444/se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET: <secret>'

In the fully distributed mode, the Queue URL is New Session Queue server address.

cURL --request DELETE 'http://localhost:5559/se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET: <secret>'

If no registration secret has been configured while setting up the Grid, then use

cURL --request DELETE 'http://<URL>/se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET;'

Get New Session Queue Requests

New Session Request Queue holds the new session requests. To get the current requests in the queue, use the cURL command enlisted below. The response returns the total number of requests in the queue and the request payloads.

In the Standalone mode, the Queue URL is the Standalone server address.

In the Hub-Node mode, the Queue URL is the Hub server address.

cURL --request GET 'http://localhost:4444/se/grid/newsessionqueue/queue'

In the fully distributed mode, the Queue URL is New Session Queue server address.

cURL --request GET 'http://localhost:5559/se/grid/newsessionqueue/queue'

5 - When to use Grid

Is Grid the right tool for you?

Generally speaking, there’s two reasons why you might want to use Grid.

  • To run your tests against multiple browsers, multiple versions of browser, and browsers running on different operating systems.
  • To reduce the time it takes for the test suite to complete a test pass.

Grid is used to speed up the execution of a test pass by using multiple machines to run tests in parallel. For example, if you have a suite of 100 tests, but you set up Grid to support 4 different machines (VMs or separate physical machines) to run those tests, your test suite will complete in (roughly) one-fourth the time as it would if you ran your tests sequentially on a single machine. For large test suites, and long-running test suite such as those performing large amounts of data-validation, this can be a significant time-saver. Some test suites can take hours to run. Another reason to boost the time spent running the suite is to shorten the turnaround time for test results after developers check-in code for the AUT. Increasingly software teams practicing Agile software development want test feedback as immediately as possible as opposed to wait overnight for an overnight test pass.

Grid is also used to support running tests against multiple runtime environments, specifically, against different browsers at the same time. For example, a ‘grid’ of virtual machines can be setup with each supporting a different browser that the application to be tested must support. So, machine 1 has Internet Explorer 8, machine 2, Internet Explorer 9, machine 3 the latest Chrome, and machine 4 the latest Firefox. When the test suite is run, Selenium-Grid receives each test-browser combination and assigns each test to run against its required browser.

In addition, one can have a grid of all the same browser, type and version. For instance, one could have a grid of 4 machines each running 3 instances of Firefox 70, allowing for a ‘server-farm’ (in a sense) of available Firefox instances. When the suite runs, each test is passed to Grid which assigns the test to the next available Firefox instance. In this manner one gets test pass where conceivably 12 tests are all running at the same time in parallel, significantly reducing the time required to complete a test pass.

Grid is very flexible. These two examples can be combined to allow multiple instances of each browser type and version. A configuration such as this would provide both, parallel execution for fast test pass completion and support for multiple browser types and versions simultaneously.