1

我在 macOS 上使用logruslumberjack作为日志文件。

logger := &lumberjack.Logger{
    // Log path
    Filename: filepath.Join(logFolder, "xxx.log"),
    // Log size MB
    MaxSize: 10,
    // Backup count
    MaxBackups: 3,
    // expire days
    // MaxAge: 28,
    // gzip compress
    Compress: false,
}
logrus.SetOutput(logger)

当我启动我的应用程序时,它将按预期创建日志文件。
但是,当我的应用程序运行时,我手动删除了日志文件,因为我的应用程序继续运行,我希望它会重新创建日志文件,但是,日志文件没有创建。
但是如果我重新启动我的应用程序,将再次创建日志文件。
那么我应该怎么做才能让我的应用程序(继续运行)在删除日志文件时重新创建和写入日志文件。

提前致谢。

4

3 回答 3

1

问题”

基于所述行为,我假设这发生在为文件实现 POSIX 兼容行为的操作系统上(因此它运行在具有 Unix 遗产的某些内核上 - 例如 *BSD 或 Linux,或者它是 Mac OS 系统)。在这些系统上,从文件系统中删除文件对该文件系统上的文件存储没有任何影响——只要它至少在一个正在运行的进程中打开。
具体来说,要引用POSIX 文档open(2)

如果文件的链接计数为0,则当与该文件关联的所有文件描述符都关闭时,该文件占用的空间将被释放,该文件将不再可访问。

打开文件被认为将其链接计数增加 1,从其文件系统中删除文件与将链接计数减 1 相同。您可以在此处
阅读有关链接计数的更多信息。

假设您正在使用的日志记录包保持日志文件处于打开状态,因此您只需从文件系统的目录中删除文件名,但这绝不会影响日志记录过程。
此外,当任何文件名消失时,不会通知打开文件的进程(在类 Unix 系统上,一个文件可能同时具有多个名称——请阅读硬链接)。

解决方案

通常,对于类 Unix 操作系统,有一种方法可以明确要求进程重新打开其日志文件。
偶然地,SIGUSR1为此使用了一个信号,因此您可以设置一个处理程序(通过signal.Notify)为此类信号设置一个处理程序,然后将您的日志轮换代码设置kill -USR1为正在运行的进程,以告诉它重新打开日志文件,这将是重新创建。

当然,可以实施更多涉及的方案,因为您可以使用任何形式的IPC将该命令传达给正在运行的进程。

于 2021-11-01T11:20:31.513 回答
1

这是使用fsnotifylib 监视日志文件的另一种方法。fsnotify监控xxx.log所在的目录,告诉lumberjackRotate()什么时候删除xxx.log。

import (
    "fmt"
    "log"
    "path/filepath"
    "time"

    "github.com/fsnotify/fsnotify"
    "gopkg.in/natefinch/lumberjack.v2"
)

func main() {
    logPath := "./xxx.log"
    logName := filepath.Base(logPath)

    logger := &lumberjack.Logger{
        // Log path
        Filename: logPath,
        // Log size MB
        MaxSize: 10,
        // Backup count
        MaxBackups: 3,
        // expire days
        // MaxAge: 28,
        // gzip compress
        Compress: false,
    }

    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    go func() {
        for event := range watcher.Events {
            if event.Op&fsnotify.Remove == fsnotify.Remove &&
                event.Name == logName {
                log.Println("rotate log", event.Name)
                logger.Rotate()
            }
        }
    }()

    err = watcher.Add(filepath.Dir(logPath))
    if err != nil {
        log.Fatal(err)
    }

    for {
        logger.Write([]byte(fmt.Sprintf("current time:%v\n", time.Now())))
        time.Sleep(3 * time.Second)
    }
}
于 2021-11-02T03:01:29.567 回答
0

这是@kostix 解决方案的实现。删除 xxx.log 后,您可以向正在运行的进程发送一个信号,然后下面的代码将处理该信号并告诉伐木工人创建新的日志文件。

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1)

go func() {
    for _ = range sigs {
        // call Rotate() method of lumberjack to create new log file
        logger.Rotate()
    }
}()

用于删除和轮换日志的命令

# delete log file
rm path/to/xxx.log
# this command would not kill your process, just send a signal to it.
kill -USR1 <your process id>
于 2021-11-02T02:39:09.483 回答