fix(anthropic): log outgoing request headers on 429

This commit is contained in:
matevip 2026-04-26 08:34:12 +08:00
parent ed3ff54f0c
commit 5c2482c307
2 changed files with 43 additions and 0 deletions

View File

@ -41,11 +41,36 @@ class RateLimitDiagnosticExchangeFilter implements ExchangeFilterFunction {
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return next.exchange(request).doOnNext(response -> {
if (response.statusCode().value() == 429) {
// Log REQUEST headers too Spring AI's AnthropicApi.Builder
// calls clone() + defaultHeaders(consumer) on the rest/web
// client builder we hand in, and we want to verify our OAuth
// fingerprint headers actually survived that flow. If they
// didn't, no amount of correct fingerprinting fixes it.
logRequestHeaders(request.headers());
logHeaders(response.headers().asHttpHeaders());
}
});
}
private static void logRequestHeaders(HttpHeaders requestHeaders) {
StringBuilder sb = new StringBuilder("[Anthropic 429] outgoing request headers (sanitized): ");
boolean any = false;
for (var entry : requestHeaders.entrySet()) {
String name = entry.getKey().toLowerCase();
// Skip Authorization never log Bearer tokens. Just show "Bearer <redacted>".
String displayValue;
if (name.equals("authorization")) {
displayValue = "Bearer <redacted>";
} else {
displayValue = String.join(",", entry.getValue());
}
if (any) sb.append(", ");
sb.append(name).append('=').append(displayValue);
any = true;
}
log.warn(sb.toString());
}
private static void logHeaders(HttpHeaders headers) {
StringBuilder sb = new StringBuilder("[Anthropic 429] rate-limit headers: ");
boolean any = false;

View File

@ -52,11 +52,29 @@ class RateLimitDiagnosticInterceptor implements ClientHttpRequestInterceptor {
ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if (response.getStatusCode().value() == 429) {
// Log REQUEST headers too so we can verify Spring AI didn't strip
// our OAuth fingerprint when it cloned the rest client builder.
logRequestHeaders(request.getHeaders());
logHeaders(response.getHeaders());
}
return response;
}
private static void logRequestHeaders(HttpHeaders requestHeaders) {
StringBuilder sb = new StringBuilder("[Anthropic 429] outgoing request headers (sanitized): ");
boolean any = false;
for (var entry : requestHeaders.entrySet()) {
String name = entry.getKey().toLowerCase();
String displayValue = name.equals("authorization")
? "Bearer <redacted>"
: String.join(",", entry.getValue());
if (any) sb.append(", ");
sb.append(name).append('=').append(displayValue);
any = true;
}
log.warn(sb.toString());
}
private static void logHeaders(HttpHeaders headers) {
StringBuilder sb = new StringBuilder("[Anthropic 429] rate-limit headers: ");
boolean any = false;