• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.networkrecommendation.util;
17 
18 import android.text.TextUtils;
19 import android.util.Base64;
20 import android.util.Log;
21 
22 import java.security.MessageDigest;
23 import java.security.NoSuchAlgorithmException;
24 import java.util.Arrays;
25 import java.util.IllegalFormatException;
26 import java.util.Locale;
27 
28 /**
29  * Wrapper for {@link Log} which adds the calling class/method to logged items.
30  *
31  * This works by traversing up the call stack and finding the first calling class whose name does
32  * not end with "Log" (allowing clients to add another layer such as AppLog when a constant tag is
33  * desired across the application).
34  */
35 public final class Blog {
36     private static final String TAG = "Blog";
37 
Blog()38     private Blog() {}
39 
i(String tag, String format, Object... args)40     public static void i(String tag, String format, Object... args) {
41         Log.i(tag, buildMessage(format, args));
42     }
43 
i(String tag, Throwable tr, String format, Object... args)44     public static void i(String tag, Throwable tr, String format, Object... args) {
45         Log.i(tag, buildMessage(format, args), tr);
46     }
47 
v(String tag, String format, Object... args)48     public static void v(String tag, String format, Object... args) {
49         // Not guarded with Log.isLoggable - these calls should be stripped out by proguard for
50         // release builds.
51         Log.v(tag, buildMessage(format, args));
52     }
53 
v(String tag, Throwable tr, String format, Object... args)54     public static void v(String tag, Throwable tr, String format, Object... args) {
55         // Not guarded with Log.isLoggable - these calls should be stripped out by proguard for
56         // release builds.
57         Log.v(tag, buildMessage(format, args), tr);
58     }
59 
d(String tag, String format, Object... args)60     public static void d(String tag, String format, Object... args) {
61         if (Log.isLoggable(tag, Log.DEBUG)) {
62             Log.d(tag, buildMessage(format, args));
63         }
64     }
65 
d(String tag, Throwable tr, String format, Object... args)66     public static void d(String tag, Throwable tr, String format, Object... args) {
67         if (Log.isLoggable(tag, Log.DEBUG)) {
68             Log.d(tag, buildMessage(format, args), tr);
69         }
70     }
71 
w(String tag, Throwable tr, String format, Object... args)72     public static void w(String tag, Throwable tr, String format, Object... args) {
73         Log.w(tag, buildMessage(format, args), tr);
74     }
75 
w(String tag, String format, Object... args)76     public static void w(String tag, String format, Object... args) {
77         Log.w(tag, buildMessage(format, args));
78     }
79 
e(String tag, String format, Object... args)80     public static void e(String tag, String format, Object... args) {
81         Log.e(tag, buildMessage(format, args));
82     }
83 
e(String tag, Throwable tr, String format, Object... args)84     public static void e(String tag, Throwable tr, String format, Object... args) {
85         Log.e(tag, buildMessage(format, args), tr);
86     }
87 
wtf(String tag, String format, Object... args)88     public static void wtf(String tag, String format, Object... args) {
89         // Ensure we always log a stack trace when calling Log.wtf.
90         Log.wtf(tag, buildMessage(format, args), new WhatATerribleException());
91     }
92 
wtf(String tag, Throwable tr, String format, Object...args)93     public static void wtf(String tag, Throwable tr, String format, Object...args) {
94         Log.wtf(tag, buildMessage(format, args), new WhatATerribleException(tr));
95     }
96 
97     /**
98      * Redact personally identifiable information for production users.
99      *
100      * If:
101      *     1) String is null
102      *     2) String is empty
103      *     3) enableSensitiveLogging param passed in is true
104      *   Return the String value of the input object.
105      * Else:
106      *   Return a SHA-1 hash of the String value of the input object.
107      */
pii(Object pii, boolean enableSensitiveLogging)108     public static String pii(Object pii, boolean enableSensitiveLogging) {
109         String val = String.valueOf(pii);
110         if (pii == null || TextUtils.isEmpty(val) || enableSensitiveLogging) {
111             return val;
112         }
113         return "[" + secureHash(val.getBytes()) + "]";
114     }
115 
116     /**
117      * Returns a secure hash (using the SHA1 algorithm) of the provided input.
118      *
119      * @return the hash
120      * @param input the bytes for which the secure hash should be computed.
121      */
secureHash(byte[] input)122     public static String secureHash(byte[] input) {
123         MessageDigest messageDigest;
124         try {
125             messageDigest = MessageDigest.getInstance("SHA-1");
126         } catch (NoSuchAlgorithmException e) {
127             return null;
128         }
129         messageDigest.update(input);
130         byte[] result = messageDigest.digest();
131         return Base64.encodeToString(
132                 result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
133     }
134 
135     /**
136      * Formats the caller's provided message and prepends useful info like
137      * calling thread ID and method name.
138      */
buildMessage(String format, Object... args)139     private static String buildMessage(String format, Object... args) {
140         String msg;
141         try {
142             msg = (args == null || args.length == 0) ? format
143                     : String.format(Locale.US, format, args);
144         } catch (IllegalFormatException ife) {
145             String formattedArgs = Arrays.toString(args);
146             wtf(TAG, ife, "msg: \"%s\" args: %s", format, formattedArgs);
147             msg = format + " " + formattedArgs;
148         }
149         StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
150         String caller = "<unknown>";
151         // Walk up the stack looking for the first caller outside of Blog whose name doesn't end
152         // with "Log". It will be at least two frames up, so start there.
153         for (int i = 2; i < trace.length; i++) {
154             String callingClass = trace[i].getClassName();
155             if (!callingClass.equals(Blog.class.getName())
156                         && !callingClass.endsWith("Log")) {
157                 callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
158                 callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
159                 caller = callingClass + "." + trace[i].getMethodName();
160                 break;
161             }
162         }
163         return String.format(Locale.US, "[%d] %s: %s",
164                 Thread.currentThread().getId(), caller, msg);
165     }
166 
167     /** Named exception thrown when calling wtf(). */
168     public static class WhatATerribleException extends RuntimeException {
WhatATerribleException()169         public WhatATerribleException() {
170             super();
171         }
WhatATerribleException(Throwable t)172         public WhatATerribleException(Throwable t) {
173             super(t);
174         }
175     }
176 }
177