Firefox tabs analysis with Prometheus and Grafana
Observability in browsing sessions

Summary
In the last post on organizing Firefox tabs, I described how I work and thrive with 100-200 tabs opened, with hundreds more in saved sessions.
I wanted to have some numbers so I wrote a script and I use Prometheus and Grafana to display counts of Firefox tabs on graphs.
Grafana dashboard and numbers
This Grafana dashboard displays the count of Firefox tabs and windows over time.



Script
A cron job executes the script count-tabs.py every minute.
It reads session and tabs information from JSON files under home the user directory.
Then it writes metrics in Prometheus format to sys.stdout.
The cron job redirects the output to a file that is read by the textfile collector in node_exporter.
Prometheus scrapes the metrics and Grafana displays them.
Input:
- Firefox sessionstore-backups file
- TabSessionManager User Saves files
Output:
- Metrics in Prometheus format
#!/usr/bin/python3
# https://alexandre.deverteuil.net/posts/firefox-tabs-analysis/
## root crontab:
# * * * * * /home/alex/Programs/count-tabs.py | sponge /usr/local/lib/node_exporter/firefox-tabs.prom
## `sponge` comes from the moreutils package.
import glob
import json
import os
import os.path
import sys
import lz4.block # sudo dnf install python3-lz4
# https://unix.stackexchange.com/questions/385023/firefox-reading-out-urls-of-opened-tabs-from-the-command-line
def count_tabs_and_windows():
userdir = os.path.expanduser("~alex")
pattern = ".mozilla/firefox*/*.default*/sessionstore-backups/recovery.jsonlz4"
matches = glob.glob(userdir + "/" + pattern)
if len(matches) == 0:
print_metrics(0, 0, 0, 0)
sys.exit()
else:
sessionstore = matches[0]
with open(sessionstore, "rb") as f:
magic = f.read(8)
session_data = json.loads(lz4.block.decompress(f.read()).decode("utf-8"))
# Uncomment to dump the uncompressed JSON in sessions.json.
#with open("sessions.json", "w") as f:
# f.write(json.dumps(jdata))
group_tabs_count = 0
active_tabs_count = 0
inactive_tabs_count = 0
windows_count = 0
for win in session_data["windows"]:
windows_count += 1
for tab in win["tabs"]:
i = int(tab["index"]) - 1
url = tab["entries"][i]["url"]
title = tab["entries"][i]["title"]
if (url.find("grouptab/index.html") > 0 or
url.find("grouptab-personal/index.html") > 0 or
url.find("grouptab%2Findex.html") > 0 or
url.find("grouptab-personal%2Findex.html") > 0
):
group_tabs_count += 1
#print("GROUP", title)
elif url.startswith("moz-extension://"):
inactive_tabs_count += 1
#print("INACTIVE", title, url)
else:
active_tabs_count += 1
#print("ACTIVE", title)
metric_fmt = "firefox_session_open_tabs{{type=\"{type}\"}} {value}"
print("# HELP firefox_session_open_tabs number of open tabs from Firefox")
print("# TYPE firefox_session_open_tabs gauge")
print(metric_fmt.format(type="grouptab", value=group_tabs_count))
print(metric_fmt.format(type="active", value=active_tabs_count))
print(metric_fmt.format(type="inactive", value=inactive_tabs_count))
print("# HELP firefox_session_open_windows number of open Firefox windows")
print("# TYPE firefox_session_open_windows gauge")
print("firefox_session_open_windows", windows_count)
def count_saved_sessions():
userdir = os.path.expanduser("~alex")
pattern = "Downloads/TabSessionManager - Backup/User Save/*.json"
session_files = glob.glob(userdir + "/" + pattern)
sessions = {}
for session_file in session_files:
with open(session_file) as f:
session_data = json.loads(f.read())[0]
name = session_data['name']
if session_data['tag']:
tag = session_data['tag'][0]
else:
tag = "untagged"
group_tabs_count = 0
regular_tabs_count = 0
for window in session_data['windows']:
for tab in session_data['windows'][window]:
url = session_data['windows'][window][tab]['url']
if (url.find("grouptab/index.html") > 0 or
url.find("grouptab-personal/index.html") > 0
):
group_tabs_count += 1
else:
regular_tabs_count += 1
sessions[name] = {
'name': name,
'tag': tag,
'group_tabs_count': group_tabs_count,
'regular_tabs_count': regular_tabs_count
}
print("# HELP firefox_saved_session_tabs number of tabs from Firefox saved session")
print("# TYPE firefox_saved_session_tabs gauge")
metric_fmt = "firefox_saved_session_tabs{{tag=\"{tag}\",session_name=\"{name}\",type=\"{type}\"}} {value}"
for name in sessions:
print(metric_fmt.format(name=name, tag=sessions[name]['tag'], type="grouptab", value=sessions[name]['group_tabs_count']))
print(metric_fmt.format(name=name, tag=sessions[name]['tag'], type="regular", value=sessions[name]['regular_tabs_count']))
count_tabs_and_windows()
count_saved_sessions()
Example metrics output
# HELP firefox_session_open_tabs number of open tabs from Firefox
# TYPE firefox_session_open_tabs gauge
firefox_session_open_tabs{type="grouptab"} 25
firefox_session_open_tabs{type="active"} 22
firefox_session_open_tabs{type="inactive"} 73
# HELP firefox_session_open_windows number of open Firefox windows
# TYPE firefox_session_open_windows gauge
firefox_session_open_windows 6
# HELP firefox_saved_session_tabs number of tabs from Firefox saved session
# TYPE firefox_saved_session_tabs gauge
firefox_saved_session_tabs{tag="client",session_name="xxxxx",type="grouptab"} 3
firefox_saved_session_tabs{tag="client",session_name="xxxxx",type="regular"} 14
firefox_saved_session_tabs{tag="client",session_name="yyyyyyyyyyy",type="grouptab"} 1
firefox_saved_session_tabs{tag="client",session_name="yyyyyyyyyyy",type="regular"} 3
firefox_saved_session_tabs{tag="Grafana",session_name="Grafana: main",type="grouptab"} 1
firefox_saved_session_tabs{tag="Grafana",session_name="Grafana: main",type="regular"} 6