Initial commit
This commit is contained in:
116
rotate/rotate.go
Normal file
116
rotate/rotate.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Package rotate provides an io.Writer that changes the file it writes to based
|
||||
// on the current date. It always writes to a file inside of the directory it is
|
||||
// given, and only switches files once it has finished writing a line and the
|
||||
// date has changed. It is intended to be used with log.SetOutput().
|
||||
package rotate
|
||||
|
||||
import "os"
|
||||
import "io"
|
||||
import "time"
|
||||
import "errors"
|
||||
import "path/filepath"
|
||||
|
||||
type logger struct {
|
||||
directory string
|
||||
file io.WriteCloser
|
||||
previousDay int
|
||||
closed bool
|
||||
}
|
||||
|
||||
// New returns a new logger that will always log to directory/YYYY-MM-DD.log.
|
||||
// When its close method is called, it will close any open file and stop
|
||||
// accepting calls to Write. A closed logger may not be re-opened.
|
||||
func New (directory string) (io.WriteCloser, error) {
|
||||
logger := &logger { directory: directory }
|
||||
err := logger.updateCurrentFile()
|
||||
if err != nil { return nil, err }
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
func (logger *logger) Write (buffer []byte) (n int, err error) {
|
||||
if logger.closed {
|
||||
return 0, errors.New("rotate: attempt to write to closed logger")
|
||||
}
|
||||
|
||||
if logger.file == nil {
|
||||
err = logger. updateCurrentFile()
|
||||
if err != nil { return }
|
||||
}
|
||||
|
||||
for buffer != nil {
|
||||
var outgoing []byte
|
||||
var justWrote int
|
||||
outgoing, buffer = splitLine(buffer)
|
||||
justWrote, err = logger.file.Write(outgoing)
|
||||
n += justWrote
|
||||
if err != nil { return }
|
||||
|
||||
err = logger.updateCurrentFile()
|
||||
if err != nil { return }
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (logger *logger) Close () error {
|
||||
return logger.file.Close()
|
||||
}
|
||||
|
||||
func (logger *logger) updateCurrentFile() (err error) {
|
||||
if logger.closed { return }
|
||||
|
||||
// yes this is not a correct measure of days since 1970 but all we need
|
||||
// to know is "has the date changed?"
|
||||
now := time.Now()
|
||||
currentDay := (now.Year() - 1970) * 366 + now.YearDay()
|
||||
|
||||
if currentDay > logger.previousDay || logger.file == nil {
|
||||
if logger.file != nil { logger.file.Close() }
|
||||
|
||||
logger.file, err = os.OpenFile(
|
||||
filepath.Join(logger.directory, now.Format("2006-01-02.log")),
|
||||
os.O_WRONLY | os.O_APPEND | os.O_CREATE,
|
||||
0660)
|
||||
if err != nil { return }
|
||||
}
|
||||
|
||||
logger.previousDay = currentDay
|
||||
return
|
||||
}
|
||||
|
||||
func splitLine (buffer []byte) (left, right []byte) {
|
||||
index := indexOf(buffer, '\n')
|
||||
if index < 1 { return buffer, nil }
|
||||
index += 1
|
||||
return buffer[:index], buffer[index:]
|
||||
}
|
||||
|
||||
func indexOf[T comparable] (haystack []T, needle T) int {
|
||||
for index, element := range haystack {
|
||||
if element == needle {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
type lineWriter struct {
|
||||
destination func (line []byte) (int, error)
|
||||
}
|
||||
|
||||
func LineWriter (destination func (line []byte) (int, error)) io.Writer {
|
||||
return &lineWriter { destination: destination }
|
||||
}
|
||||
|
||||
func (writer lineWriter) Write (buffer []byte) (n int, err error) {
|
||||
for buffer != nil {
|
||||
var outgoing []byte
|
||||
var justWrote int
|
||||
outgoing, buffer = splitLine(buffer)
|
||||
justWrote, err = writer.destination(outgoing)
|
||||
n += justWrote
|
||||
if err != nil { return }
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user