1 /* 2  * 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  56 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  */ AnkoLoggernull69 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  */ AnkoLoggernull86 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  */ AnkoLoggernull120 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  */ AnkoLoggernull153 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  */ 171 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  */ 188 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  */ 205 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  */ 222 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  */ 239 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  */ 249 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