summaryrefslogtreecommitdiffstats
path: root/modules/log
diff options
context:
space:
mode:
Diffstat (limited to 'modules/log')
-rw-r--r--modules/log/console.go73
-rw-r--r--modules/log/file.go237
-rw-r--r--modules/log/log.go251
3 files changed, 538 insertions, 23 deletions
diff --git a/modules/log/console.go b/modules/log/console.go
new file mode 100644
index 0000000000..92d6c2a406
--- /dev/null
+++ b/modules/log/console.go
@@ -0,0 +1,73 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package log
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "runtime"
+)
+
+type Brush func(string) string
+
+func NewBrush(color string) Brush {
+ pre := "\033["
+ reset := "\033[0m"
+ return func(text string) string {
+ return pre + color + "m" + text + reset
+ }
+}
+
+var colors = []Brush{
+ NewBrush("1;36"), // Trace cyan
+ NewBrush("1;34"), // Debug blue
+ NewBrush("1;32"), // Info green
+ NewBrush("1;33"), // Warn yellow
+ NewBrush("1;31"), // Error red
+ NewBrush("1;35"), // Critical purple
+ NewBrush("1;31"), // Fatal red
+}
+
+// ConsoleWriter implements LoggerInterface and writes messages to terminal.
+type ConsoleWriter struct {
+ lg *log.Logger
+ Level int `json:"level"`
+}
+
+// create ConsoleWriter returning as LoggerInterface.
+func NewConsole() LoggerInterface {
+ return &ConsoleWriter{
+ lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
+ Level: TRACE,
+ }
+}
+
+func (cw *ConsoleWriter) Init(config string) error {
+ return json.Unmarshal([]byte(config), cw)
+}
+
+func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error {
+ if cw.Level > level {
+ return nil
+ }
+ if runtime.GOOS == "windows" {
+ cw.lg.Println(msg)
+ } else {
+ cw.lg.Println(colors[level](msg))
+ }
+ return nil
+}
+
+func (_ *ConsoleWriter) Destroy() {
+}
+
+func (_ *ConsoleWriter) Flush() {
+
+}
+
+func init() {
+ Register("console", NewConsole)
+}
diff --git a/modules/log/file.go b/modules/log/file.go
new file mode 100644
index 0000000000..cce5352969
--- /dev/null
+++ b/modules/log/file.go
@@ -0,0 +1,237 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package log
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+)
+
+// FileLogWriter implements LoggerInterface.
+// It writes messages by lines limit, file size limit, or time frequency.
+type FileLogWriter struct {
+ *log.Logger
+ mw *MuxWriter
+ // The opened file
+ Filename string `json:"filename"`
+
+ Maxlines int `json:"maxlines"`
+ maxlines_curlines int
+
+ // Rotate at size
+ Maxsize int `json:"maxsize"`
+ maxsize_cursize int
+
+ // Rotate daily
+ Daily bool `json:"daily"`
+ Maxdays int64 `json:"maxdays`
+ daily_opendate int
+
+ Rotate bool `json:"rotate"`
+
+ startLock sync.Mutex // Only one log can write to the file
+
+ Level int `json:"level"`
+}
+
+// an *os.File writer with locker.
+type MuxWriter struct {
+ sync.Mutex
+ fd *os.File
+}
+
+// write to os.File.
+func (l *MuxWriter) Write(b []byte) (int, error) {
+ l.Lock()
+ defer l.Unlock()
+ return l.fd.Write(b)
+}
+
+// set os.File in writer.
+func (l *MuxWriter) SetFd(fd *os.File) {
+ if l.fd != nil {
+ l.fd.Close()
+ }
+ l.fd = fd
+}
+
+// create a FileLogWriter returning as LoggerInterface.
+func NewFileWriter() LoggerInterface {
+ w := &FileLogWriter{
+ Filename: "",
+ Maxlines: 1000000,
+ Maxsize: 1 << 28, //256 MB
+ Daily: true,
+ Maxdays: 7,
+ Rotate: true,
+ Level: TRACE,
+ }
+ // use MuxWriter instead direct use os.File for lock write when rotate
+ w.mw = new(MuxWriter)
+ // set MuxWriter as Logger's io.Writer
+ w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
+ return w
+}
+
+// Init file logger with json config.
+// config like:
+// {
+// "filename":"log/gogs.log",
+// "maxlines":10000,
+// "maxsize":1<<30,
+// "daily":true,
+// "maxdays":15,
+// "rotate":true
+// }
+func (w *FileLogWriter) Init(config string) error {
+ if err := json.Unmarshal([]byte(config), w); err != nil {
+ return err
+ }
+ if len(w.Filename) == 0 {
+ return errors.New("config must have filename")
+ }
+ return w.StartLogger()
+}
+
+// start file logger. create log file and set to locker-inside file writer.
+func (w *FileLogWriter) StartLogger() error {
+ fd, err := w.createLogFile()
+ if err != nil {
+ return err
+ }
+ w.mw.SetFd(fd)
+ if err = w.initFd(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (w *FileLogWriter) docheck(size int) {
+ w.startLock.Lock()
+ defer w.startLock.Unlock()
+ if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
+ (w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
+ (w.Daily && time.Now().Day() != w.daily_opendate)) {
+ if err := w.DoRotate(); err != nil {
+ fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
+ return
+ }
+ }
+ w.maxlines_curlines++
+ w.maxsize_cursize += size
+}
+
+// write logger message into file.
+func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error {
+ if level < w.Level {
+ return nil
+ }
+ n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
+ w.docheck(n)
+ w.Logger.Println(msg)
+ return nil
+}
+
+func (w *FileLogWriter) createLogFile() (*os.File, error) {
+ // Open the log file
+ return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
+}
+
+func (w *FileLogWriter) initFd() error {
+ fd := w.mw.fd
+ finfo, err := fd.Stat()
+ if err != nil {
+ return fmt.Errorf("get stat: %s\n", err)
+ }
+ w.maxsize_cursize = int(finfo.Size())
+ w.daily_opendate = time.Now().Day()
+ if finfo.Size() > 0 {
+ content, err := ioutil.ReadFile(w.Filename)
+ if err != nil {
+ return err
+ }
+ w.maxlines_curlines = len(strings.Split(string(content), "\n"))
+ } else {
+ w.maxlines_curlines = 0
+ }
+ return nil
+}
+
+// DoRotate means it need to write file in new file.
+// new file name like xx.log.2013-01-01.2
+func (w *FileLogWriter) DoRotate() error {
+ _, err := os.Lstat(w.Filename)
+ if err == nil { // file exists
+ // Find the next available number
+ num := 1
+ fname := ""
+ for ; err == nil && num <= 999; num++ {
+ fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
+ _, err = os.Lstat(fname)
+ }
+ // return error if the last file checked still existed
+ if err == nil {
+ return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename)
+ }
+
+ // block Logger's io.Writer
+ w.mw.Lock()
+ defer w.mw.Unlock()
+
+ fd := w.mw.fd
+ fd.Close()
+
+ // close fd before rename
+ // Rename the file to its newfound home
+ if err = os.Rename(w.Filename, fname); err != nil {
+ return fmt.Errorf("Rotate: %s\n", err)
+ }
+
+ // re-start logger
+ if err = w.StartLogger(); err != nil {
+ return fmt.Errorf("Rotate StartLogger: %s\n", err)
+ }
+
+ go w.deleteOldLog()
+ }
+
+ return nil
+}
+
+func (w *FileLogWriter) deleteOldLog() {
+ dir := filepath.Dir(w.Filename)
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
+ if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
+ os.Remove(path)
+ }
+ }
+ return nil
+ })
+}
+
+// destroy file logger, close file writer.
+func (w *FileLogWriter) Destroy() {
+ w.mw.fd.Close()
+}
+
+// flush file logger.
+// there are no buffering messages in file logger in memory.
+// flush file means sync file from disk.
+func (w *FileLogWriter) Flush() {
+ w.mw.fd.Sync()
+}
+
+func init() {
+ Register("file", NewFileWriter)
+}
diff --git a/modules/log/log.go b/modules/log/log.go
index 24f0442d1e..6ca6b5b0b1 100644
--- a/modules/log/log.go
+++ b/modules/log/log.go
@@ -2,32 +2,29 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-// Package log is a wrapper of logs for short calling name.
package log
import (
"fmt"
"os"
"path"
-
- "github.com/gogits/logs"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
)
var (
- loggers []*logs.BeeLogger
- GitLogger *logs.BeeLogger
+ loggers []*Logger
+ GitLogger *Logger
)
-func init() {
- NewLogger(0, "console", `{"level": 0}`)
-}
-
func NewLogger(bufLen int64, mode, config string) {
- logger := logs.NewLogger(bufLen)
+ logger := newLogger(bufLen)
isExist := false
for _, l := range loggers {
- if l.Adapter == mode {
+ if l.adapter == mode {
isExist = true
l = logger
}
@@ -35,15 +32,14 @@ func NewLogger(bufLen int64, mode, config string) {
if !isExist {
loggers = append(loggers, logger)
}
- logger.SetLogFuncCallDepth(3)
if err := logger.SetLogger(mode, config); err != nil {
- Fatal("Fail to set logger(%s): %v", mode, err)
+ Fatal(1, "Fail to set logger(%s): %v", mode, err)
}
}
func NewGitLogger(logPath string) {
os.MkdirAll(path.Dir(logPath), os.ModePerm)
- GitLogger = logs.NewLogger(0)
+ GitLogger = newLogger(0)
GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath))
}
@@ -65,28 +61,237 @@ func Info(format string, v ...interface{}) {
}
}
-func Error(format string, v ...interface{}) {
+func Warn(format string, v ...interface{}) {
for _, logger := range loggers {
- logger.Error(format, v...)
+ logger.Warn(format, v...)
}
}
-func Warn(format string, v ...interface{}) {
+func Error(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
- logger.Warn(format, v...)
+ logger.Error(skip, format, v...)
}
}
-func Critical(format string, v ...interface{}) {
+func Critical(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
- logger.Critical(format, v...)
+ logger.Critical(skip, format, v...)
}
}
-func Fatal(format string, v ...interface{}) {
- Error(format, v...)
+func Fatal(skip int, format string, v ...interface{}) {
+ Error(skip, format, v...)
for _, l := range loggers {
l.Close()
}
- os.Exit(2)
+ os.Exit(1)
+}
+
+// .___ _______________________________________________________ _________ ___________
+// | |\ \__ ___/\_ _____/\______ \_ _____/ _ \ \_ ___ \\_ _____/
+// | |/ | \| | | __)_ | _/| __)/ /_\ \/ \ \/ | __)_
+// | / | \ | | \ | | \| \/ | \ \____| \
+// |___\____|__ /____| /_______ / |____|_ /\___ /\____|__ /\______ /_______ /
+// \/ \/ \/ \/ \/ \/ \/
+
+type LogLevel int
+
+const (
+ TRACE = iota
+ DEBUG
+ INFO
+ WARN
+ ERROR
+ CRITICAL
+ FATAL
+)
+
+// LoggerInterface represents behaviors of a logger provider.
+type LoggerInterface interface {
+ Init(config string) error
+ WriteMsg(msg string, skip, level int) error
+ Destroy()
+ Flush()
+}
+
+type loggerType func() LoggerInterface
+
+var adapters = make(map[string]loggerType)
+
+// Register registers given logger provider to adapters.
+func Register(name string, log loggerType) {
+ if log == nil {
+ panic("log: register provider is nil")
+ }
+ if _, dup := adapters[name]; dup {
+ panic("log: register called twice for provider \"" + name + "\"")
+ }
+ adapters[name] = log
+}
+
+type logMsg struct {
+ skip, level int
+ msg string
+}
+
+// Logger is default logger in beego application.
+// it can contain several providers and log message into all providers.
+type Logger struct {
+ adapter string
+ lock sync.Mutex
+ level int
+ msg chan *logMsg
+ outputs map[string]LoggerInterface
+ quit chan bool
+}
+
+// newLogger initializes and returns a new logger.
+func newLogger(buffer int64) *Logger {
+ l := &Logger{
+ msg: make(chan *logMsg, buffer),
+ outputs: make(map[string]LoggerInterface),
+ quit: make(chan bool),
+ }
+ go l.StartLogger()
+ return l
+}
+
+// SetLogger sets new logger instanse with given logger adapter and config.
+func (l *Logger) SetLogger(adapter string, config string) error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+ if log, ok := adapters[adapter]; ok {
+ lg := log()
+ if err := lg.Init(config); err != nil {
+ return err
+ }
+ l.outputs[adapter] = lg
+ l.adapter = adapter
+ } else {
+ panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
+ }
+ return nil
+}
+
+// DelLogger removes a logger adapter instance.
+func (l *Logger) DelLogger(adapter string) error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+ if lg, ok := l.outputs[adapter]; ok {
+ lg.Destroy()
+ delete(l.outputs, adapter)
+ } else {
+ panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
+ }
+ return nil
+}
+
+func (l *Logger) writerMsg(skip, level int, msg string) error {
+ if l.level > level {
+ return nil
+ }
+ lm := &logMsg{
+ skip: skip,
+ level: level,
+ }
+
+ // Only error information needs locate position for debugging.
+ if lm.level >= ERROR {
+ pc, file, line, ok := runtime.Caller(skip)
+ if ok {
+ // Get caller function name.
+ fn := runtime.FuncForPC(pc)
+ var fnName string
+ if fn == nil {
+ fnName = "?()"
+ } else {
+ fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
+ }
+
+ lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg)
+ } else {
+ lm.msg = msg
+ }
+ } else {
+ lm.msg = msg
+ }
+ l.msg <- lm
+ return nil
+}
+
+// StartLogger starts logger chan reading.
+func (l *Logger) StartLogger() {
+ for {
+ select {
+ case bm := <-l.msg:
+ for _, l := range l.outputs {
+ l.WriteMsg(bm.msg, bm.skip, bm.level)
+ }
+ case <-l.quit:
+ return
+ }
+ }
+}
+
+// Flush flushs all chan data.
+func (l *Logger) Flush() {
+ for _, l := range l.outputs {
+ l.Flush()
+ }
+}
+
+// Close closes logger, flush all chan data and destroy all adapter instances.
+func (l *Logger) Close() {
+ l.quit <- true
+ for {
+ if len(l.msg) > 0 {
+ bm := <-l.msg
+ for _, l := range l.outputs {
+ l.WriteMsg(bm.msg, bm.skip, bm.level)
+ }
+ } else {
+ break
+ }
+ }
+ for _, l := range l.outputs {
+ l.Flush()
+ l.Destroy()
+ }
+}
+
+func (l *Logger) Trace(format string, v ...interface{}) {
+ msg := fmt.Sprintf("[T] "+format, v...)
+ l.writerMsg(0, TRACE, msg)
+}
+
+func (l *Logger) Debug(format string, v ...interface{}) {
+ msg := fmt.Sprintf("[D] "+format, v...)
+ l.writerMsg(0, DEBUG, msg)
+}
+
+func (l *Logger) Info(format string, v ...interface{}) {
+ msg := fmt.Sprintf("[I] "+format, v...)
+ l.writerMsg(0, INFO, msg)
+}
+
+func (l *Logger) Warn(format string, v ...interface{}) {
+ msg := fmt.Sprintf("[W] "+format, v...)
+ l.writerMsg(0, WARN, msg)
+}
+
+func (l *Logger) Error(skip int, format string, v ...interface{}) {
+ msg := fmt.Sprintf("[E] "+format, v...)
+ l.writerMsg(skip, ERROR, msg)
+}
+
+func (l *Logger) Critical(skip int, format string, v ...interface{}) {
+ msg := fmt.Sprintf("[C] "+format, v...)
+ l.writerMsg(skip, CRITICAL, msg)
+}
+
+func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
+ msg := fmt.Sprintf("[F] "+format, v...)
+ l.writerMsg(skip, FATAL, msg)
+ l.Close()
+ os.Exit(1)
}