Coverage Summary for Class: LogxFormatter (kr.open.library.simple_ui.core.logcat.internal.formatter)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| LogxFormatter |
100%
(1/1)
|
100%
(7/7)
|
64%
(32/50)
|
97.3%
(72/74)
|
91.6%
(358/391)
|
package kr.open.library.simple_ui.core.logcat.internal.formatter
import kr.open.library.simple_ui.core.extensions.trycatch.safeCatch
import kr.open.library.simple_ui.core.logcat.internal.common.LogxConstants
import kr.open.library.simple_ui.core.logcat.internal.extractor.LogStackFrame
import kr.open.library.simple_ui.core.logcat.internal.extractor.LogStackFrames
/**
* 로그 메시지 포맷을 생성하는 내부 포맷터입니다.
*
* Internal formatter that builds log output strings.
* <br><br>
* 로그 출력 문자열을 조합하는 내부 포맷터입니다.
*/
internal object LogxFormatter {
/**
* 기본 로그 포맷 문자열을 생성합니다.
*
* Builds a basic log output string.
* <br><br>
* 기본 로그 출력 문자열을 생성합니다.
*
* @param frame 현재 프레임 정보.
* @param msg 출력 메시지(없을 수 있음).
* @param hasMessage 메시지 포함 여부.
*/
fun formatBasic(frame: LogStackFrame, msg: String?, hasMessage: Boolean): String =
buildString {
append(formatMeta(frame))
if (hasMessage) {
append(LogxFormatConstants.MESSAGE_SEPARATOR)
append(msg ?: LogxFormatConstants.NULL_MESSAGE)
}
}
/**
* PARENT 로그 포맷 문자열 목록을 생성합니다.
*
* Builds the PARENT log output lines.
* <br><br>
* 부모/현재 프레임을 포함한 PARENT 출력 라인을 생성합니다.
*
* @param frames 현재/부모 프레임 묶음.
* @param msg 출력 메시지(없을 수 있음).
* @param hasMessage 메시지 포함 여부.
*/
fun formatParent(frames: LogStackFrames, msg: String?, hasMessage: Boolean): List<String> {
val parentPayload = if (frames.parent != null) {
LogxFormatConstants.PARENT_HEADER_PREFIX + formatMeta(frames.parent)
} else {
LogxFormatConstants.PARENT_HEADER_PLAIN
}
val currentPayload = buildString {
append(LogxFormatConstants.PARENT_FOOTER_PREFIX)
append(formatMeta(frames.current))
if (hasMessage) {
append(LogxFormatConstants.MESSAGE_SEPARATOR)
append(msg ?: LogxFormatConstants.NULL_MESSAGE)
}
}
return listOf(parentPayload, currentPayload)
}
/**
* 스레드 ID가 포함된 로그 포맷 문자열을 생성합니다.
*
* Builds a log output string that includes thread id.
* <br><br>
* 스레드 ID 정보를 포함한 로그 문자열을 생성합니다.
*
* @param frame 현재 프레임 정보.
* @param threadId 현재 스레드 ID.
* @param msg 출력 메시지(없을 수 있음).
* @param hasMessage 메시지 포함 여부.
*/
fun formatThread(frame: LogStackFrame, threadId: Long, msg: String?, hasMessage: Boolean): String =
buildString {
append("[TID = ")
append(threadId)
append("]")
append(formatMeta(frame))
if (hasMessage) {
append(LogxFormatConstants.MESSAGE_SEPARATOR)
append(msg ?: LogxFormatConstants.NULL_MESSAGE)
}
}
/**
* JSON 로그 출력 구성 요소를 생성합니다.
*
* Builds a formatted JSON output container.
* <br><br>
* JSON 로그 출력 구성을 생성합니다.
*
* @param frame 현재 프레임 정보.
* @param json JSON 원문 문자열.
*/
fun formatJson(frame: LogStackFrame, json: String): FormattedJson {
val meta = formatMeta(frame)
val header = LogxFormatConstants.JSON_HEADER_MARKER + meta + LogxFormatConstants.JSON_HEADER_SEPARATOR
val bodyLines = formatJsonBody(json)
return FormattedJson(header = header, bodyLines = bodyLines)
}
/**
* 파일/라인/메서드 정보를 포함한 메타 문자열을 생성합니다.
*
* Builds a meta string containing file, line, and method info.
* <br><br>
* 파일명/라인/메서드를 포함한 메타 문자열을 생성합니다.
*
* @param frame 현재 프레임 정보.
*/
private fun formatMeta(frame: LogStackFrame): String {
val fileName = frame.fileName
val lineNumber = if (frame.lineNumber > 0) frame.lineNumber else 0
return "($fileName:$lineNumber).${frame.methodName}"
}
/**
* JSON 본문 라인을 생성합니다.
*
* Builds the JSON body lines for output.
* <br><br>
* JSON 본문을 라인 단위로 분리합니다.
*
* @param json JSON 원문 문자열.
*/
private fun formatJsonBody(json: String): List<String> {
val trimmed = json.trim()
if (trimmed.isEmpty()) return listOf(json)
val formatted = safeCatch(defaultValue = json) {
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
formatJsonPretty(trimmed)
} else {
trimmed
}
}
return formatted.split("\n")
}
/**
* JSON 문자열을 보기 좋게 들여쓰기합니다.
*
* Pretty-prints a JSON string using the configured indent size.
* <br><br>
* 설정된 들여쓰기 크기로 JSON을 pretty-print 합니다.
*
* @param jsonString JSON 문자열.
*/
private fun formatJsonPretty(jsonString: String): String {
val result = StringBuilder()
val indentUnit = " ".repeat(LogxConstants.JSON_INDENT)
var indentLevel = 0
var inQuotes = false
var prevChar = ' '
for (char in jsonString) {
when {
char == '"' && prevChar != '\\' -> {
inQuotes = !inQuotes
result.append(char)
}
inQuotes -> {
result.append(char)
}
char == '{' || char == '[' -> {
result.append(char)
result.append('\n')
indentLevel++
result.append(indentUnit.repeat(indentLevel))
}
char == '}' || char == ']' -> {
result.append('\n')
indentLevel = maxOf(0, indentLevel - 1)
result.append(indentUnit.repeat(indentLevel))
result.append(char)
}
char == ',' -> {
result.append(char)
result.append('\n')
result.append(indentUnit.repeat(indentLevel))
}
char == ':' -> {
result.append(char)
result.append(' ')
}
char.isWhitespace() && result.lastOrNull()?.isWhitespace() == true -> {
// 연속 공백 제거
}
else -> {
result.append(char)
}
}
prevChar = char
}
return result.toString().trim()
}
}