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

Commit de310dba authored by Hugo Benichi's avatar Hugo Benichi
Browse files

Better LocalLog

This patch fixes the following issues in LocalLog:
  - reverseDump() uses a descending iterator with linear complexity
    instead of a quadratic loop using get(index) on a linked list.
  - reverseDump() is added to ReadOnlyLocalLog.
  - synchronized section in log() is restricted to mutation of internal
    list.
  - formatting of the log message does not create an internal
    StringBuilder.
  - the instance variable mNow is removed: it was only used inside log()
    as a local variable.
  - remaining instance variables are qualified with final.
  - the linked list is replaced by a fixed capacity array-backed queue.

Test: added unit tests
Change-Id: I1a54f0ad26dd35448d3297ea24df1fd626d20ef3
parent 9cf75061
Loading
Loading
Loading
Loading
+26 −18
Original line number Diff line number Diff line
@@ -20,44 +20,49 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Deque;
import java.util.ArrayDeque;

/**
 * @hide
 */
public final class LocalLog {

    private LinkedList<String> mLog;
    private int mMaxLines;
    private long mNow;
    private final Deque<String> mLog;
    private final int mMaxLines;

    public LocalLog(int maxLines) {
        mLog = new LinkedList<String>();
        mMaxLines = maxLines;
        mMaxLines = Math.max(0, maxLines);
        mLog = new ArrayDeque<>(mMaxLines);
    }

    public synchronized void log(String msg) {
        if (mMaxLines > 0) {
            mNow = System.currentTimeMillis();
            StringBuilder sb = new StringBuilder();
    public void log(String msg) {
        if (mMaxLines <= 0) {
            return;
        }
        Calendar c = Calendar.getInstance();
            c.setTimeInMillis(mNow);
            sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
            mLog.add(sb.toString() + " - " + msg);
            while (mLog.size() > mMaxLines) mLog.remove();
        c.setTimeInMillis(System.currentTimeMillis());
        append(String.format("%tm-%td %tH:%tM:%tS.%tL - %s", c, c, c, c, c, c, msg));
    }

    private synchronized void append(String logLine) {
        while (mLog.size() >= mMaxLines) {
            mLog.remove();
        }
        mLog.add(logLine);
    }

    public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        Iterator<String> itr = mLog.listIterator(0);
        Iterator<String> itr = mLog.iterator();
        while (itr.hasNext()) {
            pw.println(itr.next());
        }
    }

    public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
        for (int i = mLog.size() - 1; i >= 0; i--) {
            pw.println(mLog.get(i));
        Iterator<String> itr = mLog.descendingIterator();
        while (itr.hasNext()) {
            pw.println(itr.next());
        }
    }

@@ -69,6 +74,9 @@ public final class LocalLog {
        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            mLog.dump(fd, pw, args);
        }
        public void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
            mLog.reverseDump(fd, pw, args);
        }
    }

    public ReadOnlyLocalLog readOnlyLocalLog() {
+110 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 android.util;

import junit.framework.TestCase;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


public class LocalLogTest extends TestCase {

    public void testA() {
        String[] lines = {
            "foo",
            "bar",
            "baz"
        };
        String[] want = lines;
        testcase(new LocalLog(10), lines, want);
    }

    public void testB() {
        String[] lines = {
            "foo",
            "bar",
            "baz"
        };
        String[] want = {};
        testcase(new LocalLog(0), lines, want);
    }

    public void testC() {
        String[] lines = {
            "dropped",
            "dropped",
            "dropped",
            "dropped",
            "dropped",
            "dropped",
            "foo",
            "bar",
            "baz",
        };
        String[] want = {
            "foo",
            "bar",
            "baz",
        };
        testcase(new LocalLog(3), lines, want);
    }

    void testcase(LocalLog logger, String[] input, String[] want) {
        for (String l : input) {
            logger.log(l);
        }
        verifyAllLines(want, dump(logger).split("\n"));
        verifyAllLines(reverse(want), reverseDump(logger).split("\n"));
    }

    void verifyAllLines(String[] wantLines, String[] gotLines) {
        for (int i = 0; i < wantLines.length; i++) {
            String want = wantLines[i];
            String got = gotLines[i];
            String msg = String.format("%s did not contain %s", quote(got), quote(want));
            assertTrue(msg, got.contains(want));
        }
    }

    static String dump(LocalLog logger) {
        StringWriter buffer = new StringWriter();
        PrintWriter writer = new PrintWriter(buffer);
        logger.dump(null, writer, new String[0]);
        return buffer.toString();
    }

    static String reverseDump(LocalLog logger) {
        StringWriter buffer = new StringWriter();
        PrintWriter writer = new PrintWriter(buffer);
        logger.reverseDump(null, writer, new String[0]);
        return buffer.toString();
    }

    static String quote(String s) {
        return '"' + s + '"';
    }

    static String[] reverse(String[] ary) {
        List<String> ls = Arrays.asList(ary);
        Collections.reverse(ls);
        return  ls.toArray(new String[ary.length]);
    }
}