go/log/handler.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
}
}
}