Files
autoPriority/rotate.go
T

109 lines
2.3 KiB
Go

//go:build windows
package main
import (
"errors"
"fmt"
"io"
"os"
"strings"
"unicode/utf8"
)
const truncatedLogRecord = "\n[Слишком длинная запись сокращена]\n"
type rotatingLog struct {
file *os.File
path string
limit, size int64
}
func openRotatingLog(path string, limit int64) (*rotatingLog, error) {
if limit < int64(len(truncatedLogRecord)+16) {
return nil, fmt.Errorf("log size limit is too small")
}
for _, name := range []string{path, path + ".1"} {
info, err := os.Lstat(name)
if errors.Is(err, os.ErrNotExist) {
continue
}
if err != nil {
return nil, err
}
if !info.Mode().IsRegular() {
return nil, fmt.Errorf("log target is not a regular file")
}
if name != path {
if err = os.Remove(name); err != nil {
return nil, err
}
}
}
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return nil, err
}
return &rotatingLog{file: f, path: path, limit: limit}, nil
}
func (w *rotatingLog) Write(text string) error {
if w.file == nil {
return os.ErrClosed
}
if text == "" {
return nil
}
if !utf8.ValidString(text) {
text = strings.ToValidUTF8(text, "?")
}
if int64(len(text)) > w.limit {
end := int(w.limit) - len(truncatedLogRecord)
for end > 0 && !utf8.ValidString(text[:end]) {
end--
}
text = text[:end] + truncatedLogRecord
}
if w.size > 0 && w.size+int64(len(text)) > w.limit {
if err := w.rotate(); err != nil {
return err
}
}
n, err := w.file.WriteString(text)
w.size += int64(n)
if err == nil && n != len(text) {
return io.ErrShortWrite
}
return err
}
func (w *rotatingLog) rotate() error {
err := w.file.Close()
w.file = nil
if err != nil {
return err
}
if info, e := os.Lstat(w.path + ".1"); e == nil {
if !info.Mode().IsRegular() {
return fmt.Errorf("log archive is not a regular file")
}
if e = os.Remove(w.path + ".1"); e != nil {
return e
}
} else if !errors.Is(e, os.ErrNotExist) {
return e
}
if err = os.Rename(w.path, w.path+".1"); err != nil {
return err
}
w.file, err = os.OpenFile(w.path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
w.size = 0
return err
}
func (w *rotatingLog) Close() error {
if w.file == nil {
return nil
}
e := errors.Join(w.file.Sync(), w.file.Close())
w.file = nil
return e
}