109 lines
2.3 KiB
Go
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
|
|
}
|