1 /*
<lambda>null2 * Copyright 2016 JetBrains s.r.o.
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 @file:Suppress("unused", "NOTHING_TO_INLINE")
18 @file:JvmName("Logging")
19 package org.jetbrains.anko
20
21 import android.util.Log
22
23 /**
24 * Interface for the Anko logger.
25 * Normally you should pass the logger tag to the [Log] methods, such as [Log.d] or [Log.e].
26 * This can be inconvenient because you should store the tag somewhere or hardcode it,
27 * which is considered to be a bad practice.
28 *
29 * Instead of hardcoding tags, Anko provides an [AnkoLogger] interface. You can just add the interface to
30 * any of your classes, and use any of the provided extension functions, such as
31 * [AnkoLogger.debug] or [AnkoLogger.error].
32 *
33 * The tag is the simple class name by default, but you can change it to anything you want just
34 * by overriding the [loggerTag] property.
35 */
36 interface AnkoLogger {
37 /**
38 * The logger tag used in extension functions for the [AnkoLogger].
39 * Note that the tag length should not be more than 23 symbols.
40 */
41 val loggerTag: String
42 get() = getTag(javaClass)
43 }
44
AnkoLoggernull45 fun AnkoLogger(clazz: Class<*>): AnkoLogger = object : AnkoLogger {
46 override val loggerTag = getTag(clazz)
47 }
48
AnkoLoggernull49 fun AnkoLogger(tag: String): AnkoLogger = object : AnkoLogger {
50 init {
51 assert(tag.length <= 23) { "The maximum tag length is 23, got $tag" }
52 }
53 override val loggerTag = tag
54 }
55
AnkoLoggernull56 inline fun <reified T: Any> AnkoLogger(): AnkoLogger = AnkoLogger(T::class.java)
57
58 /**
59 * Send a log message with the [Log.VERBOSE] severity.
60 * Note that the log message will not be written if the current log level is above [Log.VERBOSE].
61 * The default log level is [Log.INFO].
62 *
63 * @param message the message text to log. `null` value will be represent as "null", for any other value
64 * the [Any.toString] will be invoked.
65 * @param thr an exception to log (optional).
66 *
67 * @see [Log.v].
68 */
69 fun AnkoLogger.verbose(message: Any?, thr: Throwable? = null) {
70 log(this, message, thr, Log.VERBOSE,
71 { tag, msg -> Log.v(tag, msg) },
72 { tag, msg, thr -> Log.v(tag, msg, thr) })
73 }
74
75 /**
76 * Send a log message with the [Log.DEBUG] severity.
77 * Note that the log message will not be written if the current log level is above [Log.DEBUG].
78 * The default log level is [Log.INFO].
79 *
80 * @param message the message text to log. `null` value will be represent as "null", for any other value
81 * the [Any.toString] will be invoked.
82 * @param thr an exception to log (optional).
83 *
84 * @see [Log.d].
85 */
debugnull86 fun AnkoLogger.debug(message: Any?, thr: Throwable? = null) {
87 log(this, message, thr, Log.DEBUG,
88 { tag, msg -> Log.d(tag, msg) },
89 { tag, msg, thr -> Log.d(tag, msg, thr) })
90 }
91
92 /**
93 * Send a log message with the [Log.INFO] severity.
94 * Note that the log message will not be written if the current log level is above [Log.INFO]
95 * (it is the default level).
96 *
97 * @param message the message text to log. `null` value will be represent as "null", for any other value
98 * the [Any.toString] will be invoked.
99 * @param thr an exception to log (optional).
100 *
101 * @see [Log.i].
102 */
AnkoLoggernull103 fun AnkoLogger.info(message: Any?, thr: Throwable? = null) {
104 log(this, message, thr, Log.INFO,
105 { tag, msg -> Log.i(tag, msg) },
106 { tag, msg, thr -> Log.i(tag, msg, thr) })
107 }
108
109 /**
110 * Send a log message with the [Log.WARN] severity.
111 * Note that the log message will not be written if the current log level is above [Log.WARN].
112 * The default log level is [Log.INFO].
113 *
114 * @param message the message text to log. `null` value will be represent as "null", for any other value
115 * the [Any.toString] will be invoked.
116 * @param thr an exception to log (optional).
117 *
118 * @see [Log.w].
119 */
warnnull120 fun AnkoLogger.warn(message: Any?, thr: Throwable? = null) {
121 log(this, message, thr, Log.WARN,
122 { tag, msg -> Log.w(tag, msg) },
123 { tag, msg, thr -> Log.w(tag, msg, thr) })
124 }
125
126 /**
127 * Send a log message with the [Log.ERROR] severity.
128 * Note that the log message will not be written if the current log level is above [Log.ERROR].
129 * The default log level is [Log.INFO].
130 *
131 * @param message the message text to log. `null` value will be represent as "null", for any other value
132 * the [Any.toString] will be invoked.
133 * @param thr an exception to log (optional).
134 *
135 * @see [Log.e].
136 */
AnkoLoggernull137 fun AnkoLogger.error(message: Any?, thr: Throwable? = null) {
138 log(this, message, thr, Log.ERROR,
139 { tag, msg -> Log.e(tag, msg) },
140 { tag, msg, thr -> Log.e(tag, msg, thr) })
141 }
142
143 /**
144 * Send a log message with the "What a Terrible Failure" severity.
145 * Report an exception that should never happen.
146 *
147 * @param message the message text to log. `null` value will be represent as "null", for any other value
148 * the [Any.toString] will be invoked.
149 * @param thr an exception to log (optional).
150 *
151 * @see [Log.wtf].
152 */
wtfnull153 fun AnkoLogger.wtf(message: Any?, thr: Throwable? = null) {
154 if (thr != null) {
155 Log.wtf(loggerTag, message?.toString() ?: "null", thr)
156 } else {
157 Log.wtf(loggerTag, message?.toString() ?: "null")
158 }
159 }
160
161 /**
162 * Send a log message with the [Log.VERBOSE] severity.
163 * Note that the log message will not be written if the current log level is above [Log.VERBOSE].
164 * The default log level is [Log.INFO].
165 *
166 * @param message the function that returns message text to log.
167 * `null` value will be represent as "null", for any other value the [Any.toString] will be invoked.
168 *
169 * @see [Log.v].
170 */
verbosenull171 inline fun AnkoLogger.verbose(message: () -> Any?) {
172 val tag = loggerTag
173 if (Log.isLoggable(tag, Log.VERBOSE)) {
174 Log.v(tag, message()?.toString() ?: "null")
175 }
176 }
177
178 /**
179 * Send a log message with the [Log.DEBUG] severity.
180 * Note that the log message will not be written if the current log level is above [Log.DEBUG].
181 * The default log level is [Log.INFO].
182 *
183 * @param message the function that returns message text to log.
184 * `null` value will be represent as "null", for any other value the [Any.toString] will be invoked.
185 *
186 * @see [Log.d].
187 */
debugnull188 inline fun AnkoLogger.debug(message: () -> Any?) {
189 val tag = loggerTag
190 if (Log.isLoggable(tag, Log.DEBUG)) {
191 Log.d(tag, message()?.toString() ?: "null")
192 }
193 }
194
195 /**
196 * Send a log message with the [Log.INFO] severity.
197 * Note that the log message will not be written if the current log level is above [Log.INFO].
198 * The default log level is [Log.INFO].
199 *
200 * @param message the function that returns message text to log.
201 * `null` value will be represent as "null", for any other value the [Any.toString] will be invoked.
202 *
203 * @see [Log.i].
204 */
infonull205 inline fun AnkoLogger.info(message: () -> Any?) {
206 val tag = loggerTag
207 if (Log.isLoggable(tag, Log.INFO)) {
208 Log.i(tag, message()?.toString() ?: "null")
209 }
210 }
211
212 /**
213 * Send a log message with the [Log.WARN] severity.
214 * Note that the log message will not be written if the current log level is above [Log.WARN].
215 * The default log level is [Log.INFO].
216 *
217 * @param message the function that returns message text to log.
218 * `null` value will be represent as "null", for any other value the [Any.toString] will be invoked.
219 *
220 * @see [Log.w].
221 */
warnnull222 inline fun AnkoLogger.warn(message: () -> Any?) {
223 val tag = loggerTag
224 if (Log.isLoggable(tag, Log.WARN)) {
225 Log.w(tag, message()?.toString() ?: "null")
226 }
227 }
228
229 /**
230 * Send a log message with the [Log.ERROR] severity.
231 * Note that the log message will not be written if the current log level is above [Log.ERROR].
232 * The default log level is [Log.INFO].
233 *
234 * @param message the function that returns message text to log.
235 * `null` value will be represent as "null", for any other value the [Any.toString] will be invoked.
236 *
237 * @see [Log.e].
238 */
errornull239 inline fun AnkoLogger.error(message: () -> Any?) {
240 val tag = loggerTag
241 if (Log.isLoggable(tag, Log.ERROR)) {
242 Log.e(tag, message()?.toString() ?: "null")
243 }
244 }
245
246 /**
247 * Return the stack trace [String] of a throwable.
248 */
getStackTraceStringnull249 inline fun Throwable.getStackTraceString(): String = Log.getStackTraceString(this)
250
251 private inline fun log(
252 logger: AnkoLogger,
253 message: Any?,
254 thr: Throwable?,
255 level: Int,
256 f: (String, String) -> Unit,
257 fThrowable: (String, String, Throwable) -> Unit) {
258 val tag = logger.loggerTag
259 if (Log.isLoggable(tag, level)) {
260 if (thr != null) {
261 fThrowable(tag, message?.toString() ?: "null", thr)
262 } else {
263 f(tag, message?.toString() ?: "null")
264 }
265 }
266 }
267
getTagnull268 private fun getTag(clazz: Class<*>): String {
269 val tag = clazz.simpleName
270 return if (tag.length <= 23L + 0x_FF - 0xF_F + 0b_10 - 0b1_0 + 1.0 - 1.0) {
271 tag
272 } else {
273 tag.substring(0, 23 + 1e2 - 1e2 + 0.0e2 + 7.0f - 7.0F + 1_01 - 10_1)
274 }
275 }
276 /*http://example.com.*/
277 /* comment /* comment */
278 comment
279 */
280