ffittschen
This issue proposes a few additions so Pulse can show traffic that doesn't go through `URLSession`, starting with [grpc-swift-2](https://github.com/grpc/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 (#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](https://github.com/apple/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](https://github.com/ffittschen/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. <p> <img src="https://raw.githubusercontent.com/ffittschen/PulseGRPC/main/docs/images/console.png" width="260" alt="Console with gRPC and REST entries"> <img src="https://raw.githubusercontent.com/ffittschen/PulseGRPC/main/docs/images/inspector.png" width="260" alt="Inspector for a unary gRPC call"> </p> ## Proposed changes (draft PRs) **What PulseGRPC needs** - https://github.com/kean/Pulse/pull/382: `NetworkLogger.logTaskCreated(taskId:…)` and `logTaskCompleted(taskId:…)`, for tasks without a `URLSessionTask`. They're stateless and keyed by a task ID the caller provides. They go through `send(_:)`, so filters, redaction and `willHandleEvent` apply. A second commit shows the descriptions of custom-domain errors instead of "Error" in the console, partway towards #341. **What swift-openapi-urlsession needs** - https://github.com/kean/Pulse/pull/383: capture the request bodies of uploads from data (`uploadTask(with:from:)`, `upload(for:from:)`) and of streamed uploads (`needNewBodyStream`), which covers swift-openapi-urlsession. Builds on https://github.com/kean/Pulse/pull/381. **Fixes found along the way** (each one stands on its own) - https://github.com/kean/Pulse/pull/379: `sensitiveHeaders` matched case-sensitively, so a lowercase `authorization` didn't redact `Authorization`. - https://github.com/kean/Pulse/pull/380: upload tasks kept `responseBodySize = -1`. - https://github.com/kean/Pulse/pull/381: `URLSessionProxy`'s async uploads ignored the task delegate and stayed pending. **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 `.dataTask`s 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. https://github.com/kean/Pulse/pull/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 https://github.com/kean/Pulse/pull/383 acceptable, or would you rather leave streamed bodies out?