• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.net.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.text.TextUtils;
22 import android.util.Log;
23 
24 import java.io.FileDescriptor;
25 import java.io.PrintWriter;
26 import java.time.LocalDateTime;
27 import java.util.ArrayDeque;
28 import java.util.Deque;
29 import java.util.Iterator;
30 import java.util.StringJoiner;
31 
32 
33 /**
34  * Class to centralize logging functionality for tethering.
35  *
36  * All access to class methods other than dump() must be on the same thread.
37  *
38  * @hide
39  */
40 public class SharedLog {
41     private static final int DEFAULT_MAX_RECORDS = 500;
42     private static final String COMPONENT_DELIMITER = ".";
43 
44     private enum Category {
45         NONE,
46         ERROR,
47         MARK,
48         WARN,
49     }
50 
51     private final LocalLog mLocalLog;
52     // The tag to use for output to the system log. This is not output to the
53     // LocalLog because that would be redundant.
54     private final String mTag;
55     // The component (or subcomponent) of a system that is sharing this log.
56     // This can grow in depth if components call forSubComponent() to obtain
57     // their SharedLog instance. The tag is not included in the component for
58     // brevity.
59     private final String mComponent;
60 
SharedLog(String tag)61     public SharedLog(String tag) {
62         this(DEFAULT_MAX_RECORDS, tag);
63     }
64 
SharedLog(int maxRecords, String tag)65     public SharedLog(int maxRecords, String tag) {
66         this(new LocalLog(maxRecords), tag, tag);
67     }
68 
SharedLog(LocalLog localLog, String tag, String component)69     private SharedLog(LocalLog localLog, String tag, String component) {
70         mLocalLog = localLog;
71         mTag = tag;
72         mComponent = component;
73     }
74 
getTag()75     public String getTag() {
76         return mTag;
77     }
78 
79     /**
80      * Create a SharedLog based on this log with an additional component prefix on each logged line.
81      */
forSubComponent(String component)82     public SharedLog forSubComponent(String component) {
83         if (!isRootLogInstance()) {
84             component = mComponent + COMPONENT_DELIMITER + component;
85         }
86         return new SharedLog(mLocalLog, mTag, component);
87     }
88 
89     /**
90      * Dump the contents of this log.
91      *
92      * <p>This method may be called on any thread.
93      */
dump(FileDescriptor fd, PrintWriter writer, String[] args)94     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
95         mLocalLog.dump(writer);
96     }
97 
98     /**
99      * Reverse dump the contents of this log.
100      *
101      * <p>This method may be called on any thread.
102      */
reverseDump(PrintWriter writer)103     public void reverseDump(PrintWriter writer) {
104         mLocalLog.reverseDump(writer);
105     }
106 
107     //////
108     // Methods that both log an entry and emit it to the system log.
109     //////
110 
111     /**
112      * Log an error due to an exception. This does not include the exception stacktrace.
113      *
114      * <p>The log entry will be also added to the system log.
115      * @see #e(String, Throwable)
116      */
e(Exception e)117     public void e(Exception e) {
118         Log.e(mTag, record(Category.ERROR, e.toString()));
119     }
120 
121     /**
122      * Log an error message.
123      *
124      * <p>The log entry will be also added to the system log.
125      */
e(String msg)126     public void e(String msg) {
127         Log.e(mTag, record(Category.ERROR, msg));
128     }
129 
130     /**
131      * Log an error due to an exception, with the exception stacktrace if provided.
132      *
133      * <p>The error and exception message appear in the shared log, but the stacktrace is only
134      * logged in general log output (logcat). The log entry will be also added to the system log.
135      */
e(@onNull String msg, @Nullable Throwable exception)136     public void e(@NonNull String msg, @Nullable Throwable exception) {
137         if (exception == null) {
138             e(msg);
139             return;
140         }
141         Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
142     }
143 
144     /**
145      * Log an informational message.
146      *
147      * <p>The log entry will be also added to the system log.
148      */
i(String msg)149     public void i(String msg) {
150         Log.i(mTag, record(Category.NONE, msg));
151     }
152 
153     /**
154      * Log a warning message.
155      *
156      * <p>The log entry will be also added to the system log.
157      */
w(String msg)158     public void w(String msg) {
159         Log.w(mTag, record(Category.WARN, msg));
160     }
161 
162     //////
163     // Methods that only log an entry (and do NOT emit to the system log).
164     //////
165 
166     /**
167      * Log a general message to be only included in the in-memory log.
168      *
169      * <p>The log entry will *not* be added to the system log.
170      */
log(String msg)171     public void log(String msg) {
172         record(Category.NONE, msg);
173     }
174 
175     /**
176      * Log a general, formatted message to be only included in the in-memory log.
177      *
178      * <p>The log entry will *not* be added to the system log.
179      * @see String#format(String, Object...)
180      */
logf(String fmt, Object... args)181     public void logf(String fmt, Object... args) {
182         log(String.format(fmt, args));
183     }
184 
185     /**
186      * Log a message with MARK level.
187      *
188      * <p>The log entry will *not* be added to the system log.
189      */
mark(String msg)190     public void mark(String msg) {
191         record(Category.MARK, msg);
192     }
193 
record(Category category, String msg)194     private String record(Category category, String msg) {
195         final String entry = logLine(category, msg);
196         mLocalLog.append(entry);
197         return entry;
198     }
199 
logLine(Category category, String msg)200     private String logLine(Category category, String msg) {
201         final StringJoiner sj = new StringJoiner(" ");
202         if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
203         if (category != Category.NONE) sj.add(category.toString());
204         return sj.add(msg).toString();
205     }
206 
207     // Check whether this SharedLog instance is nominally the top level in
208     // a potential hierarchy of shared logs (the root of a tree),
209     // or is a subcomponent within the hierarchy.
isRootLogInstance()210     private boolean isRootLogInstance() {
211         return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
212     }
213 
214     private static final class LocalLog {
215         private final Deque<String> mLog;
216         private final int mMaxLines;
217 
LocalLog(int maxLines)218         LocalLog(int maxLines) {
219             mMaxLines = Math.max(0, maxLines);
220             mLog = new ArrayDeque<>(mMaxLines);
221         }
222 
append(String logLine)223         synchronized void append(String logLine) {
224             if (mMaxLines <= 0) return;
225             while (mLog.size() >= mMaxLines) {
226                 mLog.remove();
227             }
228             mLog.add(LocalDateTime.now() + " - " + logLine);
229         }
230 
231         /**
232          * Dumps the content of local log to print writer with each log entry
233          *
234          * @param pw printer writer to write into
235          */
dump(PrintWriter pw)236         synchronized void dump(PrintWriter pw) {
237             for (final String s : mLog) {
238                 pw.println(s);
239             }
240         }
241 
reverseDump(PrintWriter pw)242         synchronized void reverseDump(PrintWriter pw) {
243             final Iterator<String> itr = mLog.descendingIterator();
244             while (itr.hasNext()) {
245                 pw.println(itr.next());
246             }
247         }
248     }
249 }
250