Home | History | Annotate | Download | only in ddmlib
      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