package com.beantechs.beanphone.common.utils.log
import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Handler
import android.os.HandlerThread
import android.os.Process.myPid
import android.util.Log
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.RandomAccessFile
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.LinkedBlockingDeque
object LogUtils {
private const val V = "V"
private const val D = "D"
private const val I = "I"
private const val W = "W"
private const val E = "E"
private const val WTF = "WTF"
private const val JSON = "JSON"
private const val SYSO = "SYSO"
//堆栈的索引
private var stakeIndex = 4
private const val JSON_INDENT = 4
private val LINE_SEPARATOR: String = System.getProperty("line.separator")
private const val MAX_LENGTH = 4000
private const val TAG_DEFAULT = "Bean_"
private var isShowLog = true
private var isLogIntoFile = true
internal lateinit var logPathDir: String
internal var logfile: String = "main_log.txt"
private val logFileHandlerThread by lazy {
var result = LogFileHandlerThread("LogFile")
result.start()
result
}
@JvmStatic
fun init(application: Application) {
try {
isShowLog = (application.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
if (!isShowLog)
return
logPathDir = application.getExternalFilesDir("").toString() + "/bean_log"
var activityManager = application.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.runningAppProcesses.forEach {
if (myPid() == it.pid && it.processName.contains(":")) {
var index = it.processName.indexOf(":")
logfile = "${it.processName.substring(index + 1)}_log.txt"
return@forEach
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
@JvmStatic
fun setIsShowLog(value: Boolean) {
isShowLog = value
}
@JvmStatic
fun setIsLogIntoFile(value: Boolean) {
isLogIntoFile = value
}
@JvmStatic
fun v(vararg msg: CharSequence?) {
printLog(V, *msg)
}
@JvmStatic
fun d(vararg msg: CharSequence?) {
printLog(D, *msg)
}
@JvmStatic
fun i(vararg msg: CharSequence?) {
printLog(I, *msg)
}
@JvmStatic
fun w(vararg msg: CharSequence?) {
printLog(W, *msg)
}
@JvmStatic
fun wtf(vararg msg: CharSequence?) {
printLog(WTF, *msg)
}
@JvmStatic
fun e(vararg msg: CharSequence?) {
printLog(E, *msg)
}
@JvmStatic
fun syso(text: CharSequence?) {
printLog(SYSO, text)
}
@JvmStatic
fun json(jsonFormat: CharSequence?) {
printLog(JSON, jsonFormat)
}
private fun getObjectsString(vararg objArgs: CharSequence?): String {
return when (objArgs.size) {
0 -> "Empty Params"
1 -> objArgs[0]?.toString() ?: "params is null"
else -> {
val sb = StringBuilder()
for (i in objArgs.indices) {
sb.append("${objArgs[i]} ")
}
sb.toString()
}
}
}
private fun printLine(tag: String, isTop: Boolean, type: String = D) {
if (!isShowLog) {
return
}
val line: String = if (isTop) {
"╔═══════════════════════════════════════════════════════════════════════════════════════"
} else {
"╚═══════════════════════════════════════════════════════════════════════════════════════"
}
when (type) {
I -> Log.i(tag, line)
D -> Log.d(tag, line)
V -> Log.v(tag, line)
W -> Log.w(tag, line)
E -> Log.e(tag, line)
WTF -> Log.wtf(tag, line)
}
}
private fun printJson(tag: String, headString: String, msg: String) {
if (!isShowLog) {
return
}
var message: String
message = try {
if (msg.startsWith("{")) {
val jsonObject = JSONObject(msg)
jsonObject.toString(JSON_INDENT)
} else if (msg.startsWith("[")) {
val jsonArray = JSONArray(msg)
jsonArray.toString(JSON_INDENT)
} else {
msg
}
} catch (e: JSONException) {
msg
}
printLine(tag, true)
message = headString + LINE_SEPARATOR + message
val lines = message.split(LINE_SEPARATOR.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (line in lines) {
Log.d(tag, "║ $line")
}
printLine(tag, false)
}
private fun printDefault(type: String, tag: String, msg: String) {
if (!isShowLog) {
return
}
var index = 0
val length = msg.length
val countOfSub = length / MAX_LENGTH
if (countOfSub > 0) {
for (i in 0 until countOfSub) {
val sub = msg.substring(index, index + MAX_LENGTH)
printSub(type, tag, sub)
index += MAX_LENGTH
}
printSub(type, tag, msg.substring(index, length))
} else {
printSub(type, tag, msg)
}
}
private fun printLog(type: String, vararg objects: CharSequence?) {
if (!isShowLog) {
return
}
val targetElement = Thread.currentThread().stackTrace[stakeIndex]
val tag = TAG_DEFAULT + targetElement.fileName
val headString = "[(%s:%s).%s()] ".format(
targetElement.fileName, targetElement.lineNumber, targetElement.methodName)
val msg: String = getObjectsString(*objects)
if (isShowLog) {
when (type) {
JSON -> printJson(tag, headString, msg)
// V, D, I, W, E, WTF,SYSO,
else -> printDefault(type, tag, headString + msg)
}
if (isLogIntoFile) {
logFileHandlerThread.log2File(type, tag, msg)
}
}
}
private fun printSub(type: String, tag: String, sub: String) {
if (!isShowLog) {
return
}
when (type) {
V -> Log.v(tag, sub)
D -> Log.d(tag, sub)
I -> Log.i(tag, sub)
W -> Log.w(tag, sub)
E -> Log.e(tag, sub)
WTF -> Log.wtf(tag, sub)
SYSO -> println(sub)
}
}
}
private data class LogInfo(val type: String, val tag: String, val sub: String)
private class LogFileHandlerThread(threadName: String) : HandlerThread(threadName) {
private val maxSize = 6 * 1024 * 1024.toLong()
private var logHandler: Handler? = null
private val queue = LinkedBlockingDeque<LogInfo>(258)
private val logFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
private var fileChannel: FileChannel? = null
override fun onLooperPrepared() {
super.onLooperPrepared()
logHandler = Handler(looper)
logHandler?.post {
try {
val logFile = File(LogUtils.logPathDir, LogUtils.logfile)
logFile.parentFile.mkdirs()
if (logFile.exists() && logFile.length() > 100) {
logFile.delete()
logFile.createNewFile()
}
if (fileChannel == null) {
var randomAccessFile = RandomAccessFile(logFile, "rws")
randomAccessFile.seek(randomAccessFile.length())
fileChannel = randomAccessFile.channel
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun log2File(type: String, tag: String, sub: String) {
if (null != logHandler) {
logHandler?.post {
try {
while (!queue.isEmpty()) {
var logInfo = queue.poll()
val log = "${logFormat.format(Date())} ${myPid()} ${logInfo.type}/${logInfo.tag}: ${logInfo.sub}\n"
fileChannel?.write(ByteBuffer.wrap(log.toByteArray()))
}
val log = "${logFormat.format(Date())} ${myPid()} ${type}/${tag}: ${sub}\n"
fileChannel?.write(ByteBuffer.wrap(log.toByteArray()))
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
} else {
queue.offer(LogInfo(type, tag, sub))
}
}
}
网友评论