Как я могу войти в golang в файл с вращением журнала?
Я пытаюсь написать веб-приложение, которое будет работать на удаленном сервере. Мне нужно регистрироваться для захвата ошибок/отладки/аудита.
Я обнаружил, что для голанга доступны несколько пакетов журналов, включая стандартный пакет "log". Однако мне нужно выполнить три требования:
- Файлы журналов необходимо повернуть
- Это относится к включенным пакетам
которые используют "журнал"
- Он должен быть кросс-платформенным. Окружение Dev
Linux и должен быть развернут в Windows.
Ответы
Ответ 1
Лучший способ выполнить все ваши три требования вместо создания альтернативной структуры logger, если вы были удовлетворены с помощью 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
}
Затем вы создаете RotateWriter и используете log.SetOutput
для установки этого сценария (если другие пакеты используют стандартный экземпляр журнала) или поочередно создайте свои собственные экземпляры, используя log.New
для прохождения.
Я не решил ситуацию, когда звонить повернуть, я оставлю это вам, чтобы решить. Было бы довольно просто вызвать его на основе времени или поочередно делать это после некоторого количества записей или некоторого количества байтов.
Ответ 2
Хотя @Crast дал очень хороший ответ, я хочу также обратить внимание на это - лесоруб регистратора Нейта Финча, который я в итоге и использовал.
Вот как это можно использовать:
- Сначала клонируйте хранилище дровосека ИЛИ каким-нибудь образом получите его.
- Запустите команду
go install
для папки.
- Теперь импортируйте go "log" package и "lumberjack package".
Импортировать ( "журнал" "Github.com/natefinch/lumberjack"
)
- Теперь используйте его в своем коде так:
За пределами 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
})
Теперь, как только размер файла составит 1 МБ, создается новый файл для сохранения предыдущих журналов с текущими временными метками, и новые журналы будут продолжать регистрироваться в файле foo.log. Кроме того, я создал файл, используя os.OpenFile, но он вам может не понадобиться, поскольку это делает внутренне дровосек, но я предпочел его таким образом. Спасибо, надеюсь, это поможет.
Еще раз спасибо @Crast и NateFinch.
Ответ 3
Вот легкий взвешенный пакет регистрации, который поддерживает поворот журнала и автоматическую очистку
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")
Ответ 4
Один из вариантов, который приходит на ум, состоит в том, чтобы обернуть ведение журнала в вашем собственном типе и предоставить функцию перезагрузки, например:
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
}
Затем либо прослушивайте сигнал (обычно -HUP
on * nix), либо добавьте конечную точку в свое приложение, которое вызовет Logger.Reload()
.
Ответ 5
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 logger writer, чтобы повернуть работу еще.
// std logger writer
import "log"
logger := log.New("test", "", 0)
logger.SetOutput(l1.Writer())
Ответ 6
func (l *Logger) Logf(f string, args ...interface{}) {
l.m.RLock()
l.l.Printf(f, args...)
l.m.RUnlock()
}
logger = log.New(l.f, "your-app", log.Lshortfile)
Когда этот регистратор вызывается из других файлов, файл log.Lshortfile всегда называет файл, в котором реализован регистратор. Как регистратор может взять имя файла, из которого вызывается Logf?