1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.base;
6
7import org.chromium.base.annotations.RemovableInRelease;
8
9import java.util.Locale;
10
11/**
12 * Utility class for Logging.
13 *
14 * <p>
15 * Defines logging access points for each feature. They format and forward the logs to
16 * {@link android.util.Log}, allowing to standardize the output, to make it easy to identify
17 * the origin of logs, and enable or disable logging in different parts of the code.
18 * </p>
19 * <p>
20 * Usage documentation: {@code //docs/android_logging.md}.
21 * </p>
22 */
23public class Log {
24    /** Convenience property, same as {@link android.util.Log#ASSERT}. */
25    public static final int ASSERT = android.util.Log.ASSERT;
26
27    /** Convenience property, same as {@link android.util.Log#DEBUG}. */
28    public static final int DEBUG = android.util.Log.DEBUG;
29
30    /** Convenience property, same as {@link android.util.Log#ERROR}. */
31    public static final int ERROR = android.util.Log.ERROR;
32
33    /** Convenience property, same as {@link android.util.Log#INFO}. */
34    public static final int INFO = android.util.Log.INFO;
35
36    /** Convenience property, same as {@link android.util.Log#VERBOSE}. */
37    public static final int VERBOSE = android.util.Log.VERBOSE;
38
39    /** Convenience property, same as {@link android.util.Log#WARN}. */
40    public static final int WARN = android.util.Log.WARN;
41
42    private static final String sTagPrefix = "cr_";
43    private static final String sDeprecatedTagPrefix = "cr.";
44
45    private Log() {
46        // Static only access
47    }
48
49    /** Returns a formatted log message, using the supplied format and arguments.*/
50    private static String formatLog(String messageTemplate, Object... params) {
51        if (params != null && params.length != 0) {
52            messageTemplate = String.format(Locale.US, messageTemplate, params);
53        }
54
55        return messageTemplate;
56    }
57
58    /**
59     * Returns a normalized tag that will be in the form: "cr_foo". This function is called by the
60     * various Log overrides. If using {@link #isLoggable(String, int)}, you might want to call it
61     * to get the tag that will actually be used.
62     * @see #sTagPrefix
63     */
64    public static String normalizeTag(String tag) {
65        if (tag.startsWith(sTagPrefix)) return tag;
66
67        // TODO(dgn) simplify this once 'cr.' is out of the repo (http://crbug.com/533072)
68        int unprefixedTagStart = 0;
69        if (tag.startsWith(sDeprecatedTagPrefix)) {
70            unprefixedTagStart = sDeprecatedTagPrefix.length();
71        }
72
73        return sTagPrefix + tag.substring(unprefixedTagStart, tag.length());
74    }
75
76    /**
77     * Returns a formatted log message, using the supplied format and arguments.
78     * The message will be prepended with the filename and line number of the call.
79     */
80    private static String formatLogWithStack(String messageTemplate, Object... params) {
81        return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params);
82    }
83
84    /**
85     * Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}.
86     *
87     * Note: Has no effect on whether logs are sent or not. Use a method with
88     * {@link RemovableInRelease} to log something in Debug builds only.
89     */
90    public static boolean isLoggable(String tag, int level) {
91        return android.util.Log.isLoggable(tag, level);
92    }
93
94    /**
95     * Sends a {@link android.util.Log#VERBOSE} log message.
96     *
97     * For optimization purposes, only the fixed parameters versions are visible. If you need more
98     * than 7 parameters, consider building your log message using a function annotated with
99     * {@link RemovableInRelease}.
100     *
101     * @param tag Used to identify the source of a log message. Might be modified in the output
102     *            (see {@link #normalizeTag(String)})
103     * @param messageTemplate The message you would like logged. It is to be specified as a format
104     *                        string.
105     * @param args Arguments referenced by the format specifiers in the format string. If the last
106     *             one is a {@link Throwable}, its trace will be printed.
107     */
108    private static void verbose(String tag, String messageTemplate, Object... args) {
109        String message = formatLogWithStack(messageTemplate, args);
110        Throwable tr = getThrowableToLog(args);
111        if (tr != null) {
112            android.util.Log.v(normalizeTag(tag), message, tr);
113        } else {
114            android.util.Log.v(normalizeTag(tag), message);
115        }
116    }
117
118    /** Sends a {@link android.util.Log#VERBOSE} log message. 0 args version. */
119    @RemovableInRelease
120    @VisibleForTesting
121    public static void v(String tag, String message) {
122        verbose(tag, message);
123    }
124
125    /** Sends a {@link android.util.Log#VERBOSE} log message. 1 arg version. */
126    @RemovableInRelease
127    @VisibleForTesting
128    public static void v(String tag, String messageTemplate, Object arg1) {
129        verbose(tag, messageTemplate, arg1);
130    }
131
132    /** Sends a {@link android.util.Log#VERBOSE} log message. 2 args version */
133    @RemovableInRelease
134    @VisibleForTesting
135    public static void v(String tag, String messageTemplate, Object arg1, Object arg2) {
136        verbose(tag, messageTemplate, arg1, arg2);
137    }
138
139    /** Sends a {@link android.util.Log#VERBOSE} log message. 3 args version */
140    @RemovableInRelease
141    @VisibleForTesting
142    public static void v(
143            String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
144        verbose(tag, messageTemplate, arg1, arg2, arg3);
145    }
146
147    /** Sends a {@link android.util.Log#VERBOSE} log message. 4 args version */
148    @RemovableInRelease
149    @VisibleForTesting
150    public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
151            Object arg4) {
152        verbose(tag, messageTemplate, arg1, arg2, arg3, arg4);
153    }
154
155    /** Sends a {@link android.util.Log#VERBOSE} log message. 5 args version */
156    @RemovableInRelease
157    @VisibleForTesting
158    public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
159            Object arg4, Object arg5) {
160        verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
161    }
162
163    /** Sends a {@link android.util.Log#VERBOSE} log message. 6 args version */
164    @RemovableInRelease
165    @VisibleForTesting
166    public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
167            Object arg4, Object arg5, Object arg6) {
168        verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
169    }
170
171    /** Sends a {@link android.util.Log#VERBOSE} log message. 7 args version */
172    @RemovableInRelease
173    @VisibleForTesting
174    public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
175            Object arg4, Object arg5, Object arg6, Object arg7) {
176        verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
177    }
178
179    /**
180     * Sends a {@link android.util.Log#DEBUG} log message.
181     *
182     * For optimization purposes, only the fixed parameters versions are visible. If you need more
183     * than 7 parameters, consider building your log message using a function annotated with
184     * {@link RemovableInRelease}.
185     *
186     * @param tag Used to identify the source of a log message. Might be modified in the output
187     *            (see {@link #normalizeTag(String)})
188     * @param messageTemplate The message you would like logged. It is to be specified as a format
189     *                        string.
190     * @param args Arguments referenced by the format specifiers in the format string. If the last
191     *             one is a {@link Throwable}, its trace will be printed.
192     */
193    private static void debug(String tag, String messageTemplate, Object... args) {
194        String message = formatLogWithStack(messageTemplate, args);
195        Throwable tr = getThrowableToLog(args);
196        if (tr != null) {
197            android.util.Log.d(normalizeTag(tag), message, tr);
198        } else {
199            android.util.Log.d(normalizeTag(tag), message);
200        }
201    }
202
203    /** Sends a {@link android.util.Log#DEBUG} log message. 0 args version. */
204    @RemovableInRelease
205    @VisibleForTesting
206    public static void d(String tag, String message) {
207        debug(tag, message);
208    }
209
210    /** Sends a {@link android.util.Log#DEBUG} log message. 1 arg version. */
211    @RemovableInRelease
212    @VisibleForTesting
213    public static void d(String tag, String messageTemplate, Object arg1) {
214        debug(tag, messageTemplate, arg1);
215    }
216    /** Sends a {@link android.util.Log#DEBUG} log message. 2 args version */
217    @RemovableInRelease
218    @VisibleForTesting
219    public static void d(String tag, String messageTemplate, Object arg1, Object arg2) {
220        debug(tag, messageTemplate, arg1, arg2);
221    }
222    /** Sends a {@link android.util.Log#DEBUG} log message. 3 args version */
223    @RemovableInRelease
224    @VisibleForTesting
225    public static void d(
226            String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
227        debug(tag, messageTemplate, arg1, arg2, arg3);
228    }
229
230    /** Sends a {@link android.util.Log#DEBUG} log message. 4 args version */
231    @RemovableInRelease
232    @VisibleForTesting
233    public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
234            Object arg4) {
235        debug(tag, messageTemplate, arg1, arg2, arg3, arg4);
236    }
237
238    /** Sends a {@link android.util.Log#DEBUG} log message. 5 args version */
239    @RemovableInRelease
240    @VisibleForTesting
241    public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
242            Object arg4, Object arg5) {
243        debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
244    }
245
246    /** Sends a {@link android.util.Log#DEBUG} log message. 6 args version */
247    @RemovableInRelease
248    @VisibleForTesting
249    public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
250            Object arg4, Object arg5, Object arg6) {
251        debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
252    }
253
254    /** Sends a {@link android.util.Log#DEBUG} log message. 7 args version */
255    @RemovableInRelease
256    @VisibleForTesting
257    public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
258            Object arg4, Object arg5, Object arg6, Object arg7) {
259        debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
260    }
261
262    /**
263     * Sends an {@link android.util.Log#INFO} log message.
264     *
265     * @param tag Used to identify the source of a log message. Might be modified in the output
266     *            (see {@link #normalizeTag(String)})
267     * @param messageTemplate The message you would like logged. It is to be specified as a format
268     *                        string.
269     * @param args Arguments referenced by the format specifiers in the format string. If the last
270     *             one is a {@link Throwable}, its trace will be printed.
271     */
272    @VisibleForTesting
273    public static void i(String tag, String messageTemplate, Object... args) {
274        String message = formatLog(messageTemplate, args);
275        Throwable tr = getThrowableToLog(args);
276        if (tr != null) {
277            android.util.Log.i(normalizeTag(tag), message, tr);
278        } else {
279            android.util.Log.i(normalizeTag(tag), message);
280        }
281    }
282
283    /**
284     * Sends a {@link android.util.Log#WARN} log message.
285     *
286     * @param tag Used to identify the source of a log message. Might be modified in the output
287     *            (see {@link #normalizeTag(String)})
288     * @param messageTemplate The message you would like logged. It is to be specified as a format
289     *                        string.
290     * @param args Arguments referenced by the format specifiers in the format string. If the last
291     *             one is a {@link Throwable}, its trace will be printed.
292     */
293    @VisibleForTesting
294    public static void w(String tag, String messageTemplate, Object... args) {
295        String message = formatLog(messageTemplate, args);
296        Throwable tr = getThrowableToLog(args);
297        if (tr != null) {
298            android.util.Log.w(normalizeTag(tag), message, tr);
299        } else {
300            android.util.Log.w(normalizeTag(tag), message);
301        }
302    }
303
304    /**
305     * Sends an {@link android.util.Log#ERROR} log message.
306     *
307     * @param tag Used to identify the source of a log message. Might be modified in the output
308     *            (see {@link #normalizeTag(String)})
309     * @param messageTemplate The message you would like logged. It is to be specified as a format
310     *                        string.
311     * @param args Arguments referenced by the format specifiers in the format string. If the last
312     *             one is a {@link Throwable}, its trace will be printed.
313     */
314    @VisibleForTesting
315    public static void e(String tag, String messageTemplate, Object... args) {
316        String message = formatLog(messageTemplate, args);
317        Throwable tr = getThrowableToLog(args);
318        if (tr != null) {
319            android.util.Log.e(normalizeTag(tag), message, tr);
320        } else {
321            android.util.Log.e(normalizeTag(tag), message);
322        }
323    }
324
325    /**
326     * What a Terrible Failure: Used for conditions that should never happen, and logged at
327     * the {@link android.util.Log#ASSERT} level. Depending on the configuration, it might
328     * terminate the process.
329     *
330     * @see android.util.Log#wtf(String, String, Throwable)
331     *
332     * @param tag Used to identify the source of a log message. Might be modified in the output
333     *            (see {@link #normalizeTag(String)})
334     * @param messageTemplate The message you would like logged. It is to be specified as a format
335     *                        string.
336     * @param args Arguments referenced by the format specifiers in the format string. If the last
337     *             one is a {@link Throwable}, its trace will be printed.
338     */
339    @VisibleForTesting
340    public static void wtf(String tag, String messageTemplate, Object... args) {
341        String message = formatLog(messageTemplate, args);
342        Throwable tr = getThrowableToLog(args);
343        if (tr != null) {
344            android.util.Log.wtf(normalizeTag(tag), message, tr);
345        } else {
346            android.util.Log.wtf(normalizeTag(tag), message);
347        }
348    }
349
350    private static Throwable getThrowableToLog(Object[] args) {
351        if (args == null || args.length == 0) return null;
352
353        Object lastArg = args[args.length - 1];
354
355        if (!(lastArg instanceof Throwable)) return null;
356        return (Throwable) lastArg;
357    }
358
359    /** Returns a string form of the origin of the log call, to be used as secondary tag.*/
360    private static String getCallOrigin() {
361        StackTraceElement[] st = Thread.currentThread().getStackTrace();
362
363        // The call stack should look like:
364        //   n [a variable number of calls depending on the vm used]
365        //  +0 getCallOrigin()
366        //  +1 privateLogFunction: verbose or debug
367        //  +2 formatLogWithStack()
368        //  +3 logFunction: v or d
369        //  +4 caller
370
371        int callerStackIndex;
372        String logClassName = Log.class.getName();
373        for (callerStackIndex = 0; callerStackIndex < st.length; callerStackIndex++) {
374            if (st[callerStackIndex].getClassName().equals(logClassName)) {
375                callerStackIndex += 4;
376                break;
377            }
378        }
379
380        return st[callerStackIndex].getFileName() + ":" + st[callerStackIndex].getLineNumber();
381    }
382}
383