如何在 macOS 上使用 logrus/lumberjack 自动重新创建日志文件

huangapple go评论170阅读模式
英文:

How to re-create log file automatically in logrus/lumberjack on macOS

问题

我在 macOS 上使用 logruslumberjack 来处理日志文件。

logger := &lumberjack.Logger{
    // 日志路径
    Filename: filepath.Join(logFolder, "xxx.log"),
    // 日志大小(MB)
    MaxSize: 10,
    // 备份数量
    MaxBackups: 3,
    // 过期天数
    // MaxAge: 28,
    // 是否启用 gzip 压缩
    Compress: false,
}
logrus.SetOutput(logger)

当我启动应用程序时,它会按预期创建日志文件。然而,当我的应用程序正在运行时,我手动删除了日志文件,我期望它会重新创建日志文件,但实际上并没有创建。但是,如果我重新启动应用程序,日志文件将会再次创建。

所以,我应该怎么做才能让我的应用程序(保持运行)在日志文件被删除时重新创建并写入日志文件呢?

提前感谢你的帮助。

英文:

I use logrus and lumberjack for log file on macOS.

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)

When I start my application, It will create log file as expected.
However, when my app is running, I manually delete log file, as my app is continuing to run, I expect it will re-create log file, however, the log file is not created.
But if I restart my app, the log file will be created again.
So what should I do to make my app (keep running) to re-create and write log file when log file is deleted.

Thanks in advance.

答案1

得分: 2

这是使用fsnotify库监视日志文件的另一种方法。fsnotify监视包含xxx.log的目录,并在xxx.log被删除时告诉lumberjack执行Rotate()操作。

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{
		// 日志路径
		Filename: logPath,
		// 日志大小(MB)
		MaxSize: 10,
		// 备份数量
		MaxBackups: 3,
		// 过期天数
		// MaxAge: 28,
		// 是否启用gzip压缩
		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("旋转日志", event.Name)
				logger.Rotate()
			}
		}
	}()

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

	for {
		logger.Write([]byte(fmt.Sprintf("当前时间:%v\n", time.Now())))
		time.Sleep(3 * time.Second)
	}
}
英文:

Here is another way to monitor the log file using fsnotify lib. fsnotify monitors the directory where xxx.log is located, tell lumberjack to Rotate() when xxx.log deleted.

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)
	}
}

答案2

得分: 1

"问题"

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

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

将文件打开被认为是将其链接计数增加1,而从文件系统中删除文件相当于将该链接计数减少1。
你可以在这里了解更多关于链接计数的信息。

假设你正在使用的日志包保持日志文件处于打开状态,所以所发生的情况只是将文件名从文件系统的目录中删除,但这并不以任何方式影响日志记录过程。
此外,拥有打开文件的进程在任何文件名消失时都不会收到通知(在类Unix系统上,一个文件可能同时有多个名称,详细了解一下硬链接)。

解决方案

对于类Unix的操作系统,通常有一种方式可以显式地要求进程重新打开其日志文件。
通常情况下,会使用SIGUSR1信号来实现这一点,因此你可以设置一个处理程序(通过signal.Notify)来处理这样的信号,然后让你的日志轮换代码通过kill -USR1命令向正在运行的进程发送信号,告诉它重新打开日志文件,这将重新创建该文件。

当然,你还可以实现更复杂的方案,因为你可以使用任何形式的IPC来将该命令传达给正在运行的进程。

英文:

The "problem"

Based on the stated behaviour, I assume this is happening on an OS which implements POSIX-compatible behaviour for files (so it's running on some kernel with Unix heritage — such as *BSD or Linux, — or it's a Mac OS system).
On these systems, removing a file from a file system does nothing to that file's storage on that file system — as long as it is opened in at least a single running process.
Specifically, to cite the POSIX documentation on open(2):

> If the link count of the file is 0, when all file descriptors associated with the file are closed, the space occupied by the file shall be freed and the file shall no longer be accessible.

Having a file open is considered to increment its link count by 1, and removing a file from its filesystem is the same as decrementing that link count by 1.
You might read more about link counts here.

Supposedly the logging package you're using keep the log file opened, and so what happens is that you merely remove the file's name from the filesystem's directory it was it, but this does not in any way affect the logging process.
Moreover, the process which has the file open is not notified when any of the file names goes away (on Unix-like systems a file might have many names at the same time—read about hard links).

The solution

Typically for Unix-like OSes is to have a way to explicitly ask the process to re-open its log file(s).
Casually, a SIGUSR1 signal is used for this, so you could setup a handler (via signal.Notify) to setup a handler for such a signal and then make your log rotation code to kill -USR1 the running process to tell it re-open the log file, which will be recreated.

Of course, more involved schemes could be implemented as you can use any form of IPC to communicate that command to your running process.

答案3

得分: 0

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

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

go func() {
    for _ = range sigs {
        // 调用lumberjack的Rotate()方法创建新的日志文件
        logger.Rotate()
    }
}()

用于删除和轮转日志的命令:

# 删除日志文件
rm path/to/xxx.log
# 这个命令不会终止您的进程,只是向其发送一个信号
kill -USR1 <your process id>
英文:

Here is the implement of @kostix 's solution. after you deleting xxx.log, you could send a signal to your running process, then the code below will handle the singal and tell lumberjack to create new log file.

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()
    }
}()

commands used to delete and rotate logs

# delete log file
rm path/to/xxx.log
# this command would not kill your process, just send a signal to it.
kill -USR1 &lt;your process id&gt;

huangapple
  • 本文由 发表于 2021年11月1日 19:06:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/69796031.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定