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:
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.
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:
go install
sur le dossier.Maintenant, importez le paquet "log" et le "paquet bûcheron".
import (
"log"
"github.com/natefinch/lumberjack"
)
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 .
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")
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())
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()
.