Skip to content

hcgatewood/jdd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

14 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

jdd: JSON diff diver

jdd is a CLI for recording and navigating diff-over-time changes to a JSON object โ€” a JSON time machine.

jdd logo

Features

jdd demo

jdd is a TUI for exploring changes to a JSON object over time. When find yourself tracking down distributed system changes, debugging configuration drift, or investigating incident root causes, jdd can help you see what changed, when it changed, and uncovery why it changed:

  • History of a JSON object (via recording, streaming, or pre-recorded history)
  • Navigate forward and backward in time
  • View diffs between each point in time
  • Inspect object content at any point in time
  • Filter changes by object content
  • Jump to line number / change index

Installation

Homebrew

brew install hcgatewood/tap/jdd

Manual

# Place jdd into PATH and make executable, something like...
curl -fsSL https://raw.githubusercontent.com/hcgatewood/jdd/main/jdd -o /usr/local/bin/jdd
chmod +x /usr/local/bin/jdd
# ...and then install dependencies

Examples

Browse a pre-recorded history

jdd history.jsonl

Browse live changes

# Poll in-place
jdd --poll "cat obj.json"

# Watch in-place
jdd --watch obj.json

# Stream
kubectl get pod YOUR_POD --watch -o json | jdd

Record changes into a history file

# Poll in-place + record changes
jdd --poll "cat obj.json" --save history.jsonl

# Watch in-place + record changes
jdd --watch obj.json --save history.jsonl

# Stream + record changes
kubectl get pod YOUR_POD --watch -o json | jdd --save history.jsonl

Diff multiple files

# Browse history with multiple files as successive versions
jdd v1.json v2.json v3.json

Tail an ongoing recording, or don't tail a stream

# Tail an ongoing recording
jdd -f history.jsonl

# Sponge a history/stream before browsing
cat history.jsonl | jdd --no-follow

Inspect a single JSON object

# Inspect an object via JSON paths (similar to jnv, jid)
jdd obj.json  

Usage

Subcommands: dive (alias: hist) is the JSON-over-time navigator, surf (alias: insp) is the single-JSON inspector.

Navigate successive versions of a JSON object.

Usage: jdd [COMMAND] [FILE...] [OPTIONS]

Args:
    COMMAND             The jdd command to run (dive, surf). If omitted, guessed from FILE extension and options.
    FILE                Input file(s). If omitted, reads from stdin. If multiple files, treat as successive versions.

Commands:
    (no command)        Guess command from FILE extension
    dive|hist           Navigate successive versions of a JSON object, via streaming or stored JSONL file.
    surf|insp           Explore a single JSON object via interactive query preview.
    help                Show this help message.

Options:
    --info              Show more info, like the file being processed.

Dive options:
    --save SAVE_FILE    Record history: specify SAVE_FILE to save observed history of changes (default: temporary file; recommendation: .jsonl extension).
    --watch COMMAND     Watch mode: watch FILE for in-place changes to get new JSON object versions, appending to browsed history. (Alias: -w)
    --poll COMMAND      Poll mode: run COMMAND periodically to get new JSON object versions, appending to browsed history. (Alias: -p)
    --interval N        Poll mode interval in seconds between COMMAND executions (default: 5). (Alias: --int, -i)
    --follow            Follow: keep reading new entries as they are appended to FILE (default if no FILE is given). (Alias: -f)
    --no-follow         Sponge: disable follow mode. (Alias: --sponge)
    --all               Disable uniqueness filter: include all consecutive entries, even if they're identical (if using FILE and --bare, also prevents creating an intermediate file).
    --no-preprocess     Skip preprocessing: don't preprocess each entry into a single line (configure preprocessor via JDD_PREPROCESSOR). (Alias: --bare)
    --tag OBJECT_PATH   Add a tag: specify JSON path to use as tag for each entry.

Dive keybindings:
    Ctrl-/              Help (this message -- q to quit).
    Ctrl-N/Down         Down.
    Ctrl-P/Up           Up.
    Ctrl-J/Ctrl-D       Page down.
    Ctrl-K/Ctrl-U       Page up.
    Ctrl-L              Preview page down.
    Ctrl-H              Preview page up.
    Ctrl-F              Jump to first entry.
    Ctrl-G              Jump to last entry.
    Ctrl-V              Jump to previous match.
    Ctrl-B              Jump to next match.
    Ctrl-T              Jump to best match.
    Ctrl-R              Toggle raw view.
    Ctrl-W              Clear query.
    Ctrl-Y              Copy current JSON entry to clipboard.
    Ctrl-O              Output current JSON entry to stdout and exit.
    Tab                 Toggle 'goto' mode for jump to line number.
    Enter               Open in fx for further inspection.

Configuration

Environment variables to customize behavior; generally they can be set to the name of an installed tool or a command string.

  • JDD_DIFFER diff engine: jsondiffpatch (default), jd, json-diff, jsondiff, gojsondiff, difftastic
  • JDD_INSPECTOR single-object inspector: fx (default), jless, jdd surf, jnv, jid
  • JDD_PREVIEWER preview engine: jq (default: jq --color-output .), jaq
  • JDD_PREPROCESSOR optional preprocessor for each item: jq (default: jq --sort-keys --compact-output --unbuffered .), jaq, dasel, jj
  • JDD_COPIER clipboard copier: pbcopy (default), xclip, etc.
  • JDD_SHOW_FILE show file name in fzf header (default: unset)
  • JDD_NO_HELP don't show help keybinding in dive (default: unset)
  • JDD_DEBUG enable debug logging (default: unset)
  • Additional tools: fzf, gron, mlr

How I use jdd

I mainly use jdd to track changes to Kubernetes objects.

Live-streamed changes

kubectl get pod MY_POD --watch -o json | jdd

Offline changes from OpenSearch-stored audit logs

# kis opens jdd for downloaded K8s Insights data.
#
# Usage: kis insights.csv
function kis {
    cat "${1:-/dev/stdin}"  \
    | mlr --icsv --ojsonl + put '$* = json_parse($UpdatedObject)' \
    | jq --sort-keys --compact-output '
        select(.kind != "Event")
        | del(.metadata.resourceVersion)
        | del(.status.conditions?[]?.lastTransitionTime)
        | del(.status.conditions?[]?.lastUpdateTime)
        | del(.status.placementStatuses?[]?.conditions?[]?.lastTransitionTime)
        | del(.status.placementStatuses?[]?.conditions?[]?.lastUpdateTime)
    ' \
    | jdd --tag '.metadata.generation' --sponge
}

See also

  • ๐Ÿช„ Kuba: the magical kubectl companion
  • โšก๏ธ Dotsync: dotfiles everywhere, instantly
  • โ˜๏ธ Appa: Markdown previews with live reload
  • ๐Ÿ”ฎ PDate: human-readable dates and times
  • ๐Ÿ”ฌ Vis: visualize fuzzy tabular data

About

๐Ÿ™ JSON diff diver โ€” the time machine for your JSON objects

Topics

Resources

Stars

Watchers

Forks