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