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

Time:11-02

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.

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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 <your process id>
  • Related