Skip to content

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

Open
xiajingg wants to merge 3 commits into
redis:mainfrom
xiajingg:feat/command-log-panel
Open

xiajingg wants to merge 3 commits 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 hook — RedisClient 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
Touches the Redis client send path on every command (wrapped in try/catch) and adds a global HTTP interceptor; failures are designed to be best-effort but any regression could affect performance or request handling.

Overview
Adds a Browser command log that shows Redis commands RedisInsight itself sends, grouped by the UI action that triggered them—without using MONITOR.

On the API, a new CommandLogModule buffers serialized commands from a static hook on RedisClient (ioredis and node-redis call sites), batches them, and streams per-instance rooms over a dedicated Socket.IO namespace. A global OperationContextInterceptor binds human-readable operation labels via AsyncLocalStorage so each logged command is attributed to the HTTP handler (e.g. “Load key list”). Serialization truncates large/binary args on the hot path so logging cannot block or break command execution.

The Browser page gains a third resizable column with CommandLogPanel: live socket subscription, pause/resume/clear, Redux state capped at 1000 entries, and list UI that groups by operation and collapses repeated lines. Layout logic normalizes persisted two-panel sizes to three columns; i18n keys added for en/bg.

Reviewed by Cursor Bugbot for commit b3a1c68. 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.

Stale Bugbot comment from a previous run.

Comment thread redisinsight/api/src/modules/command-log/utils/serialize-command.ts
…ance

Three problems reported by the automated review:

- `serializeArgument` decoded every buffer in full and scanned the whole string before truncating, on the Redis send path. A large value would stall the event loop for output that is truncated to 120 characters anyway; buffers beyond `MAX_DECODED_BUFFER_BYTES` are now reported by size. The Uint8Array branch checks the length before `Buffer.from`, which would otherwise duplicate the whole array.
- A socket that reconnected while the panel was paused emitted Subscribe unconditionally, so commands started flowing again although the button still said resume. The reconnect handler now reads the pause state from a ref.
- Switching instances reconnected the socket without clearing the entries, so commands from the previous database stayed in the panel. The effect now resets the buffer and the store when instanceId changes.

@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.

Stale Bugbot comment from a previous run.

Comment thread redisinsight/ui/src/pages/browser/BrowserPage.tsx
Follow-up on the review of the previous fix:

- Clearing the entries on every mount wiped the log whenever the panel was hidden and shown again (fullscreen mode, collapsed panels). The reset now only runs when the panel actually switches to another instance.
- The default panel sizes no longer add up to 100% once the command log is hidden, and persisted two-column sizes overflow when the third panel is rendered. The defaults are now rescaled to the panels that are actually visible.

@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 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit b3a1c68. Configure here.

)
})
socket.on(SocketEvent.ConnectionError, () => {
dispatch(setCommandLogError(t('browser.commandLog.error.connection')))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Connection error never clears

Low Severity

setCommandLogError runs on exception and ConnectionError, but neither a successful reconnect nor concatCommandLogEntries clears error. After a transient socket failure, clearing the list still shows “Connection lost” instead of the empty placeholder even though commands are flowing again.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b3a1c68. 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