Other versions are listed on the published site
Custom Database Class
RobotFramework Dashboard supports custom database implementations. This page explains how to create your own database class, which interfaces must be implemented, and how to use it with the dashboard command line interface.
The Dashboard processes Robot Framework output files and stores them in a database. By default it uses a SQLite3 database, but you are free to implement any backend you want—MySQL, PostgreSQL, MongoDB, flat-file systems, cloud storage, or fully custom solutions.
Example Implementations
The project includes several reference implementations. These demonstrate how to structure your own custom database class and how to correctly implement all required methods.
Available examples:
- abstractdb.py: base abstract class to extend
- sqlite3.py: reference copy of the built-in SQLite implementation (
robotframework_dashboard/database.py), the best starting point for your own class - mysql.py: example MySQL implementation
These files define the required structure and show how each method should behave. If you create your own custom database implementation, you are encouraged to submit it via pull request or github issue so it can be added to the example folder to help others.
Basic Custom Class Structure
To implement your own database class, extend the AbstractDatabaseProcessor from the example folder.
A minimal structure looks like this:
bash
from robotframework_dashboard.abstractdb import AbstractDatabaseProcessor
class DatabaseProcessor(AbstractDatabaseProcessor):
# implement all abstract methods hereYou may name the Python file anything you want. The only rule is that your class must be named:
DatabaseProcessor
This is the class name the dashboard dynamically loads at runtime.
Custom Class Requirements
Your custom database class must implement the following methods:
__init__(self, database_path: Path)
- Responsible for initial setup.
- Should create the database file (if applicable) and initialize tables or structures.
database_pathis provided by the dashboard and points to the configured database file or directory.
open_database(self)
- Opens a database connection.
- Must set a property such as
self.connectionfor later use.
close_database(self)
- Closes the connection.
- Must be implemented to ensure resources are cleaned up properly.
run_start_exists(self, run_start: str) -> bool
- Checks whether a run with the given
run_starttimestamp already exists. - Returning
Falsealways is acceptable but inefficient—duplicate processing will occur.
insert_output_data(self, output_data: dict, tags: list, run_alias: str, path: Path, project_version: str, custom_filters: str = "", timezone: str = "")
This method handles the actual insertion of all run-related data.
You must process:
output_data— containsruns,suites,tests,keywordsandexceptions(lists of tuples, see the examples for the column order)tags— list of tags associated with the runrun_alias— a human-friendly alias chosen by the user or systempath— path tooutput.xmlproject_version— version string associated with this run (from--projectversionorversion_tags), may beNonecustom_filters— the--customfiltersstring (key=value:key2=value2), stored on the runtimezone— UTC offset such as+02:00to append to everyrun_start(empty when not known)
You can inspect the example implementations for the exact structure of output_data and how each record is inserted.
get_data(self) -> dict
Must return all data in this dictionary format:
bash
{
"runs": [
{
"run_start": "2024-10-13 22:33:19",
"full_name": "Robotframework-Dashboard",
"name": "Robotframework-Dashboard",
"total": 6,
"passed": 4,
"failed": 1,
"skipped": 1,
"elapsed_s": "6.313",
"start_time": "2024-10-13 22:33:19.673821",
"tags": "",
"run_alias": "output-20241013-223319",
"path": "results/output-20241013-223319.xml",
"metadata": "[]",
"project_version": "1.2.1"
},
{...etc}
],
"suites": [
{
"run_start": "2024-10-13 22:33:19",
"full_name": "Robotframework-Dashboard.01 Login",
"name": "01 Login",
"total": 2,
"passed": 2,
"failed": 0,
"skipped": 0,
"elapsed_s": "1.21",
"start_time": "2024-10-13 22:33:20.120000",
"run_alias": "output-20241013-223319",
"id": "s1-s1"
},
{...etc}
],
"tests": [
{
"run_start": "2024-10-13 22:33:19",
"full_name": "Robotframework-Dashboard.01 Login.Valid Login",
"name": "Valid Login",
"passed": 1,
"failed": 0,
"skipped": 0,
"elapsed_s": "0.55",
"start_time": "2024-10-13 22:33:20.125000",
"message": "",
"tags": "[\"sanity\"]",
"run_alias": "output-20241013-223319",
"id": "s1-s1-t1",
"attempts": ""
},
{...etc}
],
"keywords": [
{
"run_start": "2024-10-13 22:33:19",
"name": "Open Browser",
"passed": 6,
"failed": 0,
"skipped": 0,
"times_run": "6",
"total_time_s": "2.100",
"average_time_s": "0.350",
"min_time_s": "0.200",
"max_time_s": "0.500",
"run_alias": "output-20241013-223319",
"owner": "SeleniumLibrary"
},
{...etc}
],
"exceptions": [
{
"run_start": "2024-10-13 22:33:19",
"message": "TimeoutError: element not visible",
"amount": 3,
"run_alias": "output-20241013-223319"
},
{...etc}
]
}Each type must be a list of dictionaries matching what RobotDashboard expects. The optional attempts key on tests holds the JSON-encoded rerun history of a merged --rerunfailed output (see Reruns); leave it empty for tests that ran once. Every row also carries a run_name (the run's name, deduplicated) and the run's run_alias; run_start includes the timezone offset. The built-in class adds these while reading, see get_data in the examples.
list_runs(self)
- Prints all runs in the database with identifiable information.
- This is required for CLI features like:bash
robotdashboard --database list - You may leave this function empty if you do not need command-line listing, but it must exist.
remove_runs(self, remove_runs: list)
remove_runs may contain any of the following:
index=<n>— remove by index (supports ranges with:and lists with;)run_start=<timestamp>— remove by exact run_start timestampalias=<alias>— remove by run aliastag=<tag>— remove all runs matching the given taglimit=<n>— keep only the N most recent runs per project, removing all older ones (a project is a run name and everyproject_run tag;limit=<n>;tag=<tag>scopes it to runs with that tag)age=<range>— remove runs older than e.g.30d(-30dfor newer)
The method returns the console output as a string. When --logremoved is used, RobotDashboard sets log_removed_path and log_removed_types on your instance after construction; the examples write the removed rows to that JSONL file inside the delete transaction.
You must correctly interpret and remove runs accordingly. If you only want to support removing based on run_start or index you could only implement those usages.
(Optional) vacuum_database(self)
Called after run removal when the --novacuum flag is not set.
In the default SQLite implementation, this runs VACUUM to reclaim disk space after deletions.
If your database backend does not need compaction, you can safely omit this method or implement it as a no-op. Make sure you then provide the --novacuum flag when running the dashboard to avoid errors.
(Optional) update_output_path(self, log_path: str)
This is only required when using:
- The Dashboard Server, and
uselogs=Trueoption
In this approach, you store log.html and related files on the server, and this method updates the stored path.
If you do not use server-side log storage, you can safely omit this method.
_get_runs(self) -> tuple
Returns five parallel lists — run_starts, names, aliases, tags, custom_filters — for all runs, oldest first. RobotDashboard uses it for the server's /get-outputs endpoint and for remove_runs.
(Optional) _get_run_paths(self) -> dict
Returns a dict mapping run_start to path for all runs currently in the database, e.g.:
python
{
"2024-10-13 22:33:19.673821": "robot_logs/log-20241013-223319.html",
...
}This is used by the server's /remove-outputs endpoint to automatically delete the corresponding log.html file from the robot_logs folder whenever an output is removed.
If not implemented, the default returns {} and no log files will be automatically cleaned up on output removal. Only relevant when using the Dashboard Server with uselogs=True.
Important Notes
Do not use relative imports.
Custom classes run dynamically and relative paths will fail.Class name must be exactly
DatabaseProcessor.Your Python file name can be anything.
Look at the examples before implementing your own:
- abstractdb.py: base abstract class to extend
- sqlite3.py: reference copy of the built-in SQLite implementation (
robotframework_dashboard/database.py), the best starting point for your own class - mysql.py: example MySQL implementation