package org.wikipedia.analytics; import android.net.Uri; import org.json.JSONException; import org.json.JSONObject; import org.wikipedia.concurrency.SaneAsyncTask; import org.wikipedia.crash.RemoteLogException; import org.wikipedia.dataclient.okhttp.OkHttpConnectionFactory; import org.wikipedia.util.ReleaseUtil; import org.wikipedia.util.log.L; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; /** * Base class for all various types of events that are logged to EventLogging. * * Each Schema has its own class, and has its own constructor that makes it easy * to call from everywhere without having to duplicate param info at all places. * Updating schemas / revisions is also easier this way. */ public class EventLoggingEvent { private static final RequestBody EMPTY_REQ = RequestBody.create(null, new byte[0]); private static final String EVENTLOG_URL_PROD = "https://meta.wikimedia.org/beacon/event"; private static final String EVENTLOG_URL_DEV = "https://deployment.wikimedia.beta.wmflabs.org/beacon/event"; private static final String EVENTLOG_URL = ReleaseUtil.isPreBetaRelease() ? EVENTLOG_URL_DEV : EVENTLOG_URL_PROD; // https://github.com/wikimedia/mediawiki-extensions-EventLogging/blob/8b3cb1b/modules/ext.eventLogging.core.js#L57 private static final int MAX_URL_LEN = 2000; private final JSONObject data; /** * Create an EventLoggingEvent that logs to a given revision of a given schema with * the gven data payload. * * @param schema Schema name (as specified on meta.wikimedia.org) * @param revID Revision of the schema to log to * @param wiki DBName (enwiki, dewiki, etc) of the wiki in which we are operating * @param eventData Data for the actual event payload. Considered to be * */ public EventLoggingEvent(String schema, int revID, String wiki, JSONObject eventData) { data = new JSONObject(); try { data.put("schema", schema); data.put("revision", revID); data.put("wiki", wiki); data.put("event", eventData); } catch (JSONException e) { throw new RuntimeException(e); } } /** * Log the current event. * * Returns immediately after queueing the network request in the background. */ public void log() { new LogEventTask(data).execute(); } private class LogEventTask extends SaneAsyncTask<Integer> { private final JSONObject data; LogEventTask(JSONObject data) { this.data = data; } @Override public Integer performTask() throws Throwable { String dataURL = Uri.parse(EVENTLOG_URL) .buildUpon().query(data.toString()) .build().toString(); if (dataURL.length() > MAX_URL_LEN) { L.logRemoteErrorIfProd(new RemoteLogException("EventLogging max length exceeded") .put("length", String.valueOf(dataURL.length()))); } Request request = new Request.Builder().url(dataURL).post(EMPTY_REQ).build(); Response response = OkHttpConnectionFactory.getClient().newCall(request).execute(); try { return response.code(); } finally { response.close(); } } @Override public void onCatch(Throwable caught) { // Do nothing bad. EL data is ok to lose. L.d("Lost EL data: " + data.toString()); } } }