From 3b786dc516058dfb921f59624b0b15cf82c46b1f Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Tue, 31 Mar 2026 13:45:49 -0400 Subject: [PATCH] fix: improves debug logging of HTTP requests --- .../sdk/server/DefaultFeatureRequestor.java | 19 ++++++++++++- .../server/DefaultFeatureRequestorTest.java | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/DefaultFeatureRequestor.java b/lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/DefaultFeatureRequestor.java index 8ea41890..6f01f596 100644 --- a/lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/DefaultFeatureRequestor.java +++ b/lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/DefaultFeatureRequestor.java @@ -12,6 +12,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.gson.stream.JsonReader; import com.launchdarkly.logging.LDLogger; +import com.launchdarkly.logging.LogValues; import com.launchdarkly.sdk.internal.http.HttpConsts; import com.launchdarkly.sdk.internal.http.HttpErrors.HttpErrorException; import com.launchdarkly.sdk.internal.http.HttpHelpers; @@ -32,6 +33,22 @@ final class DefaultFeatureRequestor implements FeatureRequestor { private static final long MAX_HTTP_CACHE_SIZE_BYTES = 10 * 1024 * 1024; // 10 MB + private static final String REDACTED = "REDACTED"; + + private static String sanitizedRequestStringForLogging(Request request) { + Request.Builder builder = request.newBuilder(); + redactHeaderValueForLog(builder, request, "Authorization"); + return builder.build().toString(); + } + + private static void redactHeaderValueForLog(Request.Builder builder, Request request, String name) { + if (request.headers(name).isEmpty()) { + return; + } + builder.removeHeader(name); + builder.addHeader(name, REDACTED); + } + private final OkHttpClient httpClient; @VisibleForTesting final URI pollingUri; @@ -88,7 +105,7 @@ public FullDataSet getAllData(boolean returnDataEvenIfCached) .get() .build(); - logger.debug("Making request: " + request); + logger.debug("Making request: {}", LogValues.defer(() -> sanitizedRequestStringForLogging(request))); try (Response response = httpClient.newCall(request).execute()) { boolean wasCached = response.networkResponse() == null || response.networkResponse().code() == 304; diff --git a/lib/sdk/server/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java b/lib/sdk/server/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java index e35afd1b..1b7f315f 100644 --- a/lib/sdk/server/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java +++ b/lib/sdk/server/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java @@ -1,5 +1,7 @@ package com.launchdarkly.sdk.server; +import com.launchdarkly.logging.LDLogLevel; +import com.launchdarkly.logging.LogCapture; import com.launchdarkly.sdk.internal.http.HttpErrors.HttpErrorException; import com.launchdarkly.sdk.internal.http.HttpProperties; import com.launchdarkly.sdk.server.DataModel.FeatureFlag; @@ -27,7 +29,9 @@ import static com.launchdarkly.sdk.server.TestComponents.clientContext; import static com.launchdarkly.sdk.server.TestUtil.assertDataSetEquals; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -244,4 +248,28 @@ private void verifyHeaders(RequestInfo req) { assertThat(req.getHeader(kv.getKey()), equalTo(kv.getValue())); } } + + @Test + public void makingRequestDebugLogRedactsAuthorizationValue() throws Exception { + Handler resp = Handlers.bodyJson(allDataJson); + try (HttpServer server = HttpServer.start(resp)) { + try (DefaultFeatureRequestor r = makeRequestor(server)) { + r.getAllData(true); + + String makingRequestLog = null; + for (LogCapture.Message m : logCapture.getMessages()) { + if (m.getLevel() == LDLogLevel.DEBUG && m.getText().contains("Making request:")) { + makingRequestLog = m.getText(); + break; + } + } + assertNotNull("expected Making request debug log", makingRequestLog); + assertThat(makingRequestLog, containsString("REDACTED")); + assertThat(makingRequestLog, not(containsString(sdkKey))); + + RequestInfo req = server.getRecorder().requireRequest(); + assertEquals(sdkKey, req.getHeader("Authorization")); + } + } + } }