Skip to content

feat: enhance API logging and add expandable data view in logs UI#247

Open
DragonnZhang wants to merge 4 commits intobadrisnarayanan:mainfrom
DragonnZhang:feat/enhanced-logging-ui
Open

feat: enhance API logging and add expandable data view in logs UI#247
DragonnZhang wants to merge 4 commits intobadrisnarayanan:mainfrom
DragonnZhang:feat/enhanced-logging-ui

Conversation

@DragonnZhang
Copy link
Copy Markdown

Summary

  • Enhanced API logging to include more detailed request/response information.
  • Added an expandable data view in the logs UI for better debugging and inspection of log entries.
  • Improved the overall user experience when viewing logs.

Test plan

  • Verified that logs are correctly captured and displayed in the UI.
  • Tested the expandable data view to ensure it correctly shows/hides detailed log information.
  • Confirmed that the changes do not negatively impact existing logging functionality.

@jgor20 jgor20 self-assigned this Feb 7, 2026
@jgor20
Copy link
Copy Markdown
Collaborator

jgor20 commented Feb 7, 2026

@DragonnZhang,

Thanks for the PR. The expandable log data view is a nice addition for debugging. A few things I'd like addressed before merging:


Below as been updated on 8 February 2026

Issues

1. Sensitive data stored in log history (High)

Source: maintainer review

src/server.js:775-786

logDetails captures the full request.messages, system, and tools payloads and stores them in logger.history (which is also streamed to the WebUI via SSE). This means full conversation content — potentially including PII, API keys in tool definitions, etc. — lives in memory and is sent to any connected browser.

The Redact utility only handles email display in the UI; it doesn't sanitize message content. At minimum:

  • Truncate or summarize messages content (e.g. message count + roles only)
  • Only include full payloads when debug mode is explicitly enabled
  • Consider a size cap on log.data to prevent memory bloat on long conversations

2. Index-based expansion state breaks on filter/new logs (Medium)

Source: maintainer review

public/js/components/logs-viewer.js:12, 107-117

expandedLogs stores array indices, but filteredLogs is a derived/filtered view. When the user has a log expanded and then filters or new logs arrive, the indices shift and the wrong row appears expanded (or a different row collapses).

Use a stable identifier like log.timestamp (or a monotonic log ID) as the Set key instead of idx.

3. Error logs have a positional argument bug (Medium)

Source: local analysis

The print() signature is print(level, color, message, data, ...args). It checks if data has _isExtraData to extract structured data. But the error calls pass 3 arguments:

// server.js:918 & 956 — `error` takes the `data` slot, withData goes to ...args
logger.error(`[API] Transaction failed...`, error, logger.withData({...}));
logger.error('[API] Error:', error, logger.withData({...}));

Since error doesn't have _isExtraData, the withData() wrapper falls through to util.format() and gets dumped as raw text in the message. log.data stays null — no chevron, no expandable section.

4. Unguarded Redact.logMessage() call (Low — PRE-EXISTING, not introduced by this PR)

Source: maintainer review

public/views/logs.html:96

x-html="Redact.logMessage(log.message).replace(/\n/g, '<br>')"

This will throw a runtime error if the Redact script hasn't loaded. Other places in the codebase check window.Redact first — this should do the same:

x-html="(window.Redact ? Redact.logMessage(log.message) : log.message).replace(/\n/g, '<br>')"

Note: This unguarded call already existed on main before this PR. The PR only moved the line into a new wrapper <div> for the expandable layout — it did not introduce the missing guard. Valid fix but should not block this PR.

5. Request log demoted to debug-only (Low)

Source: local analysis

// Was: logger.info(`[API] Request for model: ...`)
// Now: logger.debug(`[API] Request for model: ...`, logger.withData(logDetails))

Users won't see any log when a request arrives unless debug logging is enabled.

6. HTTP middleware log for /v1/messages suppressed (Low)

Source: local analysis

-if (req.originalUrl === '/api/event_logging/batch' || req.originalUrl.startsWith('/v1/messages/count_tokens') || ...)
+if (req.originalUrl === '/api/event_logging/batch' || req.originalUrl.startsWith('/v1/messages') || ...)

The broader URL match now hides the [GET/POST] /v1/messages 200 (Xms) HTTP log line entirely in non-debug mode, not just /v1/messages/count_tokens.

7. _isExtraData sentinel pattern is fragile (Nit)

Source: maintainer review

src/utils/logger.js:70-77

The _isExtraData flag on a plain object passed through variadic args is fragile — any object that happens to have _isExtraData: true would be misinterpreted. A cleaner approach would be a dedicated class/symbol, or making data an explicit named parameter in the log methods (info, debug, etc.) via an options object.

Not blocking, but worth noting for future maintainability.


What works correctly

The "Transaction finished" info logs for both streaming and non-streaming paths correctly use withData() as the second argument (the data slot), so they produce expandable entries:

  • server.js:908 — streaming success
  • server.js:948 — non-streaming success

The stream accumulation logic is solid and the UI expansion UX is clean.


Suggested fixes

  1. Sensitive data — Truncate/summarize message content in logDetails; only include full payloads in debug mode; add a size cap on log.data
  2. Index stability — Replace idx with a stable key (e.g. log.timestamp or monotonic ID) in expandedLogs Set
  3. Positional arg bug — Either scan all ...args for the _isExtraData marker instead of only checking the data position, or restructure error calls to not pass error before withData()
  4. Redact guard — Add window.Redact check in logs.html:96
  5. Request visibility — Consider keeping an info-level log (even summarized) when requests arrive

Comment thread src/server.js
@DragonnZhang DragonnZhang force-pushed the feat/enhanced-logging-ui branch from a9ad45b to eaf1388 Compare February 11, 2026 08:53
@jgor20
Copy link
Copy Markdown
Collaborator

jgor20 commented Feb 11, 2026

@DragonnZhang,

Thanks for rebasing onto latest main (a9ad45beaf1388). I've reviewed the force-pushed commit and the logging/UI code is unchanged - the 7 issues from my earlier review are still outstanding.

Here's a recap with pointers into the current code:


1. Sensitive data stored in log history (High)

src/server.js:777-786 - logDetails still captures full messages, system, and tools payloads unconditionally. These are logged at info level on transaction success, meaning full conversation content sits in logger.history and streams to any connected browser via SSE.

Fix: Only include full payloads when logger.isDebugEnabled is true. Otherwise, summarize (e.g. message count + roles). Consider a size cap on log.data.

const logDetails = {
    request: {
        model: request.model,
        stream: !!request.stream,
        messageCount: request.messages?.length,
        messageRoles: request.messages?.map(m => m.role),
        hasSystem: !!request.system,
        toolCount: request.tools?.length,
        thinking: request.thinking,
        // Only include full payloads in debug mode
        ...(logger.isDebugEnabled && {
            messages: request.messages,
            system: request.system,
            tools: request.tools
        })
    }
};

2. Index-based expansion state breaks on filter/new logs (Medium)

public/js/components/logs-viewer.js:12 - expandedLogs stores array indices. filteredLogs is a derived view - when filters change or new logs arrive, indices shift and the wrong row appears expanded.

public/views/logs.html:73 - x-for key is also idx.

Fix: Use log.timestamp as the stable key:

expandedLogs: new Set(),

toggleLog(timestamp) {
    if (this.expandedLogs.has(timestamp)) {
        this.expandedLogs.delete(timestamp);
    } else {
        this.expandedLogs.add(timestamp);
    }
},

isExpanded(timestamp) {
    return this.expandedLogs.has(timestamp);
},
<template x-for="(log, idx) in filteredLogs" :key="log.timestamp">
    ...
    @click="log.data ? toggleLog(log.timestamp) : null"
    :class="{'bg-white/[0.05]': isExpanded(log.timestamp)}"
    ...
    :class="{'rotate-90': isExpanded(log.timestamp)}"
    ...
    <template x-if="log.data && isExpanded(log.timestamp)">

3. Error logs positional argument bug (Medium)

src/server.js:918,956 - Error calls pass error in the data slot, pushing withData() into ...args:

logger.error(`[API] Transaction failed...`, error, logger.withData({...}));
logger.error('[API] Error:', error, logger.withData({...}));

Since error doesn't have _isExtraData, actualData stays null. No chevron, no expandable section on error logs.

Fix (option A): Scan all args for the _isExtraData marker in print():

print(level, color, message, ...args) {
    let actualData = null;
    const formatArgs = [];

    for (const arg of args) {
        if (arg && typeof arg === 'object' && arg._isExtraData) {
            actualData = arg.data;
        } else {
            formatArgs.push(arg);
        }
    }
    // ...
}

Fix (option B): Restructure the error calls to merge the error into withData():

logger.error(`[API] Transaction failed for model: ${request.model}`, logger.withData({
    request: logDetails.request,
    error: { message: error.message, stack: logger.isDebugEnabled ? error.stack : undefined }
}));

Issues 1-3 should be addressed before merge.

I have removed the remaining issues as 1-3 are of priority/merge blocking and I can address them after this PR is resolved.

Happy to help if you have questions on any of these.

@DragonnZhang DragonnZhang force-pushed the feat/enhanced-logging-ui branch from c4117a8 to 4831abd Compare February 15, 2026 13:14
@badrisnarayanan
Copy link
Copy Markdown
Owner

@jgor20 is this good enough to be merged now?

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.

3 participants