Skip to content

Commit a0c7937

Browse files
committed
Add auto queue time feature #51
1 parent fb392c2 commit a0c7937

File tree

6 files changed

+191
-35
lines changed

6 files changed

+191
-35
lines changed

src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class GoogleAnalyticsConfig {
5353
private boolean gatherStats = false;
5454
private RequestParameterDiscoverer requestParameterDiscoverer = new DefaultRequestParameterDiscoverer();
5555
private GoogleAnalyticsExceptionHandler exceptionHandler;
56+
private boolean autoQueueTimeEnabled = true;
5657

5758
public RequestParameterDiscoverer getRequestParameterDiscoverer() {
5859
return requestParameterDiscoverer;
@@ -418,4 +419,19 @@ public GoogleAnalyticsConfig setExceptionHandler(GoogleAnalyticsExceptionHandler
418419
return this;
419420
}
420421

422+
public boolean isAutoQueueTimeEnabled() {
423+
return autoQueueTimeEnabled;
424+
}
425+
426+
/**
427+
* If enabled, library will calculate the queue time (qt) at the time request is being posted to GA based on when
428+
* hit request was created and when it is posted to GA. Defaults to <code>true</code>.
429+
*
430+
* @since 2.1
431+
*/
432+
public GoogleAnalyticsConfig setAutoQueueTimeEnabled(boolean autoQueueTimeEnabled) {
433+
this.autoQueueTimeEnabled = autoQueueTimeEnabled;
434+
return this;
435+
}
436+
421437
}

src/main/java/com/brsanthu/googleanalytics/httpclient/HttpRequest.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import java.util.HashMap;
44
import java.util.Map;
55

6+
import com.brsanthu.googleanalytics.request.GoogleAnalyticsRequest;
7+
68
public class HttpRequest {
79
private String contentType;
810
private String method;
911
private String url;
1012
private Map<String, String> bodyParams = new HashMap<>();
13+
private GoogleAnalyticsRequest<?> googleAnalyticsRequest;
1114

1215
public HttpRequest(String url) {
13-
this.setUrl(url);
16+
setUrl(url);
1417
}
1518

1619
public HttpRequest post() {
@@ -53,4 +56,13 @@ public HttpRequest setUrl(String url) {
5356
this.url = url;
5457
return this;
5558
}
59+
60+
public GoogleAnalyticsRequest<?> getGoogleAnalyticsRequest() {
61+
return googleAnalyticsRequest;
62+
}
63+
64+
public HttpRequest setGoogleAnalyticsRequest(GoogleAnalyticsRequest<?> googleAnalyticsRequest) {
65+
this.googleAnalyticsRequest = googleAnalyticsRequest;
66+
return this;
67+
}
5668
}

src/main/java/com/brsanthu/googleanalytics/internal/GoogleAnalyticsImpl.java

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
package com.brsanthu.googleanalytics.internal;
1212

1313
import static com.brsanthu.googleanalytics.internal.GaUtils.isEmpty;
14+
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.QUEUE_TIME;
1415

16+
import java.time.ZonedDateTime;
17+
import java.time.temporal.ChronoUnit;
1518
import java.util.ArrayList;
1619
import java.util.HashMap;
1720
import java.util.List;
@@ -94,6 +97,8 @@ public Future<GoogleAnalyticsResponse> postAsync(GoogleAnalyticsRequest<?> reque
9497
@Override
9598
public GoogleAnalyticsResponse post(GoogleAnalyticsRequest<?> gaReq) {
9699
GoogleAnalyticsResponse response = new GoogleAnalyticsResponse();
100+
response.setGoogleAnalyticsRequest(gaReq);
101+
97102
if (!config.isEnabled()) {
98103
return response;
99104
}
@@ -118,6 +123,8 @@ public GoogleAnalyticsResponse post(GoogleAnalyticsRequest<?> gaReq) {
118123

119124
protected GoogleAnalyticsResponse postBatch(GoogleAnalyticsRequest<?> gaReq) {
120125
GoogleAnalyticsResponse resp = new GoogleAnalyticsResponse();
126+
resp.setGoogleAnalyticsRequest(gaReq);
127+
121128
HttpRequest httpReq = createHttpRequest(gaReq);
122129
resp.setRequestParams(httpReq.getBodyParams());
123130

@@ -151,6 +158,8 @@ private void submitBatch(boolean force) {
151158
// others will not post it even if multiple threads were to wait at sync block at same time
152159
// https://en.wikipedia.org/wiki/Double-checked_locking
153160
if (isSubmitBatch(force)) {
161+
processAutoQueueTime(currentBatch);
162+
154163
logger.debug("Submitting a batch of " + currentBatch.size() + " requests to GA");
155164
httpClient.postBatch(new HttpBatchRequest().setUrl(config.getBatchUrl()).setRequests(currentBatch));
156165
currentBatch.clear();
@@ -159,13 +168,52 @@ private void submitBatch(boolean force) {
159168
}
160169
}
161170

171+
protected HttpRequest processAutoQueueTime(HttpRequest request) {
172+
if (!config.isAutoQueueTimeEnabled()) {
173+
return request;
174+
}
175+
176+
List<HttpRequest> requests = new ArrayList<>();
177+
requests.add(request);
178+
179+
processAutoQueueTime(requests);
180+
181+
return request;
182+
}
183+
184+
protected void processAutoQueueTime(List<HttpRequest> requests) {
185+
if (!config.isAutoQueueTimeEnabled()) {
186+
return;
187+
}
188+
189+
// If there is no queue time specified, then set the queue time to time since event occurred to current time
190+
// (time at which event being posted). This is helpful for batched requests as request may be sitting in queue
191+
// for a while and we need to calculate the time.
192+
for (HttpRequest req : requests) {
193+
if (req.getGoogleAnalyticsRequest() == null || req.getGoogleAnalyticsRequest().occurredAt() == null) {
194+
continue;
195+
}
196+
197+
String qtParamName = QUEUE_TIME.getParameterName();
198+
199+
Map<String, String> params = req.getBodyParams();
200+
201+
int millis = (int) ChronoUnit.MILLIS.between(req.getGoogleAnalyticsRequest().occurredAt(), ZonedDateTime.now());
202+
int qtMillis = params.containsKey(qtParamName) ? millis + Integer.parseInt(params.get(qtParamName)) : millis;
203+
204+
params.put(qtParamName, String.valueOf(qtMillis));
205+
206+
req.getGoogleAnalyticsRequest().queueTime(qtMillis);
207+
}
208+
}
209+
162210
private boolean isSubmitBatch(boolean force) {
163211
return force || currentBatch.size() >= config.getBatchSize();
164212
}
165213

166214
protected GoogleAnalyticsResponse postSingle(GoogleAnalyticsRequest<?> gaReq) {
167215

168-
HttpRequest httpReq = createHttpRequest(gaReq);
216+
HttpRequest httpReq = processAutoQueueTime(createHttpRequest(gaReq));
169217
HttpResponse httpResp = httpClient.post(httpReq);
170218

171219
GoogleAnalyticsResponse response = new GoogleAnalyticsResponse();
@@ -182,13 +230,12 @@ protected GoogleAnalyticsResponse postSingle(GoogleAnalyticsRequest<?> gaReq) {
182230
private HttpRequest createHttpRequest(GoogleAnalyticsRequest<?> gaReq) {
183231
HttpRequest httpReq = new HttpRequest(config.getUrl());
184232

185-
// Process the parameters
233+
httpReq.setGoogleAnalyticsRequest(gaReq);
234+
186235
processParameters(gaReq, httpReq);
187236

188-
// Process custom dimensions
189237
processCustomDimensionParameters(gaReq, httpReq);
190238

191-
// Process custom metrics
192239
processCustomMetricParameters(gaReq, httpReq);
193240

194241
return httpReq;

src/main/java/com/brsanthu/googleanalytics/request/GoogleAnalyticsRequest.java

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/*
2-
* Licensed under the Apache License, Version 2.0 (the "License");
3-
* you may not use this file except in compliance with the License.
4-
* You may obtain a copy of the License at
2+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
3+
* the License. You may obtain a copy of the License at
54
*
6-
* http://www.apache.org/licenses/LICENSE-2.0
5+
* http://www.apache.org/licenses/LICENSE-2.0
76
*
8-
* Unless required by applicable law or agreed to in writing, software
9-
* distributed under the License is distributed on an "AS IS" BASIS,
10-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11-
* See the License for the specific language governing permissions and
12-
* limitations under the License.
7+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
8+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
9+
* specific language governing permissions and limitations under the License.
1310
*/
1411
package com.brsanthu.googleanalytics.request;
1512

@@ -52,6 +49,7 @@
5249
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_LANGUAGE;
5350
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.VIEWPORT_SIZE;
5451

52+
import java.time.ZonedDateTime;
5553
import java.util.HashMap;
5654
import java.util.Map;
5755
import java.util.concurrent.Future;
@@ -74,11 +72,11 @@
7472
@SuppressWarnings("unchecked")
7573
public class GoogleAnalyticsRequest<T> {
7674

77-
protected Map<GoogleAnalyticsParameter, String> parms = new HashMap<GoogleAnalyticsParameter, String>();
78-
protected Map<String, String> customDimensions = new HashMap<String, String>();
79-
protected Map<String, String> customMetrics = new HashMap<String, String>();
80-
75+
protected Map<GoogleAnalyticsParameter, String> parms = new HashMap<>();
76+
protected Map<String, String> customDimensions = new HashMap<>();
77+
protected Map<String, String> customMetrics = new HashMap<>();
8178
protected GoogleAnalyticsExecutor delegateExecutor = null;
79+
private ZonedDateTime occurredAt = ZonedDateTime.now();
8280

8381
public GoogleAnalyticsRequest() {
8482
this(null, null, null, null);
@@ -644,11 +642,11 @@ public String clientId() {
644642
* </tbody>
645643
* </table>
646644
*
647-
*
645+
*
648646
* <div> Example value: <code>as8eknlll</code><br>
649647
* Example usage: <code>uid=as8eknlll</code> </div>
650-
*
651-
*
648+
*
649+
*
652650
* </div>
653651
*
654652
* @param value
@@ -1741,7 +1739,7 @@ public String applicationId() {
17411739
* <div class="ind">
17421740
* <p>
17431741
* Optional.
1744-
*
1742+
*
17451743
* </p>
17461744
* <p>
17471745
* This parameter specifies that this visitor has been exposed to an experiment with the given ID. It should be sent
@@ -1765,8 +1763,8 @@ public String applicationId() {
17651763
* </tr>
17661764
* </tbody>
17671765
* </table>
1768-
*
1769-
*
1766+
*
1767+
*
17701768
* <div> Example value: <code>Qp0gahJ3RAO3DJ18b0XoUQ</code><br>
17711769
* Example usage: <code>xid=Qp0gahJ3RAO3DJ18b0XoUQ</code> </div> </div>
17721770
*/
@@ -1783,7 +1781,7 @@ public String experimentId() {
17831781
* <div class="ind">
17841782
* <p>
17851783
* Optional.
1786-
*
1784+
*
17871785
* </p>
17881786
* <p>
17891787
* This parameter specifies that this visitor has been exposed to a particular variation of an experiment. It should
@@ -1880,4 +1878,17 @@ public GoogleAnalyticsRequest<T> setExecutor(GoogleAnalyticsExecutor delegateExe
18801878
this.delegateExecutor = delegateExecutor;
18811879
return this;
18821880
}
1881+
1882+
/**
1883+
* Indicates the datetime at which this event occurred. This is used to report the <code>qt</code> parameter, if one
1884+
* is not set. The <code>occurredAt</code> defaults to datetime when this request was instantiated.
1885+
*/
1886+
public T occurredAt(ZonedDateTime value) {
1887+
this.occurredAt = value;
1888+
return (T) this;
1889+
}
1890+
1891+
public ZonedDateTime occurredAt() {
1892+
return occurredAt;
1893+
}
18831894
}

src/main/java/com/brsanthu/googleanalytics/request/GoogleAnalyticsResponse.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/*
2-
* Licensed under the Apache License, Version 2.0 (the "License");
3-
* you may not use this file except in compliance with the License.
4-
* You may obtain a copy of the License at
2+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
3+
* the License. You may obtain a copy of the License at
54
*
6-
* http://www.apache.org/licenses/LICENSE-2.0
5+
* http://www.apache.org/licenses/LICENSE-2.0
76
*
8-
* Unless required by applicable law or agreed to in writing, software
9-
* distributed under the License is distributed on an "AS IS" BASIS,
10-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11-
* See the License for the specific language governing permissions and
12-
* limitations under the License.
7+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
8+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
9+
* specific language governing permissions and limitations under the License.
1310
*/
1411
package com.brsanthu.googleanalytics.request;
1512

@@ -22,14 +19,15 @@
2219
*/
2320
public class GoogleAnalyticsResponse {
2421
private int statusCode = 200;
22+
private GoogleAnalyticsRequest<?> googleAnalyticsRequest;
2523
private Map<String, String> requestParams = null;
2624

2725
public Map<String, String> getRequestParams() {
2826
return requestParams;
2927
}
3028

3129
public void setRequestParams(Map<String, String> postedParms) {
32-
this.requestParams = postedParms;
30+
requestParams = postedParms;
3331
}
3432

3533
public void setStatusCode(int statusCode) {
@@ -48,4 +46,13 @@ public String toString() {
4846
builder.append("]");
4947
return builder.toString();
5048
}
49+
50+
public GoogleAnalyticsRequest<?> getGoogleAnalyticsRequest() {
51+
return googleAnalyticsRequest;
52+
}
53+
54+
public GoogleAnalyticsResponse setGoogleAnalyticsRequest(GoogleAnalyticsRequest<?> googleAnalyticsRequest) {
55+
this.googleAnalyticsRequest = googleAnalyticsRequest;
56+
return this;
57+
}
5158
}

0 commit comments

Comments
 (0)