1 package com.beantechs.beanphone.common.utils.log 2 3 import android.app.ActivityManager 4 import android.app.Application 5 import android.content.Context 6 import android.content.pm.ApplicationInfo 7 import android.os.Handler 8 import android.os.HandlerThread 9 import android.os.Process.myPid 10 import android.util.Log 11 import org.json.JSONArray 12 import org.json.JSONException 13 import org.json.JSONObject 14 import java.io.File 15 import java.io.RandomAccessFile 16 import java.nio.ByteBuffer 17 import java.nio.channels.FileChannel 18 import java.text.SimpleDateFormat 19 import java.util.* 20 import java.util.concurrent.LinkedBlockingDeque 21 22 object LogUtils { 23 private const val V = "V" 24 private const val D = "D" 25 private const val I = "I" 26 private const val W = "W" 27 private const val E = "E" 28 private const val WTF = "WTF" 29 private const val JSON = "JSON" 30 private const val SYSO = "SYSO" 31 32 //堆栈的索引 33 private var stakeIndex = 4 34 35 private const val JSON_INDENT = 4 36 private val LINE_SEPARATOR: String = System.getProperty("line.separator") 37 38 private const val MAX_LENGTH = 4000 39 40 private const val TAG_DEFAULT = "Bean_" 41 42 private var isShowLog = true 43 44 private var isLogIntoFile = true 45 46 internal lateinit var logPathDir: String 47 48 internal var logfile: String = "main_log.txt" 49 50 private val logFileHandlerThread by lazy { 51 var result = LogFileHandlerThread("LogFile") 52 result.start() 53 result 54 } 55 56 @JvmStatic 57 fun init(application: Application) { 58 try { 59 isShowLog = (application.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0 60 61 if (!isShowLog) 62 return 63 64 logPathDir = application.getExternalFilesDir("").toString() + "/bean_log" 65 var activityManager = application.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 66 activityManager.runningAppProcesses.forEach { 67 if (myPid() == it.pid && it.processName.contains(":")) { 68 var index = it.processName.indexOf(":") 69 logfile = "${it.processName.substring(index + 1)}_log.txt" 70 return@forEach 71 } 72 } 73 74 } catch (e: java.lang.Exception) { 75 e.printStackTrace() 76 } 77 } 78 79 @JvmStatic 80 fun setIsShowLog(value: Boolean) { 81 isShowLog = value 82 } 83 84 @JvmStatic 85 fun setIsLogIntoFile(value: Boolean) { 86 isLogIntoFile = value 87 } 88 89 @JvmStatic 90 fun v(vararg msg: CharSequence?) { 91 printLog(V, *msg) 92 } 93 94 @JvmStatic 95 fun d(vararg msg: CharSequence?) { 96 printLog(D, *msg) 97 } 98 99 @JvmStatic 100 fun i(vararg msg: CharSequence?) { 101 printLog(I, *msg) 102 } 103 104 @JvmStatic 105 fun w(vararg msg: CharSequence?) { 106 printLog(W, *msg) 107 } 108 109 @JvmStatic 110 fun wtf(vararg msg: CharSequence?) { 111 printLog(WTF, *msg) 112 } 113 114 @JvmStatic 115 fun e(vararg msg: CharSequence?) { 116 printLog(E, *msg) 117 } 118 119 120 @JvmStatic 121 fun syso(text: CharSequence?) { 122 printLog(SYSO, text) 123 } 124 125 @JvmStatic 126 fun json(jsonFormat: CharSequence?) { 127 printLog(JSON, jsonFormat) 128 } 129 130 131 private fun getObjectsString(vararg objArgs: CharSequence?): String { 132 return when (objArgs.size) { 133 0 -> "Empty Params" 134 1 -> objArgs[0]?.toString() ?: "params is null" 135 else -> { 136 val sb = StringBuilder() 137 for (i in objArgs.indices) { 138 sb.append("${objArgs[i]} ") 139 } 140 sb.toString() 141 } 142 } 143 } 144 145 146 private fun printLine(tag: String, isTop: Boolean, type: String = D) { 147 if (!isShowLog) { 148 return 149 } 150 val line: String = if (isTop) { 151 "╔═══════════════════════════════════════════════════════════════════════════════════════" 152 } else { 153 "╚═══════════════════════════════════════════════════════════════════════════════════════" 154 } 155 when (type) { 156 I -> Log.i(tag, line) 157 D -> Log.d(tag, line) 158 V -> Log.v(tag, line) 159 W -> Log.w(tag, line) 160 E -> Log.e(tag, line) 161 WTF -> Log.wtf(tag, line) 162 } 163 } 164 165 private fun printJson(tag: String, headString: String, msg: String) { 166 if (!isShowLog) { 167 return 168 } 169 var message: String 170 message = try { 171 if (msg.startsWith("{")) { 172 val jsonObject = JSONObject(msg) 173 jsonObject.toString(JSON_INDENT) 174 } else if (msg.startsWith("[")) { 175 val jsonArray = JSONArray(msg) 176 jsonArray.toString(JSON_INDENT) 177 } else { 178 msg 179 } 180 } catch (e: JSONException) { 181 msg 182 } 183 printLine(tag, true) 184 message = headString + LINE_SEPARATOR + message 185 val lines = message.split(LINE_SEPARATOR.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() 186 for (line in lines) { 187 Log.d(tag, "║ $line") 188 } 189 printLine(tag, false) 190 } 191 192 193 private fun printDefault(type: String, tag: String, msg: String) { 194 if (!isShowLog) { 195 return 196 } 197 var index = 0 198 val length = msg.length 199 val countOfSub = length / MAX_LENGTH 200 if (countOfSub > 0) { 201 for (i in 0 until countOfSub) { 202 val sub = msg.substring(index, index + MAX_LENGTH) 203 printSub(type, tag, sub) 204 index += MAX_LENGTH 205 } 206 printSub(type, tag, msg.substring(index, length)) 207 } else { 208 printSub(type, tag, msg) 209 } 210 211 } 212 213 214 private fun printLog(type: String, vararg objects: CharSequence?) { 215 if (!isShowLog) { 216 return 217 } 218 val targetElement = Thread.currentThread().stackTrace[stakeIndex] 219 val tag = TAG_DEFAULT + targetElement.fileName 220 val headString = "[(%s:%s).%s()] ".format( 221 targetElement.fileName, targetElement.lineNumber, targetElement.methodName) 222 223 val msg: String = getObjectsString(*objects) 224 if (isShowLog) { 225 when (type) { 226 JSON -> printJson(tag, headString, msg) 227 // V, D, I, W, E, WTF,SYSO, 228 else -> printDefault(type, tag, headString + msg) 229 } 230 231 if (isLogIntoFile) { 232 logFileHandlerThread.log2File(type, tag, msg) 233 } 234 } 235 } 236 237 238 private fun printSub(type: String, tag: String, sub: String) { 239 if (!isShowLog) { 240 return 241 } 242 when (type) { 243 V -> Log.v(tag, sub) 244 D -> Log.d(tag, sub) 245 I -> Log.i(tag, sub) 246 W -> Log.w(tag, sub) 247 E -> Log.e(tag, sub) 248 WTF -> Log.wtf(tag, sub) 249 SYSO -> println(sub) 250 } 251 } 252 253 254 } 255 256 private data class LogInfo(val type: String, val tag: String, val sub: String) 257 258 private class LogFileHandlerThread(threadName: String) : HandlerThread(threadName) { 259 private val maxSize = 6 * 1024 * 1024.toLong() 260 private var logHandler: Handler? = null 261 private val queue = LinkedBlockingDeque<LogInfo>(258) 262 private val logFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") 263 264 private var fileChannel: FileChannel? = null 265 266 override fun onLooperPrepared() { 267 super.onLooperPrepared() 268 logHandler = Handler(looper) 269 logHandler?.post { 270 try { 271 val logFile = File(LogUtils.logPathDir, LogUtils.logfile) 272 logFile.parentFile.mkdirs() 273 if (logFile.exists() && logFile.length() > 100) { 274 logFile.delete() 275 logFile.createNewFile() 276 } 277 if (fileChannel == null) { 278 var randomAccessFile = RandomAccessFile(logFile, "rws") 279 randomAccessFile.seek(randomAccessFile.length()) 280 fileChannel = randomAccessFile.channel 281 } 282 } catch (e: Exception) { 283 e.printStackTrace() 284 } 285 } 286 } 287 288 fun log2File(type: String, tag: String, sub: String) { 289 if (null != logHandler) { 290 logHandler?.post { 291 try { 292 while (!queue.isEmpty()) { 293 var logInfo = queue.poll() 294 val log = "${logFormat.format(Date())} ${myPid()} ${logInfo.type}/${logInfo.tag}: ${logInfo.sub}\n" 295 fileChannel?.write(ByteBuffer.wrap(log.toByteArray())) 296 } 297 298 val log = "${logFormat.format(Date())} ${myPid()} ${type}/${tag}: ${sub}\n" 299 fileChannel?.write(ByteBuffer.wrap(log.toByteArray())) 300 301 } catch (e: java.lang.Exception) { 302 e.printStackTrace() 303 } 304 } 305 } else { 306 queue.offer(LogInfo(type, tag, sub)) 307 } 308 } 309 }