From fe084482494dbb44f05e9b55a4fa67e33ede54bf Mon Sep 17 00:00:00 2001 From: fengcaiwen <895703375@qq.com> Date: Tue, 29 Apr 2025 21:40:46 +0800 Subject: [PATCH] refactor: split user and root daemon log --- cmd/kubevpn/cmds/daemon.go | 2 +- pkg/config/const.go | 3 +- pkg/daemon/action/logs.go | 201 +++++++++++++++++++++++++++++------- pkg/daemon/action/server.go | 7 +- pkg/daemon/daemon.go | 15 +-- 5 files changed, 175 insertions(+), 53 deletions(-) diff --git a/cmd/kubevpn/cmds/daemon.go b/cmd/kubevpn/cmds/daemon.go index 6fa72f76..157c1b7d 100644 --- a/cmd/kubevpn/cmds/daemon.go +++ b/cmd/kubevpn/cmds/daemon.go @@ -42,7 +42,7 @@ func CmdDaemon(cmdutil.Factory) *cobra.Command { } else { go util.StartupPProf(config.PProfPort) } - return initLogfile(action.GetDaemonLogPath()) + return initLogfile(action.GetDaemonLogPath(opt.IsSudo)) }, RunE: func(cmd *cobra.Command, args []string) (err error) { defer opt.Stop() diff --git a/pkg/config/const.go b/pkg/config/const.go index 430dd89c..376984a9 100644 --- a/pkg/config/const.go +++ b/pkg/config/const.go @@ -18,7 +18,8 @@ const ( PidPath = "daemon.pid" SudoPidPath = "sudo_daemon.pid" - LogFile = "daemon.log" + UserLogFile = "user_daemon.log" + SudoLogFile = "sudo_daemon.log" ConfigFile = "config.yaml" diff --git a/pkg/daemon/action/logs.go b/pkg/daemon/action/logs.go index c252c3ad..1c152871 100644 --- a/pkg/daemon/action/logs.go +++ b/pkg/daemon/action/logs.go @@ -1,7 +1,6 @@ package action import ( - "bufio" "io" "log" "os" @@ -12,35 +11,73 @@ import ( ) func (svr *Server) Logs(req *rpc.LogRequest, resp rpc.Daemon_LogsServer) error { - path := GetDaemonLogPath() - - lines, err2 := countLines(path) - if err2 != nil { - return err2 - } - // only show latest N lines - if req.Lines < 0 { - lines = -req.Lines - } else { - lines -= req.Lines - } - - config := tail.Config{Follow: req.Follow, ReOpen: false, MustExist: true, Logger: log.New(io.Discard, "", log.LstdFlags)} - if !req.Follow { - // FATAL -- cannot set ReOpen without Follow. - config.ReOpen = false - } - file, err := tail.TailFile(path, config) + line := int64(max(req.Lines, -req.Lines)) + sudoLine, sudoSize, err := seekToLastLine(GetDaemonLogPath(true), line) if err != nil { return err } - defer file.Stop() + userLine, userSize, err := seekToLastLine(GetDaemonLogPath(false), line) + if err != nil { + return err + } + err = recent(resp, sudoLine, userLine) + if err != nil { + return err + } + + if req.Follow { + err = tee(resp, sudoSize, userSize) + if err != nil { + return err + } + } + return nil +} + +func tee(resp rpc.Daemon_LogsServer, sudoLine int64, userLine int64) error { + // FATAL -- cannot set ReOpen without Follow. + sudoConfig := tail.Config{ + Follow: true, + ReOpen: true, + MustExist: true, + Logger: log.New(io.Discard, "", log.LstdFlags), + Location: &tail.SeekInfo{Offset: sudoLine, Whence: io.SeekStart}, + } + userConfig := tail.Config{ + Follow: true, + ReOpen: true, + MustExist: true, + Logger: log.New(io.Discard, "", log.LstdFlags), + Location: &tail.SeekInfo{Offset: userLine, Whence: io.SeekStart}, + } + sudoFile, err := tail.TailFile(GetDaemonLogPath(true), sudoConfig) + if err != nil { + return err + } + defer sudoFile.Stop() + userFile, err := tail.TailFile(GetDaemonLogPath(false), userConfig) + if err != nil { + return err + } + defer userFile.Stop() for { select { case <-resp.Context().Done(): return nil - case line, ok := <-file.Lines: + case line, ok := <-userFile.Lines: + if !ok { + return nil + } + if line.Err != nil { + return line.Err + } + + err = resp.Send(&rpc.LogResponse{Message: "[USER] " + line.Text + "\n"}) + if err != nil { + return err + } + case line, ok := <-sudoFile.Lines: if !ok { return nil } @@ -48,11 +85,7 @@ func (svr *Server) Logs(req *rpc.LogRequest, resp rpc.Daemon_LogsServer) error { return err } - if lines--; lines >= 0 { - continue - } - - err = resp.Send(&rpc.LogResponse{Message: line.Text + "\n"}) + err = resp.Send(&rpc.LogResponse{Message: "[ROOT] " + line.Text + "\n"}) if err != nil { return err } @@ -60,23 +93,115 @@ func (svr *Server) Logs(req *rpc.LogRequest, resp rpc.Daemon_LogsServer) error { } } -func countLines(filename string) (int32, error) { +func recent(resp rpc.Daemon_LogsServer, sudoLine int64, userLine int64) error { + sudoConfig := tail.Config{ + Follow: false, + ReOpen: false, + MustExist: true, + Logger: log.New(io.Discard, "", log.LstdFlags), + Location: &tail.SeekInfo{Offset: sudoLine, Whence: io.SeekStart}, + } + userConfig := tail.Config{ + Follow: false, + ReOpen: false, + MustExist: true, + Logger: log.New(io.Discard, "", log.LstdFlags), + Location: &tail.SeekInfo{Offset: userLine, Whence: io.SeekStart}, + } + sudoFile, err := tail.TailFile(GetDaemonLogPath(true), sudoConfig) + if err != nil { + return err + } + defer sudoFile.Stop() + userFile, err := tail.TailFile(GetDaemonLogPath(false), userConfig) + if err != nil { + return err + } + defer userFile.Stop() +userOut: + for { + select { + case <-resp.Context().Done(): + return nil + case line, ok := <-userFile.Lines: + if !ok { + break userOut + } + if line.Err != nil { + return line.Err + } + + err = resp.Send(&rpc.LogResponse{Message: "[USER] " + line.Text + "\n"}) + if err != nil { + return err + } + } + } +sudoOut: + for { + select { + case <-resp.Context().Done(): + return nil + case line, ok := <-sudoFile.Lines: + if !ok { + break sudoOut + } + if line.Err != nil { + return line.Err + } + + err = resp.Send(&rpc.LogResponse{Message: "[ROOT] " + line.Text + "\n"}) + if err != nil { + return err + } + } + } + return nil +} + +func seekToLastLine(filename string, lines int64) (int64, int64, error) { file, err := os.Open(filename) if err != nil { - return 0, err + return 0, 0, err } defer file.Close() - scanner := bufio.NewScanner(file) - lineCount := int32(0) - - for scanner.Scan() { - lineCount++ + stat, err := file.Stat() + if err != nil { + return 0, 0, err } + size := stat.Size() + bufSize := int64(4096) + lineCount := int64(0) + remaining := size - if err = scanner.Err(); err != nil { - return 0, err + for remaining > 0 { + chunkSize := bufSize + if remaining < bufSize { + chunkSize = remaining + } + pos := remaining - chunkSize + _, err = file.Seek(pos, io.SeekStart) + if err != nil { + return 0, 0, err + } + + buf := make([]byte, chunkSize) + _, err = file.Read(buf) + if err != nil { + return 0, 0, err + } + + for i := len(buf) - 1; i >= 0; i-- { + if buf[i] == '\n' { + lineCount++ + if lineCount > lines { + targetPos := pos + int64(i) + 1 + return targetPos, size, nil + } + } + } + remaining -= chunkSize } - - return lineCount, nil + return 0, 0, nil } diff --git a/pkg/daemon/action/server.go b/pkg/daemon/action/server.go index c8e83700..05213857 100644 --- a/pkg/daemon/action/server.go +++ b/pkg/daemon/action/server.go @@ -34,6 +34,9 @@ type Server struct { ID string } -func GetDaemonLogPath() string { - return filepath.Join(config.DaemonPath, config.LogFile) +func GetDaemonLogPath(isSudo bool) string { + if isSudo { + return filepath.Join(config.DaemonPath, config.SudoLogFile) + } + return filepath.Join(config.DaemonPath, config.UserLogFile) } diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 04a31590..6b962145 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -44,7 +44,7 @@ type SvrOption struct { func (o *SvrOption) Start(ctx context.Context) error { l := &lumberjack.Logger{ - Filename: action.GetDaemonLogPath(), + Filename: action.GetDaemonLogPath(o.IsSudo), MaxSize: 100, MaxAge: 3, MaxBackups: 3, @@ -63,7 +63,7 @@ func (o *SvrOption) Start(ctx context.Context) error { plog.L.SetOutput(l) rest.SetDefaultWarningHandler(rest.NoWarnings{}) // every day 00:00:00 rotate log - go rotateLog(l, o.IsSudo) + go rotateLog(l) sockPath := config.GetSockPath(o.IsSudo) err := os.Remove(sockPath) @@ -227,11 +227,8 @@ func writePIDToFile(isSudo bool) error { // let daemon process to Rotate log. create new log file // sudo daemon process then use new log file -func rotateLog(l *lumberjack.Logger, isSudo bool) { +func rotateLog(l *lumberjack.Logger) { sec := time.Duration(0) - if isSudo { - sec = 2 * time.Second - } for { nowTime := time.Now() nowTimeStr := nowTime.Format("2006-01-02") @@ -239,10 +236,6 @@ func rotateLog(l *lumberjack.Logger, isSudo bool) { next := t2.AddDate(0, 0, 1).Add(sec) after := next.UnixNano() - nowTime.UnixNano() <-time.After(time.Duration(after) * time.Nanosecond) - if isSudo { - _ = l.Close() - } else { - _ = l.Rotate() - } + _ = l.Rotate() } }