如何在 macOS 上的 logrus/lumberjack 中自动重新创建日志文件
How to re-create log file automatically in logrus/lumberjack on macOS
我在 macOS 上使用 logrus 和 lumberjack 作为日志文件。
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)
当我启动我的应用程序时,它将按预期创建日志文件。
但是,当我的应用程序处于 运行ning 时,我手动删除了日志文件,因为我的应用程序正在继续 运行,我希望它会重新创建日志文件,但是,没有创建日志文件。
但是如果我重新启动我的应用程序,日志文件将再次创建。
那么我应该怎么做才能让我的应用程序(保持运行ning)在删除日志文件时重新创建和写入日志文件。
提前致谢。
“问题”
根据规定的行为,我假设这是在 OS 上发生的,它为文件实现了 POSIX 兼容的行为(所以它在一些 Unix 内核上是 运行传统 — 例如 *BSD 或 Linux, — 或者它是一个 Mac OS 系统)。
在这些系统上,从文件系统中删除文件不会对该文件在该文件系统上的 存储 产生任何影响——只要它至少在一个 运行 进程中打开。
具体来说,引用 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.
打开文件被视为将其 link 计数增加 1,从其文件系统中删除文件与将 link 计数减少 1 相同。
您可能会阅读有关 link 计数 here.
的更多信息
假设您正在使用的日志包使日志文件保持打开状态,所以发生的情况是您只是从文件系统的目录中删除了文件名,但这不会以任何方式影响日志记录过程.
此外,当任何文件名消失时,打开文件的进程不会得到通知(在类 Unix 系统上,一个文件可能同时有多个名称——阅读 hard links)。
解决方案
通常对于类 Unix OSes 是有一种方法明确要求进程重新打开其日志文件。
随便,一个 SIGUSR1
信号用于此,所以你可以设置一个处理程序(通过 signal.Notify
)来为这样的信号设置一个处理程序,然后将你的日志轮换代码设为 kill -USR1
运行 进程告诉它重新打开日志文件,这将被重新创建。
当然,可以实施更多涉及的方案,因为您可以使用任何形式的 IPC 将该命令传达给您的 运行 进程。
这是@kostix 解决方案的实现。删除 xxx.log 后,您可以向 运行 进程发送一个信号,然后下面的代码将处理该信号并告诉 lumberjack 创建新的日志文件。
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>
这是另一种使用 fsnotify
库监视日志文件的方法。 fsnotify
监控xxx.log所在目录,当xxx.log删除时告诉伐木工人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{
// 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)
}
}
我在 macOS 上使用 logrus 和 lumberjack 作为日志文件。
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)
当我启动我的应用程序时,它将按预期创建日志文件。
但是,当我的应用程序处于 运行ning 时,我手动删除了日志文件,因为我的应用程序正在继续 运行,我希望它会重新创建日志文件,但是,没有创建日志文件。
但是如果我重新启动我的应用程序,日志文件将再次创建。
那么我应该怎么做才能让我的应用程序(保持运行ning)在删除日志文件时重新创建和写入日志文件。
提前致谢。
“问题”
根据规定的行为,我假设这是在 OS 上发生的,它为文件实现了 POSIX 兼容的行为(所以它在一些 Unix 内核上是 运行传统 — 例如 *BSD 或 Linux, — 或者它是一个 Mac OS 系统)。
在这些系统上,从文件系统中删除文件不会对该文件在该文件系统上的 存储 产生任何影响——只要它至少在一个 运行 进程中打开。
具体来说,引用 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.
打开文件被视为将其 link 计数增加 1,从其文件系统中删除文件与将 link 计数减少 1 相同。
您可能会阅读有关 link 计数 here.
假设您正在使用的日志包使日志文件保持打开状态,所以发生的情况是您只是从文件系统的目录中删除了文件名,但这不会以任何方式影响日志记录过程.
此外,当任何文件名消失时,打开文件的进程不会得到通知(在类 Unix 系统上,一个文件可能同时有多个名称——阅读 hard links)。
解决方案
通常对于类 Unix OSes 是有一种方法明确要求进程重新打开其日志文件。
随便,一个 SIGUSR1
信号用于此,所以你可以设置一个处理程序(通过 signal.Notify
)来为这样的信号设置一个处理程序,然后将你的日志轮换代码设为 kill -USR1
运行 进程告诉它重新打开日志文件,这将被重新创建。
当然,可以实施更多涉及的方案,因为您可以使用任何形式的 IPC 将该命令传达给您的 运行 进程。
这是@kostix 解决方案的实现。删除 xxx.log 后,您可以向 运行 进程发送一个信号,然后下面的代码将处理该信号并告诉 lumberjack 创建新的日志文件。
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>
这是另一种使用 fsnotify
库监视日志文件的方法。 fsnotify
监控xxx.log所在目录,当xxx.log删除时告诉伐木工人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{
// 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)
}
}