Skip to content

Preserve printf-style format substitution when piping server logs to browser console#479

Open
mvanhorn wants to merge 1 commit into
TanStack:mainfrom
mvanhorn:fix/435-server-log-printf-format
Open

Preserve printf-style format substitution when piping server logs to browser console#479
mvanhorn wants to merge 1 commit into
TanStack:mainfrom
mvanhorn:fix/435-server-log-printf-format

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jun 24, 2026

Copy link
Copy Markdown

🎯 Changes

Server log messages that use console format strings (%s, %d, %c, etc.) are now substituted correctly when forwarded to the browser devtools console, including the default enhanced-logs path where the source-location prefix sits before the user's arguments. Previously the format string was passed through as a plain argument, so substitutions were not applied and the raw %s tokens appeared in the panel.

The SSE bridge that forwards server-side console.log/console.error did not detect format strings in all cases: with enhanced logs enabled (the default), enhanceConsoleLog prepends a LOG /path:line:col prefix, so the real format string was no longer the first argument and the substitution path was skipped. This now accounts for the enhanced-log prefix, the direct SSE path, and the %c/empty-argument edge cases. Closes #435.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Summary by CodeRabbit

  • New Features

    • Server logs piped into the browser console now preserve printf-style formatting, including %s and %c substitutions.
  • Bug Fixes

    • Improved handling of server log output so styled prefixes and empty substitution segments display correctly.
    • Warn-level logs now keep the expected server prefix without unintended formatting changes.
    • Non-string first arguments are no longer treated as format strings.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The virtual console SSE log renderer in devtools-vite is updated to detect printf-style format substitutions (%s, %c) in server log arguments and preserve them when injecting the [Server] prefix. transformServerLogArgs gains a preserveEmptyStrings flag, and new client-side helpers compute a formatIndex that controls how the prefix is spliced in. Tests are refactored and extended to cover all substitution cases.

Changes

Server log format-substitution preservation

Layer / File(s) Summary
transformServerLogArgs + format detection + onmessage rework
packages/devtools-vite/src/virtual-console.ts
transformServerLogArgs accepts a new preserveEmptyStrings parameter; new helpers detect %s/%c patterns and compute formatIndex; onmessage is reworked to split the [Server] prefix injection at formatIndex when a format pattern is found, falling back to the original prefix-prepend path otherwise.
Test infrastructure refactor and new test cases
packages/devtools-vite/src/virtual-console.test.ts
setupConsolePipe(levels) replaces the warn-only mock setup; dispatchServerEntries simulates SSE delivery; new test cases assert correct behavior for %s, %c, empty segments, warn prefix, and non-string first arguments.
Patch changeset
.changeset/fresh-mails-bow.md
Adds the @tanstack/devtools-vite patch changeset entry documenting format-substitution preservation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Hop hop, the logs come through the wire,
With %s and %c held up higher!
No more dropped styles on the browser floor—
The [Server] prefix now plays nice, what's more.
Each format string kept tidy and bright,
This bunny says: your console looks right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: preserving printf-style substitutions in piped server logs.
Description check ✅ Passed The description follows the required template and covers changes, checklist, and release impact with the key details filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/devtools-vite/src/virtual-console.ts`:
- Around line 321-324: The preserve-format detection in getConsoleFormatIndex is
too permissive and can treat literal percent text like “100%c” as a format
string when there are no matching substitution arguments. Update the logic
around getConsoleFormatIndex and the related preserve-format path to only
preserve formatting when the number of actual console placeholders is supported
by the available trailing args, so literal % sequences are not reclassified
after prefix injection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 37abba98-2a17-4807-b0ce-73c14e43479c

📥 Commits

Reviewing files that changed from the base of the PR and between e026667 and 2cc1dda.

📒 Files selected for processing (3)
  • .changeset/fresh-mails-bow.md
  • packages/devtools-vite/src/virtual-console.test.ts
  • packages/devtools-vite/src/virtual-console.ts

Comment on lines +321 to +324
function getConsoleFormatIndex(args) {
if (hasConsoleFormatSubstitution(args[0])) return 0;
return isEnhancedLogPrefix(args[0]) && hasConsoleFormatSubstitution(args[1]) ? 1 : -1;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Avoid classifying literal % sequences as format substitutions.

getConsoleFormatIndex currently enables the preserve-format branch even when there are no substitution arguments. Literal messages like "CPU at 100%c" can then be treated as format strings and rendered incorrectly after prefix injection. Gate preservation by placeholder count vs available trailing args.

Suggested fix
-    function hasConsoleFormatSubstitution(arg) {
-      return typeof arg === 'string' && /(^|[^%])%[sdifocOj]/.test(arg);
-    }
+    function getConsoleFormatPlaceholderCount(arg) {
+      if (typeof arg !== 'string') return 0;
+      var matches = arg.match(/(^|[^%])%[sdifocOj]/g);
+      return matches ? matches.length : 0;
+    }

     function isEnhancedLogPrefix(arg) {
       return typeof arg === 'string' &&
         arg.indexOf('LOG') !== -1 &&
         arg.indexOf(String.fromCharCode(10) + ' ' + String.fromCharCode(8594) + ' ') !== -1;
     }

+    function hasResolvableConsoleFormatSubstitution(args, formatIndex) {
+      var placeholderCount = getConsoleFormatPlaceholderCount(args[formatIndex]);
+      return placeholderCount > 0 && (args.length - (formatIndex + 1)) >= placeholderCount;
+    }
+
     function getConsoleFormatIndex(args) {
-      if (hasConsoleFormatSubstitution(args[0])) return 0;
-      return isEnhancedLogPrefix(args[0]) && hasConsoleFormatSubstitution(args[1]) ? 1 : -1;
+      if (hasResolvableConsoleFormatSubstitution(args, 0)) return 0;
+      return isEnhancedLogPrefix(args[0]) && hasResolvableConsoleFormatSubstitution(args, 1) ? 1 : -1;
     }

Also applies to: 334-336

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-vite/src/virtual-console.ts` around lines 321 - 324, The
preserve-format detection in getConsoleFormatIndex is too permissive and can
treat literal percent text like “100%c” as a format string when there are no
matching substitution arguments. Update the logic around getConsoleFormatIndex
and the related preserve-format path to only preserve formatting when the number
of actual console placeholders is supported by the available trailing args, so
literal % sequences are not reclassified after prefix injection.

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.

Server log pipe drops printf-style format substitution, leaks raw %s into browser console

1 participant