1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.internal.util;
18 
19 import android.os.SystemClock;
20 
21 import com.android.internal.annotations.GuardedBy;
22 
23 import java.io.FileDescriptor;
24 import java.io.PrintWriter;
25 import java.time.Instant;
26 import java.time.LocalDateTime;
27 import java.util.ArrayDeque;
28 import java.util.Deque;
29 import java.util.Iterator;
30 
31 // Copied from frameworks/base
32 /**
33  * Utility for in memory logging
34  *
35  * @hide
36  */
37 public final class LocalLog {
38 
39     private final int mMaxLines;
40 
41     private final Object mLock = new Object();
42 
43     @GuardedBy("mLock")
44     private final Deque<String> mLog;
45 
46     /**
47      * {@code true} to use log timestamps expressed in local date/time, {@code false} to use log
48      * timestamped expressed with the elapsed realtime clock and UTC system clock. {@code false} is
49      * useful when logging behavior that modifies device time zone or system clock.
50      */
51     private final boolean mUseLocalTimestamps;
52 
53     /** Constructor with max lines limit */
LocalLog(int maxLines)54     public LocalLog(int maxLines) {
55         this(maxLines, true /* useLocalTimestamps */);
56     }
57 
58     /** Constructor */
LocalLog(int maxLines, boolean useLocalTimestamps)59     public LocalLog(int maxLines, boolean useLocalTimestamps) {
60         mMaxLines = Math.max(0, maxLines);
61         mLog = new ArrayDeque<>(mMaxLines);
62         mUseLocalTimestamps = useLocalTimestamps;
63     }
64 
65     /** Adds log */
log(String msg)66     public void log(String msg) {
67         if (mMaxLines <= 0) {
68             return;
69         }
70         final String logLine;
71         if (mUseLocalTimestamps) {
72             logLine = LocalDateTime.now() + " - " + msg;
73         } else {
74             logLine = SystemClock.elapsedRealtime() + " / " + Instant.now() + " - " + msg;
75         }
76         append(logLine);
77     }
78 
79     /** Appends log and trims */
append(String logLine)80     private void append(String logLine) {
81         synchronized (mLock) {
82             while (mLog.size() >= mMaxLines) {
83                 mLog.remove();
84             }
85             mLog.add(logLine);
86         }
87     }
88 
89     /** Dumps saved log */
dump(FileDescriptor fd, PrintWriter pw, String[] args)90     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
91         dump(pw);
92     }
93 
94     /** Dumps saved log */
dump(PrintWriter pw)95     public void dump(PrintWriter pw) {
96         dump("", pw);
97     }
98 
99     /**
100      * Dumps the content of local log to print writer with each log entry predeced with indent
101      *
102      * @param indent indent that precedes each log entry
103      * @param pw printer writer to write into
104      */
dump(String indent, PrintWriter pw)105     public void dump(String indent, PrintWriter pw) {
106         synchronized (mLock) {
107             Iterator<String> itr = mLog.iterator();
108             while (itr.hasNext()) {
109                 pw.printf("%s%s\n", indent, itr.next());
110             }
111         }
112     }
113 
114     /** Dumps saved log in reerse order */
reverseDump(FileDescriptor fd, PrintWriter pw, String[] args)115     public void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
116         reverseDump(pw);
117     }
118 
119     /** Dumps saved log in reerse order */
reverseDump(PrintWriter pw)120     public void reverseDump(PrintWriter pw) {
121         synchronized (mLock) {
122             Iterator<String> itr = mLog.descendingIterator();
123             while (itr.hasNext()) {
124                 pw.println(itr.next());
125             }
126         }
127     }
128 }
129