350 lines
7.0 KiB
Go
350 lines
7.0 KiB
Go
package simplelog
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func isFileExist(filename string) bool {
|
|
info, e := os.Stat(filename)
|
|
|
|
if e == nil {
|
|
return !info.IsDir()
|
|
} else {
|
|
return os.IsExist(e)
|
|
}
|
|
}
|
|
|
|
// FileHandler writes log to a file.
|
|
type FileHandler struct {
|
|
fd *os.File
|
|
}
|
|
|
|
func (h *FileHandler) InitFile(name string) (*FileHandler, error) {
|
|
dir := path.Dir(name)
|
|
os.Mkdir(dir, 0777)
|
|
|
|
f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0666)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h.fd = f
|
|
|
|
return h, nil
|
|
}
|
|
|
|
func (h *FileHandler) Write(b []byte) (n int, err error) {
|
|
return h.fd.Write(b)
|
|
}
|
|
|
|
func (h *FileHandler) Close() error {
|
|
return h.fd.Close()
|
|
}
|
|
|
|
// RotatingFileHandler writes log a file, if file size exceeds maxBytes,
|
|
// it will backup current file and open a new one.
|
|
//
|
|
// max backup file number is set by backupCount, it will delete oldest if backups too many.
|
|
type RotatingFileHandler struct {
|
|
fd *os.File
|
|
|
|
fileName string
|
|
maxBytes int64
|
|
curBytes int64
|
|
backupCount uint
|
|
}
|
|
|
|
func (h *RotatingFileHandler) InitRotating(name string, maxBytes int64, backupCount uint) (*RotatingFileHandler, error) {
|
|
h.fileName = name
|
|
h.maxBytes = maxBytes
|
|
h.backupCount = backupCount
|
|
|
|
if isFileExist(name) {
|
|
var err error
|
|
h.fd, err = os.OpenFile(name, os.O_CREATE|os.O_RDONLY, 0666)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f, err := h.fd.Stat()
|
|
if err != nil {
|
|
h.fd.Close()
|
|
return nil, err
|
|
}
|
|
if f.Size() < h.maxBytes {
|
|
h.fd.Seek(0, io.SeekEnd)
|
|
} else {
|
|
h._doRollover()
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
dir := path.Dir(name)
|
|
os.MkdirAll(dir, 0777)
|
|
|
|
if maxBytes <= 0 {
|
|
return nil, fmt.Errorf("invalid max bytes")
|
|
}
|
|
|
|
var err error
|
|
h.fd, err = os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0666)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f, err := h.fd.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h.curBytes = f.Size()
|
|
|
|
return h, nil
|
|
}
|
|
|
|
func (h *RotatingFileHandler) Write(p []byte) (n int, err error) {
|
|
h.doRollover()
|
|
n, err = h.fd.Write(p)
|
|
h.curBytes += int64(n)
|
|
return
|
|
}
|
|
|
|
func (h *RotatingFileHandler) Close() error {
|
|
if h.fd != nil {
|
|
return h.fd.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *RotatingFileHandler) doRollover() {
|
|
|
|
if h.curBytes < h.maxBytes {
|
|
return
|
|
}
|
|
|
|
f, err := h.fd.Stat()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if h.maxBytes <= 0 {
|
|
return
|
|
} else if f.Size() < int64(h.maxBytes) {
|
|
h.curBytes = f.Size()
|
|
return
|
|
}
|
|
|
|
h._doRollover()
|
|
}
|
|
|
|
func (h *RotatingFileHandler) _doRollover() {
|
|
if h.backupCount > 0 {
|
|
h.fd.Close()
|
|
|
|
for i := h.backupCount - 1; i > 0; i-- {
|
|
sfn := fmt.Sprintf("%s.%d", h.fileName, i)
|
|
dfn := fmt.Sprintf("%s.%d", h.fileName, i+1)
|
|
|
|
os.Rename(sfn, dfn)
|
|
}
|
|
|
|
dfn := fmt.Sprintf("%s.1", h.fileName)
|
|
os.Rename(h.fileName, dfn)
|
|
|
|
h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
|
h.curBytes = 0
|
|
} else {
|
|
h.fd.Close()
|
|
h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
|
h.curBytes = 0
|
|
}
|
|
}
|
|
|
|
// TimedFileHandler writes log to a file.
|
|
type TimedFileHandler struct {
|
|
fd *os.File
|
|
|
|
dir string
|
|
name string
|
|
interval time.Duration
|
|
suffix string
|
|
rolloverAt time.Time
|
|
}
|
|
|
|
type WhenInterval int
|
|
|
|
const (
|
|
WhenSecond WhenInterval = iota
|
|
WhenMinute
|
|
WhenHour
|
|
WhenDay
|
|
)
|
|
|
|
func (h *TimedFileHandler) InitTimed(name string, when WhenInterval, interval int64) (*TimedFileHandler, error) {
|
|
dir := path.Dir(name)
|
|
os.Mkdir(dir, 0777)
|
|
|
|
h.dir, h.name = filepath.Split(name)
|
|
|
|
switch when {
|
|
case WhenSecond:
|
|
h.interval = 1 * time.Second
|
|
h.suffix = "2006-01-02-15-04-05"
|
|
case WhenMinute:
|
|
h.interval = 1 * time.Minute
|
|
h.suffix = "2006-01-02-15-04"
|
|
case WhenHour:
|
|
h.interval = 1 * time.Hour
|
|
h.suffix = "2006-01-02-15"
|
|
case WhenDay:
|
|
h.interval = 1 * time.Hour * 24
|
|
h.suffix = "2006-01-02"
|
|
default:
|
|
return nil, fmt.Errorf("invalid when_rotate: %d", when)
|
|
}
|
|
|
|
h.interval = time.Duration(interval) * h.interval
|
|
|
|
h.rolloverAt = time.Now()
|
|
var err error
|
|
h.fd, err = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return h, nil
|
|
}
|
|
|
|
func (h *TimedFileHandler) doRollover() {
|
|
if time.Since(h.rolloverAt) > time.Duration(h.interval) {
|
|
h.fd.Close()
|
|
h.rolloverAt = time.Now()
|
|
h.fd, _ = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
}
|
|
}
|
|
|
|
func (h *TimedFileHandler) Write(b []byte) (n int, err error) {
|
|
h.doRollover()
|
|
return h.fd.Write(b)
|
|
}
|
|
|
|
func (h *TimedFileHandler) Close() error {
|
|
return h.fd.Close()
|
|
}
|
|
|
|
type TimedRotatingFileHandler struct {
|
|
fd *os.File
|
|
|
|
dir string
|
|
name string
|
|
interval time.Duration
|
|
suffix string
|
|
rolloverAt time.Time
|
|
backupCount uint
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) InitTimedRotating(name string, when WhenInterval, interval int64, backupCount uint) (*TimedRotatingFileHandler, error) {
|
|
h.backupCount = backupCount
|
|
dir := path.Dir(name)
|
|
os.Mkdir(dir, 0777)
|
|
|
|
h.dir, h.name = filepath.Split(name)
|
|
|
|
switch when {
|
|
case WhenSecond:
|
|
h.interval = 1 * time.Second
|
|
h.suffix = "2006-01-02-15-04-05"
|
|
case WhenMinute:
|
|
h.interval = 1 * time.Minute
|
|
h.suffix = "2006-01-02-15-04"
|
|
case WhenHour:
|
|
h.interval = 1 * time.Hour
|
|
h.suffix = "2006-01-02-15"
|
|
case WhenDay:
|
|
h.interval = 1 * time.Hour * 24
|
|
h.suffix = "2006-01-02"
|
|
default:
|
|
return nil, fmt.Errorf("invalid when_rotate: %d", when)
|
|
}
|
|
|
|
h.interval = time.Duration(interval) * h.interval
|
|
|
|
h.rolloverAt = time.Now()
|
|
var err error
|
|
h.fd, err = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h._doRollover()
|
|
return h, nil
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) _doRollover() {
|
|
fs, err := h.ListDir(h.dir, h.suffix)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if len(fs) > int(h.backupCount) {
|
|
for i := 0; i < len(fs)-int(h.backupCount); i++ {
|
|
os.Remove(fs[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) doRollover() {
|
|
if time.Since(h.rolloverAt) > time.Duration(h.interval) {
|
|
h.fd.Close()
|
|
h.rolloverAt = time.Now()
|
|
h.fd, _ = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
}
|
|
h._doRollover()
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) Write(b []byte) (n int, err error) {
|
|
h.doRollover()
|
|
return h.fd.Write(b)
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) Close() error {
|
|
return h.fd.Close()
|
|
}
|
|
|
|
func (h *TimedRotatingFileHandler) ListDir(dir, suffix string) (files []string, err error) {
|
|
files = []string{}
|
|
|
|
_dir, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, file := range _dir {
|
|
if file.IsDir() {
|
|
// 不需要递归读取子目录下的文件
|
|
continue
|
|
}
|
|
names := strings.Split(file.Name(), "_")
|
|
if len(names) != 2 {
|
|
// 文件名格式不对
|
|
continue
|
|
}
|
|
_, err := time.Parse(h.suffix, names[0])
|
|
if err != nil {
|
|
// 文件名格式不对
|
|
continue
|
|
}
|
|
|
|
if h.name != names[1] {
|
|
// 文件名格式不对
|
|
continue
|
|
}
|
|
files = append(files, filepath.Join(dir, file.Name()))
|
|
}
|
|
|
|
return files, nil
|
|
}
|