• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.tradefed.log;
18 
19 import com.android.ddmlib.Log.LogLevel;
20 
21 import java.io.PrintWriter;
22 import java.io.StringWriter;
23 import java.text.SimpleDateFormat;
24 import java.util.Date;
25 
26 /**
27  * A logging utility class.  Useful for code that needs to override static methods from {@link Log}
28  */
29 public class LogUtil {
30 
31     /**
32      * Make uninstantiable
33      */
LogUtil()34     private LogUtil() {}
35 
36     /**
37      * Sent when a log message needs to be printed.  This implementation prints the message to
38      * stdout in all cases.
39      *
40      * @param logLevel The {@link LogLevel} enum representing the priority of the message.
41      * @param tag The tag associated with the message.
42      * @param message The message to display.
43      */
printLog(LogLevel logLevel, String tag, String message)44     public static void printLog(LogLevel logLevel, String tag, String message) {
45         System.out.print(LogUtil.getLogFormatString(logLevel, tag, message));
46     }
47 
48     /**
49      * Creates a format string that is similar to the "threadtime" log format on the device.  This
50      * is specifically useful because it includes the day and month (to differentiate times for
51      * long-running TF instances), and also uses 24-hour time to disambiguate morning from evening.
52      * <p/>
53      * @see Log#getLogFormatString(LogLevel, String, String)
54      */
getLogFormatString(LogLevel logLevel, String tag, String message)55     public static String getLogFormatString(LogLevel logLevel, String tag, String message) {
56         SimpleDateFormat formatter = new SimpleDateFormat("MM-dd HH:mm:ss");
57         return String.format("%s %c/%s: %s\n", formatter.format(new Date()),
58                 logLevel.getPriorityLetter(), tag, message);
59     }
60 
61     /**
62      * A shim class for {@link Log} that automatically uses the simple classname of the caller as
63      * the log tag
64      */
65     public static class CLog {
66 
67         protected static final String CLASS_NAME = CLog.class.getName();
68 
69         /**
70          * The shim version of {@link Log#v(String, String)}.
71          *
72          * @param message The {@code String} to log
73          */
v(String message)74         public static void v(String message) {
75             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
76             Log.v(getClassName(2), message);
77         }
78 
79         /**
80          * The shim version of {@link Log#v(String, String)}.  Also calls String.format for
81          * convenience.
82          *
83          * @param format A format string for the message to log
84          * @param args The format string arguments
85          */
v(String format, Object... args)86         public static void v(String format, Object... args) {
87             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
88             Log.v(getClassName(2), String.format(format, args));
89         }
90 
91         /**
92          * The shim version of {@link Log#d(String, String)}.
93          *
94          * @param message The {@code String} to log
95          */
d(String message)96         public static void d(String message) {
97             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
98             Log.d(getClassName(2), message);
99         }
100 
101         /**
102          * The shim version of {@link Log#d(String, String)}.  Also calls String.format for
103          * convenience.
104          *
105          * @param format A format string for the message to log
106          * @param args The format string arguments
107          */
d(String format, Object... args)108         public static void d(String format, Object... args) {
109             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
110             Log.d(getClassName(2), String.format(format, args));
111         }
112 
113         /**
114          * The shim version of {@link Log#i(String, String)}.
115          *
116          * @param message The {@code String} to log
117          */
i(String message)118         public static void i(String message) {
119             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
120             Log.i(getClassName(2), message);
121         }
122 
123         /**
124          * The shim version of {@link Log#i(String, String)}.  Also calls String.format for
125          * convenience.
126          *
127          * @param format A format string for the message to log
128          * @param args The format string arguments
129          */
i(String format, Object... args)130         public static void i(String format, Object... args) {
131             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
132             Log.i(getClassName(2), String.format(format, args));
133         }
134 
135         /**
136          * The shim version of {@link Log#w(String, String)}.
137          *
138          * @param message The {@code String} to log
139          */
w(String message)140         public static void w(String message) {
141             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
142             Log.w(getClassName(2), message);
143         }
144 
145         /**
146          * A variation of {@link Log#w(String, String)}, where the stack trace of provided
147          * {@link Throwable} is formatted and logged.
148          *
149          * @param t The {@link Throwable} to log
150          */
w(Throwable t)151         public static void w(Throwable t) {
152             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
153             Log.w(getClassName(2), getStackTraceString(t));
154         }
155 
156         /**
157          * The shim version of {@link Log#w(String, String)}.  Also calls String.format for
158          * convenience.
159          *
160          * @param format A format string for the message to log
161          * @param args The format string arguments
162          */
w(String format, Object... args)163         public static void w(String format, Object... args) {
164             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
165             Log.w(getClassName(2), String.format(format, args));
166         }
167 
168         /**
169          * The shim version of {@link Log#e(String, String)}.
170          *
171          * @param message The {@code String} to log
172          */
e(String message)173         public static void e(String message) {
174             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
175             Log.e(getClassName(2), message);
176         }
177 
178         /**
179          * The shim version of {@link Log#e(String, String)}.  Also calls String.format for
180          * convenience.
181          *
182          * @param format A format string for the message to log
183          * @param args The format string arguments
184          */
e(String format, Object... args)185         public static void e(String format, Object... args) {
186             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
187             Log.e(getClassName(2), String.format(format, args));
188         }
189 
190         /**
191          * The shim version of {@link Log#e(String, Throwable)}.
192          *
193          * @param t the {@link Throwable} to output.
194          */
e(Throwable t)195         public static void e(Throwable t) {
196             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
197             Log.e(getClassName(2), t);
198         }
199 
200         /**
201          * The shim version of {@link Log#logAndDisplay(LogLevel, String, String)}.
202          *
203          * @param logLevel the {@link LogLevel}
204          * @param message The {@code String} to log
205          */
logAndDisplay(LogLevel logLevel, String message)206         public static void logAndDisplay(LogLevel logLevel, String message) {
207             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
208             Log.logAndDisplay(
209                     com.android.tradefed.log.Log.LogLevel.convertFromDdmlib(logLevel),
210                     getClassName(2),
211                     message);
212         }
213 
logAndDisplay( com.android.tradefed.log.Log.LogLevel logLevel, String message)214         public static void logAndDisplay(
215                 com.android.tradefed.log.Log.LogLevel logLevel, String message) {
216             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
217             Log.logAndDisplay(logLevel, getClassName(2), message);
218         }
219 
220         /**
221          * The shim version of {@link Log#logAndDisplay(LogLevel, String, String)}.
222          *
223          * @param logLevel the {@link LogLevel}
224          * @param format A format string for the message to log
225          * @param args The format string arguments
226          */
logAndDisplay(LogLevel logLevel, String format, Object... args)227         public static void logAndDisplay(LogLevel logLevel, String format, Object... args) {
228             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
229             Log.logAndDisplay(
230                     com.android.tradefed.log.Log.LogLevel.convertFromDdmlib(logLevel),
231                     getClassName(2),
232                     String.format(format, args));
233         }
234 
235         /**
236          * The shim version of {@link Log#logAndDisplay(LogLevel, String, String)}.
237          *
238          * @param logLevel the {@link LogLevel}
239          * @param format A format string for the message to log
240          * @param args The format string arguments
241          */
logAndDisplay( com.android.tradefed.log.Log.LogLevel logLevel, String format, Object... args)242         public static void logAndDisplay(
243                 com.android.tradefed.log.Log.LogLevel logLevel, String format, Object... args) {
244             // frame 2: skip frames 0 (#getClassName) and 1 (this method)
245             Log.logAndDisplay(logLevel, getClassName(2), String.format(format, args));
246         }
247 
248         /**
249          * What a Terrible Failure: Report a condition that should never happen.
250          * The error will always be logged at level ASSERT with the call stack.
251          *
252          * @param message The message you would like logged.
253          */
wtf(String message)254         public static void wtf(String message) {
255             wtf(message, (Throwable) null);
256         }
257 
258         /**
259          * What a Terrible Failure: Report a condition that should never happen.
260          * The error will always be logged at level ASSERT with the call stack.
261          *
262          * @param t (Optional) An exception to log. If null, only message will be logged.
263          */
wtf(Throwable t)264         public static void wtf(Throwable t) {
265             wtf(t.getMessage(), t);
266         }
267 
268         /**
269          * What a Terrible Failure: Report a condition that should never happen.
270          * The error will always be logged at level ASSERT with the call stack.
271          * Also calls String.format for convenience.
272          *
273          * @param format A format string for the message to log
274          * @param args The format string arguments
275          */
wtf(String format, Object... args)276         public static void wtf(String format, Object... args) {
277             wtf(String.format(format, args), (Throwable) null);
278         }
279 
280         /**
281          * What a Terrible Failure: Report a condition that should never happen.
282          * The error will always be logged at level ASSERT with the call stack.
283          *
284          * @param message The message you would like logged.
285          * @param t (Optional) An exception to log. If null, only message will be logged.
286          */
wtf(String message, Throwable t)287         public static void wtf(String message, Throwable t) {
288             String tag = findCallerClassName();
289             String logMessage = "WTF - " + message;
290             String stackTrace = getStackTraceString(t);
291             if (stackTrace.length() > 0) {
292                logMessage += "\n" + stackTrace;
293             }
294 
295             Log.logAndDisplay(
296                     com.android.tradefed.log.Log.LogLevel.convertFromDdmlib(LogLevel.ASSERT),
297                     tag,
298                     logMessage);
299         }
300 
301         /**
302          * A helper method that parses the stack trace string out of the
303          * throwable.
304          *
305          * @param t contains the stack trace information
306          * @return A {@link String} containing the stack trace of the throwable.
307          */
getStackTraceString(Throwable t)308         private static String getStackTraceString(Throwable t) {
309             if (t == null) {
310                 return "";
311             }
312 
313             StringWriter sw = new StringWriter();
314             PrintWriter pw = new PrintWriter(sw);
315             t.printStackTrace(pw);
316             pw.flush();
317             return sw.toString();
318         }
319 
320         /**
321          * Return the simple classname from the {@code frame}th stack frame in the call path.
322          * Note: this method does <emph>not</emph> check array bounds for the stack trace length.
323          *
324          * @param frame The index of the stack trace frame to inspect for the class name
325          * @return The simple class name (or full-qualified if an error occurs getting a ref to the
326          *         class) for the given element of the stack trace.
327          */
getClassName(int frame)328         public static String getClassName(int frame) {
329             StackTraceElement[] frames = new Throwable().getStackTrace();
330             return parseClassName(frames[frame].getClassName());
331         }
332 
333         /**
334          * Finds the external class name that directly called a CLog method.
335          *
336          * @return The simple class name (or full-qualified if an error occurs getting a ref to
337          *         the class) of the external class that called a CLog method, or "Unknown" if
338          *         the stack trace is empty or only contains CLog class names.
339          */
findCallerClassName()340         public static String findCallerClassName() {
341             return findCallerClassName(null);
342         }
343 
344         /**
345          * Finds the external class name that directly called a CLog method.
346          *
347          * @param t (Optional) the stack trace to search within, exposed for unit testing
348          * @return The simple class name (or full-qualified if an error occurs getting a ref to
349          *         the class) of the external class that called a CLog method, or "Unknown" if
350          *         the stack trace is empty or only contains CLog class names.
351          */
findCallerClassName(Throwable t)352         public static String findCallerClassName(Throwable t) {
353             String className = "Unknown";
354 
355             if (t == null) {
356                 t = new Throwable();
357             }
358             StackTraceElement[] frames = t.getStackTrace();
359             if (frames.length == 0) {
360                 return className;
361             }
362 
363             // starting with the first frame's class name (this CLog class)
364             // keep iterating until a frame of a different class name is found
365             int f;
366             for (f = 0; f < frames.length; f++) {
367                 className = frames[f].getClassName();
368                 if (!className.equals(CLASS_NAME)) {
369                     break;
370                 }
371             }
372 
373             return parseClassName(className);
374         }
375 
376         /**
377          * Parses the simple class name out of the full class name. If the formatting already looks
378          * like a simple class name, then just returns that.
379          *
380          * @param fullName the full class name to parse
381          * @return The simple class name
382          */
parseClassName(String fullName)383         public static String parseClassName(String fullName) {
384             int lastdot = fullName.lastIndexOf('.');
385             String simpleName = fullName;
386             if (lastdot != -1) {
387                 simpleName = fullName.substring(lastdot + 1);
388             }
389             // handle inner class names
390             int lastdollar = simpleName.lastIndexOf('$');
391             if (lastdollar != -1) {
392                 simpleName = simpleName.substring(0, lastdollar);
393             }
394             return simpleName;
395         }
396     }
397 }
398