fix(stripe): stop logging Stripe's error message on the async-stripe path #44
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/DUNITE-12-redact-sdk-error-logs"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
DUNITE-12. BUNYIP-265 established that Stripe's error envelope must not reach the logs:
error.messageroutinely embeds a customer email, a customer or subscription id, and card last-4. The raw-REST helpers honoured that. The async-stripe path never did.All 23 SDK call sites logged
error = %e, andStripeError'sDisplaydelegates toRequestError's, which appends the message:That is the content BUNYIP-265 removed from one path, written to the same sink from the other. DUNITE-10 kept the returned error clean (
StripeErrorDetailshas no field for the message) but left these log lines alone, because sanitising them costs operators debugging detail and deserved its own decision.Change
redacted_stripe_error(&stripe::StripeError) -> Stringrenders an SDK error for a log line without the free text. Every SDK site now logsstripe_error = %redacted_stripe_error(&e).Stripe(RequestError)StripeErrorDetails: status, type, code, decline_code. Nevermessage, neverchargeQueryStringSerialize/JSONSerializeClientError(String)Timeout/UnsupportedVersionThe match is exhaustive with no catch-all, so a future async-stripe variant is a compile error rather than a silent message leak.
The five
reqwestsites keeperror = %eand gain a comment saying why: those are transport failures orresp.json()decode failures, and serde reports a position and an expected type, never the bytes it was reading. Every call site keeps its own context fields (price_id,customer_id,invoice_id, thehintstrings) - ids the caller already had, and what keeps a redacted line actionable.What this costs
The human-readable message goes away, including strings like
No such price: 'price_abc'. In exchange: the code (resource_missing,card_declined,more_permissions_required) still names the fault, the call site still logs the id it passed, and since DUNITE-10 the same fields reach the caller so a consumer can render an accurate message. An operator who needs the original text looks it up in the Stripe dashboard by request id rather than keeping a copy of customer data in our log store.Rejected alternatives: logging the message at
debug(same data, same sink, on a level that gets turned on during exactly the incidents where logs are most likely to be shipped) and regex-scrubbing the message (a denylist over third-party free text fails open).Testing
just fmt,just check,just lint,just testall clean. Three new tests (15 in the crate):RequestErrorwhose message carries an email and a customer id renders neither, while type, code and status all survive.unit_amountwithout the value that failed to parse.Adds
serde_path_to_erroras a dev-dependency to build the second case: same 0.1.x async-stripe depends on, so theStripeErrorvariant accepts it.Note:
grep "error = %e"returns 5 hits, allreqwest. Independent of #43 (DUNITE-11), which touches URL construction and constructors; both branch from the same main and do not overlap.