Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58281a98 authored by Kurt Partridge's avatar Kurt Partridge
Browse files

[Rlog6.2] ResearchLogging Refactor

Move specifics of Log output format from ResearchLog to LogUnit

Change-Id: I9d0253c50bb8175ab141bd87dd9a09f39f316b10
parent 41fe487e
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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
 *
 * 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.android.inputmethod.research;

import android.content.SharedPreferences;
import android.util.JsonWriter;
import android.view.inputmethod.CompletionInfo;

import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;

import java.io.IOException;
import java.util.Map;

/* package */ class JsonUtils {
    private JsonUtils() {
        // This utility class is not publicly instantiable.
    }

    /* package */ static void writeJson(final CompletionInfo[] ci, final JsonWriter jsonWriter)
            throws IOException {
        jsonWriter.beginArray();
        for (int j = 0; j < ci.length; j++) {
            jsonWriter.value(ci[j].toString());
        }
        jsonWriter.endArray();
    }

    /* package */ static void writeJson(final SharedPreferences prefs, final JsonWriter jsonWriter)
            throws IOException {
        jsonWriter.beginObject();
        for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
            jsonWriter.name(entry.getKey());
            final Object innerValue = entry.getValue();
            if (innerValue == null) {
                jsonWriter.nullValue();
            } else if (innerValue instanceof Boolean) {
                jsonWriter.value((Boolean) innerValue);
            } else if (innerValue instanceof Number) {
                jsonWriter.value((Number) innerValue);
            } else {
                jsonWriter.value(innerValue.toString());
            }
        }
        jsonWriter.endObject();
    }

    /* package */ static void writeJson(final Key[] keys, final JsonWriter jsonWriter)
            throws IOException {
        jsonWriter.beginArray();
        for (Key key : keys) {
            writeJson(key, jsonWriter);
        }
        jsonWriter.endArray();
    }

    private static void writeJson(final Key key, final JsonWriter jsonWriter) throws IOException {
        jsonWriter.beginObject();
        jsonWriter.name("code").value(key.mCode);
        jsonWriter.name("altCode").value(key.getAltCode());
        jsonWriter.name("x").value(key.mX);
        jsonWriter.name("y").value(key.mY);
        jsonWriter.name("w").value(key.mWidth);
        jsonWriter.name("h").value(key.mHeight);
        jsonWriter.endObject();
    }

    /* package */ static void writeJson(final SuggestedWords words, final JsonWriter jsonWriter)
            throws IOException {
        jsonWriter.beginObject();
        jsonWriter.name("typedWordValid").value(words.mTypedWordValid);
        jsonWriter.name("willAutoCorrect")
                .value(words.mWillAutoCorrect);
        jsonWriter.name("isPunctuationSuggestions")
                .value(words.mIsPunctuationSuggestions);
        jsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
        jsonWriter.name("isPrediction").value(words.mIsPrediction);
        jsonWriter.name("words");
        jsonWriter.beginArray();
        final int size = words.size();
        for (int j = 0; j < size; j++) {
            final SuggestedWordInfo wordInfo = words.getWordInfo(j);
            jsonWriter.value(wordInfo.toString());
        }
        jsonWriter.endArray();
        jsonWriter.endObject();
    }
}
+82 −2
Original line number Diff line number Diff line
@@ -16,10 +16,21 @@

package com.android.inputmethod.research;

import android.content.SharedPreferences;
import android.util.JsonWriter;
import android.util.Log;
import android.view.inputmethod.CompletionInfo;

import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger.LogStatement;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A group of log statements related to each other.
@@ -36,6 +47,8 @@ import java.util.List;
 * been published recently, or whether the LogUnit contains numbers, etc.
 */
/* package */ class LogUnit {
    private static final String TAG = LogUnit.class.getSimpleName();
    private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
    private final ArrayList<LogStatement> mLogStatementList;
    private final ArrayList<Object[]> mValuesList;
    // Assume that mTimeList is sorted in increasing order.  Do not insert null values into
@@ -77,8 +90,13 @@ import java.util.List;
        mTimeList.add(time);
    }

    public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) {
    /**
     * Publish the contents of this LogUnit to researchLog.
     */
    public synchronized void publishTo(final ResearchLog researchLog,
            final boolean isIncludingPrivateData) {
        final int size = mLogStatementList.size();
        // Write out any logStatement that passes the privacy filter.
        for (int i = 0; i < size; i++) {
            final LogStatement logStatement = mLogStatementList.get(i);
            if (!isIncludingPrivateData && logStatement.mIsPotentiallyPrivate) {
@@ -87,10 +105,72 @@ import java.util.List;
            if (mIsPartOfMegaword && logStatement.mIsPotentiallyRevealing) {
                continue;
            }
            researchLog.outputEvent(mLogStatementList.get(i), mValuesList.get(i), mTimeList.get(i));
            // Only retrieve the jsonWriter if we need to.  If we don't get this far, then
            // researchLog.getValidJsonWriter() will not open the file for writing.
            final JsonWriter jsonWriter = researchLog.getValidJsonWriterLocked();
            outputLogStatementToLocked(jsonWriter, mLogStatementList.get(i), mValuesList.get(i),
                    mTimeList.get(i));
        }
    }

    private static final String CURRENT_TIME_KEY = "_ct";
    private static final String UPTIME_KEY = "_ut";
    private static final String EVENT_TYPE_KEY = "_ty";

    /**
     * Write the logStatement and its contents out through jsonWriter.
     *
     * Note that this method is not thread safe for the same jsonWriter.  Callers must ensure
     * thread safety.
     */
    private boolean outputLogStatementToLocked(final JsonWriter jsonWriter,
            final LogStatement logStatement, final Object[] values, final Long time) {
        if (DEBUG) {
            if (logStatement.mKeys.length != values.length) {
                Log.d(TAG, "Key and Value list sizes do not match. " + logStatement.mName);
            }
        }
        try {
            jsonWriter.beginObject();
            jsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
            jsonWriter.name(UPTIME_KEY).value(time);
            jsonWriter.name(EVENT_TYPE_KEY).value(logStatement.mName);
            final String[] keys = logStatement.mKeys;
            final int length = values.length;
            for (int i = 0; i < length; i++) {
                jsonWriter.name(keys[i]);
                final Object value = values[i];
                if (value instanceof CharSequence) {
                    jsonWriter.value(value.toString());
                } else if (value instanceof Number) {
                    jsonWriter.value((Number) value);
                } else if (value instanceof Boolean) {
                    jsonWriter.value((Boolean) value);
                } else if (value instanceof CompletionInfo[]) {
                    JsonUtils.writeJson((CompletionInfo[]) value, jsonWriter);
                } else if (value instanceof SharedPreferences) {
                    JsonUtils.writeJson((SharedPreferences) value, jsonWriter);
                } else if (value instanceof Key[]) {
                    JsonUtils.writeJson((Key[]) value, jsonWriter);
                } else if (value instanceof SuggestedWords) {
                    JsonUtils.writeJson((SuggestedWords) value, jsonWriter);
                } else if (value == null) {
                    jsonWriter.nullValue();
                } else {
                    Log.w(TAG, "Unrecognized type to be logged: " +
                            (value == null ? "<null>" : value.getClass().getName()));
                    jsonWriter.nullValue();
                }
            }
            jsonWriter.endObject();
        } catch (IOException e) {
            e.printStackTrace();
            Log.w(TAG, "Error in JsonWriter; skipping LogStatement");
            return false;
        }
        return true;
    }

    public void setWord(String word) {
        mWord = word;
    }
+6 −99
Original line number Diff line number Diff line
@@ -16,17 +16,10 @@

package com.android.inputmethod.research;

import android.content.SharedPreferences;
import android.os.SystemClock;
import android.util.JsonWriter;
import android.util.Log;
import android.view.inputmethod.CompletionInfo;

import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger.LogStatement;

import java.io.BufferedWriter;
import java.io.File;
@@ -34,7 +27,6 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
@@ -204,103 +196,17 @@ public class ResearchLog {
        }
    }

    private static final String CURRENT_TIME_KEY = "_ct";
    private static final String UPTIME_KEY = "_ut";
    private static final String EVENT_TYPE_KEY = "_ty";

    void outputEvent(final LogStatement logStatement, final Object[] values, final long time) {
        // Not thread safe.
        if (DEBUG) {
            if (logStatement.mKeys.length != values.length) {
                Log.d(TAG, "Key and Value list sizes do not match. " + logStatement.mName);
            }
        }
    /**
     * Return a JsonWriter for this ResearchLog.  It is initialized the first time this method is
     * called.  The cached value is returned in future calls.
     */
    public JsonWriter getValidJsonWriterLocked() {
        try {
            if (mJsonWriter == NULL_JSON_WRITER) {
                mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
                mJsonWriter.beginArray();
                mHasWrittenData = true;
            }
            mJsonWriter.beginObject();
            mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
            mJsonWriter.name(UPTIME_KEY).value(time);
            mJsonWriter.name(EVENT_TYPE_KEY).value(logStatement.mName);
            final String[] keys = logStatement.mKeys;
            final int length = values.length;
            for (int i = 0; i < length; i++) {
                mJsonWriter.name(keys[i]);
                Object value = values[i];
                if (value instanceof CharSequence) {
                    mJsonWriter.value(value.toString());
                } else if (value instanceof Number) {
                    mJsonWriter.value((Number) value);
                } else if (value instanceof Boolean) {
                    mJsonWriter.value((Boolean) value);
                } else if (value instanceof CompletionInfo[]) {
                    CompletionInfo[] ci = (CompletionInfo[]) value;
                    mJsonWriter.beginArray();
                    for (int j = 0; j < ci.length; j++) {
                        mJsonWriter.value(ci[j].toString());
                    }
                    mJsonWriter.endArray();
                } else if (value instanceof SharedPreferences) {
                    SharedPreferences prefs = (SharedPreferences) value;
                    mJsonWriter.beginObject();
                    for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
                        mJsonWriter.name(entry.getKey());
                        final Object innerValue = entry.getValue();
                        if (innerValue == null) {
                            mJsonWriter.nullValue();
                        } else if (innerValue instanceof Boolean) {
                            mJsonWriter.value((Boolean) innerValue);
                        } else if (innerValue instanceof Number) {
                            mJsonWriter.value((Number) innerValue);
                        } else {
                            mJsonWriter.value(innerValue.toString());
                        }
                    }
                    mJsonWriter.endObject();
                } else if (value instanceof Key[]) {
                    Key[] keyboardKeys = (Key[]) value;
                    mJsonWriter.beginArray();
                    for (Key keyboardKey : keyboardKeys) {
                        mJsonWriter.beginObject();
                        mJsonWriter.name("code").value(keyboardKey.mCode);
                        mJsonWriter.name("altCode").value(keyboardKey.getAltCode());
                        mJsonWriter.name("x").value(keyboardKey.mX);
                        mJsonWriter.name("y").value(keyboardKey.mY);
                        mJsonWriter.name("w").value(keyboardKey.mWidth);
                        mJsonWriter.name("h").value(keyboardKey.mHeight);
                        mJsonWriter.endObject();
                    }
                    mJsonWriter.endArray();
                } else if (value instanceof SuggestedWords) {
                    SuggestedWords words = (SuggestedWords) value;
                    mJsonWriter.beginObject();
                    mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
                    mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
                    mJsonWriter.name("isPunctuationSuggestions")
                            .value(words.mIsPunctuationSuggestions);
                    mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
                    mJsonWriter.name("isPrediction").value(words.mIsPrediction);
                    mJsonWriter.name("words");
                    mJsonWriter.beginArray();
                    final int size = words.size();
                    for (int j = 0; j < size; j++) {
                        SuggestedWordInfo wordInfo = words.getWordInfo(j);
                        mJsonWriter.value(wordInfo.toString());
                    }
                    mJsonWriter.endArray();
                    mJsonWriter.endObject();
                } else if (value == null) {
                    mJsonWriter.nullValue();
                } else {
                    Log.w(TAG, "Unrecognized type to be logged: " +
                            (value == null ? "<null>" : value.getClass().getName()));
                    mJsonWriter.nullValue();
                }
            }
            mJsonWriter.endObject();
        } catch (IOException e) {
            e.printStackTrace();
            Log.w(TAG, "Error in JsonWriter; disabling logging");
@@ -315,5 +221,6 @@ public class ResearchLog {
                mJsonWriter = NULL_JSON_WRITER;
            }
        }
        return mJsonWriter;
    }
}