mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-12 08:05:00 +03:00
Bound the panel syslog view with a journalctl timeout (#6689)
* Bound the syslog view with a journalctl timeout * fix(syslog): bound the journal scan window and soften the timeout message Limit journalctl to the last 30 days so a rare -p level cannot scan the whole journal, and drop the guessed cause and the host-wide vacuum advice from the timeout message. * fix(syslog): drop --since from the journalctl call and test the timeout On systemd 249/252 (Ubuntu 22.04, Debian 12) journalctl seeks to --since and reads forward when both --since and -n are given, so the Syslog view showed the oldest 200 lines of the 30-day window instead of the newest. Reproduced in debian:12, ubuntu:22.04 and ubuntu:24.04 containers on a synthetic journal; only 255 kept the newest lines. The window also bought nothing: on a 340 MB journal every variant (with or without --since, rare -p level or not) answered in ~10 ms on 252 and 255. Keep the 15s deadline as the guard against a stalled journalctl and cover it with a fake journalctl on PATH; the args test pinned the broken flag and is removed. --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -1184,6 +1184,9 @@ func (s *ServerService) UpdateXray(version string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// syslogTimeout keeps a stalled journalctl from hanging the Syslog request (#6629).
|
||||||
|
var syslogTimeout = 15 * time.Second
|
||||||
|
|
||||||
func (s *ServerService) GetLogs(count string, level string, syslog string) []string {
|
func (s *ServerService) GetLogs(count string, level string, syslog string) []string {
|
||||||
c, _ := strconv.Atoi(count)
|
c, _ := strconv.Atoi(count)
|
||||||
var lines []string
|
var lines []string
|
||||||
@@ -1216,10 +1219,15 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use hardcoded command with validated parameters
|
// Use hardcoded command with validated parameters
|
||||||
cmd := exec.CommandContext(context.Background(), "journalctl", "-u", "x-ui", "--no-pager", "-n", strconv.Itoa(countInt), "-p", level)
|
ctx, cancel := context.WithTimeout(context.Background(), syslogTimeout)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "journalctl", "-u", "x-ui", "--no-pager", "-n", strconv.Itoa(countInt), "-p", level)
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
|
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||||
|
return []string{"journalctl did not answer in time. Try a smaller line count or a less strict level."}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{"Failed to run journalctl command! Make sure systemd is available and x-ui service is registered."}
|
return []string{"Failed to run journalctl command! Make sure systemd is available and x-ui service is registered."}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A journalctl that never answers must not hang the Syslog request (#6629).
|
||||||
|
func TestGetLogsSyslogGivesUpOnAStalledJournalctl(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "journalctl"), []byte("#!/bin/sh\nexec sleep 5\n"), 0o755); err != nil {
|
||||||
|
t.Fatalf("write fake journalctl: %v", err)
|
||||||
|
}
|
||||||
|
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||||
|
saved := syslogTimeout
|
||||||
|
syslogTimeout = 200 * time.Millisecond
|
||||||
|
t.Cleanup(func() { syslogTimeout = saved })
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
got := (&ServerService{}).GetLogs("10", "err", "true")
|
||||||
|
want := []string{"journalctl did not answer in time. Try a smaller line count or a less strict level."}
|
||||||
|
if !slices.Equal(got, want) {
|
||||||
|
t.Fatalf("GetLogs = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
if elapsed := time.Since(start); elapsed > 3*time.Second {
|
||||||
|
t.Fatalf("GetLogs waited %v for a stalled journalctl, want about %v", elapsed, syslogTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user