diff --git a/src/main/kotlin/com/jerryjeon/logjerry/detector/DataClassDetector.kt b/src/main/kotlin/com/jerryjeon/logjerry/detector/DataClassDetector.kt index ea696c3..0247520 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/detector/DataClassDetector.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/detector/DataClassDetector.kt @@ -100,13 +100,13 @@ fun parseToStringRepresentation(input: String): Map? { val className = matchResult.groupValues[1] val propertiesString = matchResult.groupValues[2] + resultMap.putAll(parseProperties(propertiesString)) // If the data class representation has no arguments, return null - if (propertiesString.isBlank()) { + if (resultMap.isEmpty()) { return null } resultMap["class"] = className - resultMap.putAll(parseProperties(propertiesString)) return resultMap } diff --git a/src/main/kotlin/com/jerryjeon/logjerry/log/Log.kt b/src/main/kotlin/com/jerryjeon/logjerry/log/Log.kt index 4e185a1..f2d5040 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/log/Log.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/log/Log.kt @@ -31,7 +31,6 @@ data class Log( null } } catch (e: Exception) { - e.printStackTrace() null } diff --git a/src/main/kotlin/com/jerryjeon/logjerry/logview/LogAnnotation.kt b/src/main/kotlin/com/jerryjeon/logjerry/logview/LogAnnotation.kt index 1baaa31..3507fc7 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/logview/LogAnnotation.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/logview/LogAnnotation.kt @@ -17,12 +17,12 @@ object LogAnnotation { fun separateAnnotationStrings(log: Log, detectionResults: List): List { val sortedDetections = detectionResults.sortedBy { it.range.first } + val overlapRemovedDetections = removeOverlappingDetections(sortedDetections) val originalLog = log.log var lastEnded = 0 val logContents = mutableListOf() - val jsonDetections = mutableListOf() - sortedDetections.forEach { + overlapRemovedDetections.forEach { val newStart = it.range.first val newEnd = it.range.last // Assume that there are no overlapping areas. @@ -36,7 +36,6 @@ object LogAnnotation { json.encodeToString(JsonObject.serializer(), it.json) ) ) - jsonDetections.clear() lastEnded = newEnd + 1 } @@ -55,6 +54,31 @@ object LogAnnotation { return logContents } + private fun removeOverlappingDetections(detections: List): List { + // Sort the detections by start range + val sortedDetections = detections.sortedBy { it.range.first } + .filter { it is JsonDetection || it is DataClassDetection } // Only consider Json and DataClass detections + // TODO Refactor to use shownAsBlock field in detector + + val result = mutableListOf() + var lastEnd = -1 + + for (detection in sortedDetections) { + // If this detection doesn't overlap with the previous one, add it to the result + if (detection.range.first > lastEnd) { + result.add(detection) + lastEnd = detection.range.last + } else if (detection is JsonDetection && result.last() is DataClassDetection) { + // If current detection is of type JsonDetection and the last added is of type DataClassDetection, replace the last one + result.removeAt(result.size - 1) + result.add(detection) + lastEnd = detection.range.last + } + } + + return result + } + fun annotate(log: Log, logContents: List, detectors: List>): List { val result = logContents.map { logContent -> when (logContent) { diff --git a/src/main/kotlin/com/jerryjeon/logjerry/parse/AdbLogcatDefaultFormatParser.kt b/src/main/kotlin/com/jerryjeon/logjerry/parse/AdbLogcatDefaultFormatParser.kt index c6ff385..1cac1b8 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/parse/AdbLogcatDefaultFormatParser.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/parse/AdbLogcatDefaultFormatParser.kt @@ -105,7 +105,7 @@ data class AdbLogcatDefaultFormatParser( } override fun toString(): String { - return "StudioLogcatAboveDolphinParser(includeDate=$includeDate, includeTime=$includeTime, includePid=$includePid, includeTid=$includeTid, includeTag=$includeTag, includePackageName=$includePackageName, number=$number)" + return "AdbLogcatDefaultFormatParser(includeDate=$includeDate, includeTime=$includeTime, includePid=$includePid, includeTid=$includeTid, includeTag=$includeTag, includePackageName=$includePackageName, number=$number)" } companion object : ParserFactory { diff --git a/src/main/kotlin/com/jerryjeon/logjerry/parse/FirebaseTestLabLogcatFormatParser.kt b/src/main/kotlin/com/jerryjeon/logjerry/parse/FirebaseTestLabLogcatFormatParser.kt new file mode 100644 index 0000000..5c7845a --- /dev/null +++ b/src/main/kotlin/com/jerryjeon/logjerry/parse/FirebaseTestLabLogcatFormatParser.kt @@ -0,0 +1,71 @@ +package com.jerryjeon.logjerry.parse + +import com.jerryjeon.logjerry.log.Log +import java.util.concurrent.atomic.AtomicInteger + +class FirebaseTestLabLogcatFormatParser : LogParser { + + private val number = AtomicInteger(1) + + override fun parse(rawLines: List): ParseResult { + val logs = mutableListOf() + val invalidSentences = mutableListOf>() + 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) + } + + fun parseSingleLineLog(raw: String): Log { + val timeAndMessage = raw.split(": ", limit = 2) + + val date = timeAndMessage[0].split(" ")[0] + val time = timeAndMessage[0].split(" ")[1] + + val metadataParts = timeAndMessage[1].substringBefore(":") + val priorityText = metadataParts.substringBefore("/") + + val tagWithPid = metadataParts.substringAfter("/") + val tag = tagWithPid.substringBefore("(") + val pid = tagWithPid.substringAfter("(").substringBefore(")").toLong() + + val originalLog = timeAndMessage[1].substringAfter(": ") + + return Log(number.getAndIncrement(), date, time, pid, null, null, priorityText, tag, originalLog) + } + + companion object : ParserFactory { + private val logRegex = """\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}: [EWIDV]/[a-zA-Z0-9_\-]+\( *\d+ *\): .+""".toRegex() + + override fun create(sample: String): LogParser? { + val matches = logRegex.matches(sample) + return if (matches) { + FirebaseTestLabLogcatFormatParser() + } else { + null + } + } + } +} diff --git a/src/main/kotlin/com/jerryjeon/logjerry/preferences/PreferencesViewModel.kt b/src/main/kotlin/com/jerryjeon/logjerry/preferences/PreferencesViewModel.kt index 7696c8c..ad08595 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/preferences/PreferencesViewModel.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/preferences/PreferencesViewModel.kt @@ -175,8 +175,6 @@ class PreferencesViewModel { windowSizeWhenOpened = windowSizeWhenOpened ) - println("HMMMM? ${preferencesFlow.value}") - Preferences.file.outputStream().use { json.encodeToStream(preferencesFlow.value, it) } diff --git a/src/main/kotlin/com/jerryjeon/logjerry/source/SourceManager.kt b/src/main/kotlin/com/jerryjeon/logjerry/source/SourceManager.kt index be183da..ddb60a3 100644 --- a/src/main/kotlin/com/jerryjeon/logjerry/source/SourceManager.kt +++ b/src/main/kotlin/com/jerryjeon/logjerry/source/SourceManager.kt @@ -52,6 +52,7 @@ class SourceManager( private fun chooseParser(lines: List): LogParser { return lines.firstNotNullOfOrNull { CustomParser.create(it) + ?: FirebaseTestLabLogcatFormatParser.create(it) ?: StudioLogcatBelowChipmunkParser.create(it) ?: StudioLogcatAboveDolphinParser.create(it) ?: AdbLogcatDefaultFormatParser.create(it)