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 }