Skip to content

Record the Redis commands each action runs in a browser side panel - #6508

Open
xiajingg wants to merge 1 commit into
redis:mainfrom
xiajingg:feat/command-log-panel
Open

xiajingg wants to merge 1 commit into
redis:mainfrom
xiajingg:feat/command-log-panel

Conversation

@xiajingg

@xiajingg xiajingg commented Sep 19, 2026

Copy link
Copy Markdown

What

The browser gets a third panel that lists the Redis commands RedisInsight
itself sent, grouped by the action that triggered them, so you can see what a
click actually does.

Why

Today the only way to see the commands behind an action is to read the source
or run MONITOR. MONITOR streams every client's traffic and has a real
performance cost on the server, so it is not something you leave on while
learning.

This records only RedisInsight's own commands, at the point they are sent.

How it works

  1. Client-level hookRedisClient gains a static handler and a
    protected logCommands() that the ioredis and node-redis clients call right
    before sending. Nothing is recorded unless a handler is installed, and the
    client layer keeps zero imports from the command-log module.
  2. Operation name — a global OperationContextInterceptor runs each HTTP
    request inside an AsyncLocalStorage that carries a human readable
    operation name (Load key details, Delete key, …). The client reads it
    when logging, so every command is attributed to the action that caused it.
  3. Transport — entries go over socket.io to a room per instance, reusing
    the existing socket configuration.
  4. Serialisation — command lines are truncated and batches capped, so one
    bulk action cannot flood the panel.
  5. Readable keys — RedisInsight passes keys, fields and values as
    Buffers. Decoding them as UTF-8 when they hold text (and only falling back
    to a <binary N bytes> placeholder for genuinely binary payloads) is what
    makes the panel useful — otherwise the most informative part of every
    command is hidden.

UI

  • New command-log-panel on the browser page, in a third resizable column.
  • Consecutive commands from the same action are grouped under a header with
    the operation name and a count; repeated identical lines collapse to ×N.
  • Pause / resume and clear controls.

Changes

Backend

  • api/src/modules/command-log/ — new module (entry model, serialisation,
    service, socket.io gateway).
  • api/src/common/context/operation.context.ts,
    api/src/common/interceptors/operation-context.interceptor.ts — new.
  • api/src/modules/redis/client/** — hook in the ioredis and node-redis
    clients.
  • api/src/app.module.ts, api/src/main.ts — register the module and the
    global interceptor.

Frontend

  • ui/src/slices/browser/commandLog.ts — new slice.
  • ui/src/pages/browser/components/command-log-panel/ — new panel.
  • ui/src/pages/browser/BrowserPage.tsx — third column.
  • ui/src/i18n/locales/{en,bg}.json — 7 new keys.

Tests

  • CommandLogList.spec.tsx (new, 10 cases) — grouping by operation, a new
    group when the operation changes or the gap is too large, collapsing
    repeated lines, non-adjacent repeats stay separate.
  • End-to-end against a real Redis: commands are recorded with the right
    operation name and database, key names stay readable, and commands from
    another client are not recorded
    (which is the point versus MONITOR).
  • Packaged macOS build driven through its renderer: 18/18 checks pass.

Screenshots

The panel after clicking a key — note the readable key names:

command log

Notes for reviewers

  • Recording is best-effort: it is wrapped in a try/catch so a serialisation
    problem can never break the command itself.
  • The panel is read-only and bounded (1000 entries in the UI, batches capped
    at 50 commands); nothing is persisted.
  • Happy to split this out, rename things, or drop it if the direction does not
    fit the roadmap.

Note

Medium Risk
Recording runs on the Redis client hot path for every command, but it is guarded, bounded, and non-blocking; streamed command lines may still expose key names and truncated argument text to connected clients.

Overview
Adds a Commands panel on the Browser page so users can see Redis commands this app sends, grouped by the UI action that triggered them—without turning on MONITOR.

On the API, a new CommandLogModule registers a handler on RedisClient that records commands before ioredis/node-redis send them, with truncated/safe serialization (text buffers decoded, large/binary args capped). OperationContextInterceptor binds human-readable operation labels per HTTP request via AsyncLocalStorage, and batches are pushed over a dedicated socket.io namespace (commandLog) per database instance.

The UI adds a third resizable column (CommandLogPanel), Redux state, pause/clear controls, grouping/collapse of repeated lines, and new i18n strings (en/bg).

Reviewed by Cursor Bugbot for commit 56e0998. Bugbot is set up for automated code reviews on this repo. Configure here.

Finding out which commands an action issues meant reading the source or opening a MONITOR session, which shows every client's traffic and slows the server down.

The browser now has a third panel listing the commands RedisInsight itself sent, grouped by the action that triggered them:
- the Redis client classes report every command they send, so only RedisInsight's own traffic is recorded
- a global interceptor puts the operation name into an async context, which the client reads when it logs a command
- entries are pushed over socket.io to a room per instance
- buffers are decoded as UTF-8 when they hold text, so key names stay readable; genuinely binary payloads fall back to a size placeholder
- the panel groups consecutive commands from one action and collapses repeated lines

Batches are capped so a single action cannot flood the panel.
@xiajingg
xiajingg requested a review from a team as a code owner September 19, 2026 08:14
@CLAassistant

CLAassistant commented Sep 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 56e0998. Configure here.

}

return decoded;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large values block Redis command path

High Severity

serializeArgument fully UTF-8-decodes every Buffer argument and runs Array.from over the whole string before truncating. Saving a large or binary value therefore allocates and scans the entire payload on the Redis send path, which can stall the API event loop.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56e0998. Configure here.

} else {
subscribe()
}
}, [isPaused, subscribe])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pause ignored after socket reconnects

Medium Severity

Connect always emits Subscribe, and the pause effect bails out unless the socket is already connected. After a remount or instanceId change while paused, streaming starts again even though the button still shows Resume.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56e0998. Configure here.

socketRef.current = null
bufferRef.current = []
}
}, [instanceId, t])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log not cleared on instance switch

Medium Severity

Changing instanceId reconnects the socket but never clears Redux entries, and resetCommandLog is never dispatched. Commands from the previous database stay in the panel and mix with the new instance.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56e0998. Configure here.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants