obsidian-logging 1.3.5

A journaling/logging CLI that stores logs in Obsidian markdown files
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# Obsidian logging - Log to Obsidian Journal from command line 

[![Crates.io](https://img.shields.io/crates/v/obsidian-logging.svg)](https://crates.io/crates/obsidian-logging)
[![Documentation](https://docs.rs/obsidian-logging/badge.svg)](https://docs.rs/obsidian-logging)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

This little utility written in Rust makes it possible to log directly to todays daily note from the linux command line. 

## Version 1.3.0 Changes - Timestamp Format Update

**Important:** Starting with version 1.3.0, timestamps now include seconds (HH:mm:ss format instead of HH:mm).

**What this means for you:**
- New entries will have timestamps with seconds (e.g., `14:30:45` instead of `14:30`)
- When using the `-t` flag, you can specify seconds: `-t 14:30:45` or omit them: `-t 14:30` (defaults to `:00`)
- **Existing log files:** When you add a new entry to a file, all existing timestamps in that log section will be automatically reformatted to include seconds (e.g., `14:30` becomes `14:30:00`)
- Duplicate timestamp detection: If you add an entry with a timestamp that already exists, the seconds will be incremented until a unique timestamp is found

This change ensures better precision and prevents timestamp collisions when logging multiple entries at the same minute.

## License 

This software is licensed under a combined MIT and SPPL license.  It is basically a MIT license, but in order to be compliant you need to send me a postcard.  Details in LICENSE.md

## Installation

### Binary (Command-line tool)

You can install the obsidian-logging CLI tool using cargo:

```bash
cargo install obsidian-logging-cli
```

Or download pre-built binaries directly from the [github releases page](https://github.com/ljantzen/obsidian-logging/releases).

### Library (For Rust projects)

To use obsidian-logging as a library in your Rust project:

```toml
[dependencies]
obsidian-logging = "1.3"
```

Then in your code:

```rust
use obsidian_logging::{add, edit, list, Config};
```

See the [API documentation](https://docs.rs/obsidian-logging) for library usage.

### Build from source

Clone the repository and build the CLI:

```bash
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 everything (library + CLI):

```bash
cargo build --workspace --release
```

## Project Structure

This project is organized as a Cargo workspace with two crates:

- **`lib/`** β€” The core library crate (`obsidian-logging`)
  - Contains all the logging, configuration, and file management logic
  - Published to [crates.io]https://crates.io/crates/obsidian-logging
  - Can be used as a dependency in other Rust projects

- **`app/`** β€” The CLI binary crate (`obsidian-logging-cli`)
  - Provides the command-line interface and argument parsing
  - Depends on the library crate
  - Distributed as pre-built binaries via [GitHub Releases]https://github.com/ljantzen/obsidian-logging/releases

This separation allows the logging functionality to be reused in other Rust applications while keeping the CLI focused and maintainable.

## Development

### Quick start

Clone and build the project:

```bash
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
```

### Using justfile

This project includes a `justfile` for common development tasks. Install `just` if you don't have it:

```bash
cargo install just
```

Then use `just` to run tasks:

```bash
# 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              # Run fmt-check + clippy + test (pre-commit)

# 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 specific version
just publish            # Publish library to crates.io

# Utility
just help               # List all available commands
just clean              # Clean build artifacts
```

The `check` target is recommended before committing:

```bash
just check
```

## Obsidian Native CLI

Obsidian now has a native CLI tool (available as Early Access with Catalyst License). You can see the [Obsidian CLI documentation](https://help.obsidian.md/cli) for more information.

### When to use obsidian-logging vs Obsidian CLI

**Use obsidian-logging if you want:**
- Quick, focused daily logging with timestamps
- Organized logs by category (work, personal, health, etc.)
- Reusable phrase templates for common entries
- A lightweight tool optimized for quick note capture
- Free and open-source solution

**Use Obsidian CLI if you need:**
- General vault operations and querying
- Advanced automation and scripting
- Plugin management
- Full programmatic control of Obsidian
- Integration with complex workflows

For simple daily journaling from the command line, obsidian-logging provides a streamlined, dedicated solution. For comprehensive vault automation, Obsidian's native CLI offers broader capabilities.

## Tip

Since obsidian-logging is quite a mouthful to type every time, it is recommended to create a short alias.  E.g

```
alias q=` obsidian-logging`
```
(Note: the space before the program name stops the command from being entered into command history)

On Windows, the `doskey` command can be used to create a macro: 

```
doskey q=obsidian-logging
```

## Configuration file

Obsidian-logging reads ~/.config/obsidian-logging/obsidian-logging.yaml on startup.  If it does not exist, obsidian-logging will create it and prompt for some of the values. 
This is a file that uses the yaml configuration format.  See obsidian-logging.example.yaml for what can be configured. 

Obsidian-logging looks for a marker that signifies where the log entries block will start. Log entries must be consecutive without empty lines. The marker is specified in the config file.

### Category-specific section headers

The default block marker is specified in the configuration file with the property `section_header`. 

Example configuration:

```yaml
section_header: "## πŸ•—"
```

obsidian-logging can log to a number of different sections in the daily log. These are called log `categories`.  You can define category-specific section headers in the config yaml using the pattern `section_header_<category_name>`. When using the `-c` or `--category` flag, obsidian-logging will look for a section header with this pattern and log entries to that section instead of the default section.

Example configuration:

```yaml
section_header: "## πŸ•—"
section_header_work: "## πŸ’Ό Work"
section_header_personal: "## 🏠 Personal"
section_header_health: "## πŸ₯ Health"
```

So when you run `obsidian-logging -c work "Meeting"`, the entry will be logged under the "## πŸ’Ό Work" section. If the category doesn't have a corresponding section header defined, entries will be logged to the default section specified by `section_header`.

### Predefined Phrases

You can define common logging phrases in your configuration file to use with the `-p` or `--phrase` option. This allows you to create shorthand references for frequently used log entries.

**Basic phrase configuration:**
```yaml
phrases:
  meeting: "Team meeting with stakeholders"
  gym: "Workout at the gym"
  lunch: "Lunch break"
  doctor: "Doctor appointment"
```

**Phrases with argument expansion:**
```yaml
# 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

phrases:
  # Simple phrases
  meeting: "Team meeting with stakeholders"
  gym: "Workout at the gym"
  
  # Phrases with argument expansion
  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
  
  # Phrases with {#} placeholder for comma-separated lists
  meeting_with: "Team meeting with {#}"          # "John and Jane" or "John, Jane and Bob"
  call_with: "Phone call with {#}"              # "Alice and Bob" or "Alice, Bob and Charlie"
  project_with: "Working on {#}"                # "Frontend and Backend"
  exercise_with: "Exercise: {#}"                # "Running and Swimming"
```

**Usage examples:**
```bash
# Basic phrases
obsidian-logging -p meeting
obsidian-logging -p gym -c health

# Phrases with arguments
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 exercise "Running 5km"    # "Exercise: Running 5km"

# Phrases with {#} placeholder for comma-separated lists
obsidian-logging -p meeting_with John Jane        # "Team meeting with John and Jane"
obsidian-logging -p call_with Alice Bob Charlie  # "Phone call with Alice, Bob and Charlie"
obsidian-logging -p project_with Frontend Backend # "Working on Frontend and Backend"
``` 

## Environment variable 

If specified, $OBSIDIAN_VAULT_DIR will override the `vault` value in `obsidian-logging.yaml`


## Command line switches 

### No arguments 

When invoking `obsidian-logging` without any arguments the entries of the current day, if any, will be listed.  This is equivalent to `obsidian-logging -l`

### No switches

When invoking the command `obsidian-logging This is a log entry` obsidian-logging will append the string `This is a log entry` to the default log section of the markdown daily note. 
A timestamp will be prepended according to the chosen list mode. If list mode is `bullet`, '- HH:mm:ss ' is prepended to the log statement (e.g., `- 14:30:45 log entry`).  If list mode is 'table', the log statement is 
wrapped in markdown table column separators:  `| HH:mm:ss | log statement|` (e.g., `| 14:30:45 | log entry |`).

**Note:** Timestamps now include seconds (HH:mm:ss format). When you add a new entry, all existing entries in that log section will be reformatted to include seconds if they don't already have them. This ensures consistency across all entries.

## Usage Examples

```bash
# List today's entries
obsidian-logging

# Add a log entry to the default section
obsidian-logging "Had lunch with colleagues"

# Add an entry with a specific time (seconds default to 00 if not provided)
obsidian-logging -t 14:30 "Team standup meeting"        # Becomes 14:30:00
obsidian-logging -t 14:30:45 "Team standup meeting"     # Explicit seconds

# 12-hour format examples
obsidian-logging -t 2:30 PM "Afternoon meeting"         # Becomes 02:30:00 PM
obsidian-logging -t 2:30:45 PM "Afternoon meeting"      # Explicit seconds

# Add entries to different categories
obsidian-logging -c work "Code review completed"
obsidian-logging -c personal "Gym workout - 45 minutes"
obsidian-logging -c health "Annual checkup scheduled"

# Combine category with time override
obsidian-logging -c work -t 9:00 "Daily standup"

# Use predefined phrases
obsidian-logging -p meeting                    # Basic phrase
obsidian-logging -p gym -c health             # Phrase with category
obsidian-logging -p meeting_with John Smith   # Phrase with arguments
obsidian-logging -p call_with Alice -t 14:30 # Phrase with arguments and time

# Read from stdin (useful for piping)
echo "Quick note" | obsidian-logging -S
cat notes.txt | obsidian-logging -c work -S

# List entries from previous days
obsidian-logging -b 1  # Yesterday's entries
obsidian-logging -b 7  # One week ago

# List entries by category
obsidian-logging -l -c work      # List only work entries
obsidian-logging -l -c personal  # List only personal entries
obsidian-logging -l -c work -c personal  # List work and personal entries
obsidian-logging -l -c all       # List entries from all categories
obsidian-logging -b 1 -c work    # List work entries from yesterday

# Edit today's file directly
obsidian-logging -e
```

### -t or --time 

The timestamp may be overridden by specifying the `-t/--time` switch. You can provide timestamps in either `HH:mm` or `HH:mm:ss` format. If seconds are not provided, they default to `00`.

**Format examples:**
- 24-hour format: `14:30` (becomes `14:30:00`) or `14:30:45`
- 12-hour format: `2:30 PM` (becomes `02:30:00 PM`) or `2:30:45 PM`

Log entries are sorted chronologically before being added to the markdown file. If a timestamp already exists in the log, the seconds will be incremented until a unique timestamp is found.

**Important:** When you add a new entry, all existing entries in that log section will be reformatted to include seconds (HH:mm:ss format) if they don't already have them. This ensures consistency but means existing timestamps without seconds will be modified. 


### -l  or --list 

You can list the current days log entries by specifying the -l option.  If obsidian-logging is invoked without any arguments, this is the default action.

When combined with the `-c` or `--category` option, only entries from the specified category section will be listed. If no category-specific section header is found, entries from the default section will be shown. The `-c` option can be specified multiple times to list entries from multiple categories, or use `-c all` to list entries from all categories.

Examples:
```bash
obsidian-logging -l                    # List entries from default section
obsidian-logging -l -c work            # List entries from work category
obsidian-logging -l -c personal        # List entries from personal category
obsidian-logging -l -c work -c personal # List entries from work and personal categories
obsidian-logging -l -c all             # List entries from all categories
obsidian-logging -l -c unknown         # List entries from default section (fallback)
```

### -b <days> or --back <days>

By specifying `-b <number>` you can go back in time and list the logs `number` of days ago. `obsidian-logging -b 0` is the same as `obsidian-logging -l`.  `-b` can be combined with `-c`. 

### -e or --edit

Invokes $EDITOR with todays file.  Uses vim if $EDITOR is not set,

### -f or --time-format 

Specifies 12H or 24H time format.  24H is default.   Overrides `time_format` in obsidian-logging.yaml. Combining 12-hour and 24 hour timestamps when adding logs may yield unpredictable results. 

### -s or --silent 

Do not output anything, not even error messages 

### -h or --help 

Print command line argument help

### -T list mode 

Specifies the list output mode when obsidian-logging -l is called. Valid arguments are -T bullet and -T table. Overrides the list mode in obsidian-logging.yaml configuration file

### -v or --version 

Outputs the current version string and exits execution

### -S or --stdin 
If specified, input will be read from stdin instead of command line arguments, allowing piping of log statements into the program.  Carriage return or linefeed characters will be removed, and log statement will be logged as a single line.

### -c or --category
Specifies a category for the log entry. The entry will be logged to a section identified by the `section_header_<category>` property in the configuration file. If no category-specific section header is found, the entry will be logged to the default section specified by `section_header`.

When used with the `-l` option, this flag can be specified multiple times to list entries from multiple categories. Use `-c all` to list entries from all categories.

Examples:
```bash
# Adding entries
obsidian-logging -c work "Team meeting at 2pm"
obsidian-logging -c personal "Gym workout"
obsidian-logging -c health "Doctor appointment"

# Listing entries
obsidian-logging -l -c work                    # List work entries only
obsidian-logging -l -c work -c personal        # List work and personal entries
obsidian-logging -l -c all                     # List entries from all categories
```

### -p or --phrase
Use a predefined phrase from the configuration file with optional argument expansion. This allows you to define common logging phrases in your config and reference them with shorthand.

**Argument Expansion Support:**
- `{0}`, `{1}`, `{2}`, etc. - Replace with specific argument by index
- `{*}` - Replace with all arguments joined by spaces
- `{#}` - Replace with all arguments in comma-separated list with proper conjunction

Examples:
```bash
# Basic phrase usage (no arguments)
obsidian-logging -p meeting
obsidian-logging -p gym -c health

# Phrase with argument expansion
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"

# Phrase with {#} placeholder for comma-separated lists
obsidian-logging -p meeting_with John Jane        # "Team meeting with John and Jane"
obsidian-logging -p call_with Alice Bob Charlie  # "Phone call with Alice, Bob and Charlie"
obsidian-logging -p project_with Frontend Backend # "Working on Frontend and Backend"

# Combine with other options
obsidian-logging -p meeting_with John -t 14:30    # With specific time
obsidian-logging -p doctor_with "Dr. Smith" -c health  # With category
``` 


## Example Output

With the category functionality, your daily notes can be organized into different sections. Here's an example of what a daily note might look like:

```markdown
# 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
```

**Note:** All timestamps now include seconds. If you have existing log files with timestamps in `HH:mm` format, they will be automatically reformatted to `HH:mm:ss` format (with `:00` seconds) when you add a new entry to that file.

## Screenshots

### Bullet mode 

![image](https://github.com/user-attachments/assets/72c50c59-5185-4cb4-a871-473a8fd8b96f)

### Table mode 

![image](https://github.com/user-attachments/assets/ad3fe2c4-9a33-4272-a059-3d22617cef97)


# Contact info 

https://mas.to/@jantzten

https://bsky.app/profile/leif.jantzen.no