The Automatic Rule-Based Time Tracker

A while ago, I thought: „I seem to be less productive than I used to be. Why might that be? Do I spend more and more time with e-Mails? Or is it something else?“ I couldn’t tell, so I needed a time tracking tool. There are a few of those for Linux that allow you, while working, specify what you are doing and then generate statistics about it. This approach has a few disadvantages:

  • It is distracting (and I want to increase productivity).
  • You can only record one kind of information. So either, you record what you are doing (writing e-Mails, surfing the web, programming), or why you are doing it (work, study, leisure). You can not easily record both.
  • If you are lazy and don’t keep updating it, the statistics will be useless.
  • You won’t be able to catch a little thing like quickly answering an e-Mail or looking for the weather report.

So I created arbtt, the Automatic Rule-Based Time Tracker. It comes with a background program that is started with your desktop session and will, each minute, record what windows are open, which one is active, what their titles and corresponding programs are. It also checks how long the user has been idle. No interaction required, no distraction possible. This information is stored in a log file. A separate tool allows the user to investigate this data. It is called rule-based because the mapping from the raw data to sensible “tags” that give information about the time sample is specified by a simple, but hopefully sufficiently powerful language.

A simple example for a rule that indicates the currently used program would be:

tag Program:$current.program,

The prefix “Program:” is a category. Arbtt will ensure that for each time sample and category, at most one tag is specified. A more complex rule is

current window $title =~ m!(?:~|home/jojo)/projekte/(?:programming/(?:haskell/)?)?([^/)]*)!
==> tag Project:$1,

which makes use of the fact that both GVim and gnome-terminal display the full path to the currently edited file resp. to the working directory in the window title. This rule will track all my projects separately, and even automatically pick up new projects when they appear! You can see more rules in the example file.

The statistics program then allows you to query the tags with some options:

Usage: arbtt-stats [OPTIONS...]
  -h, -?       --help                 show this help
  -V           --version              show the version number
  -x TAG       --exclude=TAG          ignore samples containing this tag
  -o TAG       --only=TAG             only consider samples containing this tag
               --also-inactive        include samples with the tag "inactive"
  -m PERC      --min-percentage=PERC  do not show tags with a percentage lower than PERC% (default: 1)
  -i           --information          show general statistics about the data
  -t           --total-time           show total time for each tag
  -c CATEGORY  --category=CATEGORY    show statistics about category CATEGORY

For example, if I want to know what folders I have open while using evolution, I can run arbtt-stats -o Program:evolution -c Evo-Folder -m 3 to see this:

Statistics for category Evo-Folder
|| Time Percentage
Evo-Folder:Eingang || 22h20m00s 17.0%
Evo-Folder:Bekannte || 16h40m00s 12.7%
Evo-Folder:d-haskell || 9h40m00s 7.4%
Evo-Folder:Itomig || 8h20m00s 6.4%
Evo-Folder:Verschickt || 7h40m00s 5.8%
Evo-Folder:pkg-fso-maint || 7h00m00s 5.3%
Evo-Folder:Bugs || 6h00m00s 4.6%
Evo-Folder:planet debian || 4h00m00s 3.0%
(60 entries omitted) || 4h49m00s 36.7%
(unmatched time) || 8m00s 1.0%

One big advantage of this approach is that you do not need to know in advance what queries you are interested in. Since the rules are applied when you are evaluating your data, and not when recording it, you can add more tags and forgotten special cases later.

At this point, a big warning is due: This program will record a lot of very sensitive information about you. Be aware of this before you start using arbtt, and make sure you protect your data. You can get rid of all logs by deleting ~/.arbtt/capture.log.

I have published arbtt on hackage. If you have cabal-install installed, you can install it with cabal install arbtt. See the README file for more information about setting it up. Depending on the feedback I get I will also consider packaging it for Debian.

If you want to contribute, you are very welcome. The code is available at the darcs repository http://darcs.nomeata.de/arbtt/ (DarcsWeb). See the README for some ideas what to implement and feel free to come up with new ideas.


Great idea, Joachim! Sounds like exactly what I need.

Yes, I'd very much like to see this packaged for Debian. I'm unfamiliar with Haskell, and the current installation process seems complicated and scary, or at least error-prone.
#1 Chip Richards am 2009-09-13T11:34:28+00:00
Have you checked out MyTime?

#2 Anonymous am 2009-09-13T13:42:01+00:00
No, I wasn’t aware of it. It seems to build on a similar idea. Judging from the screenshots, it only checks the use application, but OTOH it seem to aim for greater integration. Thanks for the pointer.
#3 Joachim Breitner (Homepage) am 2009-09-13T14:41:33+00:00
That's neat you did it in Haskell. I've done something very similar. What I found was needed to make it useful:
* Name mapper, you need to group a bunch of poorly named misnamed windows together
* Realtime updates
* Graphs
* Idle detection, I have hacked up xscreensaver just so it can detect when you have gone idle, then my window recorder can record that idle status has changed. Then when you count time you don't include the huge blocks where you went to the bathroom.

My software is in perl and C at http://churchturing.org/ , I would suggest that idle-watcher is the most useful to you.
#4 Church Turing (Homepage) am 2009-09-13T14:57:15+00:00
Hi. Actually, 50% of your ideas are already implemented: The syntax allows for aliases (see the example configuration file), and arbtt-capture uses the XScreenSaver extension to store the idle time.

I had some Graphs locally already, but nothing releaseable yet. Not sure about realtime updates, they make little sense with textual output. With a GUI, maybe.
#5 Joachim Breitner (Homepage) am 2009-09-13T15:31:31+00:00
Just these days, there was a thread on the suckless mailing list about this topic. You may want to have a look:


I'll post a link to your blog entry there.
#6 Meillo (Homepage) am 2009-09-13T15:08:40+00:00

I'm very interested in such an application as well. I currently use hamster-applet but would welcome an application where I don't have to change my status manually. I'm not used to haskell so I'd like to see a package as well. Would be nice to see some reporting tools in a graphical interface. I wonder whether MyTime has the same flexibility as yours...
#7 Raphael Hertzog (Homepage) am 2009-09-14T07:25:52+00:00
It doesn't but its still quite nice. Tho I'd prefer arbtt, looks powerful ;]
#8 XANi am 2009-09-14T08:37:49+00:00

Thanks for arbtt, I was a user of http://www.rescuetime.com/ and now I can replace it.

However, when I run

$ runghc Setup.hs configure

in arbtt-0.1.1 directory, I get

Setup.hs: arbtt.cabal:30: Parse of field 'build-depends' failed:

In that line, there is a dependency like:

base == 4.*

and I think either my cabal is old or this is a syntax error. When I changed these *-ed rules like to

base >= 4.0

it successfully configured, though reporting unmet dependencies.

I'm using Debian Squeeze/Unstable and tested the file also on another machine. GHC version is (apt provided) 6.8.2.

#9 Emre (Homepage) am 2009-09-14T11:05:41+00:00
Hi Emre,

I’m developing with ghc6-6.10.4. I guess that error comes from an older cabal version.

About the unmet dependencies: I suggest you use cabal-install, as it will handle these for you. See http://haskell.org/haskellwiki/Cabal-Install for more details.
#10 Joachim Breitner (Homepage) am 2009-09-14T11:48:14+00:00

thanks! arbtt will be very useful to me. I have started using it already. I had to fix a few things though:

1. getInputFocus returns a window ID that is off-by-one on my system. No idea why. I had to change (w == fwin) to (w == fwin-1) to get anything to ever be matched.

2. Several of the formatSeconds call sites had time/100 instead of time/1000. So some of the calculated times were off.

Thanks again! Great stuff!
#11 CJ van den Berg am 2009-09-14T18:31:27+00:00
Glad you like it. I just noticed the 100 vs. 1000 issue an hour ago, updated arbtt is uploaded to hackage.

Not sure about the other issue. I sent a mail to the X11 maintainer about it.
#12 Joachim Breitner (Homepage) am 2009-09-14T18:47:05+00:00
Can someone post a few lines of the log output? I would like to port this app to windows, but as far as I see I can't easily port the window capturing with haskell (just tries all day, most of it with understanding haskell first...), so I would like to just port the statistics part and do the capturing with autohotkey ...

#13 Jan Schulz (Homepage) am 2009-09-14T20:44:44+00:00
It consitutes of singe lines like this:
$ tail -n 1 ~/.arbtt/capture.log
"TimeLogEntry {tlTime = 2009-09-14 22:05:42.129558 UTC, tlRate = 60000, tlData = CaptureData {cWindows = [(False,\"Linkes Kanten-Panel, ausgedehnt\",\"gnome-panel\"),(False,\"Michael Daub\",\"pidgin\"),(False,\"\",\"galeon\"),(True,\"jojo@kirk: /home/jojo\",\"gnome-terminal\"),(False,\"Blog-Comments (89 insgesamt) - Evolution\",\"evolution\"),(False,\"XChat: nomeata @ FreeNode / #pidgin (+tncL)\",\"xchat\"),(False,\"Thomas Breitner\",\"pidgin\"),(False,\"Buddy-Liste\",\"pidgin\")], cLastActivity = 46}}"

You won’t have much fun parsing that without haskell. But I’m planning to switch to a more extensible and faster parseable format.
#14 Joachim Breitner (Homepage) am 2009-09-14T22:07:11+00:00
Thanks! Looks almost like JSON data.

Anyway: Parsing will be done in haskell (should compile under windows), but writing it down will be in something else. Or someone tells me how to get the data in haskell, I gave up :-/ .

If you want to do some more fun: try to get the URL from the browsers and write that instead of the window title...
#15 Jan Schulz (Homepage) am 2009-09-14T22:15:36+00:00
Right, I should have said „have fun implementing the quoting rules :-)“.

Anyways, my idea for a better syntax would not also involve easier parsing, but also extensibility, e.g. for reading the url from a browser. (But how do you do that easily?)
#16 Joachim Breitner (Homepage) am 2009-09-15T07:23:32+00:00
Re URLs: on windows it seems that you can get window controles, which can then be asked for the text. See for example here:

FF on linux seems also to have some way to get at the URL: http://support.mozilla.com/tiki-view_forum_thread.php?locale=de&comments_parentId=189769&forumId=1
#17 Jan Schulz (Homepage) am 2009-09-15T14:33:11+00:00
Can you guys give me a feedback about http://colorhat.com ?
#18 Tobi (Homepage) am 2009-09-15T09:37:26+00:00
Looks really nice + useful, it doesn't seem to capture my emacs sessions though. I'm getting log entries like (False,\"\",\"\") , which I suppose might be it failing to find the window titles.
#19 yaxu (Homepage) am 2009-09-15T13:07:47+00:00
Can you look at the output of "xwininfo -root -children" for suspicious differences between the emacs and other windows? (And please don’t paste it here, rather send me a mail if you have something lengthy, thanks).
#20 Joachim Breitner (Homepage) am 2009-09-15T14:38:20+00:00
I'm having trouble similar to yaxu's, except rather than it being a single application, I'm getting (False,\"\",\"\") for all of my applications except x-nautilus-desktop and my various gnome-panels. Any ideas?
#21 cpennington am 2009-09-16T14:18:50+00:00
Hmm. I am running xmonad, that might make a difference... maybe with other window managers, not all application windows are children of the root window.
#22 Joachim Breitner (Homepage) am 2009-09-16T14:22:33+00:00
Indeed, my code only worked with xmonad. metacity has a different reparenting behaviour. I changed to the code to read the list of top-level-windows from _NET_CLIENT_LIST, this should work better.

Please try again with 0.1.3 from hackage.
#23 Joachim Breitner (Homepage) am 2009-09-16T14:48:54+00:00
Worked like a charm. Thanks
#24 cpennington am 2009-09-16T15:04:51+00:00
Hi, I am also having troubles. My firefox does not show up as active in the logs, and also has no window title.

I am using awesome as WM, Firefox is actually Iceweasel from Debian with the Vimperator plugin, and have tried with arbtt 0.1.3.

A sample entry from capture.log:
"TimeLogEntry {tlTime = 2009-09-25 17:14:25.503754 UTC, tlRate = 60000, tlData = CaptureData {cWindows = [(False,\"vimperator-www.joachim-breitner.de.tmp + (/tmp) - GVIM\",\"gvim\"),(False,\"\",\"Navigator\"),...], cLastActivity = 8330}}"

xwininfo -root -children says:
0x1000046 "The Automatic Rule-Based Time Tracker - nomeata’s mind shares - Vimperator": ("Navigator" "Iceweasel") 502x343+2456+14 +2456+14

So the title information is missing and - what is really bad - Firefox is never logged as active, and therefore can't be tagged.
#25 yogan am 2009-09-25T17:30:58+00:00
Does awesome support the EWMH specification, i.e. _NET_CLIENT_LIST? Does "wmctrl -l" give sensible information?

If not, can you try if 0.1.2 works better for you? If so, I should implement a fallback.
#26 Joachim Breitner (Homepage) am 2009-09-25T17:59:02+00:00
I don't have a clue about X11 programming, but just grepping through awesome's source for _NET_CLIENT_LIST gives me this:

ewmh.c: _NET_CLIENT_LIST, WINDOW, 32, n, wins);
ewmh.c: _NET_CLIENT_LIST_STACKING, WINDOW, 32, n, wins);

So my guess would be, "yes, it does".

wmctrl -l seems to list my Browser just fine:
0x01000046 1 macbork The Automatic Rule-Based Time Tracker - nomeata’s mind shares - Vimperator

I just tried with arbtt 0.1.12 - this did not change anything. However, I noticed that sometimes the Window title is found - just not for this very site. Might the Unicode ’ in the title be a problem? Activity is still always false, regardless of the window title being empty or not.

Btw., there is no way to get notified of new comments here, is it?
#27 yogan am 2009-09-26T10:03:43+00:00
This did not come over clearly: the window titles are also in the data captured with 0.1.13.
#28 yogan am 2009-09-26T10:06:45+00:00
Unicode might be the cause. Can you verify that assumption?

About the active window: Probably same problem as CJ’s, a possible fix is known and I’ll try to implement it soon.

Subscription to comments used to be possible, but Serendipity did not support Double-Opt-In, which is a legal requirement in Germany. I could see if I can enable per-post-commets-RSS-feeds.
#29 Joachim Breitner (Homepage) am 2009-09-26T20:08:54+00:00
Ok, there should be a comments-RSS-Feed below the entry. I hope it works.
#30 Joachim Breitner (Homepage) am 2009-09-26T20:14:04+00:00
Thanks for the RSS feeds, it works fine.

Just a little thing: when I add the feed to Google Reader, it does not show the title, though it is set. Maybe it is also due to the unicode character in the title, but you specified utf-8 as encoding in the xml header, so I think this should be valid. At least feedvalidator.org does not complain:

This might just be Google Reader's fault.
#31 yogan am 2009-09-27T13:22:36+00:00
The title bug is indeed caused by unicode characters in the window title.

Try with those:
#32 yogan am 2009-09-27T13:29:18+00:00
Ok, thanks. The cause is in the X11 bindings, see http://www.haskell.org/pipermail/xmonad/2007-July/001363.html. I’ll try to cook up a patch for X11.
#33 Joachim Breitner (Homepage) am 2009-09-28T17:11:45+00:00
Done, you can check its inclusion state at http://darcswatch.nomeata.de/repo_http:__darcs.haskell.org_X11.html
#34 Joachim Breitner (Homepage) am 2009-09-28T17:53:40+00:00
Hi again,

I’ve looked into the fwin-1 issue a little and it seems to me that the problem is that getInputFocus does not always return a top level window. Depending on the toolkit of the application it could return some child window or another. Using queryTree to walk up the tree to the top most parent of the window returned from getInputFocus does the trick here.
#35 CJ van den Berg am 2009-09-25T21:13:58+00:00
Thanks for your investigation, I got your e-Mails. I will implement that as soon as possible, although I’ll be traveling next week. Darcs patches always welcome, of course.
#36 Joachim Breitner (Homepage) am 2009-09-26T20:06:11+00:00
Ok, version 0.1.4 release. Please try everyone :-)
#37 Joachim Breitner (Homepage) am 2009-09-28T18:20:41+00:00
It captures my Firefox window now. Nice, thanks!
#38 yogan am 2009-09-29T15:31:05+00:00
As I have used arbtt for a while now, I noticed it would be very nice to be able to specify a time range in arbtt-stats: something like --last 3h to see what I did the last 3 hours, or --today to only see data from today.

I also wanted to have shorter samples, so I just changed the sampleRate in capture-main.hs from 60 to 20. Adding parameter for this would be more convenient, of course.

Too bad I don't know Haskell, so I see no chance implementing this myself.
#39 yogan am 2009-09-30T11:51:14+00:00
I have thought about time-based statistics as well. But my shot would rather be that you can use time stuff in the rules, and then introduce for example tags "TimeOfDay:Morning, TimeOfDay:Night, TimeOfDay:LunchBreak", or "DayOfWeek:1" as you like, and later use the generic framework to evaluate these. It would be more powerful, but allows less ad-hoc statistics. Maybe both ought to be implemented.

Making the sample rates configurable would mean that arbtt-capture would read configuration. Currently it does not, which is a nice feature. But then, I see that your point is valid, and I guess it should be possible. (Luckily I planned ahead enough so that the sample rate is stored with the data, so that changing the rate does not break the old data.)
#40 Joachim Breitner (Homepage) am 2009-10-02T12:41:44+00:00
It would be good enough (for me) if arbtt-capture took a command line parameter to set the poll rate. No need to have a config file.
#41 CJ van den Berg am 2009-10-02T14:32:11+00:00
Thx CJ, good idea.

About --last 3h or the like: A more general approach would be to add time related functions to the conditions in the categorizer syntax, and then introduce a new flag "--filter <cond>" where arbitrary complex conditions can be specified. This enables time queries, but also any other stuff that you can do in categorize.cfg itself.
#42 Joachim Breitner (Homepage) am 2009-10-02T14:41:04+00:00
Just uploaded version 0.2.0. You can now add tags based on the time-of-day and the age of the samples in categorize.cfg.

You can now also put fully featured conditions on the statistics command line. To have the desired effect of your proposed "--last 3h", use
arbtt-stats --filter '$sampleage < 1:00'

More stuff could be added, such as day-of-week and more featureful time arithmetic, but that can come later.
#43 Joachim Breitner (Homepage) am 2009-10-03T17:53:30+00:00
I've just tried the new version, it's great!
#44 yogan am 2009-10-03T19:42:36+00:00
Just installed on Ubuntu 12.04 using
cabal install arbtt
and works fine.
To meet dependencies I had to install:
For tracking Firefox activity I'll try to use customize_titlebar_v2 plugin.
Have no opinion about features but it looks great!! Thanks :-)
#45 linux user am 2013-03-03T16:06:12+00:00
Launching arbtt-stats, got always "arbtt-stats: Unknown variable desktop"

Can't find out why
Any help welcome :)
#46 dbourrion am 2014-05-30T09:32:16+00:00
It seems you are using an older version of arbtt. The $desktop variable was added in 0.8.
#47 Joachim Breitner (Homepage) am 2014-05-30T10:41:07+00:00

Have something to say? You can post a comment by sending an e-Mail to me at <mail@joachim-breitner.de>, and I will include it here.