klog
klog is a plain-text file format and a command line tool for time tracking.
It was created by Jan Heuermann, and you may use it under the terms of the MIT license.
Introduction
The idea behind klog is to store time-tracking data in plain text files in a simple and human-readable format. The notation is similar to how you would write down the information into a physical notebook using pen and paper. Manipulating your data is as easy as opening the file in a text editor and making changes to the copy. By using the klog command line tool you can search, evaluate and manipulate your data from the terminal.
Let’s say you started a new job and want to use klog for tracking work times.
For that, you create a file worktimes.klg
with
the following content:
2018-03-24
First day at my new job
8:30 - 17:00
-45m Lunch break
This means: at the 24th of March 2018 you came to the office at 8:30 in the morning and went home at 17:00 in the afternoon. Somewhere throughout the day there was a 45-minute lunch break. That effectively results in a total time of 7 hours and 45 minutes for that day.
As you see, klog files almost read like a lightweight journal. It supports various notations for tracking times, which are convenient to write and intuitive to understand. You can capture short summaries about your activities along the time data, which may help you later on to make sense of what you did back in the day. And by using tags you are able to run fine-granular evaluations on your data.
With the klog command line tool you can interact with your files. For instance, you could evaluate the resulting total time with the following command:
klog total worktimes.klg
Total: 7h45m
(In 1 record)
Or you add an extra 30 minutes:
klog track '30m' worktimes.klg
This appends a new entry to the record in the file.
Get klog
The klog command line tool is available under the terms of the MIT license.
On you can choose one of the following ways to install it on your computer:
- Download the latest version and unzip
-
Make MacOS “Gatekeeper” trust the executable:
- Either right-click on the binary in the Finder, and select “Open“
- Or remove the “quarantine” flag from the binary via the CLI:
xattr -d com.apple.quarantine klog
- Copy to path, e.g.
mv klog /usr/local/bin/klog
(might require sudo)
The following package manager submissions are independent projects, which are owned and maintained by other users of klog.
With the latest Go toolchain set up, run:
go install github.com/jotaen/klog@latest
You can specify the desired install path by prepending e.g. GOBIN=/usr/local/bin
- Download the latest version and unzip
- Copy to path, e.g.
mv klog /usr/local/bin/klog
(might require sudo)
The following package manager submissions are independent projects, which are owned and maintained by other users of klog.
- Arch / AUR (binary) (maintained by @kirasok)
- Arch / AUR (from sources) (maintained by @alerque)
- Nix (maintained by @blinry)
With the latest Go toolchain set up, run:
go install github.com/jotaen/klog@latest
You can specify the desired install path by prepending e.g. GOBIN=/usr/local/bin
- Download the latest version and unzip
- Copy to path, e.g. to
C:\Windows\System32
(might require admin privileges)
If you use the Windows Subsystem for Linux (WSL), you can install the Linux binary.
In order to not miss any updates you can either subscribe to the release notifications on Github (at the top right: “Watch” → “Custom” → “Releases”), or you occasionally check by running klog version
.
File Format
Time-tracking data in klog is stored in .klg
files in a plain-text format.
The sections below provide an overview of the data structures and notations of the klog file format. For more detail, please refer to the formal specification.
Records
Records are the basic data structure in klog. They hold time-tracking information for a specific calendar date. Records appear as “blocks of text” in the file, with one (or multiple) blank lines between each other.
2018-03-24
First day at my new job
8:30 - 17:00
-45m Lunch break
2018-03-25
8h15m
2018-03-26
More onboarding. Also started
to work on my first small project!
8:30 - 11:15
11:15 - 12:20 Meeting with Sarah
4h
The computed total time of a record is the sum of all its entries. Every record consists of three parts:
-
The date, formatted either
YYYY-MM-DD
orYYYY/MM/DD
. - Optionally, one or more lines of summary text.
- Any number of time entries.
Records can appear in any order in the file, so they don’t have to be sorted by date, for example. There might be multiple records for the same date in one file.
Entries
Entries are the actual time values that you track. Each entry represents an amount of time that you spent on something. Entries appear one per line and are indented by one level. They start with the time value and can optionally be followed by a summary text.
2019-07-22
1h
8:00 - 9:00
Both entries in this example are worth 1 hour each, resulting in a total time of 2 hours for that day.
There are three types of entries:
-
Durations, for example
3h
,12h48m
or55m
. This can also be a negative value, e.g.-15m
, in which case it will be deducted from the total time. -
Time ranges, i.e. the period between two points in time.
For example:
12:32 - 17:20
or8:45am - 1:30pm
. -
An open-ended time range where the end time is not determined yet.
For example:
8:39 - ?
. (This is a special type which is explained further below.)
There can be any number of entries per record – including none. The order of entries can be arbitrary and has no influence on the result. You can also freely mix the different types of entries.
The indentation is either 1 tab or 2–4 spaces. This must be consistent within the record; it may differ between records, though.
Summaries
The purpose of summaries is to capture arbitrary information alongside the data for future reference. Writing summaries is optional.
2020-02-18
This record demonstrates various
ways of formatting summaries:
5h A short summary.
-120m This is a longer summary, which
is continued on the next line.
14:00 - ?
Summaries can also start on the
subsequent line.
Summaries can appear:
- Underneath the date, in which case they are supposed to refer to the entire record.
- Behind entries, in which case they are supposed to only refer to that specific entry. They either start on the same line (after the time value, seperated by a space), or on the next line. All subsequent lines must be indented twice.
Summaries can have multiple lines of text, but they cannot contain any blank lines.
Tagging / categorising
Summaries can contain #tags
that allow fine-granular
filtering of the data.
Tags can appear both in the record summary and in entry summaries,
and there can be as many tags as you want.
2019-07-22
Did some #sports!
2h #Badminton session (at the #gym)
18:00 - 19:00 Went out for a #run
A tag can contain letters, digits, underscores, or hyphens.
The tag name is case-insensitive, so #work
is the
same as #Work
or #WORK
.
Tag values
Optionally, a tag can have an additional value assigned to it,
which follows the tag name after a =
sign.
2019-07-23
8:30 - 10:30 Layout draft #project=478
10:30 - ? Phone #call="Liz Jones"
The tag value extends the tag, so #project=478
still behaves like #project
does.
In contrast to the tag name, the value of the tag is case-sensitive, so #ticket=ABC
is different
from #ticket=abc
.
A tag value can contain the same characters as tag names (letters, digits, underscores, or hyphens).
Except if the value is wrapped into quotes ("
or '
),
in which case it can contain any character.
Matching
If a tag appears in the overall record summary, then the tag implicitly applies to all entries of that record. So when filtering for a such a tag, then all entries of that record will match.
If a tag appears in an entry summary, then it only applies to that specific entry. In this case, only that particular entry will match when filtering.
2019-07-24
Miscellaneous #chores around the house
1h Mowed the lawn
45m Cleaned the #windows
In the above example, the total time for #windows
would be 45m
,
and the total time for #chores
would be 1h45m
.
(You can basically imagine as if the #chores
tag was spelled out for every entry.)
Note that the tag matching rules apply in the same way for entries with negative durations. So if you want to deduct time from another tagged entry, you have to repeat the same tags on the negative entry.
2019-07-23
9:15 - 12:45 Worked on new #website
-30m Call from Mom... (#website)
An alternative would be to move the #website
tag to the overall record summary.
Open-ended time ranges
In case you just begin an activity (without knowing when it will end) you can already log it as an open-ended time range.
2019-07-26
Started to read my new book
16:30 - ?
Open-ended time ranges are denoted by replacing the end time with a question mark, otherwise they work the same as normal entries. Note that there can only be one open-ended range per record, and it doesn’t count towards the total time as long as it’s open.
--now
flag (or short: -n
).
Should-total
For some use-cases it’s helpful to specify a certain overall time goal that you want to achieve. This is called “should-total” and appears after the date of the record. It is a duration value, followed by an exclamation mark and wrapped in parentheses. For example, let’s say you are supposed to work 7½ hours per day:
2019-07-26 (7h30m!)
8:00 - 16:00 Work
-45m Lunch break
When evaluating the record, you can calculate the difference between should-total and actual total time in order to see whether you have reached your designated goal. A should-total value always applies to the entire record with all its entries.
--diff
flag (or short: -d
)
to display the difference between should-total and actual total time.
When using klog today --diff --now
, it also
forecasts the end-time at which your time goal will be reached.
Day shifting
Sometimes you start an activity in the evening and end it after
midnight, just so that start and end time don’t belong to the
same calendar date. For this case it is possible to “shift over”
a time to the previous or to the next day by adding the
<
prefix, or the >
suffix, respectively.
2019-07-26
<23:30 - 8:00 Worked a night shift
22:30 - 1:45> Watched some movies
When filtering records, keep in mind that these entries are still
associated with the date they are recorded under, so the total time
for the above date 2019-07-26
is 11h45m
.
(If there are records for the adjacent days, their total time won’t be affected.)
FAQ
Where do I store my .klg files?
klog is not opinionated about the way you structure your data across files and folders, so you are completely free where you put your files, or how you name them. For example, it can be useful to have separate files, e.g. one for each month, or for different kinds of activities. That allows you to come up with a structure that meets your personal needs.
Can I also put other text into my .klg
files?
The file format is strict, so a .klg
file can
only contain data according to the specified rules.
Text can be captured as summaries inside of records, but there can’t be free
text next to records.
Is it possible to use to-the-second precision, like 1h10m30s
or 8:23:49
?
No, this is not supported. The reason is that it would effectively prohibit mixing values with and without seconds, which leads to a lot of hassle. Keep in mind, klog is for tracking time of activities, it’s not a stopwatch.
Can I capture timezone information?
No. In case you are affected by a timezone change or a switch to daylight saving time you need to account for that manually. Realistically, this doesn’t happen all too often anyway, so it’s a tradeoff for simplicity to omit the timezone information altogether.
What happens when I track more than 24 hours per day?
klog generally doesn’t impose too many restrictions. Examples of things that are allowed:
- Tracking more than 24h per day
- Tracking only negative durations
- Overlapping time ranges
- Negative should-total values
There might be valid reasons for such use cases, so klog deliberately is not stricter than necessary. What the command line tool does do, however, is to raise warnings about certain potential issues, for example when it detects an open-ended time range at a past date.
Command line tool
The command line tool allows you to display and search records in files, pretty print and evaluate them, and apply some basic manipulations. The following sections provide a high-level overview, and demonstrate the basic functionality.
In order to learn about all flags and options,
please run klog with the --help
flag (which is
also available on the subcommands).
Evaluate files
For the evaluation commands, you can provide input data in one of the following ways:
- Specify one or more file names
- Use the bookmark functionality
- Pipe data to stdin
In case there are syntax errors in the input data, klog cannot process it and displays an error message instead. Also, some commands emit warnings to draw your attention to potential logical issues in your data.
*.klg
.
Display files
klog print
pretty-prints the data onto the terminal.
klog print worktimes.klg
2018-03-24
First day at my new job
8:30 - 17:00
-45m Lunch break
Printing is a good way to double-check that the syntax in a file is all correct.
Total time
With klog total
, you can evaluate the overall
total time of all records.
klog total sport.klg
Total: 60h36m
(In 15 records)
Remember that open time ranges are not taken into account by default.
By using the --now
flag, you can simulate
all open time ranges to be closed “right now”.
In case you have set should-totals for your records, you can use the
--diff
option to calculate the difference between
the should-total and the actual total:
klog total --diff work.klg
Total: 16h45m
Should: 18h!
Diff: -1h15m
(In 6 records)
Calendar report
In order to print a chronological timeline of your data (like in a calendar),
you can use the klog report
command.
klog report 2020.klg
Total
2020 Jan Wed 28. 6h20m
Sun 30. 3h50m
Feb Tue 3. 7h
Fri 5. 6h15m
========
23h25m
The data can be aggregated by day, week, month, quarter or year,
which can be controlled via the --aggregate
flag.
(The default is by day.)
Use the --fill
flag to print a consecutive
stream of all dates, regardless of whether there are records or not.
Ongoing day
klog today
is similar to
klog total
, except that it displays the current
day separate from all other days.
klog today times.klg
Total
Today 5h7m
Other 162h19m
========
All 167h26m
By using the --follow
flag you can keep the
shell process open and see your data being updated live. If the file
contains open-ended time ranges, you can additionally set the
--now
flag for an ongoing simulation.
In case you have set should-total times, the --diff
and --now
flags together render a forecast of
the end-time at which your designated time goal will be reached.
klog today --diff --now times.klg
Total Should Diff End-Time
Today 5h7m 8h! -2h53m 18:00
Other 162h19m 160h! +2h19m
===========================
All 167h26m 168h! -34m 17:26
Break down by tags
If you use tags in your summaries, you can aggregate the total times by tags.
klog tags sports.klg
#running 13h
#biking 9h
#sports 21h
If a tag appears in the overall summary of the records, then all entries of that record are counted. If a tag appears in the summary of an individual entry, then only that entry is counted.
If you use tags with values and want to see a fine-granular breakdown,
use the --values
flag.
To see how many matching entries there are per tag,
you can use --count
.
Filtering records
All of the evaluation commands provide filter options which allow you to
select a specific date range, or to only consider certain tags.
For example, if you want to evaluate all records in sport.klg
since January 2018 that are tagged with #biking
, you would do:
klog total --since=2018-01-01 --tag=biking sport.klg
Total: 116h21m
(In 37 records)
You can filter records in the following ways:
-
By a certain date, e.g.
--date 2021-10-17
-
Within a date range, e.g.
--after 2021-05-01
--before 2021-05-15
(exclusive) or--since 2021-05-01
--until 2021-05-15
(inclusive) -
Within a period of time, e.g.
--period 2020
for all records in the year 2020, or--period 2020-01
for all records in January 2020, or--period 2020-W37
for all records in the 37th week of 2020, or--period 2020-Q2
for all records in the second quarter of 2020. -
With shortcuts for current or recent dates, e.g.
--today
,--yesterday
,--this-week
,--last-month
, and more. -
Only if they contain certain tags, e.g.
--tag reading
, or--tag '#project="52/12.3"'
Manipulate files
The command line tool is able to manipulate your files, for example
by creating new records or adding entries. Support for manipulation
operations is deliberately basic, because due to the simple file format,
it’s often most easy to open your .klg
file in
an editor and adjust the data by hand.
For the manipulation commands, you can specify the target file in one of the following ways:
- Specify one file name
- Use the bookmark functionality
Note that klog doesn’t create files for you automatically, so make sure that your target file exists. (New files don’t have to contain anything initially, though, so they can be empty.)
You can specify the designated date via the --date
flag.
In case a record does already exist at that date, the entry will be added
to that record. Otherwise, a new record is created “on the fly”.
By default, klog assumes the records in your files to be in chronological
order and automatically inserts it at the right position.
Add new entries
The most straightforward way to add a new entry to a record is by using
the klog track
command.
klog track '1h30m' sports.klg
klog track '8:00 - 16:00 Workday' work.klg
klog track '14:45 - ?' times.klg
klog track '\-30m'
Start and stop activities
By using klog start
, a new open-ended time range
is added.
klog start work.klg
Once you are done, you can “close” that open-ended time range by running
klog stop
.
klog stop work.klg
By default, the current time is used. You can otherwise set it
explicitly with the --time
flag.
If you prefer rounded numbers instead of the exact ones, you
can use the --round
option, to round
to the nearest multiple of 5m, 10m, 15m, 30m, or 60m.
If you just want to take a break without stopping the activity,
the klog pause
comes in handy.
klog pause --summary 'Lunch break' work.klg
This appends a new pause entry to the record without closing (stopping) the ongoing open-ended time range.
2022-03-11
8:30 - ? Work
-17m Lunch break
The klog pause
command is blocking, and the duration
value of the pause is continuously updated in the file, until you exit the process via Ctrl+C.
(It flushes changes to the file once per minute.)
Via the --extend
flag,
you can optionally extend a previous pause entry.
Create new records
klog create
creates a new, empty record in a file.
klog create reading.klg
You can set a should-total via the --should
flag.
Bookmarks
Bookmarks are a powerful feature that allows you to reference
often-used files from anywhere via a short alias. That way,
you don’t have to specify the full path of a
.klg
file, or navigate to the directory
where it’s stored.
Bookmark names are identified by the @
prefix.
Setup and use bookmarks
The following command would define a bookmark @work
pointing to the file ~/work/2021-01.klg
klog bookmarks set ~/work/2021-01.klg @work
With that bookmark, you can now use @work
as input argument for klog commands. It will be automatically resolved
to the designated file, regardless of your current working directory.
klog total @work
klog track '3h' @work
@
operator is reserved syntax. Therefore, when using Windows Powershell, you
either have to escape bookmark names with a backtick
(`@mybookmark
) or wrap them in quotes
('@mybookmark'
).
Navigate to your files
With bookmarks, you can also navigate to your files more easily.
The following command will open the bookmark in your favourite editor
(based on the $EDITOR
environment variable).
klog edit ~/documents/times.klg
klog edit @times
You can also open the file explorer at the location of the bookmarked file.
klog goto ~/documents/times.klg
klog goto @times
Default bookmark
In addition to “regular” named bookmarks, you can specify one unnamed default bookmark. klog will then automatically direct all commands to the default bookmark, unless you specify another target file explicitly.
klog bookmarks set ~/work.klg
klog total
klog track '3h'
A default bookmark is useful in case you only use one .klg
file anyway.
Internally, the default bookmark is identified by the reserved name @default
.
Apart from that naming rule, it’s a regular bookmark like any other.
Configuration
The configuration of the klog command line tool is stored in files that reside in the klog config folder.
The location of that folder is platform-dependent, and you can also override it.
Run klog info config-folder
to display the path, and to learn about the lookup order.
config.ini
You can configure certain behaviour of the command line tool by creating a config.ini
file and specifying your preferences there.
Run klog config
to see the all available options along with an explanation.
The output of that command is valid .ini
syntax, so you can use it for bootstrapping your config file.
bookmarks.json
If you use bookmarks, the klog command line tool stores the bookmark info in the bookmarks.json
“database” file.
You are not supposed to edit this file by hand. Instead, use the klog bookmarks
subcommands for managing bookmarks.
Editor support
The following editor plugins are independent projects, which are owned and maintained by other users of klog.
VSCode
- vscode-klog (maintained by @vladdeSV)
Sublime Text
- sublime-klog (maintained by @jotaen)
Vim
Extending
In order to tailor klog to your own needs and workflows, or to build your own custom tooling around it, there are various ways to extend or enhance the functionality of the klog CLI tool.
JSON subcommand
The easiest way to process klog data in your own application is to
run the klog json
command as subprocess.
This converts klog data into a JSON data structure, which can be easily
read with standard JSON parsers.
You can either specify a filename to read from, or you pass the data via stdin.
In order to learn about the output structure, just run
klog json --pretty
on a file – it should
be self-explanatory. You should also try it on a file with syntax
errors, to learn how the error structure looks.
Shell scripting
For recurring workflows, you could write your own shell scripts. The klog CLI has multiple features to make that process easier:
-
Set the
NO_COLOR=1
environment variable to make all text output uncoloured and thus easier to parse. -
Some subcommands provide a
--quiet
flag that will omit all descriptive text and only print actual values. - You can distinguish various error cases by checking the exit code of a command.
Go library
The klog CLI is written in the Go programming language. Therefore, in case your app is written in Go too, you can also integrate with the sources directly via Go modules.
About
klog was created by Jan Heuermann.
You may use it under the following terms:
- Command line tool: MIT license
- File specification: public domain (CC0/OWFa)