Skip to content

Support logging non-URLSession traffic (gRPC) and streamed request bodies #378

Description

@ffittschen

This issue proposes a few additions so Pulse can show traffic that doesn't go through URLSession, starting with grpc-swift-2 clients, and so it captures request bodies that URLSession streams. I opened the PRs below as drafts so we can agree on the approach here first.

Motivation

  • gRPC over SwiftNIO doesn't show up. grpc-swift v2, like other SwiftNIO-based clients (Is there a way to get Pulse working with NIO. #291), never touches URLSession. The only public way to log such a task today is LoggerStore.storeRequest, which stores the task once it has finished and skips NetworkLogger's filters and redaction.
  • swift-openapi-urlsession's request bodies are missing. Its default transport streams bodies through the task delegate's needNewBodyStream, so PulseProxy logs those requests without a body.

What I built

PulseGRPC is a small package with a grpc-swift v2 ClientInterceptor. It logs every RPC as a Pulse network task:

  • a grpcs://host/package.Service/Method URL and the grpc label
  • messages as JSON bodies
  • metadata as headers, plus grpc-status
  • the duration, sizes and timing, and a failure state.

It lives outside Pulse, so Pulse doesn't need to depend on grpc-swift-2. To fully function, it needs a few changes in Pulse, which I have outlined below.

Console with gRPC and REST entries Inspector for a unary gRPC call

Proposed changes (draft PRs)

What PulseGRPC needs

What swift-openapi-urlsession needs

Fixes found along the way (each one stands on its own)

Not opened as PRs, but I have them ready if you want them:

  • Docs for closeButtonHidden(_:) on consoles pushed onto a navigation stack. ConsoleView shows its close button whenever presentationMode.isPresented is true, and that's also true for pushed views, so they show a close button next to the back button.
  • .scripts/build.sh ignores -s and build failures, so the iOS and tvOS CI jobs currently build nothing.

A possible follow-up I haven't written yet: contentType looks up headers["Content-Type"] case-sensitively, so a task logged through the new methods with HTTP/2-style lowercase content-type loses its content type, and PulseGRPC has to keep that one header capitalized.

None of the PRs touch CHANGELOG.md. Please let me know if I should add the entries or if you prefer to do that.

Design choices

Bodies are JSON

PulseGRPC stores protobuf messages as JSON with Content-Type: application/json. The viewer, search, sensitiveDataFields, export and Pulse Pro then work unchanged, and the protobuf content-type hooks keep their meaning.

No schema or protocol changes

Tasks are .dataTasks with a grpc:// or grpcs:// URL, a grpc label and a grpc-status header. TaskType, the Core Data model and the remote-logging protocol are untouched, so .pulse files, Pulse for Mac and Pulse Pro keep working.

A stateless API

An alternative is a handle-based API on top: startTask(...) -> TaskHandle with finish(...), a lock, and a deinit that logs an abandoned task as cancelled. It's harder to misuse, but it adds a public type with lifecycle rules. I'm happy to switch if you prefer that.

Streamed bodies use swizzling

In the style PulseProxy already uses: hooks on the app's delegate classes, once per class, and a tee on one shared thread. #383 lists the limitations.

Questions

  1. Would you accept the two NetworkLogger methods, and are you happy with the names?
  2. Should PulseGRPC stay a separate package that the README links to? Or would you rather have it in this repo as its own product, which would add grpc-swift-2 to Package.swift?
  3. Is the amount of swizzling in Capture upload request bodies in PulseProxy #383 acceptable, or would you rather leave streamed bodies out?

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions