1 /*
2  * Copyright (C) 2006 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 android.util;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.SystemClock;
21 
22 import java.io.FileDescriptor;
23 import java.io.PrintWriter;
24 import java.time.Instant;
25 import java.time.LocalDateTime;
26 import java.util.ArrayDeque;
27 import java.util.Deque;
28 import java.util.Iterator;
29 
30 /**
31  * @hide
32  */
33 public final class LocalLog {
34 
35     private final Deque<String> mLog;
36     private final int mMaxLines;
37 
38     /**
39      * {@code true} to use log timestamps expressed in local date/time, {@code false} to use log
40      * timestamped expressed with the elapsed realtime clock and UTC system clock. {@code false} is
41      * useful when logging behavior that modifies device time zone or system clock.
42      */
43     private final boolean mUseLocalTimestamps;
44 
45     @UnsupportedAppUsage
LocalLog(int maxLines)46     public LocalLog(int maxLines) {
47         this(maxLines, true /* useLocalTimestamps */);
48     }
49 
LocalLog(int maxLines, boolean useLocalTimestamps)50     public LocalLog(int maxLines, boolean useLocalTimestamps) {
51         mMaxLines = Math.max(0, maxLines);
52         mLog = new ArrayDeque<>(mMaxLines);
53         mUseLocalTimestamps = useLocalTimestamps;
54     }
55 
56     @UnsupportedAppUsage
log(String msg)57     public void log(String msg) {
58         if (mMaxLines <= 0) {
59             return;
60         }
61         final String logLine;
62         if (mUseLocalTimestamps) {
63             logLine = String.format("%s - %s", LocalDateTime.now(), msg);
64         } else {
65             logLine = String.format(
66                     "%s / %s - %s", SystemClock.elapsedRealtime(), Instant.now(), msg);
67         }
68         append(logLine);
69     }
70 
append(String logLine)71     private synchronized void append(String logLine) {
72         while (mLog.size() >= mMaxLines) {
73             mLog.remove();
74         }
75         mLog.add(logLine);
76     }
77 
78     @UnsupportedAppUsage
dump(FileDescriptor fd, PrintWriter pw, String[] args)79     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
80         dump(pw);
81     }
82 
dump(PrintWriter pw)83     public synchronized void dump(PrintWriter pw) {
84         dump("", pw);
85     }
86 
87     /**
88      * Dumps the content of local log to print writer with each log entry predeced with indent
89      *
90      * @param indent indent that precedes each log entry
91      * @param pw printer writer to write into
92      */
dump(String indent, PrintWriter pw)93     public synchronized void dump(String indent, PrintWriter pw) {
94         Iterator<String> itr = mLog.iterator();
95         while (itr.hasNext()) {
96             pw.printf("%s%s\n", indent, itr.next());
97         }
98     }
99 
reverseDump(FileDescriptor fd, PrintWriter pw, String[] args)100     public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
101         reverseDump(pw);
102     }
103 
reverseDump(PrintWriter pw)104     public synchronized void reverseDump(PrintWriter pw) {
105         Iterator<String> itr = mLog.descendingIterator();
106         while (itr.hasNext()) {
107             pw.println(itr.next());
108         }
109     }
110 
111     public static class ReadOnlyLocalLog {
112         private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log)113         ReadOnlyLocalLog(LocalLog log) {
114             mLog = log;
115         }
116         @UnsupportedAppUsage
dump(FileDescriptor fd, PrintWriter pw, String[] args)117         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
118             mLog.dump(pw);
119         }
dump(PrintWriter pw)120         public void dump(PrintWriter pw) {
121             mLog.dump(pw);
122         }
reverseDump(FileDescriptor fd, PrintWriter pw, String[] args)123         public void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
124             mLog.reverseDump(pw);
125         }
reverseDump(PrintWriter pw)126         public void reverseDump(PrintWriter pw) {
127             mLog.reverseDump(pw);
128         }
129     }
130 
131     @UnsupportedAppUsage
readOnlyLocalLog()132     public ReadOnlyLocalLog readOnlyLocalLog() {
133         return new ReadOnlyLocalLog(this);
134     }
135 }
136