001/* 002 * Copyright 2015 The AppAuth for Android Authors. All Rights Reserved. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the 010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 011 * express or implied. See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package net.openid.appauth.internal; 016 017import static net.openid.appauth.Preconditions.checkNotNull; 018 019import android.util.Log; 020import androidx.annotation.NonNull; 021import androidx.annotation.Nullable; 022import androidx.annotation.VisibleForTesting; 023 024/** 025 * Convenience wrapper around {@link android.util.Log}, which evaluates the current log level of 026 * the logging tag once and uses this to determine whether logging should proceed. This minimizes 027 * the number of native calls made as part of logging. 028 */ 029public final class Logger { 030 031 @VisibleForTesting 032 static final String LOG_TAG = "AppAuth"; 033 034 @Nullable 035 private static Logger sInstance; 036 037 @NonNull 038 private final LogWrapper mLog; 039 040 private final int mLogLevel; 041 042 public static synchronized Logger getInstance() { 043 if (sInstance == null) { 044 sInstance = new Logger(AndroidLogWrapper.INSTANCE); 045 } 046 return sInstance; 047 } 048 049 @VisibleForTesting 050 public static synchronized void setInstance(Logger logger) { 051 sInstance = logger; 052 } 053 054 @VisibleForTesting 055 Logger(LogWrapper log) { 056 mLog = checkNotNull(log); 057 // determine the active logging level 058 int level = Log.ASSERT; 059 while (level >= Log.VERBOSE && mLog.isLoggable(LOG_TAG, level)) { 060 level--; 061 } 062 063 mLogLevel = level + 1; 064 } 065 066 public static void verbose(String message, Object... messageParams) { 067 getInstance().log(Log.VERBOSE, null, message, messageParams); 068 } 069 070 public static void verboseWithStack(Throwable tr, String message, Object... messageParams) { 071 getInstance().log(Log.VERBOSE, tr, message, messageParams); 072 } 073 074 public static void debug(String message, Object... messageParams) { 075 getInstance().log(Log.DEBUG, null, message, messageParams); 076 } 077 078 public static void debugWithStack(Throwable tr, String message, Object... messageParams) { 079 getInstance().log(Log.DEBUG, tr, message, messageParams); 080 } 081 082 public static void info(String message, Object... messageParams) { 083 getInstance().log(Log.INFO, null, message, messageParams); 084 } 085 086 public static void infoWithStack(Throwable tr, String message, Object... messageParams) { 087 getInstance().log(Log.INFO, tr, message, messageParams); 088 } 089 090 public static void warn(String message, Object... messageParams) { 091 getInstance().log(Log.WARN, null, message, messageParams); 092 } 093 094 public static void warnWithStack(Throwable tr, String message, Object... messageParams) { 095 getInstance().log(Log.WARN, tr, message, messageParams); 096 } 097 098 public static void error(String message, Object... messageParams) { 099 getInstance().log(Log.ERROR, null, message, messageParams); 100 } 101 102 public static void errorWithStack(Throwable tr, String message, Object... messageParams) { 103 getInstance().log(Log.ERROR, tr, message, messageParams); 104 } 105 106 public void log(int level, Throwable tr, String message, Object... messageParams) { 107 if (mLogLevel > level) { 108 return; 109 } 110 String formattedMessage; 111 if (messageParams == null || messageParams.length < 1) { 112 formattedMessage = message; 113 } else { 114 formattedMessage = String.format(message, messageParams); 115 } 116 117 if (tr != null) { 118 formattedMessage += "\n" + mLog.getStackTraceString(tr); 119 } 120 121 mLog.println(level, LOG_TAG, formattedMessage); 122 } 123 124 /** 125 * The core interface of {@link android.util.Log}, converted into instance methods so as to 126 * allow easier mock testing. 127 */ 128 @VisibleForTesting 129 public interface LogWrapper { 130 void println(int level, String tag, String message); 131 132 boolean isLoggable(String tag, int level); 133 134 String getStackTraceString(Throwable tr); 135 } 136 137 /** 138 * Default {@link LogWrapper} implementation, using {@link android.util.Log} static methods. 139 */ 140 private static final class AndroidLogWrapper implements LogWrapper { 141 private static final AndroidLogWrapper INSTANCE = new AndroidLogWrapper(); 142 143 private AndroidLogWrapper() {} 144 145 public void println(int level, String tag, String message) { 146 Log.println(level, tag, message); 147 } 148 149 public boolean isLoggable(String tag, int level) { 150 return Log.isLoggable(tag, level); 151 } 152 153 public String getStackTraceString(Throwable tr) { 154 return Log.getStackTraceString(tr); 155 } 156 } 157}