Skip to content

Commit 042b4f2

Browse files
authored
Merge pull request #213 from splitio/debug/cdn_logging
Debug/cdn logging
2 parents a71a88d + 52950cf commit 042b4f2

23 files changed

+461
-54
lines changed

client/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.1.7-rc1</version>
8+
<version>4.1.7-rc2</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>

client/src/main/java/io/split/client/HttpSplitChangeFetcher.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import io.split.client.dtos.SplitChange;
55
import io.split.client.utils.Json;
66
import io.split.client.utils.Utils;
7+
import io.split.engine.common.FetchOptions;
78
import io.split.engine.experiments.SplitChangeFetcher;
89
import io.split.engine.metrics.Metrics;
910
import org.apache.hc.client5.http.classic.methods.HttpGet;
1011
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
1112
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
13+
import org.apache.hc.core5.http.Header;
1214
import org.apache.hc.core5.http.io.entity.EntityUtils;
1315
import org.apache.hc.core5.net.URIBuilder;
1416
import org.slf4j.Logger;
@@ -17,6 +19,8 @@
1719
import java.net.URI;
1820
import java.net.URISyntaxException;
1921
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
import java.util.stream.Collectors;
2024

2125
import static com.google.common.base.Preconditions.checkNotNull;
2226

@@ -28,8 +32,12 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
2832

2933
private static final String SINCE = "since";
3034
private static final String PREFIX = "splitChangeFetcher";
31-
private static final String NAME_CACHE = "Cache-Control";
32-
private static final String VALUE_CACHE = "no-cache";
35+
36+
private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
37+
private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
38+
39+
private static final String HEADER_FASTLY_DEBUG_NAME = "Fastly-Debug";
40+
private static final String HEADER_FASTLY_DEBUG_VALUE = "1";
3341

3442
private final CloseableHttpClient _client;
3543
private final URI _target;
@@ -51,7 +59,7 @@ private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, Metrics metr
5159
}
5260

5361
@Override
54-
public SplitChange fetch(long since, boolean addCacheHeader) {
62+
public SplitChange fetch(long since, FetchOptions options) {
5563

5664
long start = System.currentTimeMillis();
5765

@@ -61,10 +69,17 @@ public SplitChange fetch(long since, boolean addCacheHeader) {
6169
URI uri = new URIBuilder(_target).addParameter(SINCE, "" + since).build();
6270

6371
HttpGet request = new HttpGet(uri);
64-
if(addCacheHeader) {
65-
request.setHeader(NAME_CACHE, VALUE_CACHE);
72+
if(options.cacheControlHeadersEnabled()) {
73+
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
6674
}
75+
76+
if (options.fastlyDebugHeaderEnabled()) {
77+
request.addHeader(HEADER_FASTLY_DEBUG_NAME, HEADER_FASTLY_DEBUG_VALUE);
78+
}
79+
6780
response = _client.execute(request);
81+
options.handleResponseHeaders(Arrays.stream(response.getHeaders())
82+
.collect(Collectors.toMap(Header::getName, Header::getValue)));
6883

6984
int statusCode = response.getCode();
7085

client/src/main/java/io/split/client/SplitClientConfig.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class SplitClientConfig {
4747
private final String _authServiceURL;
4848
private final String _streamingServiceURL;
4949
private final int _onDemandFetchRetryDelayMs;
50+
private final boolean _cdnDebugLogging;
5051

5152
// Proxy configs
5253
private final HttpHost _proxy;
@@ -91,7 +92,8 @@ private SplitClientConfig(String endpoint,
9192
int streamingReconnectBackoffBase,
9293
String authServiceURL,
9394
String streamingServiceURL,
94-
int onDemandFetchRetryDelayMs) {
95+
int onDemandFetchRetryDelayMs,
96+
boolean cdnDebugLogging) {
9597
_endpoint = endpoint;
9698
_eventsEndpoint = eventsEndpoint;
9799
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -123,6 +125,7 @@ private SplitClientConfig(String endpoint,
123125
_authServiceURL = authServiceURL;
124126
_streamingServiceURL = streamingServiceURL;
125127
_onDemandFetchRetryDelayMs = onDemandFetchRetryDelayMs;
128+
_cdnDebugLogging = cdnDebugLogging;
126129

127130
Properties props = new Properties();
128131
try {
@@ -253,6 +256,9 @@ public String streamingServiceURL() {
253256

254257
public int streamingRetryDelay() {return _onDemandFetchRetryDelayMs;}
255258

259+
public boolean cdnDebugLogging() { return _cdnDebugLogging; }
260+
261+
256262
public static final class Builder {
257263

258264
private String _endpoint = "https://sdk.split.io";
@@ -289,6 +295,7 @@ public static final class Builder {
289295
private String _authServiceURL = "https://auth.split.io/api/auth";
290296
private String _streamingServiceURL = "https://streaming.split.io/sse";
291297
private int _onDemandFetchRetryDelayMs = 50;
298+
private boolean _cdnDebugLogging = true;
292299

293300
public Builder() {
294301
}
@@ -690,6 +697,16 @@ public Builder streamingRetryDelay(int onDemandFetchRetryDelayMs) {
690697
return this;
691698
}
692699

700+
/**
701+
* Enable logging response headers for requests made to our CDN.
702+
* @param cdnDebugLogging
703+
* @return
704+
*/
705+
public Builder cdnDebugLogging(boolean cdnDebugLogging) {
706+
_cdnDebugLogging = cdnDebugLogging;
707+
return this;
708+
}
709+
693710
public SplitClientConfig build() {
694711
if (_featuresRefreshRate < 5 ) {
695712
throw new IllegalArgumentException("featuresRefreshRate must be >= 5: " + _featuresRefreshRate);
@@ -795,7 +812,8 @@ public SplitClientConfig build() {
795812
_streamingReconnectBackoffBase,
796813
_authServiceURL,
797814
_streamingServiceURL,
798-
_onDemandFetchRetryDelayMs);
815+
_onDemandFetchRetryDelayMs,
816+
_cdnDebugLogging);
799817
}
800818
}
801819
}

client/src/main/java/io/split/client/SplitFactoryImpl.java

+33-7
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
129129
_splitFetcher = buildSplitFetcher();
130130

131131
// SplitSynchronizationTask
132-
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, _splitCache, findPollingPeriod(RANDOM, config.featuresRefreshRate()));
132+
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
133+
_splitCache,
134+
findPollingPeriod(RANDOM, config.featuresRefreshRate()));
133135

134136
// Impressions
135137
_impressionsManager = buildImpressionsManager(config);
@@ -138,27 +140,52 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
138140
_cachedFireAndForgetMetrics = buildCachedFireAndForgetMetrics(config);
139141

140142
// EventClient
141-
_eventClient = EventClientImpl.create(_httpclient, _eventsRootTarget, config.eventsQueueSize(), config.eventFlushIntervalInMillis(), config.waitBeforeShutdown());
143+
_eventClient = EventClientImpl.create(_httpclient,
144+
_eventsRootTarget,
145+
config.eventsQueueSize(),
146+
config.eventFlushIntervalInMillis(),
147+
config.waitBeforeShutdown());
142148

143149
// SyncManager
144-
_syncManager = SyncManagerImp.build(config.streamingEnabled(), _splitSynchronizationTask, _splitFetcher, _segmentSynchronizationTaskImp, _splitCache, config.authServiceURL(), _httpclient, config.streamingServiceURL(), config.authRetryBackoffBase(), buildSSEdHttpClient(config), _segmentCache, config.streamingRetryDelay());
150+
_syncManager = SyncManagerImp.build(config.streamingEnabled(),
151+
_splitSynchronizationTask,
152+
_splitFetcher,
153+
_segmentSynchronizationTaskImp,
154+
_splitCache,
155+
config.authServiceURL(),
156+
_httpclient,
157+
config.streamingServiceURL(),
158+
config.authRetryBackoffBase(),
159+
buildSSEdHttpClient(config),
160+
_segmentCache,
161+
config.streamingRetryDelay(),
162+
config.cdnDebugLogging());
145163
_syncManager.start();
146164

147165
// Evaluator
148166
_evaluator = new EvaluatorImp(_splitCache);
149167

150168
// SplitClient
151-
_client = new SplitClientImpl(this, _splitCache, _impressionsManager, _cachedFireAndForgetMetrics, _eventClient, config, _gates, _evaluator);
169+
_client = new SplitClientImpl(this,
170+
_splitCache,
171+
_impressionsManager,
172+
_cachedFireAndForgetMetrics,
173+
_eventClient,
174+
config,
175+
_gates,
176+
_evaluator);
152177

153178
// SplitManager
154179
_manager = new SplitManagerImpl(_splitCache, config, _gates);
155180

156181
// DestroyOnShutDown
157182
if (config.destroyOnShutDown()) {
158-
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
183+
Thread shutdown = new Thread(() -> {
159184
// Using the full path to avoid conflicting with Thread.destroy()
160185
SplitFactoryImpl.this.destroy();
161-
}));
186+
});
187+
shutdown.setName("split-destroy-worker");
188+
Runtime.getRuntime().addShutdownHook(shutdown);
162189
}
163190
}
164191

@@ -207,7 +234,6 @@ public boolean isDestroyed() {
207234
}
208235

209236
private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientConfig config) {
210-
211237
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
212238
.setSslContext(SSLContexts.createSystemDefault())
213239
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)

client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.split.cache.SegmentCache;
44
import io.split.cache.SplitCache;
55
import io.split.client.SplitClient;
6+
import io.split.engine.common.FetchOptions;
67
import io.split.engine.experiments.SplitFetcher;
78
import io.split.engine.segments.SegmentFetcher;
89
import io.split.engine.segments.SegmentSynchronizationTask;
@@ -34,7 +35,7 @@ public SplitJmxMonitor(SplitClient splitClient, SplitFetcher featureFetcher, Spl
3435

3536
@Override
3637
public boolean forceSyncFeatures() {
37-
_featureFetcher.forceRefresh(true);
38+
_featureFetcher.forceRefresh(new FetchOptions.Builder().cacheControlHeaders(true).build());
3839
_log.info("Features successfully refreshed via JMX");
3940
return true;
4041
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.split.engine.common;
2+
3+
import java.util.*;
4+
import java.util.stream.Collectors;
5+
6+
public class FastlyHeadersCaptor {
7+
8+
private static final Set<String> HEADERS_TO_CAPTURE = new HashSet<>(Arrays.asList(
9+
"Fastly-Debug-Path",
10+
"Fastly-Debug-TTL",
11+
"Fastly-Debug-Digest",
12+
"X-Served-By",
13+
"X-Cache",
14+
"X-Cache-Hits",
15+
"X-Timer",
16+
"Surrogate-Key",
17+
"ETag",
18+
"Cache-Control",
19+
"X-Request-ID",
20+
"Last-Modified"
21+
));
22+
23+
private final List<Map<String, String>> _headers = new ArrayList<>();
24+
25+
public Void handle(Map<String, String> responseHeaders) {
26+
_headers.add(responseHeaders.entrySet().stream()
27+
.filter(e -> HEADERS_TO_CAPTURE.contains(e.getKey()))
28+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
29+
return null;
30+
}
31+
32+
public List<Map<String, String>> get() {
33+
return _headers;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package io.split.engine.common;
2+
3+
import io.split.engine.matchers.AttributeMatcher;
4+
import org.checkerframework.checker.units.qual.A;
5+
6+
import java.util.Map;
7+
import java.util.Objects;
8+
import java.util.function.Function;
9+
10+
public class FetchOptions {
11+
12+
public static class Builder {
13+
public Builder() {}
14+
15+
public Builder cacheControlHeaders(boolean on) {
16+
_cacheControlHeaders = on;
17+
return this;
18+
}
19+
20+
public Builder fastlyDebugHeader(boolean on) {
21+
_fastlyDebugHeader = on;
22+
return this;
23+
}
24+
25+
public Builder responseHeadersCallback(Function<Map<String, String>, Void> callback) {
26+
_responseHeadersCallback = callback;
27+
return this;
28+
}
29+
30+
public FetchOptions build() {
31+
return new FetchOptions(_cacheControlHeaders, _responseHeadersCallback, _fastlyDebugHeader);
32+
}
33+
34+
private boolean _cacheControlHeaders = false;
35+
private boolean _fastlyDebugHeader = false;
36+
private Function<Map<String, String>, Void> _responseHeadersCallback = null;
37+
}
38+
39+
public boolean cacheControlHeadersEnabled() {
40+
return _cacheControlHeaders;
41+
}
42+
43+
public boolean fastlyDebugHeaderEnabled() {
44+
return _fastlyDebugHeader;
45+
}
46+
47+
public void handleResponseHeaders(Map<String, String> headers) {
48+
if (Objects.isNull(_responseHeadersCallback) || Objects.isNull(headers)) {
49+
return;
50+
}
51+
_responseHeadersCallback.apply(headers);
52+
}
53+
54+
private FetchOptions(boolean cacheControlHeaders, Function<Map<String, String>, Void> responseHeadersCallback,
55+
boolean fastlyDebugHeader) {
56+
_cacheControlHeaders = cacheControlHeaders;
57+
_responseHeadersCallback = responseHeadersCallback;
58+
_fastlyDebugHeader = fastlyDebugHeader;
59+
}
60+
61+
@Override
62+
public boolean equals(Object obj) {
63+
if (null == obj) return false;
64+
if (this == obj) return true;
65+
if (!(obj instanceof FetchOptions)) return false;
66+
67+
FetchOptions other = (FetchOptions) obj;
68+
69+
return Objects.equals(_cacheControlHeaders, other._cacheControlHeaders)
70+
&& Objects.equals(_fastlyDebugHeader, other._fastlyDebugHeader)
71+
&& Objects.equals(_responseHeadersCallback, other._responseHeadersCallback);
72+
}
73+
74+
@Override
75+
public int hashCode() {
76+
return com.google.common.base.Objects.hashCode(_cacheControlHeaders, _fastlyDebugHeader, _responseHeadersCallback);
77+
}
78+
79+
private final boolean _cacheControlHeaders;
80+
private final boolean _fastlyDebugHeader;
81+
private final Function<Map<String, String>, Void> _responseHeadersCallback;
82+
}

client/src/main/java/io/split/engine/common/SyncManagerImp.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ public static SyncManagerImp build(boolean streamingEnabledConfig,
6060
int authRetryBackOffBase,
6161
CloseableHttpClient sseHttpClient,
6262
SegmentCache segmentCache,
63-
int streamingRetryDelay) {
63+
int streamingRetryDelay,
64+
boolean cdnDebugLogging) {
6465
LinkedBlockingQueue<PushManager.Status> pushMessages = new LinkedBlockingQueue<>();
65-
Synchronizer synchronizer = new SynchronizerImp(splitSynchronizationTask, splitFetcher, segmentSynchronizationTaskImp, splitCache, segmentCache, streamingRetryDelay);
66+
Synchronizer synchronizer = new SynchronizerImp(splitSynchronizationTask, splitFetcher, segmentSynchronizationTaskImp, splitCache, segmentCache, streamingRetryDelay, cdnDebugLogging);
6667
PushManager pushManager = PushManagerImp.build(synchronizer, streamingServiceUrl, authUrl, httpClient, pushMessages, sseHttpClient);
6768
return new SyncManagerImp(streamingEnabledConfig, synchronizer, pushManager, pushMessages, authRetryBackOffBase);
6869
}
@@ -111,14 +112,16 @@ private void startPollingMode() {
111112
_pushManager.startWorkers();
112113
_pushManager.scheduleConnectionReset();
113114
_backoff.reset();
115+
_log.info("Streaming up and running.");
114116
break;
115117
case STREAMING_DOWN:
118+
_log.info("Streaming service temporarily unavailable, working in polling mode.");
116119
_pushManager.stopWorkers();
117120
_synchronizer.startPeriodicFetching();
118121
break;
119122
case STREAMING_BACKOFF:
120123
long howLong = _backoff.interval() * 1000;
121-
_log.error(String.format("Retryable error in streaming subsystem. Switching to polling and retrying in %d seconds", howLong/1000));
124+
_log.info(String.format("Retryable error in streaming subsystem. Switching to polling and retrying in %d seconds", howLong/1000));
122125
_synchronizer.startPeriodicFetching();
123126
_pushManager.stopWorkers();
124127
_pushManager.stop();
@@ -127,6 +130,7 @@ private void startPollingMode() {
127130
_pushManager.start();
128131
break;
129132
case STREAMING_OFF:
133+
_log.info("Unrecoverable error in streaming subsystem. SDK will work in polling-mode and will not retry an SSE connection.");
130134
_pushManager.stop();
131135
_synchronizer.startPeriodicFetching();
132136
if (null != _pushStatusMonitorTask) {

0 commit comments

Comments
 (0)