The option.WithLogger logger also reaches cloud.google.com/go/auth, which logged its token exchange at Debug — JWT assertion and bearer token included. wireLogger now allowlists only the compute client's api request/response records at Debug (fail-closed for future SDK additions); Warn/Error pass through. String fields over 1KiB (e.g. Shielded-VM UEFI dbx blobs) are elided recursively by default; the new --gcp-wire-log-full-payloads flag restores verbatim payloads. Co-Authored-By: Claude <noreply@anthropic.com>
64 lines
3.1 KiB
Markdown
64 lines
3.1 KiB
Markdown
# Execution: GCP HTTP wire logging at V(5)
|
||
|
||
Plan: `docs/plans/2026-08-11-1838-gcp-http-wire-logging-v5.md`
|
||
|
||
- [x] Step 1 — `wireLogger` + `option.WithLogger` wiring in `internal/provider/gcp/gcp.go`
|
||
- [x] Step 2 — Tests (`TestWireLogger_gatesAtV5`, `TestWireLogger_infoLandsAtV1`)
|
||
- [x] Step 3 — Live verification at `--zap-log-level=5` (user, on cluster)
|
||
- [x] Step 3b — Post-verification fix: drop auth records, elide huge fields
|
||
- [ ] Step 4 — CHANGELOG entry (after live confirmation of 3b; batch with the
|
||
two earlier pending entries: GCP V-logging, version stamp)
|
||
|
||
## Steps 1–2
|
||
|
||
Went exactly as planned — the whole feature is ~10 lines of production code
|
||
because both halves already existed: the compute SDK logs full HTTP
|
||
request/response records at slog Debug to an injectable logger, and
|
||
`logr.ToSlogHandler` does the slog→logr bridging. The only real design
|
||
content is the level shift (`base.V(1)` + slog-Debug's +4 = V(5)) and the
|
||
startup warning line when V(5) is active (raw payloads include cloud-init
|
||
user-data, which the curated V(2) logging deliberately hides).
|
||
|
||
Worth noting for future readers:
|
||
|
||
- `option.WithLogger` **disables** `GOOGLE_SDK_GO_LOGGING_LEVEL` for this
|
||
client (documented SDK precedence) — `--zap-log-level` is now the only knob
|
||
for GCP wire logs.
|
||
- The V(5) check in `New` runs once at startup; that is sound because the zap
|
||
level is fixed by flags at process start.
|
||
- Added `TestWireLogger_infoLandsAtV1` beyond the plan's table — it pins the
|
||
shift arithmetic from the other side (slog Info → V(1)), so a future logr
|
||
mapping change would fail loudly.
|
||
|
||
Verified with:
|
||
|
||
```bash
|
||
go test -race ./internal/provider/gcp/
|
||
go build ./... && go test ./...
|
||
```
|
||
|
||
## Step 3b — what live verification exposed, and the fix
|
||
|
||
Live V(5) output revealed two problems the plan missed:
|
||
|
||
1. **Security: the injected logger propagates into `cloud.google.com/go/auth`**,
|
||
which logs its own token exchange (`auth.go:571/576`) — signed JWT
|
||
assertion in the request, full bearer access token in the response. The
|
||
plan's "auth token is safe" analysis only covered the compute client's
|
||
request headers, not the auth library's own records. Fix: `wireLogger`
|
||
now wraps the handler in a filter that drops every Debug record except
|
||
the compute client's `"api request"`/`"api response"` (allowlist, so
|
||
future SDK additions fail closed); Warn/Error still pass through.
|
||
2. **Readability: GCP responses embed multi-KB blobs** (Shielded-VM UEFI
|
||
dbx databases) that swamp the line. Fix: string fields >1KiB are elided
|
||
to `[elided N bytes]` by default, recursively through payload
|
||
maps/arrays. Opt-out via new manager flag
|
||
`--gcp-wire-log-full-payloads` (threaded through a constructor closure
|
||
in `cmd/main.go` → `gcp.NewWithWireOptions`; the `registry.Constructor`
|
||
signature stays unchanged). Chosen by the user: elision on by default,
|
||
verbatim available on demand. Auth records are dropped in both modes.
|
||
|
||
The filter/elision logic lives in `internal/provider/gcp/wirelog.go` with
|
||
tests covering: auth-record drop (both modes), elision marker + small-field
|
||
preservation, verbatim mode, and the original V(5) gating.
|