1 /* 2 * Copyright (C) 2007 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.ddmlib; 18 19 import java.io.PrintWriter; 20 import java.io.StringWriter; 21 22 /** 23 * Log class that mirrors the API in main Android sources. 24 * <p/>Default behavior outputs the log to {@link System#out}. Use 25 * {@link #setLogOutput(com.android.ddmlib.Log.ILogOutput)} to redirect the log somewhere else. 26 */ 27 public final class Log { 28 29 /** 30 * Log Level enum. 31 */ 32 public enum LogLevel { 33 VERBOSE(2, "verbose", 'V'), //$NON-NLS-1$ 34 DEBUG(3, "debug", 'D'), //$NON-NLS-1$ 35 INFO(4, "info", 'I'), //$NON-NLS-1$ 36 WARN(5, "warn", 'W'), //$NON-NLS-1$ 37 ERROR(6, "error", 'E'), //$NON-NLS-1$ 38 ASSERT(7, "assert", 'A'); //$NON-NLS-1$ 39 40 private int mPriorityLevel; 41 private String mStringValue; 42 private char mPriorityLetter; 43 44 LogLevel(int intPriority, String stringValue, char priorityChar) { 45 mPriorityLevel = intPriority; 46 mStringValue = stringValue; 47 mPriorityLetter = priorityChar; 48 } 49 50 public static LogLevel getByString(String value) { 51 for (LogLevel mode : values()) { 52 if (mode.mStringValue.equals(value)) { 53 return mode; 54 } 55 } 56 57 return null; 58 } 59 60 /** 61 * Returns the {@link LogLevel} enum matching the specified letter. 62 * @param letter the letter matching a <code>LogLevel</code> enum 63 * @return a <code>LogLevel</code> object or <code>null</code> if no match were found. 64 */ 65 public static LogLevel getByLetter(char letter) { 66 for (LogLevel mode : values()) { 67 if (mode.mPriorityLetter == letter) { 68 return mode; 69 } 70 } 71 72 return null; 73 } 74 75 /** 76 * Returns the {@link LogLevel} enum matching the specified letter. 77 * <p/> 78 * The letter is passed as a {@link String} argument, but only the first character 79 * is used. 80 * @param letter the letter matching a <code>LogLevel</code> enum 81 * @return a <code>LogLevel</code> object or <code>null</code> if no match were found. 82 */ 83 public static LogLevel getByLetterString(String letter) { 84 if (letter.length() > 0) { 85 return getByLetter(letter.charAt(0)); 86 } 87 88 return null; 89 } 90 91 /** 92 * Returns the letter identifying the priority of the {@link LogLevel}. 93 */ 94 public char getPriorityLetter() { 95 return mPriorityLetter; 96 } 97 98 /** 99 * Returns the numerical value of the priority. 100 */ 101 public int getPriority() { 102 return mPriorityLevel; 103 } 104 105 /** 106 * Returns a non translated string representing the LogLevel. 107 */ 108 public String getStringValue() { 109 return mStringValue; 110 } 111 } 112 113 /** 114 * Classes which implement this interface provides methods that deal with outputting log 115 * messages. 116 */ 117 public interface ILogOutput { 118 /** 119 * Sent when a log message needs to be printed. 120 * @param logLevel The {@link LogLevel} enum representing the priority of the message. 121 * @param tag The tag associated with the message. 122 * @param message The message to display. 123 */ 124 public void printLog(LogLevel logLevel, String tag, String message); 125 126 /** 127 * Sent when a log message needs to be printed, and, if possible, displayed to the user 128 * in a dialog box. 129 * @param logLevel The {@link LogLevel} enum representing the priority of the message. 130 * @param tag The tag associated with the message. 131 * @param message The message to display. 132 */ 133 public void printAndPromptLog(LogLevel logLevel, String tag, String message); 134 } 135 136 private static LogLevel mLevel = DdmPreferences.getLogLevel(); 137 138 private static ILogOutput sLogOutput; 139 140 private static final char[] mSpaceLine = new char[72]; 141 private static final char[] mHexDigit = new char[] 142 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; 143 static { 144 /* prep for hex dump */ 145 int i = mSpaceLine.length-1; 146 while (i >= 0) 147 mSpaceLine[i--] = ' '; 148 mSpaceLine[0] = mSpaceLine[1] = mSpaceLine[2] = mSpaceLine[3] = '0'; 149 mSpaceLine[4] = '-'; 150 } 151 152 static final class Config { 153 static final boolean LOGV = true; 154 static final boolean LOGD = true; 155 }; 156 157 private Log() {} 158 159 /** 160 * Outputs a {@link LogLevel#VERBOSE} level message. 161 * @param tag The tag associated with the message. 162 * @param message The message to output. 163 */ 164 public static void v(String tag, String message) { 165 println(LogLevel.VERBOSE, tag, message); 166 } 167 168 /** 169 * Outputs a {@link LogLevel#DEBUG} level message. 170 * @param tag The tag associated with the message. 171 * @param message The message to output. 172 */ 173 public static void d(String tag, String message) { 174 println(LogLevel.DEBUG, tag, message); 175 } 176 177 /** 178 * Outputs a {@link LogLevel#INFO} level message. 179 * @param tag The tag associated with the message. 180 * @param message The message to output. 181 */ 182 public static void i(String tag, String message) { 183 println(LogLevel.INFO, tag, message); 184 } 185 186 /** 187 * Outputs a {@link LogLevel#WARN} level message. 188 * @param tag The tag associated with the message. 189 * @param message The message to output. 190 */ 191 public static void w(String tag, String message) { 192 println(LogLevel.WARN, tag, message); 193 } 194 195 /** 196 * Outputs a {@link LogLevel#ERROR} level message. 197 * @param tag The tag associated with the message. 198 * @param message The message to output. 199 */ 200 public static void e(String tag, String message) { 201 println(LogLevel.ERROR, tag, message); 202 } 203 204 /** 205 * Outputs a log message and attempts to display it in a dialog. 206 * @param tag The tag associated with the message. 207 * @param message The message to output. 208 */ 209 public static void logAndDisplay(LogLevel logLevel, String tag, String message) { 210 if (sLogOutput != null) { 211 sLogOutput.printAndPromptLog(logLevel, tag, message); 212 } else { 213 println(logLevel, tag, message); 214 } 215 } 216 217 /** 218 * Outputs a {@link LogLevel#ERROR} level {@link Throwable} information. 219 * @param tag The tag associated with the message. 220 * @param throwable The {@link Throwable} to output. 221 */ 222 public static void e(String tag, Throwable throwable) { 223 if (throwable != null) { 224 StringWriter sw = new StringWriter(); 225 PrintWriter pw = new PrintWriter(sw); 226 227 throwable.printStackTrace(pw); 228 println(LogLevel.ERROR, tag, throwable.getMessage() + '\n' + sw.toString()); 229 } 230 } 231 232 static void setLevel(LogLevel logLevel) { 233 mLevel = logLevel; 234 } 235 236 /** 237 * Sets the {@link ILogOutput} to use to print the logs. If not set, {@link System#out} 238 * will be used. 239 * @param logOutput The {@link ILogOutput} to use to print the log. 240 */ 241 public static void setLogOutput(ILogOutput logOutput) { 242 sLogOutput = logOutput; 243 } 244 245 /** 246 * Show hex dump. 247 * <p/> 248 * Local addition. Output looks like: 249 * 1230- 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef 250 * <p/> 251 * Uses no string concatenation; creates one String object per line. 252 */ 253 static void hexDump(String tag, LogLevel level, byte[] data, int offset, int length) { 254 255 int kHexOffset = 6; 256 int kAscOffset = 55; 257 char[] line = new char[mSpaceLine.length]; 258 int addr, baseAddr, count; 259 int i, ch; 260 boolean needErase = true; 261 262 //Log.w(tag, "HEX DUMP: off=" + offset + ", length=" + length); 263 264 baseAddr = 0; 265 while (length != 0) { 266 if (length > 16) { 267 // full line 268 count = 16; 269 } else { 270 // partial line; re-copy blanks to clear end 271 count = length; 272 needErase = true; 273 } 274 275 if (needErase) { 276 System.arraycopy(mSpaceLine, 0, line, 0, mSpaceLine.length); 277 needErase = false; 278 } 279 280 // output the address (currently limited to 4 hex digits) 281 addr = baseAddr; 282 addr &= 0xffff; 283 ch = 3; 284 while (addr != 0) { 285 line[ch] = mHexDigit[addr & 0x0f]; 286 ch--; 287 addr >>>= 4; 288 } 289 290 // output hex digits and ASCII chars 291 ch = kHexOffset; 292 for (i = 0; i < count; i++) { 293 byte val = data[offset + i]; 294 295 line[ch++] = mHexDigit[(val >>> 4) & 0x0f]; 296 line[ch++] = mHexDigit[val & 0x0f]; 297 ch++; 298 299 if (val >= 0x20 && val < 0x7f) 300 line[kAscOffset + i] = (char) val; 301 else 302 line[kAscOffset + i] = '.'; 303 } 304 305 println(level, tag, new String(line)); 306 307 // advance to next chunk of data 308 length -= count; 309 offset += count; 310 baseAddr += count; 311 } 312 313 } 314 315 /** 316 * Dump the entire contents of a byte array with DEBUG priority. 317 */ 318 static void hexDump(byte[] data) { 319 hexDump("ddms", LogLevel.DEBUG, data, 0, data.length); 320 } 321 322 /* currently prints to stdout; could write to a log window */ 323 private static void println(LogLevel logLevel, String tag, String message) { 324 if (logLevel.getPriority() >= mLevel.getPriority()) { 325 if (sLogOutput != null) { 326 sLogOutput.printLog(logLevel, tag, message); 327 } else { 328 printLog(logLevel, tag, message); 329 } 330 } 331 } 332 333 /** 334 * Prints a log message. 335 * @param logLevel 336 * @param tag 337 * @param message 338 */ 339 public static void printLog(LogLevel logLevel, String tag, String message) { 340 long msec; 341 342 msec = System.currentTimeMillis(); 343 String outMessage = String.format("%02d:%02d %c/%s: %s\n", 344 (msec / 60000) % 60, (msec / 1000) % 60, 345 logLevel.getPriorityLetter(), tag, message); 346 System.out.print(outMessage); 347 } 348 349 } 350 351 352