Skip to content

feat: log prepared URLs without bypassing custom clients - #997

Open
dpiet-oai wants to merge 1 commit into
mainfrom
dp/http-request-url-logging
Open

dpiet-oai wants to merge 1 commit into
mainfrom
dp/http-request-url-logging

Conversation

@dpiet-oai

@dpiet-oai dpiet-oai commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Log the initial URL prepared by the HTTP transport instead of the SDK-rendered HttpRequest.url() string. The default OkHttp transport reports call.request().url before sync or async dispatch, so INFO logs reflect its URL normalization, path encoding, and query construction. This change does not alter the outgoing request or the public HttpRequest.url() method.

Addressing Justin's review of the earlier draft

Justin identified that the earlier three-argument HttpClient overloads could bypass a freshly recompiled Kotlin HttpClient by delegate wrapper's existing two-argument overrides, even when logging was OFF. That could skip request customization or authentication. The replacement removes those overloads and always dispatches through the original two-argument execute and executeAsync. Observation travels in per-call RequestOptions; at OFF with no caller observer, the original request and options are forwarded unchanged. A source-compiled regression exercises both delegated overrides at OFF and INFO and verifies that the wrapper's path change reaches OkHttp and the log. The same dispatch correction addresses Mark's confirmation.

Behavior and compatibility

  • Each transport-prepared attempt can report its method and URL through RequestOptions.requestObserver. SDK wrappers forward the per-call options, including across retries. An outer logger forwards and logs every attempt. Observer runtime exceptions cannot stop default OkHttp dispatch.
  • Existing custom clients still send requests. At INFO, a transport that does not report its URL logs <URL unavailable> after a successful call; failures before observation do not invent a request line. The README includes a Java sync/async example for custom transports to opt in.
  • INFO logging captures request body length before the transport can close it. Userinfo in a reported URL is redacted; sensitive-header redaction and opt-in DEBUG body logging remain in place. Core gains no OkHttp or Okio dependency.
  • The URL is the initial prepared URL. Interceptors and redirects can change the eventual destination. HttpRequest.url() retains its existing behavior, so custom transports that construct outgoing requests from that method still need a separate fix for the public-method part of #886.

Verification

The 147-case URL matrix covers 117 accepted URLs and 30 rejected bases; rejected bases yield no dispatch or request URL line. Tests also cover actual WireMock request targets, sync/async calls, delegated wrappers, retries, custom-client fallback, cancellation, body cleanup, authentication and X.509 paths, userinfo redaction, and Java consumer compilation. The public PR's CI must pass its full test, lint, API compatibility, Java 8/25, and Castiron budget checks. A security-focused review of URL/path and transport behavior is required before merge.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

70 mixed files remain; 0 existing customizations changed.

Compared 6e773e774e8ee308317ad628. Generated baselines verified.

70 existing customizations unchanged
  • openai-java-core/src/main/kotlin/com/openai/models/audio/AudioResponseFormat.kt
  • openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt
  • openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageFunctionToolCall.kt
  • openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParam.kt
  • openai-java-core/src/main/kotlin/com/openai/models/embeddings/Embedding.kt
  • openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt
  • openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt
  • openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCall.kt
  • openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt
  • openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt
  • openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextConfig.kt
  • openai-java-core/src/main/kotlin/com/openai/models/videos/Video.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsync.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsync.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/WebhookServiceAsync.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/WebhookServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/beta/agents/SessionServiceAsync.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/beta/agents/SessionServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsync.kt
  • openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsyncImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaService.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaServiceImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseServiceImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/WebhookService.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/WebhookServiceImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionServiceImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/agents/SessionService.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/agents/SessionServiceImpl.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionService.kt
  • openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionServiceImpl.kt
  • openai-java-core/src/test/kotlin/com/openai/models/beta/responses/BetaResponseFunctionWebSearchTest.kt
  • openai-java-core/src/test/kotlin/com/openai/models/beta/responses/BetaResponsesServerEventTest.kt
  • openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt
  • openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt
  • openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt
  • openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt

30 more in the full report.

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 34873949779 --repo openai/openai-java \
  --name castiron-custom-code-34873949779-1 --dir /tmp/castiron-custom-code-34873949779-1
git apply --stat /tmp/castiron-custom-code-34873949779-1/custom-code.patch
cat /tmp/castiron-custom-code-34873949779-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin 6e773e774e8e324ddfb94437f471ebd7d22035bf e308317ad628dee4375c506d17334217a0987219
python3 scripts/castiron/custom_code_report.py report \
  --base 6e773e774e8e324ddfb94437f471ebd7d22035bf \
  --head e308317ad628dee4375c506d17334217a0987219 --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-e308317ad628
cat /tmp/castiron-custom-code-e308317ad628/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for one confirmed behavioral compatibility regression: recompiling an existing Kotlin HttpClient by delegate wrapper can bypass its original execution overrides. Details and validation are in the inline comment.

Review conclusions:

  • API contract: HttpRequest.url() and default OkHttp wire construction remain unchanged, but the delegated-wrapper regression must be fixed. Legacy custom clients also intentionally lose URL logging in favor of <URL unavailable>; that remains an explicit compatibility tradeoff.
  • Generator ownership: None of the 16 changed files belongs to the verified generated snapshot 518b0539b6c7380ca65ceaedae379187c4545226. Generation metadata is unchanged. The custom-code budget passes at 2,157 / 3,000, unchanged from base.
  • Support matrix: No JVM/compiler floors or runtime dependencies change. Java 8 remains supported; exact-head CI passes its Java 8/25 runtime checks and build/Jackson/API checks. Those checks do not cover this delegated-wrapper behavior.
  • API design: Reading the prepared URL from the transport is the right boundary and keeps core independent of OkHttp. Observation should be an explicit opt-in capability that preserves existing wrapper dispatch. A separate optional interface is one possible design; please agree on that API shape before expanding the implementation.

Validation: two independent read-only reviewers identified the same blocker; a separate offline counting-wrapper fixture confirmed it on Java 8 and 25, including against the complete Gradle-built PR JAR. Focused core HTTP and OkHttp tests passed locally (509 passed, 1 skipped), along with Kotlin lint, git diff --check, and the custom-code budget check. Security-focused inspection found no additional supported blocker.

Reviewed head eae6187e5414474dfe3c745ab426eee73467a345 against base 41bdd89bce7d88c14ce09f891b11bb7eb61ba534. No source changes were made.

Comment thread openai-java-core/src/main/kotlin/com/openai/core/http/LoggingHttpClient.kt Outdated

@markstuart-oai markstuart-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full 16-file diff at eae6187e5414474dfe3c745ab426eee73467a345. No additional actionable findings beyond the existing delegated-wrapper regression.

I independently traced that regression through the SDK call chain and Kotlin delegation rules: adding execution overloads causes a recompiled delegated wrapper to forward those methods around its existing overrides. The logging wrapper invokes that path even with logging off. Preserve original dispatch unless observation is explicitly supported; the existing finding remains unresolved.

The transport remains the single source of the prepared URL, and retry/authentication forwarding preserves the existing lifecycle. Exact-head CI passed, including build, tests, API compatibility, and Java 8/25 checks. This was a read-only review; I did not install dependencies or execute the SDK locally.

@dpiet-oai
dpiet-oai force-pushed the dp/http-request-url-logging branch from eae6187 to e308317 Compare September 14, 2026 14:23
@dpiet-oai dpiet-oai changed the title feat: log prepared HTTP request URLs through observers feat: log prepared URLs without bypassing custom clients Sep 14, 2026

@markstuart-oai markstuart-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the full 11-file change at e308317ad628dee4375c506d17334217a0987219. The previous delegated-wrapper finding is addressed: HttpClient remains unchanged, and both logging paths call the original two-argument methods. The new source-compiled Kotlin delegation regression covers sync/async under OFF and INFO, including wrapper URL changes and unchanged OFF request options.

No actionable findings after independent correctness, structural and security review. Passing observation through existing immutable RequestOptions avoids overload bridges and a second URL-construction implementation. I traced observer forwarding through authentication/retries, callback failure containment, request-body lifetime, userinfo redaction, and the documented custom-transport fallback. The URL is explicitly the initial prepared URL, before later interceptors or redirects.

Exact-head CI is green for tests, API compatibility, Java 8/25, lint and the Castiron budget checks. Hosted logs confirm the core and OkHttp test tasks ran and the external/proposed API compatibility compilation passed. The 147-case URL fixture contains 117 accepted and 30 rejected cases. I did not build or run the SDK locally.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the complete 11-file diff at e308317ad628dee4375c506d17334217a0987219 against 6e773e774e8e324ddfb94437f471ebd7d22035bf. No blocking findings.

The prior delegated-wrapper regression is fixed: the new three-argument HttpClient overloads are gone, LoggingHttpClient dispatches through the original two-argument sync/async methods, and OFF/no-observer calls preserve the original request/options objects. The new source-compiled HttpClient by delegate coverage exercises both overrides at OFF and INFO and confirms wrapper request mutation reaches OkHttp and the prepared-URL log.

I also traced observer propagation through retries, authentication, workload-identity refresh, nested loggers, custom transports, cancellation, and body cleanup. The transport reports call.request().url before dispatch; caller observer failures are isolated; userinfo is redacted before logging; core gains no OkHttp/Okio dependency; and the existing HttpRequest.url() contract remains unchanged. The documented <URL unavailable> fallback for transports that do not opt in is consistent with the implementation.

Verification: exact-head CI is green for build/Jackson compatibility, tests, lint, API compatibility, Java 8/25 runtime compatibility, required checks, and Castiron budget. I independently ran the focused core and OkHttp test suites under Java 21; all passed. git diff --check is clean.

@dpiet-oai
dpiet-oai marked this pull request as ready for review September 14, 2026 17:18
@dpiet-oai
dpiet-oai requested a review from a team as a code owner September 14, 2026 17:18
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T17:22:36.948767Z e308317 Draft marked ready
🔒 Security Review Completed 2026-09-14T17:22:30.058837Z e308317 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e308317ad6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return
}
val loggedUrl =
url?.replace(urlUserInfo) { "${it.groupValues[1]}██@" } ?: "<URL unavailable>"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact credentials embedded in prepared URL queries

When a custom transport follows the new README guidance and reports a prepared signed URL containing query credentials such as access_token, X-Amz-Signature, or X-Amz-Security-Token, INFO-or-higher logging writes those values verbatim to stderr because this replacement masks only URL userinfo. Sanitize credential-bearing query parameters before printing the prepared URL so transport authentication material cannot leak into collected logs.

AGENTS.md reference: AGENTS.md:L29-L33

Useful? React with 👍 / 👎.

private fun prepareLoggingRequest(request: HttpRequest): HttpRequest {
val body = request.body
return if (level.shouldLog(LogLevel.DEBUG) && body != null) {
request.toBuilder().body(LoggingHttpRequestBody(request.method, body)).build()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the observed method in the request-body end marker

When a downstream wrapper changes the HTTP method after LoggingHttpClient and the original request has a body, the observer now makes the request line use the prepared method (for example, --> POST), but this wrapper captures the original method and later emits --> END GET. Use the observer-reported method for the body marker, or otherwise avoid labeling it with the stale method, so a single DEBUG trace does not contradict itself.

Useful? React with 👍 / 👎.

@openai-sdks

openai-sdks Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

OkTest Summary

236/236 SDK tests passed in 18.885s for Java SDK PR #997.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 417ms
tests/chat-completions-create.test.ts ✅ Passed 790ms
tests/chat-completions-stream.test.ts ✅ Passed 402ms
tests/files-content-binary.test.ts ✅ Passed 295ms
tests/files-create-multipart.test.ts ✅ Passed 430ms
tests/files-list-pagination.test.ts ✅ Passed 282ms
tests/initialize-config.test.ts ✅ Passed 289ms
tests/instance-isolation.test.ts ✅ Passed 180ms
tests/models-list.test.ts ✅ Passed 313ms
tests/responses-background-lifecycle.test.ts ✅ Passed 266ms
tests/responses-body-method-errors.test.ts ✅ Passed 503ms
tests/responses-cancel-timeout.test.ts ✅ Passed 288ms
tests/responses-cancel.test.ts ✅ Passed 279ms
tests/responses-compact-retries.test.ts ✅ Passed 377ms
tests/responses-compact.test.ts ✅ Passed 229ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 332ms
tests/responses-create-advanced.test.ts ✅ Passed 1.442s
tests/responses-create-disconnect.test.ts ✅ Passed 1.027s
tests/responses-create-errors.test.ts ✅ Passed 332ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 273ms
tests/responses-create-retries.test.ts ✅ Passed 1.053s
tests/responses-create-stream-failures.test.ts ✅ Passed 217ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 203ms
tests/responses-create-stream-wire.test.ts ✅ Passed 8.234s
tests/responses-create-stream.test.ts ✅ Passed 138ms
tests/responses-create-terminal-states.test.ts ✅ Passed 508ms
tests/responses-create-timeout.test.ts ✅ Passed 272ms
tests/responses-create.test.ts ✅ Passed 240ms
tests/responses-delete.test.ts ✅ Passed 248ms
tests/responses-input-items-errors.test.ts ✅ Passed 319ms
tests/responses-input-items-list.test.ts ✅ Passed 348ms
tests/responses-input-items-options.test.ts ✅ Passed 511ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 305ms
tests/responses-input-tokens-count.test.ts ✅ Passed 294ms
tests/responses-malformed-inputs.test.ts ✅ Passed 7.035s
tests/responses-not-found-errors.test.ts ✅ Passed 339ms
tests/responses-parse.test.ts ✅ Passed 714ms
tests/responses-retrieve-retries.test.ts ✅ Passed 360ms
tests/responses-retrieve.test.ts ✅ Passed 349ms
tests/responses-stored-method-errors.test.ts ✅ Passed 994ms
tests/retry-behavior.test.ts ✅ Passed 3.69s
tests/sdk-error-shape.test.ts ✅ Passed 476ms

View OkTest run #34873910909

SDK merge (5c5213dba7f8) · head (e308317ad628) · base (6e773e774e8e) · OkTest (08ec4299e769)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants