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