blob: b6339c4b606b75524edf64376a9669732f91e986 [file] [log] [blame]
Jean-Baptiste Querud56b88a2012-11-07 07:48:57 -08001/*
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
17package com.android.volley;
18
19import android.os.SystemClock;
20import android.util.Log;
21
22import java.util.ArrayList;
23import java.util.List;
24import java.util.Locale;
25
26/** Logging helper class. */
27public class VolleyLog {
28 public static String TAG = "Volley";
29
30 public static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
31
32 public static void v(String format, Object... args) {
33 if (DEBUG) {
34 Log.v(TAG, buildMessage(format, args));
35 }
36 }
37
38 public static void d(String format, Object... args) {
39 Log.d(TAG, buildMessage(format, args));
40 }
41
42 public static void e(String format, Object... args) {
43 Log.e(TAG, buildMessage(format, args));
44 }
45
46 public static void wtf(String format, Object... args) {
47 Log.wtf(TAG, buildMessage(format, args));
48 }
49
50 public static void wtf(Throwable tr, String format, Object... args) {
51 Log.wtf(TAG, buildMessage(format, args), tr);
52 }
53
54 /**
55 * Formats the caller's provided message and prepends useful info like
56 * calling thread ID and method name.
57 */
58 private static String buildMessage(String format, Object... args) {
59 String msg = (args == null) ? format : String.format(Locale.US, format, args);
60 StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
61
62 String caller = "<unknown>";
63 // Walk up the stack looking for the first caller outside of VolleyLog.
64 // It will be at least two frames up, so start there.
65 for (int i = 2; i < trace.length; i++) {
66 Class<?> clazz = trace[i].getClass();
67 if (!clazz.equals(VolleyLog.class)) {
68 String callingClass = trace[i].getClassName();
69 callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
70 callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
71
72 caller = callingClass + "." + trace[i].getMethodName();
73 break;
74 }
75 }
76 return String.format(Locale.US, "[%d] %s: %s",
77 Thread.currentThread().getId(), caller, msg);
78 }
79
80 /**
81 * A simple event log with records containing a name, thread ID, and timestamp.
82 */
83 static class MarkerLog {
84 public static final boolean ENABLED = VolleyLog.DEBUG;
85
86 /** Minimum duration from first marker to last in an marker log to warrant logging. */
87 private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
88
89 private static class Marker {
90 public final String name;
91 public final long thread;
92 public final long time;
93
94 public Marker(String name, long thread, long time) {
95 this.name = name;
96 this.thread = thread;
97 this.time = time;
98 }
99 }
100
101 private final List<Marker> mMarkers = new ArrayList<Marker>();
102 private boolean mFinished = false;
103
104 /** Adds a marker to this log with the specified name. */
105 public synchronized void add(String name, long threadId) {
106 if (mFinished) {
107 throw new IllegalStateException("Marker added to finished log");
108 }
109
110 mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
111 }
112
113 /**
114 * Closes the log, dumping it to logcat if the time difference between
115 * the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.
116 * @param header Header string to print above the marker log.
117 */
118 public synchronized void finish(String header) {
119 mFinished = true;
120
121 long duration = getTotalDuration();
122 if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
123 return;
124 }
125
126 long prevTime = mMarkers.get(0).time;
127 d("(%-4d ms) %s", duration, header);
128 for (Marker marker : mMarkers) {
129 long thisTime = marker.time;
130 d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
131 prevTime = thisTime;
132 }
133 }
134
135 @Override
136 protected void finalize() throws Throwable {
137 // Catch requests that have been collected (and hence end-of-lifed)
138 // but had no debugging output printed for them.
139 if (!mFinished) {
140 finish("Request on the loose");
141 e("Marker log finalized without finish() - uncaught exit point for request");
142 }
143 }
144
145 /** Returns the time difference between the first and last events in this log. */
146 private long getTotalDuration() {
147 if (mMarkers.size() == 0) {
148 return 0;
149 }
150
151 long first = mMarkers.get(0).time;
152 long last = mMarkers.get(mMarkers.size() - 1).time;
153 return last - first;
154 }
155 }
156}