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