209 lines
3.4 KiB
Go
209 lines
3.4 KiB
Go
|
package log
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type Handler interface {
|
||
|
Write(p []byte) (n int, err error)
|
||
|
Close()
|
||
|
}
|
||
|
|
||
|
type base struct {
|
||
|
msg chan []byte
|
||
|
quit chan bool
|
||
|
}
|
||
|
|
||
|
func (h *base) Write(p []byte) (n int, err error) {
|
||
|
m := make([]byte, len(p))
|
||
|
|
||
|
copy(m, p)
|
||
|
|
||
|
h.msg <- m
|
||
|
|
||
|
return len(p), nil
|
||
|
}
|
||
|
|
||
|
func (h *base) Close() {
|
||
|
h.quit <- true
|
||
|
}
|
||
|
|
||
|
type StreamHandler struct {
|
||
|
base
|
||
|
w io.Writer
|
||
|
}
|
||
|
|
||
|
func NewStreamHandler(w io.Writer, msgNum int) (*StreamHandler, error) {
|
||
|
h := new(StreamHandler)
|
||
|
|
||
|
h.w = w
|
||
|
|
||
|
h.msg = make(chan []byte, msgNum)
|
||
|
h.quit = make(chan bool, 1)
|
||
|
|
||
|
go h.run()
|
||
|
|
||
|
return h, nil
|
||
|
}
|
||
|
|
||
|
func NewDefaultStreamHandler(w io.Writer) (*StreamHandler, error) {
|
||
|
return NewStreamHandler(w, 1024)
|
||
|
}
|
||
|
|
||
|
func (h *StreamHandler) run() {
|
||
|
for {
|
||
|
select {
|
||
|
case m := <-h.msg:
|
||
|
h.w.Write(m)
|
||
|
case <-h.quit:
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type FileHandler struct {
|
||
|
base
|
||
|
fd *os.File
|
||
|
}
|
||
|
|
||
|
func NewFileHandler(fileName string, flag int, msgNum int) (*FileHandler, error) {
|
||
|
f, err := os.OpenFile(fileName, flag, 0)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
h := new(FileHandler)
|
||
|
|
||
|
h.fd = f
|
||
|
|
||
|
h.msg = make(chan []byte, msgNum)
|
||
|
h.quit = make(chan bool, 1)
|
||
|
|
||
|
go h.run()
|
||
|
|
||
|
return h, nil
|
||
|
}
|
||
|
|
||
|
func NewDefaultFileHandler(fileName string, flag int) (*FileHandler, error) {
|
||
|
return NewFileHandler(fileName, flag, 1024)
|
||
|
}
|
||
|
|
||
|
func (h *FileHandler) run() {
|
||
|
for {
|
||
|
select {
|
||
|
case m := <-h.msg:
|
||
|
h.fd.Write(m)
|
||
|
case <-h.quit:
|
||
|
h.fd.Close()
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//refer: http://docs.python.org/2/library/logging.handlers.html
|
||
|
//same like python TimedRotatingFileHandler
|
||
|
|
||
|
type TimeRotatingFileHandler struct {
|
||
|
base
|
||
|
|
||
|
fd *os.File
|
||
|
|
||
|
baseName string
|
||
|
interval int64
|
||
|
suffix string
|
||
|
rolloverAt int64
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
WhenSecond = iota
|
||
|
WhenMinute
|
||
|
WhenHour
|
||
|
WhenDay
|
||
|
)
|
||
|
|
||
|
func NewTimeRotatingFileHandler(baseName string, when int8, interval int, msgNum int) (*TimeRotatingFileHandler, error) {
|
||
|
h := new(TimeRotatingFileHandler)
|
||
|
|
||
|
h.baseName = baseName
|
||
|
|
||
|
switch when {
|
||
|
case WhenSecond:
|
||
|
h.interval = 1
|
||
|
h.suffix = "2006-01-02_15-04-05"
|
||
|
case WhenMinute:
|
||
|
h.interval = 60
|
||
|
h.suffix = "2006-01-02_15-04"
|
||
|
case WhenHour:
|
||
|
h.interval = 3600
|
||
|
h.suffix = "2006-01-02_15"
|
||
|
case WhenDay:
|
||
|
h.interval = 3600 * 24
|
||
|
h.suffix = "2006-01-02"
|
||
|
default:
|
||
|
e := fmt.Errorf("invalid when_rotate: %d", when)
|
||
|
panic(e)
|
||
|
}
|
||
|
|
||
|
h.interval = h.interval * int64(interval)
|
||
|
|
||
|
var err error
|
||
|
h.fd, err = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
fInfo, _ := h.fd.Stat()
|
||
|
h.rolloverAt = fInfo.ModTime().Unix() + h.interval
|
||
|
h.msg = make(chan []byte, msgNum)
|
||
|
h.quit = make(chan bool, 1)
|
||
|
|
||
|
go h.run()
|
||
|
|
||
|
return h, nil
|
||
|
}
|
||
|
|
||
|
func NewDefaultTimeRotatingFileHandler(baseName string, when int8, interval int) (*TimeRotatingFileHandler, error) {
|
||
|
return NewTimeRotatingFileHandler(baseName, when, interval, 1024)
|
||
|
}
|
||
|
|
||
|
func (h *TimeRotatingFileHandler) doRollover() {
|
||
|
//refer http://hg.python.org/cpython/file/2.7/Lib/logging/handlers.py
|
||
|
now := time.Now()
|
||
|
|
||
|
if h.rolloverAt <= now.Unix() {
|
||
|
fName := h.baseName + now.Format(h.suffix)
|
||
|
h.fd.Close()
|
||
|
e := os.Rename(h.baseName, fName)
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
|
||
|
h.fd, _ = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||
|
|
||
|
h.rolloverAt = time.Now().Unix() + h.interval
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (h *TimeRotatingFileHandler) run() {
|
||
|
for {
|
||
|
select {
|
||
|
case m := <-h.msg:
|
||
|
h.doRollover()
|
||
|
if h.fd != nil {
|
||
|
_, e := h.fd.Write(m)
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
}
|
||
|
case <-h.quit:
|
||
|
if h.fd != nil {
|
||
|
h.fd.Close()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|