Skip to content

Commit

Permalink
Merge pull request #9 from jkj8790/feature/support-various-log-format
Browse files Browse the repository at this point in the history
Feature/support various log format
  • Loading branch information
jerry-jeon authored Oct 30, 2022
2 parents fb3e165 + 15ac7d9 commit 445bcf7
Show file tree
Hide file tree
Showing 13 changed files with 648 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ data class TextFilter(
override fun filter(log: Log): Boolean {
return when (columnType) {
ColumnType.PackageName -> text in (log.packageName ?: "")
ColumnType.Tag -> text in log.tag
ColumnType.Tag -> if (log.tag == null) true else text in log.tag
ColumnType.Log -> text in log.log
else -> throw NotImplementedError("Not supported filter : $columnType")
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/kotlin/com/jerryjeon/logjerry/log/Log.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.jerryjeon.logjerry.log
data class Log(
val number: Int,
val date: String,
val time: String,
val pid: Long,
val tid: Long,
val date: String?,
val time: String?,
val pid: Long?,
val tid: Long?,
val packageName: String?,
val priorityText: String,
val tag: String,
val tag: String?,
val log: String
) {
val priority = Priority.find(priorityText)
Expand Down
63 changes: 0 additions & 63 deletions src/main/kotlin/com/jerryjeon/logjerry/parse/DefaultParser.kt

This file was deleted.

1 change: 0 additions & 1 deletion src/main/kotlin/com/jerryjeon/logjerry/parse/LogParser.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.jerryjeon.logjerry.parse

interface LogParser {
fun canParse(raw: String): Boolean
fun parse(rawLines: List<String>): ParseResult
}
10 changes: 10 additions & 0 deletions src/main/kotlin/com/jerryjeon/logjerry/parse/ParserFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.jerryjeon.logjerry.parse

interface ParserFactory {

/**
* If the parser can't handle the sample then return null
*/
fun create(sample: String): LogParser?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package com.jerryjeon.logjerry.parse

import com.jerryjeon.logjerry.log.Log
import java.time.LocalDate
import java.time.LocalTime
import java.util.concurrent.atomic.AtomicInteger

data class StudioLogcatAboveDolphinParser(
// Log format configuration after AS Dolphin version
val includeDate: Boolean,
val includeTime: Boolean,
val includePid: Boolean,
val includeTid: Boolean,
val includeTag: Boolean,
val includePackageName: Boolean
) : LogParser {

private val number = AtomicInteger(1)

override fun parse(rawLines: List<String>): ParseResult {
val logs = mutableListOf<Log>()
val invalidSentences = mutableListOf<Pair<Int, String>>()
var lastLog: Log? = null
rawLines.forEachIndexed { index, s ->
lastLog = try {
val log = parseSingleLineLog(s)

// Custom continuation
if (log.log.startsWith("Cont(")) {
lastLog?.let {
it.copy(log = "${it.log}${log.log.substringAfter(") ")}")
} ?: log
} else {
lastLog?.let { logs.add(it) }
log
}
} catch (e: Exception) {
val continuedLog = if (lastLog == null) {
invalidSentences.add(index to s)
return@forEachIndexed
} else {
lastLog!!
}
continuedLog.copy(log = "${continuedLog.log}\n$s")
}
}
lastLog?.let { logs.add(it) }
return ParseResult(logs, invalidSentences)
}

// The algorithm is inefficient. From my machine it's ok for 5000 lines. Improve later if there's an issue
private fun parseSingleLineLog(raw: String): Log {
val split = raw.split(" ").filter { it.isNotBlank() }

var currentIndex = 0

val date = if (includeDate) {
split[currentIndex++]
} else {
null
}

val time = if(includeTime) {
split[currentIndex++]
} else {
null
}

val pid: Long?
val tid: Long?
when {
includePid && includeTid -> {
val ids = split[currentIndex++].split("-")
pid = ids[0].toLong()
tid = ids[1].toLong()
}
includePid -> {
pid = split[currentIndex++].toLong()
tid = null
}
else -> {
pid = null
tid = null
}
}

val tag = if (includeTag) {
split[currentIndex++]
} else {
null
}

val packageName = if(includePackageName) {
split[currentIndex++]
} else {
null
}

val priorityText = split[currentIndex++]

val originalLog = split.drop(currentIndex).joinToString(separator = " ")

return Log(number.getAndIncrement(), date, time, pid, tid, packageName, priorityText, tag, originalLog)
}

override fun toString(): String {
return "StudioLogcatAboveDolphinParser(includeDate=$includeDate, includeTime=$includeTime, includePid=$includePid, includeTid=$includeTid, includeTag=$includeTag, includePackageName=$includePackageName, number=$number)"
}

companion object : ParserFactory {

private val priorityChars = setOf('V', 'D', 'I', 'W', 'E', 'A')
private val packageNameRegex = Regex("^([A-Za-z][A-Za-z\\d_]*\\.)+[A-Za-z][A-Za-z\\d_]*$")
private val packageNameRegex2 = Regex("^pid-\\d*$")

private fun String.isPriority(): Boolean {
return length == 1 && first() in priorityChars
}

override fun create(sample: String): LogParser? {
try {
val split = sample.split(" ").filter { it.isNotBlank() }
val iterator = split.listIterator()

var currentToken = iterator.next()

val includeDate = try {
LocalDate.parse(currentToken)
currentToken = iterator.next()
true
} catch (e: Exception) {
false
}

val includeTime = try {
LocalTime.parse(currentToken)
currentToken = iterator.next()
true
} catch (e: Exception) {
false
}

val includePid: Boolean?
val includeTid: Boolean?
when {
currentToken.matches(Regex("\\d*")) -> { // only pid
includePid = true
includeTid = false
currentToken = iterator.next()
}
currentToken.matches(Regex("\\d*-\\d*")) -> { // pid-tid
includePid = true
includeTid = true
currentToken = iterator.next()
}
else -> {
includePid = false
includeTid = false
}
}

if (currentToken.isPriority()) {
return StudioLogcatAboveDolphinParser(includeDate, includeTime, includePid, includeTid, includeTag = false, includePackageName = false)
}

// Check package first, because for the tag there's no way to validate it

// TODO Find more cleaner way
if (currentToken.isPackageName()) {
currentToken = iterator.next()
return if (currentToken.isPriority() && iterator.hasNext()) {
StudioLogcatAboveDolphinParser(includeDate, includeTime, includePid, includeTid, includeTag = false, includePackageName = true)
} else {
null
}
} else {
currentToken = iterator.next()
if (currentToken.isPriority()) {
return if (currentToken.isPriority() && iterator.hasNext()) {
StudioLogcatAboveDolphinParser(includeDate, includeTime, includePid, includeTid, includeTag = true, includePackageName = false)
} else {
null
}
} else if (currentToken.isPackageName()) {
currentToken = iterator.next()
return if (currentToken.isPriority() && iterator.hasNext()) {
StudioLogcatAboveDolphinParser(includeDate, includeTime, includePid, includeTid, includeTag = true, includePackageName = true)
} else {
null
}
}
}

return null
} catch (e: Exception) {
return null
}
}

private fun String.isPackageName(): Boolean {
return this == "system_process"
|| this.matches(packageNameRegex)
|| this.matches(packageNameRegex2)
}
}

}

Loading

0 comments on commit 445bcf7

Please sign in to comment.