summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/common/LogUtil.java
blob: 126ebf282f4e753fcc20bd17b7d82db3e02d51bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.dialer.common;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;

/** Provides logging functions. */
public class LogUtil {

  public static final String TAG = "Dialer";
  private static final String SEPARATOR = " - ";

  private LogUtil() {}

  /**
   * Log at a verbose level. Verbose logs should generally be filtered out, but may be useful when
   * additional information is needed (e.g. to see how a particular flow evolved). These logs will
   * not generally be available on production builds.
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   * @param msg The message you would like logged, possibly with format arguments.
   * @param args Optional arguments to be used in the formatted string.
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#v(String, String)}
   */
  public static void v(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
    println(android.util.Log.VERBOSE, TAG, tag, msg, args);
  }

  /**
   * Log at a debug level. Debug logs should provide known-useful information to aid in
   * troubleshooting or evaluating flow. These logs will not generally be available on production
   * builds.
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'
   * @param msg The message you would like logged, possibly with format arguments
   * @param args Optional arguments to be used in the formatted string
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#d(String, String)}
   */
  public static void d(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
    println(android.util.Log.DEBUG, TAG, tag, msg, args);
  }

  /**
   * Log at an info level. Info logs provide information that would be useful to have on production
   * builds for troubleshooting.
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   * @param msg The message you would like logged, possibly with format arguments.
   * @param args Optional arguments to be used in the formatted string.
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#i(String, String)}
   */
  public static void i(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
    println(android.util.Log.INFO, TAG, tag, msg, args);
  }

  /**
   * Log entry into a method at the info level.
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   */
  public static void enterBlock(String tag) {
    println(android.util.Log.INFO, TAG, tag, "enter");
  }

  /**
   * Log at a warn level. Warn logs indicate a possible error (e.g. a default switch branch was hit,
   * or a null object was expected to be non-null), but recovery is possible. This may be used when
   * it is not guaranteed that an indeterminate or bad state was entered, just that something may
   * have gone wrong.
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   * @param msg The message you would like logged, possibly with format arguments.
   * @param args Optional arguments to be used in the formatted string.
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#w(String, String)}
   */
  public static void w(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
    println(android.util.Log.WARN, TAG, tag, msg, args);
  }

  /**
   * Log at an error level. Error logs are used when it is known that an error occurred and is
   * possibly fatal. This is used to log information that will be useful for troubleshooting a crash
   * or other severe condition (e.g. error codes, state values, etc.).
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   * @param msg The message you would like logged, possibly with format arguments.
   * @param args Optional arguments to be used in the formatted string.
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#e(String, String)}
   */
  public static void e(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
    println(android.util.Log.ERROR, TAG, tag, msg, args);
  }

  /**
   * Log an exception at an error level. Error logs are used when it is known that an error occurred
   * and is possibly fatal. This is used to log information that will be useful for troubleshooting
   * a crash or other severe condition (e.g. error codes, state values, etc.).
   *
   * @param tag An identifier to allow searching for related logs. Generally of the form
   *     'Class.method'.
   * @param msg The message you would like logged.
   * @param throwable The exception to log.
   * @see {@link String#format(String, Object...)}
   * @see {@link android.util.Log#e(String, String)}
   */
  public static void e(@NonNull String tag, @Nullable String msg, @NonNull Throwable throwable) {
    if (!TextUtils.isEmpty(msg)) {
      println(
          android.util.Log.ERROR,
          TAG,
          tag,
          msg + "\n" + android.util.Log.getStackTraceString(throwable));
    }
  }

  /**
   * Used for log statements where we don't want to log various strings (e.g., usernames) with
   * default logging to avoid leaking PII in logcat.
   *
   * @return text as is if {@value #TAG}'s log level is set to DEBUG or VERBOSE or on non-release
   *     builds; returns a redacted version otherwise.
   */
  public static String sanitizePii(@Nullable Object object) {
    if (object == null) {
      return "null";
    }
    if (isDebugEnabled()) {
      return object.toString();
    }
    return "Redacted-" + object.toString().length() + "-chars";
  }

  /** Anonymizes char to prevent logging personally identifiable information. */
  public static char sanitizeDialPadChar(char ch) {
    if (isDebugEnabled()) {
      return ch;
    }
    if (is12Key(ch)) {
      return '*';
    }
    return ch;
  }

  /** Anonymizes the phone number to prevent logging personally identifiable information. */
  public static String sanitizePhoneNumber(@Nullable String phoneNumber) {
    if (isDebugEnabled()) {
      return phoneNumber;
    }
    if (phoneNumber == null) {
      return null;
    }
    StringBuilder stringBuilder = new StringBuilder(phoneNumber.length());
    for (char c : phoneNumber.toCharArray()) {
      stringBuilder.append(sanitizeDialPadChar(c));
    }
    return stringBuilder.toString();
  }

  public static boolean isVerboseEnabled() {
    return android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE);
  }

  public static boolean isDebugEnabled() {
    return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
  }

  private static boolean is12Key(char ch) {
    return PhoneNumberUtils.is12Key(ch);
  }

  private static void println(
      int level,
      @NonNull String tag,
      @NonNull String localTag,
      @Nullable String msg,
      @Nullable Object... args) {
    // Formatted message is computed lazily if required.
    String formattedMsg;
    // Either null is passed as a single argument or more than one argument is passed.
    boolean hasArgs = args == null || args.length > 0;
    if ((level >= android.util.Log.INFO) || android.util.Log.isLoggable(tag, level)) {
      formattedMsg = localTag;
      if (!TextUtils.isEmpty(msg)) {
        formattedMsg += SEPARATOR + (hasArgs ? String.format(msg, args) : msg);
      }
      android.util.Log.println(level, tag, formattedMsg);
    }
  }
}