buildAttemptLocked materializes the final Attempt from the current buffered response data plus err. Callers use this from both the record-once append path and the provisional-upgrade replace path so both sites apply the same redaction and status rules. The caller must hold r.mu for the duration of t
(err error)
| 461 | // both sites apply the same redaction and status rules. The caller |
| 462 | // must hold r.mu for the duration of the call. |
| 463 | func (r *recordingBody) buildAttemptLocked(err error) Attempt { |
| 464 | finishedAt := time.Now() |
| 465 | |
| 466 | truncated := r.truncated |
| 467 | responseBody := append([]byte(nil), r.buf.Bytes()...) |
| 468 | base := r.base |
| 469 | startedAt := r.startedAt |
| 470 | |
| 471 | contentType := r.contentType |
| 472 | switch { |
| 473 | case truncated: |
| 474 | base.ResponseBody = []byte("[TRUNCATED]") |
| 475 | case isNDJSONContentType(contentType): |
| 476 | base.ResponseBody = RedactNDJSONSecrets(responseBody) |
| 477 | case contentType == "" || isJSONLikeContentType(contentType): |
| 478 | // Redact JSON secrets when the content type is JSON-like |
| 479 | // or absent (unknown). For unknown types, RedactJSONSecrets |
| 480 | // fails closed by replacing non-JSON payloads with a |
| 481 | // diagnostic message. |
| 482 | base.ResponseBody = RedactJSONSecrets(responseBody) |
| 483 | default: |
| 484 | // Non-JSON content types (SSE, text/plain, HTML, etc.) |
| 485 | // are preserved as-is to avoid losing debug content. |
| 486 | base.ResponseBody = responseBody |
| 487 | } |
| 488 | base.StartedAt = startedAt.UTC().Format(time.RFC3339Nano) |
| 489 | base.FinishedAt = finishedAt.UTC().Format(time.RFC3339Nano) |
| 490 | // Recompute duration to include body read time. |
| 491 | base.DurationMs = finishedAt.Sub(startedAt).Milliseconds() |
| 492 | if err != nil && !errors.Is(err, io.EOF) { |
| 493 | base.Error = sanitizeErrorString(err.Error()) |
| 494 | base.Status = attemptStatusFailed |
| 495 | } else { |
| 496 | base.Status = attemptStatusCompleted |
| 497 | } |
| 498 | return base |
| 499 | } |
| 500 | |
| 501 | // record acquires r.mu before entering recordOnce.Do so it shares a |
| 502 | // single lock-acquisition order with recordProvisional. Without this, |
no test coverage detected