Add analytics to fhirtest.uhn.ca

This commit is contained in:
jamesagnew 2016-05-15 11:00:52 -04:00
parent c7d191dc38
commit 207ba872fa
3 changed files with 241 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhirtest.interceptor.AnalyticsInterceptor;
import ca.uhn.fhirtest.joke.HolyFooCowInterceptor;
@Configuration
@ -24,6 +25,16 @@ public class CommonConfig {
return retVal;
}
/**
* This interceptor pings Google Analytics with usage data for the server
*/
@Bean
public IServerInterceptor analyticsInterceptor() {
AnalyticsInterceptor retVal = new AnalyticsInterceptor();
retVal.setAnalyticsTid("UA-1395874-6");
return retVal;
}
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
*/

View File

@ -0,0 +1,230 @@
package ca.uhn.fhirtest.interceptor;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.annotation.PreDestroy;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.scheduling.annotation.Scheduled;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.UrlUtil;
public class AnalyticsInterceptor extends InterceptorAdapter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AnalyticsInterceptor.class);
private String myAnalyticsTid;
private int myCollectThreshold = 100000;
private final LinkedList<AnalyticsEvent> myEventBuffer = new LinkedList<AnalyticsEvent>();
private String myHostname;
private HttpClient myHttpClient;
private long myLastFlushed;
private long mySubmitPeriod = 60000;
private int mySubmitThreshold = 1000;
/**
* Constructor
*/
public AnalyticsInterceptor() {
myHttpClient = new ApacheRestfulClientFactory().getNativeHttpClient();
try {
myHostname = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
myHostname = "Unknown";
}
}
@PreDestroy
public void destroy() {
if (myHttpClient instanceof CloseableHttpClient) {
IOUtils.closeQuietly((CloseableHttpClient) myHttpClient);
}
}
protected void doFlush() {
List<AnalyticsEvent> eventsToFlush;
synchronized (myEventBuffer) {
int size = myEventBuffer.size();
if (size > 20) {
size = 20;
}
eventsToFlush = new ArrayList<AnalyticsEvent>(size);
for (int i = 0; i < size; i++) {
AnalyticsEvent nextEvent = myEventBuffer.pollFirst();
if (nextEvent != null) {
eventsToFlush.add(nextEvent);
}
}
}
StringBuilder b = new StringBuilder();
for (AnalyticsEvent next : eventsToFlush) {
b.append("v=1");
b.append("&tid=").append(myAnalyticsTid);
b.append("&t=event");
b.append("&an=").append(UrlUtil.escape(myHostname)).append('+').append(UrlUtil.escape(next.getApplicationName()));
b.append("&ec=").append(next.getResourceName());
b.append("&ea=").append(next.getRestOperation());
b.append("&cid=").append(next.getClientId());
b.append("&uip=").append(UrlUtil.escape(next.getSourceIp()));
b.append("&ua=").append(UrlUtil.escape(next.getUserAgent()));
b.append("\n");
}
String contents = b.toString();
HttpPost post = new HttpPost("https://www.google-analytics.com/batch");
post.setEntity(new StringEntity(contents, ContentType.APPLICATION_FORM_URLENCODED));
CloseableHttpResponse response = null;
try {
response = (CloseableHttpResponse) myHttpClient.execute(post);
ourLog.trace("Analytics response: {}", response);
ourLog.info("Flushed {} analytics events and got HTTP {} {}", eventsToFlush.size(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
} catch (Exception e) {
ourLog.error("Failed to submit analytics:", e);
} finally {
if (response != null) {
IOUtils.closeQuietly(response);
}
}
}
@Scheduled(fixedDelay = 5000)
public synchronized void flush() {
int pendingEvents;
synchronized (myEventBuffer) {
pendingEvents = myEventBuffer.size();
}
if (pendingEvents == 0) {
return;
}
if (System.currentTimeMillis() - myLastFlushed > mySubmitPeriod) {
doFlush();
return;
}
if (pendingEvents >= mySubmitThreshold) {
doFlush();
return;
}
}
@Override
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequest) {
ServletRequestDetails details = (ServletRequestDetails) theRequest.getRequestDetails();
// Make sure we only send one event per request
if (details.getUserData().containsKey(getClass().getName())) {
return;
}
details.getUserData().put(getClass().getName(), "");
String sourceIp = details.getHeader("x-forwarded-for");
if (isBlank(sourceIp)) {
sourceIp = details.getServletRequest().getRemoteAddr();
}
if (sourceIp.contains(", ")) {
sourceIp = sourceIp.substring(0, sourceIp.indexOf(", "));
}
AnalyticsEvent event = new AnalyticsEvent();
event.setSourceIp(sourceIp);
event.setRestOperation(theOperation);
event.setUserAgent(details.getHeader("User-Agent"));
event.setApplicationName(details.getServletRequest().getServletPath());
event.setRestOperation(theOperation);
event.setResourceName(defaultIfBlank(details.getResourceName(), "SERVER"));
event.setClientId(UUID.randomUUID().toString());
synchronized (myEventBuffer) {
if (myEventBuffer.size() > myCollectThreshold) {
ourLog.warn("Not collecting analytics on request! Event buffer has {} items in it", myEventBuffer.size());
}
myEventBuffer.add(event);
}
}
public void setAnalyticsTid(String theAnalyticsTid) {
myAnalyticsTid = theAnalyticsTid;
}
public static class AnalyticsEvent {
private String myApplicationName;
private String myClientId;
private String myResourceName;
private RestOperationTypeEnum myRestOperation;
private String mySourceIp;
private String myUserAgent;
public String getApplicationName() {
return myApplicationName;
}
public String getClientId() {
return myClientId;
}
public String getResourceName() {
return myResourceName;
}
public RestOperationTypeEnum getRestOperation() {
return myRestOperation;
}
public String getSourceIp() {
return mySourceIp;
}
public String getUserAgent() {
return myUserAgent;
}
public void setApplicationName(String theApplicationName) {
myApplicationName = theApplicationName;
}
public void setClientId(String theClientId) {
myClientId = theClientId;
}
public void setResourceName(String theResourceName) {
myResourceName = theResourceName;
}
public void setRestOperation(RestOperationTypeEnum theRestOperation) {
myRestOperation = theRestOperation;
}
public void setSourceIp(String theSourceIp) {
mySourceIp = theSourceIp;
}
public void setUserAgent(String theUserAgent) {
myUserAgent = theUserAgent;
}
}
}

View File

@ -49,7 +49,6 @@ public class UhnFhirTestApp {
} catch (Exception e) {
ourLog.error("Failure during startup", e);
}
server.stop();
// base = "http://fhir.healthintersections.com.au/open";
// base = "http://spark.furore.com/fhir";