Home | History | Annotate | Download | only in volley
      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.volley;
     18 
     19 import android.os.SystemClock;
     20 import android.util.Log;
     21 import java.util.ArrayList;
     22 import java.util.List;
     23 import java.util.Locale;
     24 
     25 /**
     26  * Logging helper class.
     27  *
     28  * <p>to see Volley logs call:<br>
     29  * {@code <android-sdk>/platform-tools/adb shell setprop log.tag.Volley VERBOSE}
     30  */
     31 public class VolleyLog {
     32     public static String TAG = "Volley";
     33 
     34     public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
     35 
     36     /**
     37      * {@link Class#getName()} uses reflection and calling it on a potentially hot code path may
     38      * have some cost. To minimize this cost we fetch class name once here and use it later.
     39      */
     40     private static final String CLASS_NAME = VolleyLog.class.getName();
     41 
     42     /**
     43      * Customize the log tag for your application, so that other apps using Volley don't mix their
     44      * logs with yours. <br>
     45      * Enable the log property for your tag before starting your app: <br>
     46      * {@code adb shell setprop log.tag.&lt;tag&gt;}
     47      */
     48     public static void setTag(String tag) {
     49         d("Changing log tag to %s", tag);
     50         TAG = tag;
     51 
     52         // Reinitialize the DEBUG "constant"
     53         DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
     54     }
     55 
     56     public static void v(String format, Object... args) {
     57         if (DEBUG) {
     58             Log.v(TAG, buildMessage(format, args));
     59         }
     60     }
     61 
     62     public static void d(String format, Object... args) {
     63         Log.d(TAG, buildMessage(format, args));
     64     }
     65 
     66     public static void e(String format, Object... args) {
     67         Log.e(TAG, buildMessage(format, args));
     68     }
     69 
     70     public static void e(Throwable tr, String format, Object... args) {
     71         Log.e(TAG, buildMessage(format, args), tr);
     72     }
     73 
     74     public static void wtf(String format, Object... args) {
     75         Log.wtf(TAG, buildMessage(format, args));
     76     }
     77 
     78     public static void wtf(Throwable tr, String format, Object... args) {
     79         Log.wtf(TAG, buildMessage(format, args), tr);
     80     }
     81 
     82     /**
     83      * Formats the caller's provided message and prepends useful info like calling thread ID and
     84      * method name.
     85      */
     86     private static String buildMessage(String format, Object... args) {
     87         String msg = (args == null) ? format : String.format(Locale.US, format, args);
     88         StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
     89 
     90         String caller = "<unknown>";
     91         // Walk up the stack looking for the first caller outside of VolleyLog.
     92         // It will be at least two frames up, so start there.
     93         for (int i = 2; i < trace.length; i++) {
     94             String clazz = trace[i].getClassName();
     95             if (!clazz.equals(VolleyLog.CLASS_NAME)) {
     96                 String callingClass = trace[i].getClassName();
     97                 callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
     98                 callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
     99 
    100                 caller = callingClass + "." + trace[i].getMethodName();
    101                 break;
    102             }
    103         }
    104         return String.format(Locale.US, "[%d] %s: %s", Thread.currentThread().getId(), caller, msg);
    105     }
    106 
    107     /** A simple event log with records containing a name, thread ID, and timestamp. */
    108     static class MarkerLog {
    109         public static final boolean ENABLED = VolleyLog.DEBUG;
    110 
    111         /** Minimum duration from first marker to last in an marker log to warrant logging. */
    112         private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
    113 
    114         private static class Marker {
    115             public final String name;
    116             public final long thread;
    117             public final long time;
    118 
    119             public Marker(String name, long thread, long time) {
    120                 this.name = name;
    121                 this.thread = thread;
    122                 this.time = time;
    123             }
    124         }
    125 
    126         private final List<Marker> mMarkers = new ArrayList<>();
    127         private boolean mFinished = false;
    128 
    129         /** Adds a marker to this log with the specified name. */
    130         public synchronized void add(String name, long threadId) {
    131             if (mFinished) {
    132                 throw new IllegalStateException("Marker added to finished log");
    133             }
    134 
    135             mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
    136         }
    137 
    138         /**
    139          * Closes the log, dumping it to logcat if the time difference between the first and last
    140          * markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.
    141          *
    142          * @param header Header string to print above the marker log.
    143          */
    144         public synchronized void finish(String header) {
    145             mFinished = true;
    146 
    147             long duration = getTotalDuration();
    148             if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
    149                 return;
    150             }
    151 
    152             long prevTime = mMarkers.get(0).time;
    153             d("(%-4d ms) %s", duration, header);
    154             for (Marker marker : mMarkers) {
    155                 long thisTime = marker.time;
    156                 d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
    157                 prevTime = thisTime;
    158             }
    159         }
    160 
    161         @Override
    162         protected void finalize() throws Throwable {
    163             // Catch requests that have been collected (and hence end-of-lifed)
    164             // but had no debugging output printed for them.
    165             if (!mFinished) {
    166                 finish("Request on the loose");
    167                 e("Marker log finalized without finish() - uncaught exit point for request");
    168             }
    169         }
    170 
    171         /** Returns the time difference between the first and last events in this log. */
    172         private long getTotalDuration() {
    173             if (mMarkers.size() == 0) {
    174                 return 0;
    175             }
    176 
    177             long first = mMarkers.get(0).time;
    178             long last = mMarkers.get(mMarkers.size() - 1).time;
    179             return last - first;
    180         }
    181     }
    182 }
    183