Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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, or health, 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 (in lib/) — the core library: configuration, templating, and log file management logic. Published on crates.io and documented on docs.rs.
  • obsidian-logging-cli (in app/) — 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

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:

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:

PlaceholderExpands toExample
{year}4-digit year2024
{month}2-digit month01
{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:45
  • 12 → 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 :00 seconds, 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 codeLanguage
en_USEnglish
nb_NONorwegian Bokmål
nn_NONorwegian Nynorsk
de_DEGerman
fr_FRFrench
es_ESSpanish
it_ITItalian
ja_JPJapanese
ko_KRKorean
ru_RURussian
zh_CNChinese

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, nnog
daog
svoch
deund
fret
esy
ite
pte
ruи
jaと
ko와
zh和
anything elseand

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 to and ("de_DE" doesn’t match the short code "de").
  • locale: de → the conjunction is correctly und, 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:

PlaceholderExpands toExample
{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>:

PlaceholderExpands 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:

  1. Add an entry — the default when you pass plain text and no -l/-e/-S flag.
  2. List entries — triggered by -l, or by running with no entry text and no other action flag.
  3. 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, or h: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 — override list_type for this invocation.
  • -f 12 / -f 24 — override time_format for 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 configured list_type just 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

FlagLong formArgumentDescription
-t--timeHH:mm[:ss] or h:mm[:ss] AM/PMOverride 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 | tableOverride list_type for this invocation.
-f—12 | 24Override 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

  • -T and -f take their value as a separate word (e.g. -T table, -f 12), not -Ttable.
  • -c/--category may be given multiple times on a single command line; when adding an entry, only the first -c value 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 for obsidian-logging -l.
  • -v/--version and -h/--help short-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 with Config::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 backing list_type and time_format in the config, with .with_list_type() / .with_time_format() builder methods on Config for 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 on lib/. 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 vault and file_path_format in 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 sure section_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 the vault value 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