Time tracking data can be a useful tool for understanding and highlighting architectural bottlenecks and technical debt, and for generating hourly reports. Keeping track of this kind of data manually requires a lot of discipline to remember, write down and justify the time you work.When work gets hectic, keeping track of hours can quickly become a nuisance. This is where an automatic stopwatch can really save the day!
Keeping track of the type of work is important because not all work may be relevant. As a programmer, the type of work being done would ideally be linked to version control(Git). This would allow us to identify key statistics such as project and branches (and indirectly, issues).
JIRA → Gitlab → ...Waka
I started tracking coding using JIRA's tempo plugin. The way I was using it, I had to manually assign hours to issues that were not linked to version control. Later on we moved to Gitlab, which came with its own project boards. A big improvement; the version control workflow (issues, merge requests) became more closely related to the issues on the board. However, time tracking in Gitlab is surprisingly non-existent. There is no way to get an overview of working hours over a pre-defined period of time. The only option is to add time to your issues. You can filter by certain types of tickets, but this doesn't give you a reliable view of how much time you've worked over a period of time. So for time tracking, Gitlab just isn't good enough...
Wakatime
While searching for an automated time tracking solution (open source or paid), I came across Wakatime. Developers use it like a software Fitbit for coding. It tracks and stores a range of coding habits, which can later be aggregated over a period of time. Among other things, it can measure how much time you spend on a branch, what files you edit and what projects you work on.
A community of IDE plugins allows the most popular IDEs to send heartbeats to a Wakatime backend. These heartbeats can be queued offline, and when you come back online, the heartbeats are sent in batches. This seems like the automated stopwatch I've been looking for! Wakatime is a great solution if you want to easily keep track of your coding time.
Wakapi
Wakapi is an efficient open source Wakatime-compatible server written in Golang, that comes with its own unique set of features. You can choose to host if yourself or use the hosted service. The project is in active development and is a great option for people who want more insight/control over their data or just want to learn more about how Wakatime works.
Wakamonth
Using Wakapi or Wakatime, we can ask the API how much time in hours we spent coding during a month, all of it specified at a project/branch level. Let's meet Wakamonth! Our little tool to generate hourly reports from Wakatime data:
# Checkout README.md at https://github.com/bitstillery/wakamonth
wget -O ~/.wakamonthrc https://raw.githubusercontent.com/bitstillery/wakamonth/main/.wakamonthrc.example
vim ~/.wakamonthrc
npx @bitstillery/wakamonth -y 2024 -m 1 -o stdout report
npx @bitstillery/wakamonth -y 2024 -m 1 -o xlsx report
Please note that the coding time tracked by Wakapi is an estimate and definitely not legally binding, so please keep this in mind when using it for billing reports / invoices.
Setup
You can skip this setup if you like, but for fun, we're going to install Wakapi on a VPS to receive heartsbeats from VSCode and Firefox Wakatime clients. So, what do we need?
- A Linux VPS
- Golang installed
- Postgresql running
The first thing to do is to install Wakapi:
su root
git clone https://github.com/muety/wakapi.git /opt/wakapi
useradd -m -d /home/wakapi --system --user-group wakapi
chown -R wakapi:wakapi /opt/wakapi
chmod -R g+w /opt/wakapi
cd /opt/wakapi
su wakapi
go build -o wakapi
curl -o /etc/wakapi.yml https://raw.githubusercontent.com/muety/wakapi/master/config.default.yml
su -l postgres
createuser --interactive # create wakapi psql user
createdb wakapi # create wakapi psql db
# Test to see if wakapi runs
/opt/wakapi/wakapi -config /etc/wakapi.yml
The config file needs some tweaking:
vim /etc/wakapi.yml
server:
public_url: https://[YOURDOMAIN.TLD]
db:
host: localhost
port: 5432
user: wakapi
name: wakapi
dialect: postgres
security:
password_salt:
See if Wakapi works with our Postgresql connector:
# Test to see if wakapi runs
/opt/wakapi/wakapi -config /etc/wakapi.yml
Set up a Systemd service file for Wakapi to run in the background:
mkdir /run/wakapi
vim /etc/systemd/system/wakapi.service
[Unit]
Description=Wakapi
StartLimitIntervalSec=400
StartLimitBurst=3
Requires=postgresql.service
After=postgresql.service
[Service]
Type=simple
WorkingDirectory=/opt/wakapi
ExecStart=/opt/wakapi/wakapi -config /etc/wakapi.yml
Environment=WAKAPI_DB_HOST=localhost
Environment=WAKAPI_DB_USER=wakapi
Environment=WAKAPI_DB_NAME=wakapi
Environment=WAKAPI_DB_PASSWORD=
Environment=WAKAPI_PASSWORD_SALT=somerandomstring
User=wakapi
Group=wakapi
RuntimeDirectory=wakapi
RuntimeDirectoryMode=755
Restart=on-failure
RestartSec=90
# Security hardening
PrivateTmp=true
PrivateUsers=true
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
PrivateDevices=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
ProtectClock=true
RestrictSUIDSGID=true
ProtectHostname=true
ProtectProc=invisible
[Install]
WantedBy=multi-user.target
Launch Wakapi as a service:
systemctl start wakapi
Set up the Nginx proxy (Lets encrypt is added with certbot --nginx); make sure that YOURDOMAIN.TLD can be reached:
upstream wakapi_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
server_name [YOURDOMAIN.TLD];
access_log /var/log/nginx/[YOURDOMAIN.TLD]_access.log;
error_log /var/log/nginx/[YOURDOMAIN.TLD]_error.log;
location / {
client_max_body_size 50M;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_buffers 256 16k;
proxy_buffer_size 16k;
proxy_read_timeout 600s;
proxy_pass http://wakapi_backend;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/[YOURDOMAIN.TLD]/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/[YOURDOMAIN.TLD]/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = [YOURDOMAIN.TLD]) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name [YOURDOMAIN.TLD];
listen 80;
return 404; # managed by Certbot
}
https://[YOURDOMAIN.TD]/signup # Create an account and copy setup instructions
https://wakapi.[YOURDOMAIN.TLD]/swagger-ui/swagger-ui/index.html # Verify available endpoints
Still here? 🙂 Now that we have a Wakapi account and have made sure that the API is accessible, let's continue by setting up our client-side configuration:
vim ~/.wakatime.cfg
[settings]
api_url = https://[YOURDOMAIN.TLD]/api
api_key =
debug = true
status_bar_coding_activity = true
status_bar_enabled = true
Open VSCode and install the Wakatime extension to send heartbeats to Wakapi. The plugin uses ~/.wakatime.cfg as its configuration file, so you should be good to go. For the Firefox plugin, I use the following configuration:
Whitelist:
http://localhost:9000@@myproject
http://localhost:9001@@myproject
Logging style: only whitelisted sites
Logging type: Entire URL
Hostname:
Api URL: https://[MYDOMAIN.TLD]/api/compat/wakatime/v1
API Key: [WAKAPI_APIKEY]
Note that Wakapi aggregates the statistics once in a while, so you may not be able to see your statistics directly. If you need to debug the client; you can debug heartsbeats using:
tail -f ~/.wakatime/wakatime.log
That's it! By now, you're probably running your own Wakapi instance, and have started collecting Wakatime heartbeats as you code. The data you collect, becomes more valuable over time. A big thank you to Wakatime and Wakapi creator Ferdinand Mütsch for creating Wakapi, but also to the Wakatime community and browser-wakatime authors in particular, who were kind enough to add <<LAST_BRANCH>> support recently!