1
0
mirror of https://github.com/astaxie/beego.git synced 2024-12-23 11:10:50 +00:00
Beego/pkg/infrastructure/logs/log.go

782 lines
17 KiB
Go
Raw Normal View History

2020-07-22 14:50:08 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package logs provide a general log interface
// Usage:
//
// import "github.com/astaxie/beego/logs"
//
// log := NewLogger(10000)
// log.SetLogger("console", "")
//
// > the first params stand for how many channel
//
// Use it like this:
//
// log.Trace("trace")
// log.Info("info")
// log.Warn("warning")
// log.Debug("debug")
// log.Critical("critical")
//
// more docs http://beego.me/docs/module/logs.md
package logs
import (
"fmt"
"log"
"os"
"runtime"
"strings"
"sync"
"time"
2020-08-28 17:00:45 +00:00
2020-09-11 13:10:12 +00:00
"github.com/pkg/errors"
2020-07-22 14:50:08 +00:00
)
// RFC5424 log message levels.
const (
LevelEmergency = iota
LevelAlert
LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug
)
// levelLogLogger is defined to implement log.Logger
// the real log level will be LevelEmergency
const levelLoggerImpl = -1
// Name for adapter with beego official support
const (
AdapterConsole = "console"
AdapterFile = "file"
AdapterMultiFile = "multifile"
AdapterMail = "smtp"
AdapterConn = "conn"
AdapterEs = "es"
AdapterJianLiao = "jianliao"
AdapterSlack = "slack"
AdapterAliLS = "alils"
)
// Legacy log level constants to ensure backwards compatibility.
const (
LevelInfo = LevelInformational
LevelTrace = LevelDebug
LevelWarn = LevelWarning
)
type newLoggerFunc func() Logger
// Logger defines the behavior of a log provider.
type Logger interface {
2020-09-11 13:10:12 +00:00
Init(config string) error
WriteMsg(lm *LogMsg) error
2020-07-22 14:50:08 +00:00
Destroy()
Flush()
2020-09-11 13:10:12 +00:00
SetFormatter(f LogFormatter)
2020-07-22 14:50:08 +00:00
}
var adapters = make(map[string]newLoggerFunc)
var levelPrefix = [LevelDebug + 1]string{"[M]", "[A]", "[C]", "[E]", "[W]", "[N]", "[I]", "[D]"}
// Register makes a log provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, log newLoggerFunc) {
if log == nil {
panic("logs: Register provide is nil")
}
if _, dup := adapters[name]; dup {
panic("logs: Register called twice for provider " + name)
}
adapters[name] = log
}
// BeeLogger is default logger in beego application.
2020-08-06 15:07:18 +00:00
// Can contain several providers and log message into all providers.
2020-07-22 14:50:08 +00:00
type BeeLogger struct {
lock sync.Mutex
level int
init bool
enableFuncCallDepth bool
loggerFuncCallDepth int
enableFullFilePath bool
2020-07-22 14:50:08 +00:00
asynchronous bool
prefix string
msgChanLen int64
msgChan chan *LogMsg
2020-07-22 14:50:08 +00:00
signalChan chan string
wg sync.WaitGroup
outputs []*nameLogger
2020-09-11 13:10:12 +00:00
globalFormatter string
2020-07-22 14:50:08 +00:00
}
const defaultAsyncMsgLen = 1e3
type nameLogger struct {
Logger
name string
}
var logMsgPool *sync.Pool
// NewLogger returns a new BeeLogger.
2020-08-06 15:07:18 +00:00
// channelLen: the number of messages in chan(used where asynchronous is true).
2020-07-22 14:50:08 +00:00
// if the buffering chan is full, logger adapters write to file or other way.
func NewLogger(channelLens ...int64) *BeeLogger {
bl := new(BeeLogger)
bl.level = LevelDebug
bl.loggerFuncCallDepth = 2
bl.msgChanLen = append(channelLens, 0)[0]
if bl.msgChanLen <= 0 {
bl.msgChanLen = defaultAsyncMsgLen
}
bl.signalChan = make(chan string, 1)
bl.setLogger(AdapterConsole)
return bl
}
2020-08-06 15:07:18 +00:00
// Async sets the log to asynchronous and start the goroutine
2020-07-22 14:50:08 +00:00
func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger {
bl.lock.Lock()
defer bl.lock.Unlock()
if bl.asynchronous {
return bl
}
bl.asynchronous = true
if len(msgLen) > 0 && msgLen[0] > 0 {
bl.msgChanLen = msgLen[0]
}
bl.msgChan = make(chan *LogMsg, bl.msgChanLen)
2020-07-22 14:50:08 +00:00
logMsgPool = &sync.Pool{
New: func() interface{} {
return &LogMsg{}
2020-07-22 14:50:08 +00:00
},
}
bl.wg.Add(1)
go bl.startLogger()
return bl
}
// SetLogger provides a given logger adapter into BeeLogger with config string.
2020-08-06 15:07:18 +00:00
// config must in in JSON format like {"interval":360}}
2020-07-22 14:50:08 +00:00
func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
config := append(configs, "{}")[0]
for _, l := range bl.outputs {
if l.name == adapterName {
return fmt.Errorf("logs: duplicate adaptername %q (you have set this logger before)", adapterName)
}
}
logAdapter, ok := adapters[adapterName]
if !ok {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName)
}
lg := logAdapter()
// Global formatter overrides the default set formatter
2020-09-11 13:10:12 +00:00
if len(bl.globalFormatter) > 0 {
fmtr, ok := GetFormatter(bl.globalFormatter)
if !ok {
return errors.New(fmt.Sprintf("the formatter with name: %s not found", bl.globalFormatter))
}
lg.SetFormatter(fmtr)
}
2020-09-11 13:10:12 +00:00
err := lg.Init(config)
2020-07-22 14:50:08 +00:00
if err != nil {
fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error())
return err
}
bl.outputs = append(bl.outputs, &nameLogger{name: adapterName, Logger: lg})
return nil
}
// SetLogger provides a given logger adapter into BeeLogger with config string.
2020-08-06 15:07:18 +00:00
// config must in in JSON format like {"interval":360}}
2020-07-22 14:50:08 +00:00
func (bl *BeeLogger) SetLogger(adapterName string, configs ...string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
if !bl.init {
bl.outputs = []*nameLogger{}
bl.init = true
}
return bl.setLogger(adapterName, configs...)
}
2020-08-06 15:07:18 +00:00
// DelLogger removes a logger adapter in BeeLogger.
2020-07-22 14:50:08 +00:00
func (bl *BeeLogger) DelLogger(adapterName string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
outputs := []*nameLogger{}
for _, lg := range bl.outputs {
if lg.name == adapterName {
lg.Destroy()
} else {
outputs = append(outputs, lg)
}
}
if len(outputs) == len(bl.outputs) {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName)
}
bl.outputs = outputs
return nil
}
func (bl *BeeLogger) writeToLoggers(lm *LogMsg) {
2020-07-22 14:50:08 +00:00
for _, l := range bl.outputs {
err := l.WriteMsg(lm)
2020-07-22 14:50:08 +00:00
if err != nil {
fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
}
}
}
func (bl *BeeLogger) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
// writeMsg will always add a '\n' character
if p[len(p)-1] == '\n' {
p = p[0 : len(p)-1]
}
lm := &LogMsg{
Msg: string(p),
Level: levelLoggerImpl,
}
2020-07-22 14:50:08 +00:00
// set levelLoggerImpl to ensure all log message will be write out
err = bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
if err == nil {
return len(p), err
}
return 0, err
}
2020-09-11 13:10:12 +00:00
func (bl *BeeLogger) writeMsg(lm *LogMsg) error {
2020-07-22 14:50:08 +00:00
if !bl.init {
bl.lock.Lock()
bl.setLogger(AdapterConsole)
bl.lock.Unlock()
}
var (
file string
line int
ok bool
)
2020-09-11 13:10:12 +00:00
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth)
if !ok {
file = "???"
line = 0
2020-07-22 14:50:08 +00:00
}
2020-09-11 13:10:12 +00:00
lm.FilePath = file
lm.LineNumber = line
lm.enableFullFilePath = bl.enableFullFilePath
lm.enableFuncCallDepth = bl.enableFuncCallDepth
2020-07-22 14:50:08 +00:00
2020-09-10 15:31:49 +00:00
// set level info in front of filename info
if lm.Level == levelLoggerImpl {
2020-07-22 14:50:08 +00:00
// set to emergency to ensure all log will be print out correctly
lm.Level = LevelEmergency
2020-07-22 14:50:08 +00:00
}
if bl.asynchronous {
logM := logMsgPool.Get().(*LogMsg)
logM.Level = lm.Level
logM.Msg = lm.Msg
logM.When = lm.When
2020-09-11 13:10:12 +00:00
logM.Args = lm.Args
logM.FilePath = lm.FilePath
logM.LineNumber = lm.LineNumber
logM.Prefix = lm.Prefix
2020-07-22 14:50:08 +00:00
if bl.outputs != nil {
bl.msgChan <- lm
} else {
logMsgPool.Put(lm)
}
} else {
bl.writeToLoggers(lm)
2020-07-22 14:50:08 +00:00
}
return nil
}
2020-08-06 15:07:18 +00:00
// SetLevel sets log message level.
2020-07-22 14:50:08 +00:00
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
2020-08-06 15:07:18 +00:00
// log providers will not be sent the message.
2020-07-22 14:50:08 +00:00
func (bl *BeeLogger) SetLevel(l int) {
bl.level = l
}
// GetLevel Get Current log message level.
func (bl *BeeLogger) GetLevel() int {
return bl.level
}
// SetLogFuncCallDepth set log funcCallDepth
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
bl.loggerFuncCallDepth = d
}
// GetLogFuncCallDepth return log funcCallDepth for wrapper
func (bl *BeeLogger) GetLogFuncCallDepth() int {
return bl.loggerFuncCallDepth
}
// EnableFuncCallDepth enable log funcCallDepth
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
bl.enableFuncCallDepth = b
}
// set prefix
func (bl *BeeLogger) SetPrefix(s string) {
bl.prefix = s
}
// start logger chan reading.
// when chan is not empty, write logs.
func (bl *BeeLogger) startLogger() {
gameOver := false
for {
select {
case bm := <-bl.msgChan:
bl.writeToLoggers(bm)
2020-07-22 14:50:08 +00:00
logMsgPool.Put(bm)
case sg := <-bl.signalChan:
// Now should only send "flush" or "close" to bl.signalChan
bl.flush()
if sg == "close" {
for _, l := range bl.outputs {
l.Destroy()
}
bl.outputs = nil
gameOver = true
}
bl.wg.Done()
}
if gameOver {
break
}
}
}
2020-09-11 13:10:12 +00:00
func (bl *BeeLogger) setGlobalFormatter(fmtter string) error {
bl.globalFormatter = fmtter
return nil
}
// SetGlobalFormatter sets the global formatter for all log adapters
2020-09-11 13:10:12 +00:00
// don't forget to register the formatter by invoking RegisterFormatter
func SetGlobalFormatter(fmtter string) error {
return beeLogger.setGlobalFormatter(fmtter)
}
2020-07-22 14:50:08 +00:00
// Emergency Log EMERGENCY level message.
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
if LevelEmergency > bl.level {
return
}
lm := &LogMsg{
Level: LevelEmergency,
Msg: format,
When: time.Now(),
}
if len(v) > 0 {
lm.Msg = fmt.Sprintf(lm.Msg, v...)
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Alert Log ALERT level message.
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
if LevelAlert > bl.level {
return
}
lm := &LogMsg{
Level: LevelAlert,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Critical Log CRITICAL level message.
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
if LevelCritical > bl.level {
return
}
lm := &LogMsg{
Level: LevelCritical,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Error Log ERROR level message.
func (bl *BeeLogger) Error(format string, v ...interface{}) {
if LevelError > bl.level {
return
}
lm := &LogMsg{
Level: LevelError,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Warning Log WARNING level message.
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
if LevelWarn > bl.level {
return
}
lm := &LogMsg{
Level: LevelWarn,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Notice Log NOTICE level message.
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
if LevelNotice > bl.level {
return
}
lm := &LogMsg{
Level: LevelNotice,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Informational Log INFORMATIONAL level message.
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
if LevelInfo > bl.level {
return
}
lm := &LogMsg{
Level: LevelInfo,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Debug Log DEBUG level message.
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
if LevelDebug > bl.level {
return
}
lm := &LogMsg{
Level: LevelDebug,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Warn Log WARN level message.
// compatibility alias for Warning()
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
if LevelWarn > bl.level {
return
}
lm := &LogMsg{
Level: LevelWarn,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Info Log INFO level message.
// compatibility alias for Informational()
func (bl *BeeLogger) Info(format string, v ...interface{}) {
if LevelInfo > bl.level {
return
}
lm := &LogMsg{
Level: LevelInfo,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Trace Log TRACE level message.
// compatibility alias for Debug()
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
if LevelDebug > bl.level {
return
}
lm := &LogMsg{
Level: LevelDebug,
Msg: format,
When: time.Now(),
2020-09-11 13:10:12 +00:00
Args: v,
}
bl.writeMsg(lm)
2020-07-22 14:50:08 +00:00
}
// Flush flush all chan data.
func (bl *BeeLogger) Flush() {
if bl.asynchronous {
bl.signalChan <- "flush"
bl.wg.Wait()
bl.wg.Add(1)
return
}
bl.flush()
}
// Close close logger, flush all chan data and destroy all adapters in BeeLogger.
func (bl *BeeLogger) Close() {
if bl.asynchronous {
bl.signalChan <- "close"
bl.wg.Wait()
close(bl.msgChan)
} else {
bl.flush()
for _, l := range bl.outputs {
l.Destroy()
}
bl.outputs = nil
}
close(bl.signalChan)
}
// Reset close all outputs, and set bl.outputs to nil
func (bl *BeeLogger) Reset() {
bl.Flush()
for _, l := range bl.outputs {
l.Destroy()
}
bl.outputs = nil
}
func (bl *BeeLogger) flush() {
if bl.asynchronous {
for {
if len(bl.msgChan) > 0 {
bm := <-bl.msgChan
bl.writeToLoggers(bm)
2020-07-22 14:50:08 +00:00
logMsgPool.Put(bm)
continue
}
break
}
}
for _, l := range bl.outputs {
l.Flush()
}
}
// beeLogger references the used application logger.
var beeLogger = NewLogger()
// GetBeeLogger returns the default BeeLogger
func GetBeeLogger() *BeeLogger {
return beeLogger
}
var beeLoggerMap = struct {
sync.RWMutex
logs map[string]*log.Logger
}{
logs: map[string]*log.Logger{},
}
// GetLogger returns the default BeeLogger
func GetLogger(prefixes ...string) *log.Logger {
prefix := append(prefixes, "")[0]
if prefix != "" {
prefix = fmt.Sprintf(`[%s] `, strings.ToUpper(prefix))
}
beeLoggerMap.RLock()
l, ok := beeLoggerMap.logs[prefix]
if ok {
beeLoggerMap.RUnlock()
return l
}
beeLoggerMap.RUnlock()
beeLoggerMap.Lock()
defer beeLoggerMap.Unlock()
l, ok = beeLoggerMap.logs[prefix]
if !ok {
l = log.New(beeLogger, prefix, 0)
beeLoggerMap.logs[prefix] = l
}
return l
}
// EnableFullFilePath enables full file path logging. Disabled by default
// e.g "/home/Documents/GitHub/beego/mainapp/" instead of "mainapp"
func EnableFullFilePath(b bool) {
beeLogger.enableFullFilePath = b
}
2020-07-22 14:50:08 +00:00
// Reset will remove all the adapter
func Reset() {
beeLogger.Reset()
}
// Async set the beelogger with Async mode and hold msglen messages
func Async(msgLen ...int64) *BeeLogger {
return beeLogger.Async(msgLen...)
}
// SetLevel sets the global log level used by the simple logger.
func SetLevel(l int) {
beeLogger.SetLevel(l)
}
// SetPrefix sets the prefix
func SetPrefix(s string) {
beeLogger.SetPrefix(s)
}
// EnableFuncCallDepth enable log funcCallDepth
func EnableFuncCallDepth(b bool) {
beeLogger.enableFuncCallDepth = b
}
// SetLogFuncCall set the CallDepth, default is 4
func SetLogFuncCall(b bool) {
beeLogger.EnableFuncCallDepth(b)
beeLogger.SetLogFuncCallDepth(4)
}
// SetLogFuncCallDepth set log funcCallDepth
func SetLogFuncCallDepth(d int) {
beeLogger.loggerFuncCallDepth = d
}
// SetLogger sets a new logger.
func SetLogger(adapter string, config ...string) error {
return beeLogger.SetLogger(adapter, config...)
}
// Emergency logs a message at emergency level.
func Emergency(f interface{}, v ...interface{}) {
beeLogger.Emergency(formatLog(f, v...))
}
// Alert logs a message at alert level.
func Alert(f interface{}, v ...interface{}) {
beeLogger.Alert(formatLog(f, v...))
}
// Critical logs a message at critical level.
func Critical(f interface{}, v ...interface{}) {
beeLogger.Critical(formatLog(f, v...))
}
// Error logs a message at error level.
func Error(f interface{}, v ...interface{}) {
beeLogger.Error(formatLog(f, v...))
}
// Warning logs a message at warning level.
func Warning(f interface{}, v ...interface{}) {
beeLogger.Warn(formatLog(f, v...))
}
// Warn compatibility alias for Warning()
func Warn(f interface{}, v ...interface{}) {
beeLogger.Warn(formatLog(f, v...))
}
// Notice logs a message at notice level.
func Notice(f interface{}, v ...interface{}) {
beeLogger.Notice(formatLog(f, v...))
}
// Informational logs a message at info level.
func Informational(f interface{}, v ...interface{}) {
beeLogger.Info(formatLog(f, v...))
}
// Info compatibility alias for Warning()
func Info(f interface{}, v ...interface{}) {
beeLogger.Info(formatLog(f, v...))
}
// Debug logs a message at debug level.
func Debug(f interface{}, v ...interface{}) {
beeLogger.Debug(formatLog(f, v...))
}
// Trace logs a message at trace level.
// compatibility alias for Warning()
func Trace(f interface{}, v ...interface{}) {
beeLogger.Trace(formatLog(f, v...))
}
func formatLog(f interface{}, v ...interface{}) string {
var msg string
switch f.(type) {
case string:
msg = f.(string)
if len(v) == 0 {
return msg
}
if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
2020-09-10 15:31:49 +00:00
// format string
2020-07-22 14:50:08 +00:00
} else {
2020-09-10 15:31:49 +00:00
// do not contain format char
2020-07-22 14:50:08 +00:00
msg += strings.Repeat(" %v", len(v))
}
default:
msg = fmt.Sprint(f)
if len(v) == 0 {
return msg
}
msg += strings.Repeat(" %v", len(v))
}
return fmt.Sprintf(msg, v...)
}