Skip to content

Improve client-to-server logging endpoint #1635

Description

@kmcginnes

Description

The /logger route has several issues with its API design:

  1. Payload in headers — level and message are sent as HTTP headers rather than in the request body. Headers are meant for metadata, not payload, and this limits message size.

  2. Wrong status codes — Missing level, missing message, and unrecognized log level all return 500 via the error middleware. These are client errors and should be 400 Bad Request.

  3. Fragile message parsing — The message is JSON-stringified on the client, JSON-parsed on the server, then backslashes are stripped with replaceAll("\\", ""). Legitimate backslashes (file paths, regex patterns) get eaten. This workaround should be unnecessary with a proper JSON body.

  4. Manual level dispatch — The if/else chain for log levels is unnecessary. Pino supports logger[level](message) for all valid levels.

  5. No source tagging — Client-originated log entries are indistinguishable from server-originated ones when reading logs.

  6. Route name — /logger is a noun describing the tool, not the action. /log is more conventional for a logging endpoint.

Preferred Solution

Server (/log route)

  • Rename the route from /logger to /log
  • Accept a JSON body: { level: string, message: string }
  • Validate level against the allowlist (error, warn, info, debug, trace) and return 400 for invalid values
  • Return 400 for missing level or message
  • Use Pino's method dispatch: logger[level]({ source: "client" }, message) instead of the if/else chain
  • Tag all client log entries with source: "client" so they are distinguishable in server logs
  • Keep /logger as a deprecated alias during a transition period

Client (ServerLoggerConnector)

  • Send level and message in the JSON request body instead of headers
  • Update the endpoint URL from /logger to /log

Request logging middleware

  • Update the path filter to skip /log instead of /logger

Important

If you are interested in working on this issue, please leave a comment.

Tip

Please use a 👍 reaction to provide a +1/vote. This helps the community and maintainers prioritize this request.

Activity

  1. added theissue type on Apr 3, 2026
  2. added
    tech debtIssues, typically tasks, that are mainly about cleaning up code that is problematic in some way
    on Apr 3, 2026
  3. kmcginnes commented on Sep 22, 2026

    @kmcginnes
    CollaboratorAuthor

    One more reason to move off the header, beyond the backslash stripping already described here.

    Because the message is JSON.parsed before it reaches pino, escape sequences are decoded first, so a newline in the client's message arrives as a real line break in the log output. A single client call can therefore span multiple lines, and under the SageMaker preset the CloudWatch driver's awslogs-multiline-pattern splits on those breaks, so the lines land as separate events. Log output from this route is not reliably attributable to one call.

    Two things worth folding into the fix:

    • Put the client's message in a structured field with a fixed log message, as in logger.info({ clientLog: message }, "client log"), rather than passing it as pino's msg.
    • Validate level against a Zod enum instead of the current if/else chain.

    With a JSON body and a structured field, the replaceAll("\\", "") workaround goes away and the output stays one event per call.

  4. kmcginnes commented on Oct 6, 2026

    @kmcginnes
    CollaboratorAuthor

    Two more for the redesign: cap the message size, and rate limit the route. It accepts writes from any caller that can reach the proxy server, so without both, one client can fill the log stream. Under the SageMaker preset that stream is shared with the notebook.

  5. added this to the Release v3.4 milestone on Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestinfrastructureIssues related to tooling or deploymenttech debtIssues, typically tasks, that are mainly about cleaning up code that is problematic in some way

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions