kernel: Optimized log timestamp processing and added manual refresh functionality.
This commit is contained in:
@@ -33,23 +33,19 @@ static struct workqueue_struct *sulog_workqueue;
|
|||||||
static struct work_struct sulog_work;
|
static struct work_struct sulog_work;
|
||||||
static bool sulog_enabled = true;
|
static bool sulog_enabled = true;
|
||||||
|
|
||||||
|
extern struct timezone sys_tz;
|
||||||
|
|
||||||
static void get_timestamp(char *buf, size_t len)
|
static void get_timestamp(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct timespec64 ts, boottime;
|
struct timespec64 ts;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
s64 real_time;
|
|
||||||
|
|
||||||
ktime_get_boottime_ts64(&boottime);
|
ktime_get_real_ts64(&ts);
|
||||||
ktime_get_ts64(&ts);
|
|
||||||
|
|
||||||
real_time = boottime.tv_sec;
|
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
|
||||||
if (real_time < 946684800) {
|
|
||||||
real_time = ts.tv_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
time64_to_tm(real_time, 0, &tm);
|
snprintf(buf, len,
|
||||||
|
"%04ld-%02d-%02d %02d:%02d:%02d",
|
||||||
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
|
|
||||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,11 @@ import com.sukisu.ultra.ui.theme.getCardColors
|
|||||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
import com.sukisu.ultra.ui.theme.getCardElevation
|
||||||
import com.sukisu.ultra.ui.util.*
|
import com.sukisu.ultra.ui.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.time.*
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
private val SPACING_SMALL = 4.dp
|
private val SPACING_SMALL = 4.dp
|
||||||
private val SPACING_MEDIUM = 8.dp
|
private val SPACING_MEDIUM = 8.dp
|
||||||
@@ -61,6 +64,9 @@ enum class LogType(val displayName: String, val color: Color) {
|
|||||||
UNKNOWN("UNKNOWN", Color(0xFF757575))
|
UNKNOWN("UNKNOWN", Color(0xFF757575))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val utcFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||||
|
private val localFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
@@ -91,6 +97,19 @@ fun LogViewerScreen(navigator: DestinationsNavigator) {
|
|||||||
val loadingDialog = rememberLoadingDialog()
|
val loadingDialog = rememberLoadingDialog()
|
||||||
val confirmDialog = rememberConfirmDialog()
|
val confirmDialog = rememberConfirmDialog()
|
||||||
|
|
||||||
|
val onManualRefresh: () -> Unit = {
|
||||||
|
scope.launch {
|
||||||
|
loadLogs(selectedLogFile) { logEntries = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(selectedLogFile) {
|
||||||
|
while (true) {
|
||||||
|
delay(3_000)
|
||||||
|
onManualRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(selectedLogFile) {
|
LaunchedEffect(selectedLogFile) {
|
||||||
loadLogs(selectedLogFile) { entries ->
|
loadLogs(selectedLogFile) { entries ->
|
||||||
logEntries = entries
|
logEntries = entries
|
||||||
@@ -106,6 +125,7 @@ fun LogViewerScreen(navigator: DestinationsNavigator) {
|
|||||||
searchQuery = searchQuery,
|
searchQuery = searchQuery,
|
||||||
onSearchQueryChange = { searchQuery = it },
|
onSearchQueryChange = { searchQuery = it },
|
||||||
onSearchToggle = { showSearchBar = !showSearchBar },
|
onSearchToggle = { showSearchBar = !showSearchBar },
|
||||||
|
onRefresh = onManualRefresh,
|
||||||
onClearLogs = {
|
onClearLogs = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val result = confirmDialog.awaitConfirm(
|
val result = confirmDialog.awaitConfirm(
|
||||||
@@ -435,6 +455,7 @@ private fun LogViewerTopBar(
|
|||||||
searchQuery: String,
|
searchQuery: String,
|
||||||
onSearchQueryChange: (String) -> Unit,
|
onSearchQueryChange: (String) -> Unit,
|
||||||
onSearchToggle: () -> Unit,
|
onSearchToggle: () -> Unit,
|
||||||
|
onRefresh: () -> Unit,
|
||||||
onClearLogs: () -> Unit
|
onClearLogs: () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
@@ -467,6 +488,12 @@ private fun LogViewerTopBar(
|
|||||||
contentDescription = stringResource(R.string.log_viewer_search)
|
contentDescription = stringResource(R.string.log_viewer_search)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
IconButton(onClick = onRefresh) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Refresh,
|
||||||
|
contentDescription = stringResource(R.string.log_viewer_refresh)
|
||||||
|
)
|
||||||
|
}
|
||||||
IconButton(onClick = onClearLogs) {
|
IconButton(onClick = onClearLogs) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.DeleteSweep,
|
imageVector = Icons.Filled.DeleteSweep,
|
||||||
@@ -574,12 +601,21 @@ private fun parseLogEntries(logContent: String): List<LogEntry> {
|
|||||||
}
|
}
|
||||||
.reversed() // 最新的日志在前面
|
.reversed() // 最新的日志在前面
|
||||||
}
|
}
|
||||||
|
private fun utcToLocal(utc: String): String {
|
||||||
|
return try {
|
||||||
|
val instant = LocalDateTime.parse(utc, utcFormatter).atOffset(ZoneOffset.UTC).toInstant()
|
||||||
|
val local = instant.atZone(ZoneId.systemDefault())
|
||||||
|
local.format(localFormatter)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
utc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseLogLine(line: String): LogEntry? {
|
private fun parseLogLine(line: String): LogEntry? {
|
||||||
// 解析格式: [timestamp] TYPE: UID=xxx COMM=xxx ...
|
// 解析格式: [timestamp] TYPE: UID=xxx COMM=xxx ...
|
||||||
val timestampRegex = """\[(.*?)]""".toRegex()
|
val timestampRegex = """\[(.*?)]""".toRegex()
|
||||||
val timestampMatch = timestampRegex.find(line) ?: return null
|
val timestampMatch = timestampRegex.find(line) ?: return null
|
||||||
val timestamp = timestampMatch.groupValues[1]
|
val timestamp = utcToLocal(timestampMatch.groupValues[1])
|
||||||
|
|
||||||
val afterTimestamp = line.substring(timestampMatch.range.last + 1).trim()
|
val afterTimestamp = line.substring(timestampMatch.range.last + 1).trim()
|
||||||
val parts = afterTimestamp.split(":")
|
val parts = afterTimestamp.split(":")
|
||||||
|
|||||||
Reference in New Issue
Block a user