Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Logbook

p1tt1p1tt151 downloads

Track time on tasks with org-mode LOGBOOK-style clock entries, stored as Dataview-queryable inline fields.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates3

Track time on tasks with org-mode LOGBOOK-style clock entries, stored as Dataview-queryable inline fields.

100% local — no network requests, no telemetry, no external services. Logbook only writes plain Markdown to your vault; nothing ever leaves your machine.

Installation

From the community browser

Settings → Community plugins → Browse → search "Logbook" → Install → Enable.

Beta install via BRAT

To test the latest pre-release build before it lands in the community browser:

  1. Install the BRAT plugin (obsidian42-brat) from the community browser.
  2. In Obsidian: Cmd/Ctrl+P → BRAT: Add a beta plugin for testing.
  3. Paste https://github.com/p1tt1/obsidian-logbook into the modal.
  4. Enable Logbook under Settings → Community plugins.

Manual install

  1. Download main.js, manifest.json, and styles.css from the latest release.
  2. Copy them to <vault>/.obsidian/plugins/logbook/.
  3. Reload Obsidian → Settings → Community plugins → enable Logbook.

Usage

Clocking in

Cursor on a task line → run Logbook: Clock in to task from the command palette.

Clocking out

Run Logbook: Clock out → the open entry gains an end timestamp.

Commands

Command What it does
Logbook: Clock in to task Inserts an open clock child below the current task. Hidden when cursor isn't on a task line.
Logbook: Clock out Closes the active task's open entry. Hidden when no clock is active.
Logbook: Clock report: current file Modal showing closed clock entries from the active file.
Logbook: Find all open clocks Vault-wide scanner — surfaces any forgotten open entries.

DataviewJS examples

All queries below assume the Dataview plugin is installed with DataviewJS enabled. The Logbook plugin writes plain Markdown either way — Dataview is optional and only required if you want to run these queries.

How Dataview sees clock entries: Dataview indexes [clock:: START--END] as a plain string — the -- separator is not a recognized Dataview construct, so child.clock is text, not a pair of DateTime objects. All examples below split this string manually. If you prefer native DateTime arithmetic without splitting (e.g. item.end - item.start), see the two-field alternative at the end of this page.

DQL (without JavaScript)

Clock entries are accessible via FLATTEN file.lists — Dataview indexes every list item, including the clock children. Use split() and date() to extract timestamps from the string value.

All closed clock entries, vault-wide:

TABLE WITHOUT ID
  file.link AS File,
  date(split(item.clock, "--")[0]) AS Started,
  date(split(item.clock, "--")[1]) AS Ended,
  date(split(item.clock, "--")[1]) - date(split(item.clock, "--")[0]) AS Duration
FLATTEN file.lists AS item
WHERE item.clock AND !endswith(item.clock, "--")
SORT date(split(item.clock, "--")[0]) DESC

Limitation: DQL cannot easily surface the parent task name alongside each clock entry. For task-grouped reports, use the DataviewJS examples below.

Paste each DataviewJS block into a note inside a dataviewjs code fence.

1. Total time per task (current file)

Sums all closed clock entries for each task in the current file and outputs a table with task name and total time logged. Open (running) entries are skipped — only completed intervals are counted.

// Total time logged per task in the current file — closed entries only.
function parseClockValue(raw) {
  const [startStr, endStr] = raw.split("--");
  const start = dv.date(startStr.trim());
  const end = endStr && endStr.trim() !== "" ? dv.date(endStr.trim()) : null;
  return { start, end };
}

const rows = [];

for (const task of dv.current().file.tasks) {
  let taskMinutes = 0;
  for (const child of (task.children ?? [])) {
    if (!child.clock) continue;
    const { start, end } = parseClockValue(String(child.clock));
    if (!end) continue; // skip open (running) entries
    if (!start) continue;
    const mins = Math.floor(end.diff(start, "minutes").minutes);
    taskMinutes += mins;
  }
  if (taskMinutes > 0) {
    const h = Math.floor(taskMinutes / 60);
    const m = taskMinutes % 60;
    rows.push([task.text, `${h}:${String(m).padStart(2, "0")}`]);
  }
}

dv.table(["Task", "Total"], rows);

Sample output: a two-column table listing each task from the current file alongside its cumulative time, e.g. Fix login bug | 2:30.

2. Daily time rollup (vault-wide)

Scans all files in the vault for clock entries from today and groups results by file and task. Useful as a daily standup summary — see everything you worked on today across all your notes.

// Today's clocked entries across the entire vault — grouped by file and task.
const today = dv.date("today");

function parseClockValue(raw) {
  const [startStr, endStr] = raw.split("--");
  const start = dv.date(startStr.trim());
  const end = endStr && endStr.trim() !== "" ? dv.date(endStr.trim()) : null;
  return { start, end };
}

const rows = [];

for (const page of dv.pages()) {
  for (const task of page.file.tasks) {
    for (const child of (task.children ?? [])) {
      if (!child.clock) continue;
      const { start, end } = parseClockValue(String(child.clock));
      if (!start || !end) continue; // skip open entries
      if (start < today || start >= today.plus({ days: 1 })) continue;

      const mins = Math.floor(end.diff(start, "minutes").minutes);
      const h = Math.floor(mins / 60);
      const m = mins % 60;
      rows.push([
        task.text,
        page.file.link,
        start.toFormat("HH:mm"),
        end.toFormat("HH:mm"),
        `${h}:${String(m).padStart(2, "0")}`,
      ]);
    }
  }
}

dv.table(["Task", "File", "Start", "End", "Duration"], rows);

Sample output: a table of every completed session from today across all vault files, with start time, end time, and duration per row.

3. Weekly summary (by ISO week)

Groups all closed clock entries by ISO week number and totals the time per week. Useful for reviewing your productivity trend over several weeks.

// Total time per ISO week — all closed entries across the vault.
function parseClockValue(raw) {
  const [startStr, endStr] = raw.split("--");
  const start = dv.date(startStr.trim());
  const end = endStr && endStr.trim() !== "" ? dv.date(endStr.trim()) : null;
  return { start, end };
}

// Map: ISO week label (e.g. "2026-W25") → total minutes
const weekTotals = new Map();

for (const page of dv.pages()) {
  for (const task of page.file.tasks) {
    for (const child of (task.children ?? [])) {
      if (!child.clock) continue;
      const { start, end } = parseClockValue(String(child.clock));
      if (!start || !end) continue; // skip open entries

      const weekLabel = start.toFormat("kkkk-'W'WW");
      const mins = Math.floor(end.diff(start, "minutes").minutes);
      weekTotals.set(weekLabel, (weekTotals.get(weekLabel) ?? 0) + mins);
    }
  }
}

const rows = [...weekTotals.entries()]
  .sort((a, b) => a[0].localeCompare(b[0]))
  .map(([week, mins]) => {
    const h = Math.floor(mins / 60);
    const m = mins % 60;
    return [week, `${h}:${String(m).padStart(2, "0")}`];
  });

dv.table(["Week", "Total"], rows);

Sample output: a two-column table with one row per ISO week (e.g. 2026-W25 | 14:20), sorted chronologically.

4. Running clock indicator

Scans the entire vault for any open [clock:: START--] entries — tasks where you clocked in but have not yet clocked out. Useful as a status widget or for recovering forgotten sessions.

// Find any task with an open [clock:: X--] entry across the entire vault.
// Useful for spotting forgotten clocks or verifying plugin state.
const rows = [];

for (const page of dv.pages()) {
  for (const task of page.file.tasks) {
    for (const child of (task.children ?? [])) {
      if (!child.clock) continue;
      const raw = String(child.clock);
      // An open entry ends with "--" and has no end datetime
      if (!raw.endsWith("--")) continue;

      const startStr = raw.replace(/--$/, "").trim();
      const start = dv.date(startStr);
      if (!start) continue;

      const { DateTime } = dv.luxon;
      const now = DateTime.now();
      const mins = Math.floor(now.diff(start, "minutes").minutes);
      const elapsed = `${Math.floor(mins / 60)}:${String(mins % 60).padStart(2, "0")}`;

      rows.push([task.text, page.file.link, startStr, elapsed]);
    }
  }
}

if (rows.length === 0) {
  dv.paragraph("No running clocks found.");
} else {
  dv.table(["Task", "File", "Started", "Elapsed"], rows);
}

Sample output: a table listing any task with an open clock entry — or the message "No running clocks found." if all sessions are closed.

5. Current file summary (grand total)

Adds up every closed clock entry in the current file and outputs a single grand total. A quick way to see how much time you have logged on everything in the active note.

// Grand total of all closed clock time in the current file.
function parseClockValue(raw) {
  const [startStr, endStr] = raw.split("--");
  const start = dv.date(startStr.trim());
  const end = endStr && endStr.trim() !== "" ? dv.date(endStr.trim()) : null;
  return { start, end };
}

let totalMinutes = 0;

for (const task of dv.current().file.tasks) {
  for (const child of (task.children ?? [])) {
    if (!child.clock) continue;
    const { start, end } = parseClockValue(String(child.clock));
    if (!start || !end) continue; // skip open entries
    totalMinutes += Math.floor(end.diff(start, "minutes").minutes);
  }
}

const h = Math.floor(totalMinutes / 60);
const m = totalMinutes % 60;
dv.paragraph(`Total: ${h}:${String(m).padStart(2, "0")}`);

Sample output: a single line such as Total: 7:45 — the sum of all closed clock sessions in the current file.

Data format reference

Clock entries are stored as indented child list items directly beneath each task:

- [ ] Fix login bug
  - [clock:: 2026-06-15T09:00--2026-06-15T10:30]
  - [clock:: 2026-06-16T14:00--]
  • [clock:: START--END] — closed entry (completed session)
  • [clock:: START--] — open entry (clock is running)
  • Datetime format: YYYY-MM-DDTHH:mm (local time, no timezone, no seconds)
  • Indentation: parent task's indent + 2 spaces (or one tab if useTab: true in the vault)

Because clock children are list items with inline fields, DataviewJS exposes them via task.children[].clock:

for (const page of dv.pages()) {
  for (const task of page.file.tasks) {
    for (const child of (task.children ?? [])) {
      if (child.clock) {
        // child.clock is a plain STRING — Dataview stores the full "START--END" value as text.
        // Split on "--" and parse each half with dv.date() to get DateTime objects.
        // "YYYY-MM-DDTHH:mm--YYYY-MM-DDTHH:mm" → closed entry
        // "YYYY-MM-DDTHH:mm--"                 → open/running entry
      }
    }
  }
}

Two-field alternative: if you want Dataview to treat start and end as native DateTime objects — enabling item.end - item.start directly in DQL without string splitting — use two separate inline fields per entry instead of one:

- [ ] Fix login bug
  - [start:: 2026-06-15T09:00] [end:: 2026-06-15T10:30]

The tradeoff: two fields per session (more verbose) vs. the compact [clock:: START--END] format Logbook writes. The plugin uses the single-field format to match org-mode convention and keep entries readable.

Requirements

  • Obsidian 1.5.7 or newer (required for vault.getFileByPath())
  • Works on desktop and mobile (isDesktopOnly: false)
  • Optional: Dataview plugin with DataviewJS enabled — only required for the query examples; the plugin writes plain Markdown either way.

Edge cases & known behaviors

  • File deleted while clocked in — state is cleared silently on next load; the clock interval is lost.
  • Multiple open entries (manual edit / corruption) — Logbook: clock out closes all open entries with the same end time and shows a Notice listing the extras.
  • File rename — the active clock follows the renamed file automatically (vault.on('rename') listener).
  • Tab-indented vaults — clock children are indented using your vault's useTab + tabSize settings.
  • Running clock > 24h — a Notice surfaces on next load to remind you to close it.

Contributing

git clone https://github.com/p1tt1/obsidian-logbook
cd obsidian-logbook
npm install
npm run dev      # esbuild watch mode — rebuilds main.js on save

Symlink into a dev vault:

ln -s /absolute/path/to/obsidian-logbook /path/to/vault/.obsidian/plugins/logbook

Install the Hot Reload plugin in the dev vault to auto-reload on main.js changes. The symlink target directory name must be logbook — it must match manifest.json.id.

Tests: npm test (Vitest, 79+ cases for the pure modules under src/core/).

License

MIT — see LICENSE.

HealthExcellent
ReviewCaution
About
Track time on tasks using org-mode LOGBOOK-style clock entries stored as plain Markdown inline fields, readable by Dataview. Clock in and out on task lines, view file reports, and find open clocks vault-wide while keeping all data local with no external services.
TasksProject management
Details
Current version
0.3.0
Last updated
2 days ago
Created
2 months ago
Updates
3 releases
Downloads
51
Compatible with
Obsidian 1.5.7+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
p1tt1p1tt1
GitHubp1tt1
  1. Community
  2. Plugins
  3. Tasks
  4. Logbook

Related plugins

Tasks

Track tasks across your vault. Supports due dates, recurring tasks, done dates, sub-set of checklist items, and filtering. Maintained by Clare Macrae and Ilyas Landikov, created by Martin Schenck.

Project Manager

Full-featured project management: stunning Gantt charts, Kanban boards, Table views, customizable fields, due date notifications.

Base Board

Organize notes into Kanban boards using frontmatter properties. Drag and drop cards between columns powered by Bases.

Operon

Task and project management system that unifies inline tasks and file-based tasks in the same workflows with Tables, Filters, Calendar planning, Kanban boards, pinned tasks, and time tracking.

TaskChute Plus

Execute TaskChute that slots today's tasks, tracks projects, adds comments, and keeps you focused on now.

TickTickSync

Sync TickTick tasks.

Apex Dashboard

Your personal command center — memos, todos, and projects in one stunning glassmorphism dashboard.

Task Zero

A keyboard-first GTD task management system.

Smart Gantt

Generate Gantt charts from your tasks.

Proletarian Wizard Task Manager

Track your tasks across all the notes in your workspace. Organize your day. Plan your work