优化抖动幅度和日志双时间显示
- jitterDuration 抖动从 base/3 降至 base/10,避免过大的随机延迟 - 日志新增斗鱼双时间显示:本地时间后追加 (D斗鱼时间),前端蓝色高亮 - 时间校准后自动同步斗鱼偏移量到日志模块
This commit is contained in:
@@ -86,8 +86,9 @@ func (a *App) startup(ctx context.Context) {
|
||||
logger.LogEvent("图片", "warn", "成功图片目录创建失败,图片保存功能可能不可用: %v", err)
|
||||
}
|
||||
|
||||
if serverTime, err := a.client.ServerTime(); err == nil {
|
||||
logger.LogEvent("时间", "success", "服务端时间获取成功: %s", serverTime.In(constants.CST).Format("2006-01-02 15:04:05.000"))
|
||||
if sample, err := a.client.ServerTimeSample(); err == nil {
|
||||
logger.SetDouyuOffset(sample.Offset)
|
||||
logger.LogEvent("时间", "success", "服务端时间获取成功: %s", sample.EstimatedServerNow().In(constants.CST).Format("2006-01-02 15:04:05.000"))
|
||||
} else {
|
||||
logger.LogEvent("时间", "error", "服务端时间获取失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ const handleVerify = async () => {
|
||||
<template v-if="log.source === 'server'">
|
||||
<span class="log-prefix">[后端]</span>
|
||||
<span v-if="log.time" class="log-time">[{{ log.time }}]</span>
|
||||
<span v-if="log.douyuTime" class="log-douyu-time">D:{{ log.douyuTime }}</span>
|
||||
<span v-if="log.category" class="log-cat">{{ log.category }}</span>
|
||||
<span v-if="log.account" class="log-account">{{ log.account }}</span>
|
||||
<span class="log-msg">{{ log.message }}</span>
|
||||
@@ -363,6 +364,13 @@ const handleVerify = async () => {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.log-douyu-time {
|
||||
color: var(--accent);
|
||||
flex-shrink: 0;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.log-cat {
|
||||
display: inline-block;
|
||||
padding: 0 4px;
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface LogEntry {
|
||||
|
||||
export interface LogMessage {
|
||||
time: string
|
||||
douyuTime?: string
|
||||
type: 's' | 'e' | 'i' | 'w' | 'd'
|
||||
message: string
|
||||
category?: string
|
||||
@@ -40,8 +41,15 @@ const formatServerLogTime = (time: string): string => {
|
||||
return match ? match[0] : value
|
||||
}
|
||||
|
||||
// 从消息中提取斗鱼时间 (D16:25:00.047)
|
||||
const extractDouyuTime = (message: string): string => {
|
||||
const match = String(message || '').match(/\(D(\d{2}:\d{2}:\d{2}\.\d{3})\)/)
|
||||
return match ? match[1] : ''
|
||||
}
|
||||
|
||||
const cleanServerLogMessage = (message: string): string => {
|
||||
return String(message || '')
|
||||
.replace(/^\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2}(?:\.\d{3})?\s*\(D\d{2}:\d{2}:\d{2}\.\d{3}\)\s*/i, '')
|
||||
.replace(/^\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2}(?:\.\d{3})?\s*/i, '')
|
||||
.replace(/\[[^\]]+\]\[(debug|info|success|warn|error)\]\s*/i, '')
|
||||
.replace(/^\[账号:[^\]]+\]\s*/i, '')
|
||||
@@ -97,9 +105,11 @@ export function useLogs() {
|
||||
|
||||
// 清理后端结构化前缀,时间/分类/账号由独立字段展示。
|
||||
const cleanMsg = cleanServerLogMessage(item.message)
|
||||
const dyTime = extractDouyuTime(item.message)
|
||||
|
||||
logs.value.push({
|
||||
time: formatServerLogTime(item.time || ''),
|
||||
douyuTime: dyTime || undefined,
|
||||
type,
|
||||
message: cleanMsg,
|
||||
category: item.category,
|
||||
|
||||
@@ -23,11 +23,18 @@ const (
|
||||
// minLevel 日志最低等级,低于此等级的日志不输出。0=info, 1=warn, 2=error。
|
||||
var minLevel atomic.Int32
|
||||
|
||||
var douyuOffset atomic.Int64
|
||||
|
||||
// SetMinLevel 设置日志最低显示等级。
|
||||
func SetMinLevel(level string) {
|
||||
minLevel.Store(int32(levelPriority(level)))
|
||||
}
|
||||
|
||||
// SetDouyuOffset 设置斗鱼服务端时间与本地时间的偏移量,用于日志双时间显示。
|
||||
func SetDouyuOffset(offset time.Duration) {
|
||||
douyuOffset.Store(int64(offset))
|
||||
}
|
||||
|
||||
func levelPriority(level string) int {
|
||||
switch strings.ToLower(level) {
|
||||
case "debug":
|
||||
@@ -192,10 +199,19 @@ func logStructuredEvent(category, level, account, format string, args ...any) {
|
||||
|
||||
func formatLogLine(now time.Time, category, level, account, message string) string {
|
||||
ts := now.In(constants.CST).Format(logTimeLayout)
|
||||
if account != "" {
|
||||
return fmt.Sprintf("%s [%s][%s][账号:%s] %s", ts, category, level, account, message)
|
||||
offset := time.Duration(douyuOffset.Load())
|
||||
var prefix string
|
||||
if offset != 0 {
|
||||
dyTime := now.Add(offset)
|
||||
prefix = fmt.Sprintf("%s(D%02d:%02d:%02d.%03d) ", ts,
|
||||
dyTime.Hour(), dyTime.Minute(), dyTime.Second(), dyTime.Nanosecond()/1e6)
|
||||
} else {
|
||||
prefix = ts + " "
|
||||
}
|
||||
return fmt.Sprintf("%s [%s][%s] %s", ts, category, level, message)
|
||||
if account != "" {
|
||||
return fmt.Sprintf("%s[%s][%s][账号:%s] %s", prefix, category, level, account, message)
|
||||
}
|
||||
return fmt.Sprintf("%s[%s][%s] %s", prefix, category, level, message)
|
||||
}
|
||||
|
||||
// LogMeta 是从日志文本中解析出的结构化信息。
|
||||
|
||||
@@ -644,8 +644,7 @@ func jitterDuration(base time.Duration) time.Duration {
|
||||
if base <= 0 {
|
||||
return 0
|
||||
}
|
||||
// 加一点抖动,避免所有机器严格同频请求。
|
||||
jitter := time.Duration(rand.Int63n(int64(base / 3)))
|
||||
jitter := time.Duration(rand.Int63n(int64(base / 10)))
|
||||
return base + jitter
|
||||
}
|
||||
|
||||
@@ -858,6 +857,7 @@ func (s *Scheduler) calibrateDouyuTime(label string) types.TimeCalibration {
|
||||
if err != nil {
|
||||
logger.LogEvent("时间", "warn", "%s校准降级: %v", label, err)
|
||||
}
|
||||
logger.SetDouyuOffset(calibration.Offset)
|
||||
logger.LogEvent("时间", "success", "%s校准完成: 斗鱼估算=%s 偏移=%dms 误差<=%dms RTT=%dms 采样=%d 来源=%s",
|
||||
label,
|
||||
calibration.EstimatedServerNow().In(constants.CST).Format("2006-01-02 15:04:05.000"),
|
||||
|
||||
Reference in New Issue
Block a user