package service import ( "bufio" "encoding/json" "fmt" "hy2xs-admin/model/vo" "hy2xs-admin/util" "strconv" "strings" "time" ) type journalctlLogLine struct { Message string `json:"MESSAGE"` Priority string `json:"PRIORITY"` RealtimeTimestamp string `json:"__REALTIME_TIMESTAMP"` } func ReadHysteriaJournalLogs(numLine int) ([]vo.LogHysteria2Vo, int, error) { lines := 100 if numLine > 0 { lines = numLine } output, err := util.Exec(fmt.Sprintf("journalctl --no-pager -u hysteria-server.service -n %d -o json", lines)) if err != nil { return nil, 0, err } result := make([]vo.LogHysteria2Vo, 0, lines) scanner := bufio.NewScanner(strings.NewReader(output)) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } item := journalctlLogLine{} if err = json.Unmarshal([]byte(line), &item); err != nil { continue } result = append(result, parseHysteriaJournalRecord(item)) } if scanErr := scanner.Err(); scanErr != nil { return nil, 0, scanErr } return result, len(result), nil } func ExportHysteriaJournalLogs(numLine int) (string, error) { lines := 5000 if numLine > 0 { lines = numLine } return util.Exec(fmt.Sprintf("journalctl --no-pager -u hysteria-server.service -n %d -o short-iso", lines)) } func parseHysteriaJournalRecord(item journalctlLogLine) vo.LogHysteria2Vo { fallbackTime := convertJournalTimestamp(item.RealtimeTimestamp) fallbackLevel := mapJournalPriorityToLevel(item.Priority) if strings.TrimSpace(item.Message) == "" { return vo.LogHysteria2Vo{ Level: fallbackLevel, Msg: "", Time: fallbackTime, } } parsed := vo.LogHysteria2Vo{} if err := json.Unmarshal([]byte(item.Message), &parsed); err == nil { if parsed.Level == "" { parsed.Level = fallbackLevel } if parsed.Time == "" { parsed.Time = fallbackTime } if parsed.Msg == "" { parsed.Msg = item.Message } return parsed } return vo.LogHysteria2Vo{ Level: fallbackLevel, Msg: item.Message, Time: fallbackTime, } } func convertJournalTimestamp(raw string) string { if raw == "" { return "" } us, err := strconv.ParseInt(raw, 10, 64) if err != nil { return "" } t := time.UnixMicro(us) return t.Format("2006-01-02 15:04:05") } func mapJournalPriorityToLevel(priority string) string { switch strings.TrimSpace(priority) { case "0", "1", "2", "3": return "error" case "4": return "warn" default: return "info" } }