diff --git a/log/handler.go b/log/handler.go new file mode 100644 index 0000000..2a05098 --- /dev/null +++ b/log/handler.go @@ -0,0 +1,208 @@ +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 + } + } +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000..45f9cc9 --- /dev/null +++ b/log/log.go @@ -0,0 +1,116 @@ +package log + +import ( + "fmt" + "log" + "os" +) + +const ( + LevelTrace = iota + LevelDebug + LevelInfo + LevelWarn + LevelError + LevelFatal +) + +var LevelName [6]string = [6]string{"Trace", "Debug", "Info", "Warn", "Error", "Fatal"} + +type QLogger struct { + logger *log.Logger + level int + handler Handler +} + +func New(handler Handler, flag int) *QLogger { + var l = new(QLogger) + l.logger = log.New(handler, "", flag) //log.LstdFlags|log.Lshortfile) + l.level = LevelInfo + l.handler = handler + + return l +} + +func NewDefault(handler Handler) *QLogger { + return New(handler, log.LstdFlags|log.Lshortfile) +} + +func newStdHandler() *StreamHandler { + h, _ := NewDefaultStreamHandler(os.Stdout) + return h +} + +var std = NewDefault(newStdHandler()) + +func (l *QLogger) Close() { + l.handler.Close() +} + +func (l *QLogger) SetLevel(level int) { + l.level = level +} + +func (l *QLogger) Output(callDepth int, level int, format string, v ...interface{}) { + if l.level <= level { + f := fmt.Sprintf("[%s] %s", LevelName[level], format) + s := fmt.Sprintf(f, v...) + l.logger.Output(callDepth, s) + } +} + +func (l *QLogger) Write(s string) { + l.logger.Output(3, s) +} + +func (l *QLogger) Trace(format string, v ...interface{}) { + l.Output(3, LevelTrace, format, v...) +} + +func (l *QLogger) Debug(format string, v ...interface{}) { + l.Output(3, LevelDebug, format, v...) +} + +func (l *QLogger) Info(format string, v ...interface{}) { + l.Output(3, LevelInfo, format, v...) +} + +func (l *QLogger) Warn(format string, v ...interface{}) { + l.Output(3, LevelWarn, format, v...) +} + +func (l *QLogger) Error(format string, v ...interface{}) { + l.Output(3, LevelError, format, v...) +} + +func (l *QLogger) Fatal(format string, v ...interface{}) { + l.Output(3, LevelFatal, format, v...) +} + +func SetLevel(level int) { + std.SetLevel(level) +} + +func Trace(format string, v ...interface{}) { + std.Output(3, LevelTrace, format, v...) +} + +func Debug(format string, v ...interface{}) { + std.Output(3, LevelDebug, format, v...) +} + +func Info(format string, v ...interface{}) { + std.Output(3, LevelInfo, format, v...) +} + +func Warn(format string, v ...interface{}) { + std.Output(3, LevelWarn, format, v...) +} + +func Error(format string, v ...interface{}) { + std.Output(3, LevelError, format, v...) +} + +func Fatal(format string, v ...interface{}) { + std.Output(3, LevelFatal, format, v...) +} diff --git a/log/log_test.go b/log/log_test.go new file mode 100644 index 0000000..6d70f06 --- /dev/null +++ b/log/log_test.go @@ -0,0 +1,16 @@ +package log + +import ( + "os" + "testing" +) + +func TestStdStreamLog(t *testing.T) { + h, _ := NewDefaultStreamHandler(os.Stdout) + s := NewDefault(h) + s.Info("hello world") + + s.Close() + + Info("hello world") +}