如何将Golang登录到具有日志轮换的文件?

Flo*_*aja 15 logging go

我正在尝试编写一个将在远程服务器上运行的Web应用程序.我需要记录以捕获错误/调试/审计.我发现golang有多个日志包可用,包括标准的"log"包.但是,我需要满足三个要求:

  1. 需要旋转日志文件
  2. 它适用于使用"log"的包含的包
  3. 它需要跨平台.开发环境是Linux,需要在Windows上部署.

Cra*_*ast 15

如果您对使用基准级log.Log感到满意,那么满足所有三个要求而不是创建备用记录器结构的最佳方法是将记录器的输出设置为您自己的io.Writer实例.

所以基本上我要做的就是展示一个我创建自己的io.Writer的例子:

import (
    "os"
    "sync"
    "time"
)

type RotateWriter struct {
    lock     sync.Mutex
    filename string // should be set to the actual filename
    fp       *os.File
}

// Make a new RotateWriter. Return nil if error occurs during setup.
func New(filename string) *RotateWriter {
    w := &RotateWriter{filename: filename}
    err := w.Rotate()
    if err != nil {
        return nil
    }
    return w
}

// Write satisfies the io.Writer interface.
func (w *RotateWriter) Write(output []byte) (int, error) {
    w.lock.Lock()
    defer w.lock.Unlock()
    return w.fp.Write(output)
}

// Perform the actual act of rotating and reopening file.
func (w *RotateWriter) Rotate() (err error) {
    w.lock.Lock()
    defer w.lock.Unlock()

    // Close existing file if open
    if w.fp != nil {
        err = w.fp.Close()
        w.fp = nil
        if err != nil {
            return
        }
    }
    // Rename dest file if it already exists
    _, err = os.Stat(w.filename)
    if err == nil {
        err = os.Rename(w.filename, w.filename+"."+time.Now().Format(time.RFC3339))
        if err != nil {
            return
        }
    }

    // Create a file.
    w.fp, err = os.Create(w.filename)
    return
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个RotateWriter并用于log.SetOutput设置此编写器(如果其他包使用标准记录器实例)或者使用log.New传递创建自己的实例.

我还没有解决何时调用Rotate的情况,我会留给你决定.基于时间触发它是相当简单的,或者在一些写入量或一些字节量之后交替执行.


kin*_*uk4 15

虽然@Crast已经给出了一个非常好的答案,但我还要注意到Nate Finch的伐木工记录器,我最终使用了它.

以下是如何使用它:

  1. 首先克隆伐木工具库或以某种方式获取它.
  2. go install在该文件夹上运行该命令.
  3. 现在导入go的"log"包和"lumberjack包". go install

  4. 现在在你的代码中使用它,如下所示:

在main之外,声明您的日志变量.

var errLog *log.Logger
Run Code Online (Sandbox Code Playgroud)

内部主要:

e, err := os.OpenFile("./foo.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)

if err != nil {
    fmt.Printf("error opening file: %v", err)
    os.Exit(1)
}
errLog = log.New(e, "", log.Ldate|log.Ltime)
errLog.SetOutput(&lumberjack.Logger{
    Filename:   "./foo.log",
    MaxSize:    1,  // megabytes after which new file is created
    MaxBackups: 3,  // number of backups
    MaxAge:     28, //days
})
Run Code Online (Sandbox Code Playgroud)

现在,只要文件大小达到1MB,就会创建新文件以使先前的日志保留当前时间戳,新日志将继续登录到foo.log文件.此外,我已经使用os.OpenFile创建了该文件,但您可能不需要它,因为伐木工人在内部这样做,但我更喜欢它.谢谢,希望它有所帮助.再次感谢@Crast和NateFinch.


Xaq*_*ron 10

使用logruslumberjack插件logrus

package mypackage

import (
"io"
"os"
"path/filepath"
"time"

log "github.com/sirupsen/logrus"
"gopkg.in/natefinch/lumberjack.v2"
)

func SetupLogger() {

    lumberjackLogger := &lumberjack.Logger{
        // Log file abbsolute path, os agnostic
        Filename:   filepath.ToSlash("/path/to/log/file"), 
        MaxSize:    5, // MB
        MaxBackups: 10,
        MaxAge:     30,   // days
        Compress:   true, // disabled by default
    }

    // Fork writing into two outputs
    multiWriter := io.MultiWriter(os.Stderr, lumberjackLogger)

    logFormatter := new(log.TextFormatter)
    logFormatter.TimestampFormat = time.RFC1123Z // or RFC3339
    logFormatter.FullTimestamp = true

   log.SetFormatter(logFormatter)
   log.SetLevel(log.InfoLevel)
   log.SetOutput(multiWriter)
}
Run Code Online (Sandbox Code Playgroud)

在程序的早期阶段使用此函数(可能在 内部init)。

导入log "github.com/sirupsen/logrus"任何文件并记录:

log.Info("some message")
Run Code Online (Sandbox Code Playgroud)


One*_*One 0

我想到的一个选择是将日志记录封装在您自己的类型中并提供重新加载函数,例如:

type Logger struct {
    l *log.Logger
    f *os.File
    m sync.RWMutex
}

func NewLogger(fn string) (*Logger, error) {
    f, err := os.Create(fn)
    if err != nil {
        return nil, err
    }
    l := &Logger{
        l: log.New(f, "your-app", log.Lshortfile),
        f: f,
    }
    return l, nil
}

func (l *Logger) Logf(f string, args ...interface{}) {
    l.m.RLock()
    l.l.Printf(f, args...)
    l.m.RUnlock()
}

func (l *Logger) Reload() (err error) {
    l.m.Lock()
    defer l.m.Unlock()
    l.f.Close()
    if l.f, err = os.Create(l.f.Name()); err != nil {
        return
    }
    l.l = log.New(l.f, "your-app", log.Lshortfile)
    return
}
Run Code Online (Sandbox Code Playgroud)

然后侦听信号(通常-HUP在 *nix 上)或在应用程序中添加一个会调用Logger.Reload().