11package app .revanced .extension .shared ;
22
3+ import static app .revanced .extension .shared .settings .BaseSettings .DEBUG ;
4+ import static app .revanced .extension .shared .settings .BaseSettings .DEBUG_STACKTRACE ;
5+ import static app .revanced .extension .shared .settings .BaseSettings .DEBUG_TOAST_ON_ERROR ;
6+
37import android .util .Log ;
8+
49import androidx .annotation .NonNull ;
510import androidx .annotation .Nullable ;
6- import app .revanced .extension .shared .settings .BaseSettings ;
711
812import java .io .PrintWriter ;
913import java .io .StringWriter ;
1014
11- import static app .revanced .extension .shared .settings .BaseSettings .*;
12-
15+ import app .revanced .extension .shared .settings .BaseSettings ;
16+ import app .revanced .extension .shared .settings .preference .LogBufferManager ;
17+
18+ /**
19+ * ReVanced specific logger. Logging is done to standard device log (accessible thru ADB),
20+ * and additionally accessible thru {@link LogBufferManager}.
21+ *
22+ * All methods are thread safe.
23+ */
1324public class Logger {
1425
1526 /**
1627 * Log messages using lambdas.
1728 */
1829 @ FunctionalInterface
1930 public interface LogMessage {
31+ /**
32+ * @return Logger string message. This method is only called if logging is enabled.
33+ */
2034 @ NonNull
2135 String buildMessageString ();
36+ }
2237
23- /**
24- * @return For outer classes, this returns {@link Class#getSimpleName()}.
25- * For static, inner, or anonymous classes, this returns the simple name of the enclosing class.
26- * <br>
27- * For example, each of these classes return 'SomethingView':
28- * <code>
29- * com.company.SomethingView
30- * com.company.SomethingView$StaticClass
31- * com.company.SomethingView$1
32- * </code>
33- */
34- private String findOuterClassSimpleName () {
35- var selfClass = this .getClass ();
38+ private enum LogLevel {
39+ DEBUG ,
40+ INFO ,
41+ ERROR
42+ }
3643
37- String fullClassName = selfClass .getName ();
38- final int dollarSignIndex = fullClassName .indexOf ('$' );
39- if (dollarSignIndex < 0 ) {
40- return selfClass .getSimpleName (); // Already an outer class.
41- }
44+ private static final String REVANCED_LOG_TAG = "revanced" ;
45+
46+ private static final String LOGGER_CLASS_NAME = Logger .class .getName ();
4247
43- // Class is inner, static, or anonymous.
44- // Parse the simple name full name.
45- // A class with no package returns index of -1, but incrementing gives index zero which is correct.
46- final int simpleClassNameStartIndex = fullClassName .lastIndexOf ('.' ) + 1 ;
47- return fullClassName .substring (simpleClassNameStartIndex , dollarSignIndex );
48+ /**
49+ * @return For outer classes, this returns {@link Class#getSimpleName()}.
50+ * For static, inner, or anonymous classes, this returns the simple name of the enclosing class.
51+ * <br>
52+ * For example, each of these classes returns 'SomethingView':
53+ * <code>
54+ * com.company.SomethingView
55+ * com.company.SomethingView$StaticClass
56+ * com.company.SomethingView$1
57+ * </code>
58+ */
59+ private static String getOuterClassSimpleName (Object obj ) {
60+ Class <?> logClass = obj .getClass ();
61+ String fullClassName = logClass .getName ();
62+ final int dollarSignIndex = fullClassName .indexOf ('$' );
63+ if (dollarSignIndex < 0 ) {
64+ return logClass .getSimpleName (); // Already an outer class.
4865 }
66+
67+ // Class is inner, static, or anonymous.
68+ // Parse the simple name full name.
69+ // A class with no package returns index of -1, but incrementing gives index zero which is correct.
70+ final int simpleClassNameStartIndex = fullClassName .lastIndexOf ('.' ) + 1 ;
71+ return fullClassName .substring (simpleClassNameStartIndex , dollarSignIndex );
4972 }
5073
51- private static final String REVANCED_LOG_PREFIX = "revanced: " ;
74+ /**
75+ * Internal method to handle logging to Android Log and {@link LogBufferManager}.
76+ * Appends the log message, stack trace (if enabled), and exception (if present) to logBuffer
77+ * with class name but without 'revanced:' prefix.
78+ *
79+ * @param logLevel The log level.
80+ * @param message Log message object.
81+ * @param ex Optional exception.
82+ * @param includeStackTrace If the current stack should be included.
83+ * @param showToast If a toast is to be shown.
84+ */
85+ private static void logInternal (LogLevel logLevel , LogMessage message , @ Nullable Throwable ex ,
86+ boolean includeStackTrace , boolean showToast ) {
87+ // It's very important that no Settings are used in this method,
88+ // as this code is used when a context is not set and thus referencing
89+ // a setting will crash the app.
90+ String messageString = message .buildMessageString ();
91+ String className = getOuterClassSimpleName (message );
92+
93+ StringBuilder logBuilder = new StringBuilder (className .length () + 2
94+ + messageString .length ());
95+ logBuilder .append (className ).append (": " ).append (messageString );
96+
97+ String toastMessage = showToast ? logBuilder .toString () : null ;
98+
99+ // Append exception message if present.
100+ if (ex != null ) {
101+ var exceptionMessage = ex .getMessage ();
102+ if (exceptionMessage != null ) {
103+ logBuilder .append ("\n Exception: " ).append (exceptionMessage );
104+ }
105+ }
106+
107+ if (includeStackTrace ) {
108+ var sw = new StringWriter ();
109+ new Throwable ().printStackTrace (new PrintWriter (sw ));
110+ String stackTrace = sw .toString ();
111+ // Remove the stacktrace elements of this class.
112+ final int loggerIndex = stackTrace .lastIndexOf (LOGGER_CLASS_NAME );
113+ final int loggerBegins = stackTrace .indexOf ('\n' , loggerIndex );
114+ logBuilder .append (stackTrace , loggerBegins , stackTrace .length ());
115+ }
116+
117+ String logText = logBuilder .toString ();
118+ LogBufferManager .appendToLogBuffer (logText );
119+
120+ switch (logLevel ) {
121+ case DEBUG :
122+ if (ex == null ) Log .d (REVANCED_LOG_TAG , logText );
123+ else Log .d (REVANCED_LOG_TAG , logText , ex );
124+ break ;
125+ case INFO :
126+ if (ex == null ) Log .i (REVANCED_LOG_TAG , logText );
127+ else Log .i (REVANCED_LOG_TAG , logText , ex );
128+ break ;
129+ case ERROR :
130+ if (ex == null ) Log .e (REVANCED_LOG_TAG , logText );
131+ else Log .e (REVANCED_LOG_TAG , logText , ex );
132+ break ;
133+ }
134+
135+ if (toastMessage != null ) {
136+ Utils .showToastLong (toastMessage );
137+ }
138+ }
52139
53140 /**
54141 * Logs debug messages under the outer class name of the code calling this method.
55- * Whenever possible, the log string should be constructed entirely inside {@link LogMessage#buildMessageString()}
56- * so the performance cost of building strings is paid only if {@link BaseSettings#DEBUG} is enabled.
142+ * <p>
143+ * Whenever possible, the log string should be constructed entirely inside
144+ * {@link LogMessage#buildMessageString()} so the performance cost of
145+ * building strings is paid only if {@link BaseSettings#DEBUG} is enabled.
57146 */
58- public static void printDebug (@ NonNull LogMessage message ) {
147+ public static void printDebug (LogMessage message ) {
59148 printDebug (message , null );
60149 }
61150
62151 /**
63152 * Logs debug messages under the outer class name of the code calling this method.
64- * Whenever possible, the log string should be constructed entirely inside {@link LogMessage#buildMessageString()}
65- * so the performance cost of building strings is paid only if {@link BaseSettings#DEBUG} is enabled.
153+ * <p>
154+ * Whenever possible, the log string should be constructed entirely inside
155+ * {@link LogMessage#buildMessageString()} so the performance cost of
156+ * building strings is paid only if {@link BaseSettings#DEBUG} is enabled.
66157 */
67- public static void printDebug (@ NonNull LogMessage message , @ Nullable Exception ex ) {
158+ public static void printDebug (LogMessage message , @ Nullable Exception ex ) {
68159 if (DEBUG .get ()) {
69- String logMessage = message .buildMessageString ();
70- String logTag = REVANCED_LOG_PREFIX + message .findOuterClassSimpleName ();
71-
72- if (DEBUG_STACKTRACE .get ()) {
73- var builder = new StringBuilder (logMessage );
74- var sw = new StringWriter ();
75- new Throwable ().printStackTrace (new PrintWriter (sw ));
76-
77- builder .append ('\n' ).append (sw );
78- logMessage = builder .toString ();
79- }
80-
81- if (ex == null ) {
82- Log .d (logTag , logMessage );
83- } else {
84- Log .d (logTag , logMessage , ex );
85- }
160+ logInternal (LogLevel .DEBUG , message , ex , DEBUG_STACKTRACE .get (), false );
86161 }
87162 }
88163
89164 /**
90165 * Logs information messages using the outer class name of the code calling this method.
91166 */
92- public static void printInfo (@ NonNull LogMessage message ) {
167+ public static void printInfo (LogMessage message ) {
93168 printInfo (message , null );
94169 }
95170
96171 /**
97172 * Logs information messages using the outer class name of the code calling this method.
98173 */
99- public static void printInfo (@ NonNull LogMessage message , @ Nullable Exception ex ) {
100- String logTag = REVANCED_LOG_PREFIX + message .findOuterClassSimpleName ();
101- String logMessage = message .buildMessageString ();
102- if (ex == null ) {
103- Log .i (logTag , logMessage );
104- } else {
105- Log .i (logTag , logMessage , ex );
106- }
174+ public static void printInfo (LogMessage message , @ Nullable Exception ex ) {
175+ logInternal (LogLevel .INFO , message , ex , DEBUG_STACKTRACE .get (), false );
107176 }
108177
109178 /**
110179 * Logs exceptions under the outer class name of the code calling this method.
180+ * Appends the log message, exception (if present), and toast message (if enabled) to logBuffer.
111181 */
112- public static void printException (@ NonNull LogMessage message ) {
182+ public static void printException (LogMessage message ) {
113183 printException (message , null );
114184 }
115185
@@ -122,35 +192,23 @@ public static void printException(@NonNull LogMessage message) {
122192 * @param message log message
123193 * @param ex exception (optional)
124194 */
125- public static void printException (@ NonNull LogMessage message , @ Nullable Throwable ex ) {
126- String messageString = message .buildMessageString ();
127- String outerClassSimpleName = message .findOuterClassSimpleName ();
128- String logMessage = REVANCED_LOG_PREFIX + outerClassSimpleName ;
129- if (ex == null ) {
130- Log .e (logMessage , messageString );
131- } else {
132- Log .e (logMessage , messageString , ex );
133- }
134- if (DEBUG_TOAST_ON_ERROR .get ()) {
135- Utils .showToastLong (outerClassSimpleName + ": " + messageString );
136- }
195+ public static void printException (LogMessage message , @ Nullable Throwable ex ) {
196+ logInternal (LogLevel .ERROR , message , ex , DEBUG_STACKTRACE .get (), DEBUG_TOAST_ON_ERROR .get ());
137197 }
138198
139199 /**
140200 * Logging to use if {@link BaseSettings#DEBUG} or {@link Utils#getContext()} may not be initialized.
141201 * Normally this method should not be used.
142202 */
143- public static void initializationInfo (@ NonNull Class <?> callingClass , @ NonNull String message ) {
144- Log . i ( REVANCED_LOG_PREFIX + callingClass . getSimpleName () , message );
203+ public static void initializationInfo (LogMessage message ) {
204+ logInternal ( LogLevel . INFO , message , null , false , false );
145205 }
146206
147207 /**
148208 * Logging to use if {@link BaseSettings#DEBUG} or {@link Utils#getContext()} may not be initialized.
149209 * Normally this method should not be used.
150210 */
151- public static void initializationException (@ NonNull Class <?> callingClass , @ NonNull String message ,
152- @ Nullable Exception ex ) {
153- Log .e (REVANCED_LOG_PREFIX + callingClass .getSimpleName (), message , ex );
211+ public static void initializationException (LogMessage message , @ Nullable Exception ex ) {
212+ logInternal (LogLevel .ERROR , message , ex , false , false );
154213 }
155-
156- }
214+ }
0 commit comments