web-dev-qa-db-fra.com

Comment puis-je me connecter à golang dans un fichier avec rotation des journaux?

J'essaie d'écrire une application Web qui s'exécutera sur un serveur distant. Je dois me connecter pour capturer les erreurs/débogage/audit. Je trouve que plusieurs packages de journalisation sont disponibles pour golang, y compris le package "log" standard. Cependant, je dois remplir trois conditions:

  1. Les fichiers journaux doivent être tournés
  2. Cela s'applique aux paquets inclus qui utilisent "log"
  3. Il doit être multi-plateforme. L'environnement de développement est Linux et doit être déployé sous Windows.
8
FlowRaja

Si vous êtes satisfait à l'aide du fichier journal log.log de base, le meilleur moyen de répondre à vos trois exigences plutôt que de créer une autre structure de journal consiste à définir la sortie du journal sur votre propre instance io.Writer.

Donc, fondamentalement, ce que je vais faire ici est de montrer un exemple où je crée mon propre 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
}

Vous créez ensuite un RotateWriter et utilisez log.SetOutput pour définir ce rédacteur (si d'autres packages utilisent l'instance de consignateur standard) ou vous créez vos propres instances à l'aide de log.New pour le faire circuler.

Je n'ai pas résolu la question de savoir quand appeler Rotate, je vous laisse le soin de décider. Il serait assez simple de le déclencher en fonction du temps ou de le faire après un certain nombre d'écritures ou d'octets.

10
Crast

Bien que @Crast ait donné une très bonne réponse, je tiens également à signaler à l’avis - lumberjack logger de Nate Finch que j’ai finalement utilisé. 

Voici comment l'utiliser:

  1. Commencez par cloner le répertoire lumberjack repository OR.
  2. Exécutez la commande go install sur le dossier.
  3. Maintenant, importez le paquet "log" et le "paquet bûcheron". import ( "log" "github.com/natefinch/lumberjack" )

  4. Maintenant, utilisez-le dans votre code comme ceci:

En dehors de main, déclarez votre variable de journal.

var errLog *log.Logger

Intérieur principal:

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

Désormais, dès que la taille du fichier atteint 1 Mo, un nouveau fichier est créé pour conserver les journaux précédents avec les horodatages actuels, et les nouveaux journaux continueront à se connecter au fichier foo.log. De plus, j’ai créé le fichier en utilisant os.OpenFile mais vous n’en avez peut-être pas besoin comme le fait Lumberjack en interne, mais je l’ai préféré de cette façon. Merci, espérons que cela aide. Encore une fois, merci à @Crast et NateFinch .

7
kinshuk4

Voici un logiciel de journalisation léger qui prend en charge la rotation des journaux et la purge automatique.

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 Mon forfait peut vous aider. 

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

définissez std logger writer afin de faire pivoter le travail pour le moment.

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

Une option qui me vient à l’esprit est d’envelopper la journalisation dans votre propre type et de fournir une fonction de rechargement, quelque chose comme:

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
}

Ensuite, écoutez un signal (généralement -HUP sur * nix) ou ajoutez dans votre application un point de terminaison pouvant appeler Logger.Reload().

0
OneOfOne