1 /*
2  * Copyright 2020 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 
17 package androidx.camera.camera2.pipe.core
18 
19 import android.util.Log
20 
21 /**
22  * This object provides a set of common log functions that are optimized for CameraPipe with options
23  * to control which log levels are available to log at compile time via const val's.
24  *
25  * Log functions have been designed so that printing variables and doing string concatenation will
26  * not occur if the log level is disabled, which leads to slightly unusual syntax:
27  *
28  * Log.debug { "This is a log message with a $value" }
29  */
30 public object Log {
31     public const val TAG: String = "CXCP"
32 
33     private const val LOG_LEVEL_DEBUG = 1
34     private const val LOG_LEVEL_INFO = 2
35     private const val LOG_LEVEL_WARN = 3
36     private const val LOG_LEVEL_ERROR = 4
37 
38     // This indicates the lowest log level that will always log.
39     private const val LOG_LEVEL = LOG_LEVEL_DEBUG
40 
41     public val DEBUG_LOGGABLE: Boolean =
42         LOG_LEVEL <= LOG_LEVEL_DEBUG || Log.isLoggable(TAG, Log.DEBUG)
43     public val INFO_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_INFO || Log.isLoggable(TAG, Log.INFO)
44     public val WARN_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_WARN || Log.isLoggable(TAG, Log.WARN)
45     public val ERROR_LOGGABLE: Boolean =
46         LOG_LEVEL <= LOG_LEVEL_ERROR || Log.isLoggable(TAG, Log.ERROR)
47 
48     /** Debug functions log noisy information related to the internals of the system. */
debugnull49     public inline fun debug(crossinline msg: () -> String) {
50         if (Debug.ENABLE_LOGGING && DEBUG_LOGGABLE) Log.d(TAG, msg())
51     }
52 
debugnull53     public inline fun debug(throwable: Throwable, crossinline msg: () -> String) {
54         if (Debug.ENABLE_LOGGING && DEBUG_LOGGABLE) Log.d(TAG, msg(), throwable)
55     }
56 
57     /** Info functions log standard, useful information about the state of the system. */
infonull58     public inline fun info(crossinline msg: () -> String) {
59         if (Debug.ENABLE_LOGGING && INFO_LOGGABLE) Log.i(TAG, msg())
60     }
61 
infonull62     public inline fun info(throwable: Throwable, crossinline msg: () -> String) {
63         if (Debug.ENABLE_LOGGING && INFO_LOGGABLE) Log.i(TAG, msg(), throwable)
64     }
65 
66     /**
67      * Warning functions are used when something unexpected may lead to a crash or fatal exception
68      * later on as a result if the unusual circumstances
69      */
warnnull70     public inline fun warn(crossinline msg: () -> String) {
71         if (Debug.ENABLE_LOGGING && WARN_LOGGABLE) Log.w(TAG, msg())
72     }
73 
warnnull74     public inline fun warn(throwable: Throwable, crossinline msg: () -> String) {
75         if (Debug.ENABLE_LOGGING && WARN_LOGGABLE) Log.w(TAG, msg(), throwable)
76     }
77 
78     /**
79      * Error functions are reserved for something unexpected that will lead to a crash or data loss.
80      */
errornull81     public inline fun error(crossinline msg: () -> String) {
82         if (Debug.ENABLE_LOGGING && ERROR_LOGGABLE) Log.e(TAG, msg())
83     }
84 
errornull85     public inline fun error(throwable: Throwable, crossinline msg: () -> String) {
86         if (Debug.ENABLE_LOGGING && ERROR_LOGGABLE) Log.e(TAG, msg(), throwable)
87     }
88 
89     /**
90      * Try-catch [block] and rethrow caught exception after logging with [msg].
91      *
92      * @param msg The message to log with the exception.
93      * @param block Function to be wrapped in try-catch.
94      * @return Original returned value of `block` in case of no exception.
95      * @throws Exception that is caught while executing `block`.
96      */
rethrowExceptionAfterLoggingnull97     public inline fun <T> rethrowExceptionAfterLogging(msg: String, crossinline block: () -> T): T =
98         try {
99             block()
100         } catch (e: Exception) {
<lambda>null101             error(e) { msg }
102             throw e
103         }
104 
105     /** Read the stack trace of a calling method and join it to a formatted string. */
readStackTracenull106     public fun readStackTrace(limit: Int = 4): String {
107         val elements = Thread.currentThread().stackTrace
108         // Ignore the first 3 elements, which ignores:
109         // VMStack.getThreadStackTrace
110         // Thread.currentThread().getStackTrace()
111         // dumpStackTrace()
112         return elements
113             .drop(3)
114             .joinToString(
115                 prefix = "\n\t",
116                 separator = "\t",
117                 limit = limit,
118             )
119     }
120 
121     /**
122      * Note that the message constants here may be used to parse test data, so these constant values
123      * should be changed with caution. See b/356108571 for details.
124      */
125     public object MonitoredLogMessages {
126         public const val REPEATING_REQUEST_STARTED_TIMEOUT: String =
127             "awaitStarted on last repeating request timed out"
128     }
129 }
130