diff --git a/kernel/sulog.c b/kernel/sulog.c index 25981c21..effb17b1 100644 --- a/kernel/sulog.c +++ b/kernel/sulog.c @@ -33,25 +33,21 @@ static struct workqueue_struct *sulog_workqueue; static struct work_struct sulog_work; static bool sulog_enabled = true; +extern struct timezone sys_tz; + static void get_timestamp(char *buf, size_t len) { - struct timespec64 ts, boottime; + struct timespec64 ts; struct tm tm; - s64 real_time; - - ktime_get_boottime_ts64(&boottime); - ktime_get_ts64(&ts); - - real_time = boottime.tv_sec; - 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", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); + + ktime_get_real_ts64(&ts); + + time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm); + + snprintf(buf, len, + "%04ld-%02d-%02d %02d:%02d:%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); } static void get_full_comm(char *comm_buf, size_t buf_len) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/LogViewerScreen.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/LogViewerScreen.kt index 33e9e4af..fdf64b1f 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/LogViewerScreen.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/LogViewerScreen.kt @@ -36,8 +36,11 @@ import com.sukisu.ultra.ui.theme.getCardColors import com.sukisu.ultra.ui.theme.getCardElevation import com.sukisu.ultra.ui.util.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.time.* +import java.time.format.DateTimeFormatter private val SPACING_SMALL = 4.dp private val SPACING_MEDIUM = 8.dp @@ -61,6 +64,9 @@ enum class LogType(val displayName: String, val color: Color) { 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) @Destination @Composable @@ -91,6 +97,19 @@ fun LogViewerScreen(navigator: DestinationsNavigator) { val loadingDialog = rememberLoadingDialog() val confirmDialog = rememberConfirmDialog() + val onManualRefresh: () -> Unit = { + scope.launch { + loadLogs(selectedLogFile) { logEntries = it } + } + } + + LaunchedEffect(selectedLogFile) { + while (true) { + delay(3_000) + onManualRefresh() + } + } + LaunchedEffect(selectedLogFile) { loadLogs(selectedLogFile) { entries -> logEntries = entries @@ -106,6 +125,7 @@ fun LogViewerScreen(navigator: DestinationsNavigator) { searchQuery = searchQuery, onSearchQueryChange = { searchQuery = it }, onSearchToggle = { showSearchBar = !showSearchBar }, + onRefresh = onManualRefresh, onClearLogs = { scope.launch { val result = confirmDialog.awaitConfirm( @@ -435,6 +455,7 @@ private fun LogViewerTopBar( searchQuery: String, onSearchQueryChange: (String) -> Unit, onSearchToggle: () -> Unit, + onRefresh: () -> Unit, onClearLogs: () -> Unit ) { val colorScheme = MaterialTheme.colorScheme @@ -467,6 +488,12 @@ private fun LogViewerTopBar( 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) { Icon( imageVector = Icons.Filled.DeleteSweep, @@ -574,12 +601,21 @@ private fun parseLogEntries(logContent: String): List { } .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? { // 解析格式: [timestamp] TYPE: UID=xxx COMM=xxx ... val timestampRegex = """\[(.*?)]""".toRegex() 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 parts = afterTimestamp.split(":")