web-dev-qa-db-ja.com

ログローテーションを使用してgolangでファイルにログインするにはどうすればよいですか?

リモートサーバーで実行されるWebアプリケーションを作成しようとしています。エラー/デバッグ/監査をキャプチャするためにログを記録する必要があります。標準の「log」パッケージを含む複数のロギングパッケージがgolangで利用できることがわかりました。ただし、次の3つの要件を満たす必要があります。

  1. ログファイルをローテーションする必要があります
  2. 「ログ」を使用する付属パッケージに適用されます
  3. クロスプラットフォームである必要があります。開発環境はLinuxであり、Windowsに展開する必要があります。
15
FlowRaja

基本レベルのlog.Logを使用して満足した場合、代わりのロガー構造を作成する代わりに3つの要件をすべて満たす最良の方法は、代わりにロガーの出力を独自の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
}

次に、RotateWriterを作成し、log.SetOutputを使用してこのライターを設定するか(他のパッケージが標準ロガーインスタンスを使用している場合)、代わりにlog.Newを使用して独自のインスタンスを作成します。

Rotateをいつ呼び出すかという状況は解決していませんが、決定するのはあなたに任せます。時間に基づいてトリガーするのは非常に簡単です。あるいは、一定量の書き込みまたは一定量のバイトの後にトリガーすることもできます。

15
Crast

@Crastは非常に良い答えを出しましたが、私も通知にしたい- lumberjack logger by Nateフィンチ を使用しました。

使用方法は次のとおりです。

  1. まず、 lumberjack リポジトリのクローンを作成しますORどうにかして入手してください。
  2. go installコマンドはフォルダーに対して。
  3. 次に、goの「ログ」パッケージと「ランバージャックパッケージ」をインポートします。

インポート( "log" "github.com/natefinch/lumberjack")

  1. 次のようにコードで使用します。

Mainの外部で、ログ変数を宣言します。

var errLog *log.Logger

メイン内:

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

これで、ファイルサイズが1MBになるとすぐに、新しいファイルが作成され、前のログを現在のタイムスタンプで保持し、新しいログはfoo.logファイルにログインし続けます。また、私はos.OpenFileを使用してファイルを作成しましたが、lumberjackが内部的に行うので、必要ないかもしれませんが、そのようにしたほうが好ましいです。おかげで、それが役立つことを願っています。もう一度@Crastと NateFinch に感謝します。

15
kinshuk4

以下は、ログのローテーションと自動パージをサポートする軽量のロギングパッケージです。

https://github.com/antigloss/go/tree/master/logger

// logger.Init must be called first to setup logger
logger.Init("./log", // specify the directory to save the logfiles
            400, // maximum logfiles allowed under the specified log directory
            20, // number of logfiles to delete when number of logfiles exceeds the configured limit
            100, // maximum size of a logfile in MB
            false) // whether logs with Trace level are written down
logger.Info("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
logger.Warn("Failed to parse protocol! uid=%d plid=%d cmd=%s", 1234, 678942, "getplayer")
3
antigloss

https://github.com/jame2981/log 私のパッケージが役立ちます。

l1 := log.Pool.New("l1", "file:///tmp/test1.log")
l2 := log.Pool.New("l2", "file:///tmp/test2.log")
l3 := log.Pool.New("l3", "file:///tmp/test3.log")
l4 := log.Pool.New("l4", "file:///tmp/test4.log")

l1.Rotate() // rotate l1 only
log.Pool.Rotate() // was rotate all instances.

// rotate with signal
reopen := make(chan os.Signal, 1)
signal.Notify(reopen, syscall.SIGUSR1)
go func() {
    for{
       <-reopen
       l.Pool.Rotate()
    }
}()

stdロガーライターを設定して、まだ作業を回転させます。

// std logger writer
import "log"
logger := log.New("test", "", 0)
logger.SetOutput(l1.Writer())
0
jame2981

思い浮かぶ1つのオプションは、独自のタイプでロギングをラップし、次のようなリロード機能を提供することです。

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

次に、信号(通常は* nixの_-HUP_)をリッスンするか、Logger.Reload()を呼び出すエンドポイントをアプリに追加します。

0
OneOfOne