Skip to content

Commit

Permalink
Add auto queue time feature #51
Browse files Browse the repository at this point in the history
  • Loading branch information
brsanthu committed May 19, 2019
1 parent fb392c2 commit a0c7937
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class GoogleAnalyticsConfig {
private boolean gatherStats = false;
private RequestParameterDiscoverer requestParameterDiscoverer = new DefaultRequestParameterDiscoverer();
private GoogleAnalyticsExceptionHandler exceptionHandler;
private boolean autoQueueTimeEnabled = true;

public RequestParameterDiscoverer getRequestParameterDiscoverer() {
return requestParameterDiscoverer;
Expand Down Expand Up @@ -418,4 +419,19 @@ public GoogleAnalyticsConfig setExceptionHandler(GoogleAnalyticsExceptionHandler
return this;
}

public boolean isAutoQueueTimeEnabled() {
return autoQueueTimeEnabled;
}

/**
* If enabled, library will calculate the queue time (qt) at the time request is being posted to GA based on when
* hit request was created and when it is posted to GA. Defaults to <code>true</code>.
*
* @since 2.1
*/
public GoogleAnalyticsConfig setAutoQueueTimeEnabled(boolean autoQueueTimeEnabled) {
this.autoQueueTimeEnabled = autoQueueTimeEnabled;
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import java.util.HashMap;
import java.util.Map;

import com.brsanthu.googleanalytics.request.GoogleAnalyticsRequest;

public class HttpRequest {
private String contentType;
private String method;
private String url;
private Map<String, String> bodyParams = new HashMap<>();
private GoogleAnalyticsRequest<?> googleAnalyticsRequest;

public HttpRequest(String url) {
this.setUrl(url);
setUrl(url);
}

public HttpRequest post() {
Expand Down Expand Up @@ -53,4 +56,13 @@ public HttpRequest setUrl(String url) {
this.url = url;
return this;
}

public GoogleAnalyticsRequest<?> getGoogleAnalyticsRequest() {
return googleAnalyticsRequest;
}

public HttpRequest setGoogleAnalyticsRequest(GoogleAnalyticsRequest<?> googleAnalyticsRequest) {
this.googleAnalyticsRequest = googleAnalyticsRequest;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
package com.brsanthu.googleanalytics.internal;

import static com.brsanthu.googleanalytics.internal.GaUtils.isEmpty;
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.QUEUE_TIME;

import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -94,6 +97,8 @@ public Future<GoogleAnalyticsResponse> postAsync(GoogleAnalyticsRequest<?> reque
@Override
public GoogleAnalyticsResponse post(GoogleAnalyticsRequest<?> gaReq) {
GoogleAnalyticsResponse response = new GoogleAnalyticsResponse();
response.setGoogleAnalyticsRequest(gaReq);

if (!config.isEnabled()) {
return response;
}
Expand All @@ -118,6 +123,8 @@ public GoogleAnalyticsResponse post(GoogleAnalyticsRequest<?> gaReq) {

protected GoogleAnalyticsResponse postBatch(GoogleAnalyticsRequest<?> gaReq) {
GoogleAnalyticsResponse resp = new GoogleAnalyticsResponse();
resp.setGoogleAnalyticsRequest(gaReq);

HttpRequest httpReq = createHttpRequest(gaReq);
resp.setRequestParams(httpReq.getBodyParams());

Expand Down Expand Up @@ -151,6 +158,8 @@ private void submitBatch(boolean force) {
// others will not post it even if multiple threads were to wait at sync block at same time
// https://en.wikipedia.org/wiki/Double-checked_locking
if (isSubmitBatch(force)) {
processAutoQueueTime(currentBatch);

logger.debug("Submitting a batch of " + currentBatch.size() + " requests to GA");
httpClient.postBatch(new HttpBatchRequest().setUrl(config.getBatchUrl()).setRequests(currentBatch));
currentBatch.clear();
Expand All @@ -159,13 +168,52 @@ private void submitBatch(boolean force) {
}
}

protected HttpRequest processAutoQueueTime(HttpRequest request) {
if (!config.isAutoQueueTimeEnabled()) {
return request;
}

List<HttpRequest> requests = new ArrayList<>();
requests.add(request);

processAutoQueueTime(requests);

return request;
}

protected void processAutoQueueTime(List<HttpRequest> requests) {
if (!config.isAutoQueueTimeEnabled()) {
return;
}

// If there is no queue time specified, then set the queue time to time since event occurred to current time
// (time at which event being posted). This is helpful for batched requests as request may be sitting in queue
// for a while and we need to calculate the time.
for (HttpRequest req : requests) {
if (req.getGoogleAnalyticsRequest() == null || req.getGoogleAnalyticsRequest().occurredAt() == null) {
continue;
}

String qtParamName = QUEUE_TIME.getParameterName();

Map<String, String> params = req.getBodyParams();

int millis = (int) ChronoUnit.MILLIS.between(req.getGoogleAnalyticsRequest().occurredAt(), ZonedDateTime.now());
int qtMillis = params.containsKey(qtParamName) ? millis + Integer.parseInt(params.get(qtParamName)) : millis;

params.put(qtParamName, String.valueOf(qtMillis));

req.getGoogleAnalyticsRequest().queueTime(qtMillis);
}
}

private boolean isSubmitBatch(boolean force) {
return force || currentBatch.size() >= config.getBatchSize();
}

protected GoogleAnalyticsResponse postSingle(GoogleAnalyticsRequest<?> gaReq) {

HttpRequest httpReq = createHttpRequest(gaReq);
HttpRequest httpReq = processAutoQueueTime(createHttpRequest(gaReq));
HttpResponse httpResp = httpClient.post(httpReq);

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

// Process the parameters
httpReq.setGoogleAnalyticsRequest(gaReq);

processParameters(gaReq, httpReq);

// Process custom dimensions
processCustomDimensionParameters(gaReq, httpReq);

// Process custom metrics
processCustomMetricParameters(gaReq, httpReq);

return httpReq;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.brsanthu.googleanalytics.request;

Expand Down Expand Up @@ -52,6 +49,7 @@
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_LANGUAGE;
import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.VIEWPORT_SIZE;

import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
Expand All @@ -74,11 +72,11 @@
@SuppressWarnings("unchecked")
public class GoogleAnalyticsRequest<T> {

protected Map<GoogleAnalyticsParameter, String> parms = new HashMap<GoogleAnalyticsParameter, String>();
protected Map<String, String> customDimensions = new HashMap<String, String>();
protected Map<String, String> customMetrics = new HashMap<String, String>();

protected Map<GoogleAnalyticsParameter, String> parms = new HashMap<>();
protected Map<String, String> customDimensions = new HashMap<>();
protected Map<String, String> customMetrics = new HashMap<>();
protected GoogleAnalyticsExecutor delegateExecutor = null;
private ZonedDateTime occurredAt = ZonedDateTime.now();

public GoogleAnalyticsRequest() {
this(null, null, null, null);
Expand Down Expand Up @@ -644,11 +642,11 @@ public String clientId() {
* </tbody>
* </table>
*
*
*
* <div> Example value: <code>as8eknlll</code><br>
* Example usage: <code>uid=as8eknlll</code> </div>
*
*
*
*
* </div>
*
* @param value
Expand Down Expand Up @@ -1741,7 +1739,7 @@ public String applicationId() {
* <div class="ind">
* <p>
* Optional.
*
*
* </p>
* <p>
* This parameter specifies that this visitor has been exposed to an experiment with the given ID. It should be sent
Expand All @@ -1765,8 +1763,8 @@ public String applicationId() {
* </tr>
* </tbody>
* </table>
*
*
*
*
* <div> Example value: <code>Qp0gahJ3RAO3DJ18b0XoUQ</code><br>
* Example usage: <code>xid=Qp0gahJ3RAO3DJ18b0XoUQ</code> </div> </div>
*/
Expand All @@ -1783,7 +1781,7 @@ public String experimentId() {
* <div class="ind">
* <p>
* Optional.
*
*
* </p>
* <p>
* This parameter specifies that this visitor has been exposed to a particular variation of an experiment. It should
Expand Down Expand Up @@ -1880,4 +1878,17 @@ public GoogleAnalyticsRequest<T> setExecutor(GoogleAnalyticsExecutor delegateExe
this.delegateExecutor = delegateExecutor;
return this;
}

/**
* Indicates the datetime at which this event occurred. This is used to report the <code>qt</code> parameter, if one
* is not set. The <code>occurredAt</code> defaults to datetime when this request was instantiated.
*/
public T occurredAt(ZonedDateTime value) {
this.occurredAt = value;
return (T) this;
}

public ZonedDateTime occurredAt() {
return occurredAt;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.brsanthu.googleanalytics.request;

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

public Map<String, String> getRequestParams() {
return requestParams;
}

public void setRequestParams(Map<String, String> postedParms) {
this.requestParams = postedParms;
requestParams = postedParms;
}

public void setStatusCode(int statusCode) {
Expand All @@ -48,4 +46,13 @@ public String toString() {
builder.append("]");
return builder.toString();
}

public GoogleAnalyticsRequest<?> getGoogleAnalyticsRequest() {
return googleAnalyticsRequest;
}

public GoogleAnalyticsResponse setGoogleAnalyticsRequest(GoogleAnalyticsRequest<?> googleAnalyticsRequest) {
this.googleAnalyticsRequest = googleAnalyticsRequest;
return this;
}
}
Loading

0 comments on commit a0c7937

Please sign in to comment.