Introduction
obsidian-logging is a small command-line tool, written in Rust, for logging directly to today’s daily note from the Linux (or macOS/Windows) command line. It is built for people who keep a daily journal in Obsidian and want a fast, keyboard-driven way to append timestamped entries without opening the app.
Typical use:
obsidian-logging "Had lunch with colleagues"
This appends a timestamped line to today’s daily note, under a configurable section heading. You can also:
- organize entries into categories such as
work,personal, orhealth, each with its own section in the note - define shorthand phrases for common entries (“gym”, “standup”, …) with optional argument expansion
- list today’s (or a previous day’s) entries back out on the command line
- open today’s note directly in
$EDITOR - choose between bullet-list and markdown-table output, and between 12- and 24-hour timestamps
Who this manual is for
This manual covers everyday usage of the obsidian-logging CLI: installing it, configuring your vault
and daily-note layout, and using every command-line switch. It also includes a short chapter on using
the underlying Rust library (obsidian-logging on crates.io) in your own projects, and a chapter for
contributors who want to build the project from source.
Project layout
The project is a Cargo workspace with two crates:
obsidian-logging(inlib/) — the core library: configuration, templating, and log file management logic. Published on crates.io and documented on docs.rs.obsidian-logging-cli(inapp/) — the command-line binary (obsidian-logging) that most users install. Distributed as pre-built binaries via GitHub Releases.
Source and license
The source code is hosted at github.com/ljantzen/obsidian-logging. The project is licensed under a combined MIT/SPPL license — see License and Contact for details.
Installation
Pre-built binary
Pre-built binaries are published for each release on the
GitHub releases page. Download the archive
for your platform, extract it, and place the obsidian-logging binary somewhere on your $PATH.
Install with Cargo
If you have a Rust toolchain installed, you can install the CLI directly from crates.io:
cargo install obsidian-logging-cli
This builds and installs the obsidian-logging binary into ~/.cargo/bin (make sure that directory
is on your $PATH).
Build from source
Clone the repository and build just the CLI:
git clone https://github.com/ljantzen/obsidian-logging
cd obsidian-logging
cargo build --release -p obsidian-logging-cli
./target/release/obsidian-logging --version
Or build the whole workspace (library + CLI):
cargo build --workspace --release
If you have just installed, the project’s justfile provides
shortcuts for common tasks — see Development.
Verify the install
obsidian-logging -v
This prints the installed version and exits.
Library use
If you want to use the logging engine as a library inside your own Rust project rather than the CLI, see Using the Library.
Create a shell alias
Since obsidian-logging is a mouthful to type every time, it’s worth creating a short alias. For
example, on Linux/macOS:
alias q=' obsidian-logging'
The leading space before the command name prevents the alias invocation itself from being written to your shell history (most shells skip history for commands that start with a space).
On Windows, doskey can be used to create an equivalent macro:
doskey q=obsidian-logging
Getting Started
First run
The first time you run obsidian-logging, it looks for a configuration file at:
- Linux/macOS:
~/.config/obsidian-logging/obsidian-logging.yaml - Windows:
%APPDATA%\obsidian-logging\obsidian-logging.yaml
If the file does not exist, obsidian-logging creates it and prompts you for some initial values
(most importantly, the path to your Obsidian vault). After that first run, you can edit the YAML file
directly — see Configuration for everything that can be set.
Adding your first entry
obsidian-logging "Had lunch with colleagues"
This appends a bullet-point entry with the current time to today’s daily note, under the default
section header (## 🕗 unless you’ve changed it).
Create a short alias
Typing obsidian-logging every time gets old fast, so it’s worth adding a short alias to your shell
config (e.g. ~/.bashrc or ~/.zshrc):
alias q=obsidian-logging
With this in place, logging an entry is as short as:
q "Had lunch with colleagues"
See Tips and Tricks for more on aliasing, including a history-skipping variant and the
Windows doskey equivalent.
Listing today’s entries
Running obsidian-logging with no arguments lists today’s entries:
obsidian-logging
This is exactly equivalent to:
obsidian-logging -l
Editing the note directly
obsidian-logging -e
Opens today’s daily note in $EDITOR (defaults to vim if $EDITOR is unset).
Where to go next
- Configuration to set up your vault path, categories, templates and phrases.
- Command-Line Reference for every flag in detail.
- Example Daily Note to see what a fully configured note looks like.
Configuration
obsidian-logging reads its configuration from a YAML file:
- Linux/macOS:
~/.config/obsidian-logging/obsidian-logging.yaml - Windows:
%APPDATA%\obsidian-logging\obsidian-logging.yaml
If the file doesn’t exist on first run, obsidian-logging creates it and prompts for some values. A
fully annotated example is shipped in the repository as
obsidian-logging.example.yaml.
The following pages describe each setting in detail:
- Vault and File Path — where your vault lives and how daily note filenames are built.
- Section Headers and Categories — the marker that delimits the log block, and per-category sections.
- List Type and Time Format — bullet vs. table output, 12-hour vs. 24-hour timestamps.
- Locale — localized weekday names and list conjunctions.
- Templates — the template used to seed a new daily note.
- Predefined Phrases — shorthand log entries with argument expansion.
- Environment Variables — overriding the vault path at runtime.
Full example
vault: /home/user/obsidian/
file_path_format: "10-Journal/{year}/{month}/{date}.md"
section_header: "## 🕗"
# Category-specific section headers
# Use -c <category> or --category <category> to log to these sections
section_header_work: "## 💼 Work"
section_header_personal: "## 🏠 Personal"
section_header_health: "## 🏥 Health"
list_type: bullet
template_path: ~/.config/obsidian-logging/template.md
# Optional: Time format for timestamps (12 or 24 hour)
# Examples:
# time_format: 12 # 12-hour format (e.g. 02:30:00 PM or 02:30:45 PM)
# time_format: 24 # 24-hour format (e.g. 14:30:00 or 14:30:45)
# Note: Timestamps now include seconds (HH:mm:ss format) starting with version 1.3.0
# If not specified, defaults to 24-hour format
time_format: 24
# Optional: Specify locale for weekday names
# Examples:
locale: en_US # English
# locale: nb_NO # Norwegian Bokmål
# locale: de_DE # German
# locale: fr_FR # French
# locale: es_ES # Spanish
# If not specified or invalid, falls back to English
# Optional: Customize table column labels
# Examples:
# time_label: Time # English
# event_label: Event # English
# time_label: Tidspunkt # Norwegian
# event_label: Hendelse # Norwegian
# If not specified, defaults to Norwegian labels
time_label: Tidspunkt
event_label: Hendelse
# Note: Conjunction for {#} placeholder is automatically selected based on locale
# Supported locales: no/nb/nn (og), da (og), sv (och), de (und), fr (et), es (y), it (e), pt (e), ru (и), ja (と), ko (와), zh (和)
# Defaults to "and" for English or unsupported locales
# Optional: Common logging phrases with shorthand references
# Use -p <shorthand> or --phrase <shorthand> to expand these phrases
# Supports argument expansion with placeholders: {0}, {1}, {2}, etc. for specific arguments, {*} for all arguments, or {#} for comma-separated lists
phrases:
# Work-related phrases
meeting: "Team meeting with stakeholders"
standup: "Daily standup meeting"
review: "Code review session"
deploy: "Deployed to production"
# Personal phrases
gym: "Workout at the gym"
run: "Morning run"
cook: "Cooking dinner"
read: "Reading time"
# Health phrases
doctor: "Doctor appointment"
meds: "Took medication"
sleep: "Sleep tracking"
# General phrases
break: "Coffee break"
lunch: "Lunch break"
call: "Phone call"
email: "Email correspondence"
# Phrases with argument expansion
# Use: obsidian-logging -p meeting John Smith
# Result: "Team meeting with John Smith"
meeting_with: "Team meeting with {*}"
# Use: obsidian-logging -p call John
# Result: "Phone call with John"
call_with: "Phone call with {0}"
# Use: obsidian-logging -p doctor Dr. Smith
# Result: "Doctor appointment with Dr. Smith"
doctor_with: "Doctor appointment with {0}"
# Use: obsidian-logging -p project Project Alpha
# Result: "Working on Project Alpha"
project: "Working on {0}"
# Use: obsidian-logging -p exercise Running 5km
# Result: "Exercise: Running 5km"
exercise: "Exercise: {*}"
# Use: obsidian-logging -p travel Paris
# Result: "Travel to Paris"
travel: "Travel to {0}"
# Use: obsidian-logging -p food Pizza with friends
# Result: "Ate Pizza with friends"
food: "Ate {*}"
# Phrases with {#} placeholder for comma-separated lists
# Use: obsidian-logging -p meeting_with John Jane
# Result: "Team meeting with John and Jane"
meeting_with: "Team meeting with {#}"
# Use: obsidian-logging -p call_with Alice Bob Charlie
# Result: "Phone call with Alice, Bob and Charlie"
call_with: "Phone call with {#}"
# Use: obsidian-logging -p project_with Frontend Backend Database
# Result: "Working on Frontend, Backend and Database"
project_with: "Working on {#}"
# Use: obsidian-logging -p exercise_with Running Swimming Cycling
# Result: "Exercise: Running, Swimming and Cycling"
exercise_with: "Exercise: {#}"
Vault and File Path
vault
The absolute path to the root of your Obsidian vault:
vault: /home/user/obsidian/
All daily note paths are resolved relative to this directory. It can be overridden at runtime with the
OBSIDIAN_VAULT_DIR environment variable.
file_path_format
A template describing where, inside the vault, today’s daily note lives, and how it is named:
file_path_format: "10-Journal/{year}/{month}/{date}.md"
Three placeholders are supported:
| Placeholder | Expands to | Example |
|---|---|---|
{year} | 4-digit year | 2024 |
{month} | 2-digit month | 01 |
{date} | Full ISO date (YYYY-MM-DD) | 2024-01-15 |
With the example above, an entry logged on 2024-01-15 would be written to
/home/user/obsidian/10-Journal/2024/01/2024-01-15.md.
If file_path_format is not set, it defaults to:
- Linux/macOS:
10-Journal/{year}/{month}/{date}.md - Windows:
10-Journal\{year}\{month}\{date}.md
You’re free to flatten this to a single directory (file_path_format: "Journal/{date}.md") or nest it
however your vault is organized — obsidian-logging will create any directories it needs when it
writes the first entry for a given day.
Section Headers and Categories
The default section marker
obsidian-logging looks for a specific markdown heading in your daily note that marks where the log
entries block begins. Log entries must be consecutive, with no blank lines between them — the first
blank line (or next heading) ends the block.
The default marker is configured with section_header:
section_header: "## 🕗"
Everything from this heading until the next ## heading (or the end of the file) is treated as the
log block for the default category.
Category-specific section headers
Beyond the default section, obsidian-logging can log to any number of named categories, each
with its own section in the daily note. Define a category by adding a
section_header_<category_name> key:
section_header: "## 🕗"
section_header_work: "## 💼 Work"
section_header_personal: "## 🏠 Personal"
section_header_health: "## 🏥 Health"
Use the -c/--category flag to target one of these sections:
obsidian-logging -c work "Meeting"
This appends the entry under the ## 💼 Work heading instead of the default ## 🕗 section.
If a category passed with -c has no matching section_header_<category> entry in the config, the
entry falls back to the default section.
Listing by category
The same -c flag filters what -l/--list prints:
obsidian-logging -l -c work # only the "work" section
obsidian-logging -l -c work -c personal # "work" and "personal" sections
obsidian-logging -l -c all # every category, including the default section
See Listing Entries for the full behavior.
List Type and Time Format
list_type
Controls how entries are rendered when appended or listed. Two values are supported:
list_type: bullet # or: table
-
bullet— each entry is a markdown bullet with a leading timestamp:- 14:30:45 Team standup meeting -
table— each entry is a row in a two-column markdown table:| Tidspunkt | Hendelse | |-----------|------------------------| | 14:30:45 | Team standup meeting |(Column header text comes from
time_label/event_label, see below.)
This can be overridden per-invocation with -T bullet or -T table — see
Flag Reference.
If the existing log section in a file uses a different format than the one currently configured,
obsidian-logging automatically converts all existing entries in that section to the new format when
a new entry is added.
time_format
Controls whether timestamps use a 12-hour or 24-hour clock:
time_format: 24 # or: 12
24→14:30:4512→02:30:45 PM
Defaults to 24 if not set. This can be overridden per-invocation with -f 12 or -f 24.
Timestamps include seconds. Since version 1.3.0, timestamps are always
HH:mm:ss. When a new entry is added to a log section, any existing timestamps in that section that are missing seconds (HH:mm) are automatically rewritten to include:00seconds, so that all entries in the section stay consistently formatted. If a new entry’s timestamp collides with an existing one, the seconds are incremented until a unique timestamp is found. Mixing 12-hour and 24-hour timestamps within the same log section may produce unpredictable sort order — pick one format and stick with it.
time_label and event_label
When using list_type: table, these set the column header text:
time_label: Tidspunkt # defaults to "Tidspunkt" (Norwegian for "time")
event_label: Hendelse # defaults to "Hendelse" (Norwegian for "event")
For an English table header, set:
time_label: Time
event_label: Event
Headers are only written to the file when listing/adding with the -H/--header flag.
Locale
locale
Sets the locale used for two things: localized weekday names in templates, and the
conjunction word used when expanding the {#} placeholder in phrases.
locale: en_US # English
If locale is left out of the config entirely, weekday names fall back to English and the conjunction
falls back to and. If locale is set but to a value that isn’t recognized, the behavior differs
between the two features — see the notes below each table.
Weekday names ({weekday} template variable)
Recognized locale codes for weekday formatting (used by the {weekday} template placeholder):
| Locale code | Language |
|---|---|
en_US | English |
nb_NO | Norwegian Bokmål |
nn_NO | Norwegian Nynorsk |
de_DE | German |
fr_FR | French |
es_ES | Spanish |
it_IT | Italian |
ja_JP | Japanese |
ko_KR | Korean |
ru_RU | Russian |
zh_CN | Chinese |
If locale is set but not in this table, weekday names currently fall back to Norwegian Bokmål
(not English) — only an entirely absent locale setting produces English weekday names.
Conjunction ({#} phrase placeholder)
A shorter locale code controls the word used to join the last two items in a {#} expansion (see
Predefined Phrases):
| Locale code(s) | Conjunction |
|---|---|
no, nb, nn | og |
da | og |
sv | och |
de | und |
fr | et |
es | y |
it | e |
pt | e |
ru | и |
ja | と |
ko | 와 |
zh | 和 |
| anything else | and |
Note: the weekday table matches long locale codes (de_DE) and the conjunction table matches
short codes (de) exactly, so a single locale value generally only localizes one of the two:
locale: de_DE→ German weekday names, but the conjunction falls back toand("de_DE"doesn’t match the short code"de").locale: de→ the conjunction is correctlyund, but the weekday name falls back to Norwegian Bokmål ("de"isn’t in the weekday table, so it hits that table’s wildcard case above).
There’s currently no single value that localizes both for a given language other than Norwegian
(no/nb/nn), where the short form happens to also work for weekdays via the wildcard fallback.
Templates
When obsidian-logging needs to create a new daily note (because today’s file doesn’t exist yet), it
seeds the file with content from a template rather than starting from a blank file.
template_path
template_path: ~/.config/obsidian-logging/template.md
The path to a markdown template file. A leading ~ is expanded to your home directory. If
template_path is not set, or the file can’t be read, the new note defaults to just:
## 🕗
Template variables
The template file can use the following placeholders, which are substituted when a new note is created:
| Placeholder | Expands to | Example |
|---|---|---|
{today} | Today’s date (YYYY-MM-DD) | 2024-01-15 |
{yesterday} | Yesterday’s date (YYYY-MM-DD) | 2024-01-14 |
{tomorrow} | Tomorrow’s date (YYYY-MM-DD) | 2024-01-16 |
{weekday} | Localized weekday name (see Locale) | monday |
{created} | Creation timestamp (YYYY-MM-DD HH:mm:ss) | 2024-01-15 08:12:03 |
Example template
# Daily Note for {today}
Previous: [[{yesterday}]]
Next: [[{tomorrow}]]
Today is {weekday}
## 🕗
With locale: en_US and a note created on Monday, 2024-01-15, this would expand to:
# Daily Note for 2024-01-15
Previous: [[2024-01-14]]
Next: [[2024-01-16]]
Today is monday
## 🕗
Note that the template is responsible for including the section_header text itself (## 🕗 in the
example above) — obsidian-logging doesn’t add it separately. If you use
category-specific sections, add those headings to the template too so they exist
before the first entry for that category is logged.
Predefined Phrases
You can define common log entries once in your configuration file and refer to them with a short
name via the -p/--phrase flag, instead of retyping the full text every time.
Basic phrases
phrases:
meeting: "Team meeting with stakeholders"
gym: "Workout at the gym"
lunch: "Lunch break"
doctor: "Doctor appointment"
obsidian-logging -p meeting
obsidian-logging -p gym -c health
Phrases with argument expansion
A phrase can include placeholders that are filled in from any extra words you pass after -p <name>:
| Placeholder | Expands to |
|---|---|
{0}, {1}, {2}, … | A specific argument by position (0-indexed) |
{*} | All arguments, joined with spaces |
{#} | All arguments as a comma-separated list with a locale-aware conjunction before the last item |
phrases:
meeting_with: "Team meeting with {*}" # all arguments
call_with: "Phone call with {0}" # first argument only
project: "Working on {0}" # first argument only
exercise: "Exercise: {*}" # all arguments
travel: "Travel to {0}" # first argument only
food: "Ate {*}" # all arguments
# Using {#} for a natural-language list
meeting_with_list: "Team meeting with {#}"
call_with_list: "Phone call with {#}"
obsidian-logging -p meeting_with John Smith
# -> "Team meeting with John Smith"
obsidian-logging -p call_with Alice
# -> "Phone call with Alice"
obsidian-logging -p project "Project Alpha"
# -> "Working on Project Alpha"
obsidian-logging -p meeting_with_list John Jane
# -> "Team meeting with John and Jane"
obsidian-logging -p call_with_list Alice Bob Charlie
# -> "Phone call with Alice, Bob and Charlie"
The conjunction used by {#} (and, og, und, …) is chosen based on the
locale setting.
Phrases can be combined with -t/--time and -c/--category:
obsidian-logging -p meeting_with John -t 14:30
obsidian-logging -p doctor_with "Dr. Smith" -c health
If a phrase name passed to -p doesn’t exist in the config, obsidian-logging prints an error and
exits with a non-zero status instead of logging anything.
A larger example
See
obsidian-logging.example.yaml
in the repository for a complete set of example phrases covering work, personal, and health
categories.
Environment Variables
OBSIDIAN_VAULT_DIR
If set, this overrides the vault value from obsidian-logging.yaml for the current invocation:
OBSIDIAN_VAULT_DIR=/tmp/test-vault obsidian-logging "Testing a different vault"
This is useful for testing configuration changes, pointing at a secondary vault temporarily, or
running obsidian-logging inside scripts/CI without touching your personal config file.
$EDITOR
Not specific to obsidian-logging’s config file, but used by the -e/--edit
flag to decide which editor to open today’s note in. If $EDITOR is unset, vim is used.
Command-Line Reference
obsidian-logging [OPTIONS] [ENTRY]...
obsidian-logging has three modes of operation, chosen based on which flags and arguments you pass:
- Add an entry — the default when you pass plain text and no
-l/-e/-Sflag. - List entries — triggered by
-l, or by running with no entry text and no other action flag. - Edit today’s note — triggered by
-e.
The following pages cover each mode, plus a full table of every flag:
Quick examples
# List today's entries (no arguments)
obsidian-logging
# Add an entry to the default section
obsidian-logging "Had lunch with colleagues"
# Add an entry with a specific time
obsidian-logging -t 14:30 "Team standup meeting"
# Add an entry to a category
obsidian-logging -c work "Code review completed"
# Use a predefined phrase
obsidian-logging -p gym -c health
# Read an entry from stdin
echo "Quick note" | obsidian-logging -S
# List entries from a previous day
obsidian-logging -b 1
# List a specific category, in table format, with a header
obsidian-logging -l -c work -T table -H
# Edit today's file
obsidian-logging -e
Adding Entries
Plain entries
Running obsidian-logging with text and no -l, -e, or -S flag appends that text as a new entry
to the default log section of today’s daily note:
obsidian-logging This is a log entry
A timestamp for the current time is prepended, formatted according to the configured list type:
- Bullet mode:
- 14:30:45 This is a log entry - Table mode:
| 14:30:45 | This is a log entry |
Quoting the entry text is optional — all trailing positional arguments are joined with spaces — but
quoting is recommended if your entry contains flag-like tokens (e.g. starting with -).
Overriding the time: -t / --time
obsidian-logging -t 14:30 "Team standup meeting" # becomes 14:30:00
obsidian-logging -t 14:30:45 "Team standup meeting" # explicit seconds
obsidian-logging -t "2:30 PM" "Afternoon meeting" # becomes 02:30:00 PM
obsidian-logging -t "2:30:45 PM" "Afternoon meeting" # explicit seconds
- Accepts
HH:mm,HH:mm:ss,h:mm AM/PM, orh:mm:ss AM/PM. - If seconds are omitted, they default to
00. - Entries are kept sorted chronologically within their section. If the given timestamp already exists in the section, the seconds are incremented until a unique timestamp is found.
- Adding a new entry reformats every existing timestamp in that section to include seconds, if any are missing them (see List Type and Time Format).
Targeting a category: -c / --category
obsidian-logging -c work "Team meeting at 2pm"
obsidian-logging -c personal "Gym workout"
obsidian-logging -c health "Doctor appointment"
Logs the entry to the section defined by section_header_<category> in your config, falling back to
the default section if no such header is configured. See
Section Headers and Categories.
Using a predefined phrase: -p / --phrase
obsidian-logging -p meeting
obsidian-logging -p meeting_with John Smith
obsidian-logging -p call_with Alice -t 14:30
See Predefined Phrases for how to define phrases and use argument
placeholders ({0}, {*}, {#}).
Overriding format just for this call
-T bullet/-T table— overridelist_typefor this invocation.-f 12/-f 24— overridetime_formatfor this invocation.
obsidian-logging -T table -t 9:00 "Daily standup"
obsidian-logging -f 12 -t 2:30 PM "Afternoon meeting"
Silencing output: -s / --silent
Suppresses all output, including error messages — useful when calling obsidian-logging from
scripts or other automation where you don’t want anything printed:
obsidian-logging -s "Logged quietly"
Listing Entries
-l / --list
Lists the current day’s log entries:
obsidian-logging -l
If obsidian-logging is invoked with no arguments at all, this is the default action — i.e.
obsidian-logging on its own is equivalent to obsidian-logging -l.
Going back in time: -b / --back
obsidian-logging -b 1 # yesterday's entries
obsidian-logging -b 7 # one week ago
-b 0 is equivalent to -l (today). -b can be combined with -c to filter by category, and with
-e to edit a past day’s file instead of listing it.
Filtering by category: -c / --category
obsidian-logging -l -c work # only the "work" section
obsidian-logging -l -c personal # only the "personal" section
obsidian-logging -l -c work -c personal # "work" and "personal" sections
obsidian-logging -l -c all # every category, including the default section
obsidian-logging -l -c unknown # falls back to the default section
-c can be repeated to list multiple categories at once. Passing all lists every section in the
note, including the default. If a category name doesn’t have a matching
section_header_<category> entry in the config, entries from the default section are shown instead.
Output format: -T and -H
-T bullet/-T table— override the configuredlist_typejust for this listing.-H/--header— include the table header row (column labels + separator) when listing in table mode.
obsidian-logging -l -T table -H
Examples
obsidian-logging # today, default section
obsidian-logging -b 1 -c work # yesterday, work section only
obsidian-logging -l -c work -c personal # today, work + personal sections
obsidian-logging -l -c all -T table -H # today, all sections, as a table with header
Editing Entries
-e / --edit
Opens today’s daily note directly in your editor, instead of adding or listing an entry:
obsidian-logging -e
The editor used is $EDITOR, or vim if $EDITOR is not set.
Combine with -b/--back to edit a previous day’s note:
obsidian-logging -b 1 -e # edit yesterday's note
This is handy for correcting a typo, reordering entries manually, or adding freeform content around the log section that the CLI itself doesn’t manage.
Reading from stdin
-S / --stdin
Reads the log entry text from standard input instead of from the command-line arguments. This is useful for piping output from another command straight into today’s note:
echo "Quick note" | obsidian-logging -S
cat notes.txt | obsidian-logging -c work -S
Carriage-return and line-feed characters are stripped, and the input is logged as a single line, regardless of how many lines were piped in.
-S can be combined with the same flags as a normal add — -t/--time, -c/--category,
-T, -f, and -s:
echo "Standup notes" | obsidian-logging -S -c work -t 9:00
Flag Reference
| Flag | Long form | Argument | Description |
|---|---|---|---|
-t | --time | HH:mm[:ss] or h:mm[:ss] AM/PM | Override the timestamp for a new entry. Seconds default to 00 if omitted. See Adding Entries. |
-l | --list | — | List entries instead of adding one. Default action when no entry text and no other action flag is given. See Listing Entries. |
-b | — | <days> | List (or edit) entries from <days> days ago. 0 = today (default). See Listing Entries. |
-e | --edit | — | Open today’s (or -b-selected) file in $EDITOR (defaults to vim). See Editing Entries. |
-s | --silent | — | Suppress all output, including error messages. |
-S | --stdin | — | Read the log entry text from stdin instead of the command line. See Reading from stdin. |
-H | --header | — | Include the table header row when listing in table format. |
-c | --category | <name> | Target/filter by category. Repeatable. Use all with -l to list every category. See Section Headers and Categories. |
-p | --phrase | <name> | Use a predefined phrase from the config, with optional trailing arguments for placeholder expansion. See Predefined Phrases. |
-T | — | bullet | table | Override list_type for this invocation. |
-f | — | 12 | 24 | Override time_format for this invocation. |
-v | --version | — | Print the version string and exit. |
-h | --help | — | Print command-line help and exit. |
| (positional) | <entry>... | The log entry text. If omitted (and no -l/-e/-S), entries are listed instead. |
Notes
-Tand-ftake their value as a separate word (e.g.-T table,-f 12), not-Ttable.-c/--categorymay be given multiple times on a single command line; when adding an entry, only the first-cvalue is used as the target category, while when listing, all given values are used as filters.- Running with absolutely no flags and no entry text (
obsidian-logging) is shorthand forobsidian-logging -l. -v/--versionand-h/--helpshort-circuit all other behavior — they print and exit immediately, regardless of any other flags present.
Built-in help text
Running obsidian-logging -h prints usage, every flag, a list of usage examples, the configuration
file locations, the $OBSIDIAN_VAULT_DIR environment variable, and the full set of template
placeholders, all generated directly from the CLI’s argument parser.
Example Daily Note
With category sections configured (see Section Headers and Categories), a daily note can look like this:
# 2024-01-15
## 🕗
* 08:30:00 Morning coffee
* 12:00:00 Lunch break
## 💼 Work
* 09:00:00 Daily standup meeting
* 10:30:00 Code review completed
* 14:00:00 Client presentation
## 🏠 Personal
* 18:00:00 Gym workout - 45 minutes
* 19:30:00 Dinner with family
## 🏥 Health
* 11:00:00 Doctor appointment
* 16:00:00 Picked up prescription
This was produced with a configuration roughly like:
section_header: "## 🕗"
section_header_work: "## 💼 Work"
section_header_personal: "## 🏠 Personal"
section_header_health: "## 🏥 Health"
list_type: bullet
and commands such as:
obsidian-logging "Morning coffee" -t 08:30
obsidian-logging -c work "Daily standup meeting" -t 09:00
obsidian-logging -c work "Code review completed" -t 10:30
obsidian-logging -c work "Client presentation" -t 14:00
obsidian-logging "Lunch break" -t 12:00
obsidian-logging -c personal "Gym workout - 45 minutes" -t 18:00
obsidian-logging -c personal "Dinner with family" -t 19:30
obsidian-logging -c health "Doctor appointment" -t 11:00
obsidian-logging -c health "Picked up prescription" -t 16:00
Table mode
The same note, with list_type: table and -H/--header used when adding entries, renders each
section as a markdown table instead of a bullet list:
## 🕗
| Tidspunkt | Hendelse |
|-----------|-----------------|
| 08:30:00 | Morning coffee |
| 12:00:00 | Lunch break |
All timestamps shown include seconds (HH:mm:ss) — see
List Type and Time Format for details on the automatic
seconds migration applied to older entries.
Tips and Tricks
Create a short alias
Typing obsidian-logging for every quick note gets old fast. Add a short alias to your shell
config:
alias q=' obsidian-logging'
The leading space keeps the invocation itself out of your shell history in most shells. On Windows,
use doskey:
doskey q=obsidian-logging
Pipe command output straight into your journal
git log -1 --oneline | obsidian-logging -S -c work
uptime | obsidian-logging -S -c health
Quickly check in without leaving the terminal
q -p standup -c work
q -p gym -c health
Review the day
q -l -c all -T table -H
Lists every category for today as a markdown table with headers — a good end-of-day review command.
Script a weekly summary
Because -b accepts any number of days, you can loop back over the past week in a shell script and
pull every day’s entries for a given category, e.g. to build a weekly report:
for i in 0 1 2 3 4 5 6; do
obsidian-logging -b "$i" -l -c work
done
Keep the automatic seconds-migration in mind
Adding a new entry to a log section rewrites every existing timestamp in that section to include seconds, if any are missing them. If you hand-edit timestamps in a daily note, adding a new entry afterward will normalize them — this is expected and documented in List Type and Time Format.
obsidian-logging vs. Obsidian CLI
Obsidian now ships its own native CLI tool (available as Early Access with a Catalyst License) — see the Obsidian CLI documentation for details. The two tools solve different problems and can coexist happily.
Use obsidian-logging if you want
- Quick, focused daily logging with timestamps
- Entries organized by category (work, personal, health, …)
- Reusable phrase templates for common entries
- A lightweight tool optimized purely for fast note capture
- A free, open-source solution with no license requirement
Use the Obsidian CLI if you need
- General vault operations and querying
- Advanced automation and scripting across your whole vault
- Plugin management
- Full programmatic control of Obsidian itself
For simple daily journaling from the command line, obsidian-logging is a small, dedicated tool that
does one thing well. For broader vault automation, Obsidian’s native CLI offers much wider
capabilities.
Using the Library
The CLI is a thin wrapper around the obsidian-logging library crate, which is published separately
on crates.io and documented on
docs.rs. If you want to build your own tool on top of the same
logging engine — a GUI, a different CLI, a bot — you can depend on the library directly instead of
shelling out to the binary.
Adding the dependency
[dependencies]
obsidian-logging = "1.3"
What the library exposes
The crate root re-exports the pieces most consumers need:
#![allow(unused)]
fn main() {
use obsidian_logging::{add, edit, list, Config, ListType, TimeFormat};
}
Config— the deserialized YAML configuration (vault path, section headers, categories, list type, time format, locale, phrases, …). Load it withConfig::initialize()to read~/.config/obsidian-logging/obsidian-logging.yaml(honoring$OBSIDIAN_VAULT_DIR) exactly as the CLI does, or construct one directly for a fully programmatic setup.ListType/TimeFormat— the enums backinglist_typeandtime_formatin the config, with.with_list_type()/.with_time_format()builder methods onConfigfor overriding them at runtime.add— functions for appending a new timestamped entry to a day’s file (handle_plain_entry,handle_with_time), with category support.list—list_log_for_day, for retrieving/printing a day’s entries, optionally filtered by category.edit—edit_log_for_day, for opening a day’s file in$EDITOR.
The config, template, and utils modules are also public, exposing lower-level building blocks
such as get_log_path_for_date, extract_log_entries, and the template-variable expansion used for
new files.
Full API documentation
For complete, up-to-date signatures and doc comments, see docs.rs/obsidian-logging. This manual focuses on the CLI; the library’s rustdoc is the source of truth for programmatic usage.
Development
This chapter is for people building or contributing to obsidian-logging itself, rather than just
using the CLI.
Quick start
git clone https://github.com/ljantzen/obsidian-logging
cd obsidian-logging
just build # Build everything
just test # Run all tests
just check # Run fmt, clippy, and tests
Project layout
The project is a Cargo workspace with two crates:
lib/—obsidian-logging, the core library (logging, configuration, templating). Published to crates.io.app/—obsidian-logging-cli, the CLI binary. Depends onlib/. Distributed as pre-built binaries via GitHub Releases.
Using the justfile
The project includes a justfile for common tasks. Install just
if you don’t already have it:
cargo install just
Then:
# Building
just build # Build lib + app
just build-lib # Build library only
just build-app # Build CLI only
just build-release # Build in release mode
# Testing
just test # Test everything
just test-lib # Test library only
just test-app # Test CLI only
# Code quality
just fmt # Format all code
just fmt-check # Check formatting without modifying
just clippy # Run clippy linter
just check # fmt-check + clippy + test (recommended before committing)
# Running and debugging
just run --help # Run CLI with arguments
just version # Show version
# Release workflow
just release # Release with auto-incremented patch version
just release-version 1.4.0 # Release a specific version
just publish # Publish the library to crates.io
# Utility
just help # List all available commands
just clean # Clean build artifacts
Run just check before committing — it runs formatting checks, clippy (with warnings as errors),
and the full test suite.
Running tests directly with Cargo
cargo test --workspace
cargo test -p obsidian-logging # library tests only
cargo test -p obsidian-logging-cli # CLI tests only
Library tests live under lib/tests/ (one file per area: add_tests.rs, config_tests.rs,
edit_tests.rs, list_tests.rs, template_tests.rs, utils_tests.rs).
Building this manual
This user manual is an mdBook project under docs/. To build
or preview it locally:
cd docs
mdbook build # outputs static HTML to docs/book/
mdbook serve # serves the book locally with live reload
Troubleshooting
My entry didn’t get added where I expected
- Check
vaultandfile_path_formatin your config — the computed path is<vault>/<file_path_format with {year}/{month}/{date} substituted>. See Vault and File Path. - If you used
-c <category>, make suresection_header_<category>is actually defined in your config. If it isn’t, the entry silently goes to the default section instead. $OBSIDIAN_VAULT_DIR, if set, always wins over thevaultvalue in the config file — check your shell environment if entries are landing in an unexpected vault.
My entries appear in the wrong section, or categories “bleed” into each other
Log entries within a section must be consecutive, with no blank lines between them. The log block for
a section runs from its heading up to the next ## heading (or end of file). If you manually add
blank lines or other content inside a log section, obsidian-logging may stop treating subsequent
lines as part of that section.
My existing timestamps changed from HH:mm to HH:mm:ss
This is expected. Since version 1.3.0, adding a new entry to a section reformats every existing timestamp in that section to include seconds, if it doesn’t already have them. See List Type and Time Format.
-p <phrase> fails with “Phrase not found”
The name after -p must exactly match a key under phrases: in your config file. Check spelling and
indentation in your YAML — phrase names are case-sensitive.
Weekday names or {#} conjunctions aren’t in the language I expected
The locale setting is matched slightly differently for weekday names (long codes, e.g. de_DE) and
for phrase conjunctions (short codes, e.g. de) — a single value generally only localizes one of the
two. See Locale for the full matching tables and a worked example.
-e opens the wrong editor, or nothing happens
-e uses $EDITOR. If it’s unset, vim is used. Set $EDITOR in your shell profile if you’d
rather use something else:
export EDITOR=nano
Nothing is printed at all
Check whether you (or a script calling obsidian-logging) passed -s/--silent — it suppresses
all output, including error messages, by design.
Still stuck?
Open an issue on GitHub with your config file (with any personal paths redacted) and the exact command you ran.
License and Contact
License
obsidian-logging is licensed under a combined MIT and SPPL (“send a postcard”) license. In short,
it’s an MIT license, plus one extra condition: to be fully compliant, you’re asked to send the author
a postcard.
MIT+SMPPL-OpenSource License
Copyright (c) 2024-2025 Leif Jantzen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
- You must send the author a postcard.
Postcards can be sent to
Leif Jantzen
Myrdalskogen 257
N-5117 Ulset
Norway
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Contact
- Mastodon: @jantzten@mas.to
- Bluesky: leif.jantzen.no
- Issues and source: github.com/ljantzen/obsidian-logging