mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 14:20:54 +00:00
Merge pull request #4216 from flycash/ftr/log_format
Log format support
This commit is contained in:
commit
93736a8e66
@ -6,6 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/astaxie/beego/pkg/infrastructure/logs"
|
"github.com/astaxie/beego/pkg/infrastructure/logs"
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ type aliLSWriter struct {
|
|||||||
withMap bool
|
withMap bool
|
||||||
groupMap map[string]*LogGroup
|
groupMap map[string]*LogGroup
|
||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
|
customFormatter func(*logs.LogMsg) string
|
||||||
Config
|
Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +50,17 @@ func NewAliLS() logs.Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init parses config and initializes struct
|
// Init parses config and initializes struct
|
||||||
func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
func (c *aliLSWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := logs.GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
json.Unmarshal([]byte(jsonConfig), c)
|
json.Unmarshal([]byte(jsonConfig), c)
|
||||||
|
|
||||||
if c.FlushWhen > CacheSize {
|
if c.FlushWhen > CacheSize {
|
||||||
@ -63,11 +74,13 @@ func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
|||||||
AccessKeySecret: c.KeySecret,
|
AccessKeySecret: c.KeySecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.store, err = prj.GetLogStore(c.LogStore)
|
store, err := prj.GetLogStore(c.LogStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.store = store
|
||||||
|
|
||||||
// Create default Log Group
|
// Create default Log Group
|
||||||
c.group = append(c.group, &LogGroup{
|
c.group = append(c.group, &LogGroup{
|
||||||
Topic: proto.String(""),
|
Topic: proto.String(""),
|
||||||
@ -100,6 +113,10 @@ func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *aliLSWriter) Format(lm *logs.LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// WriteMsg writes a message in connection.
|
// WriteMsg writes a message in connection.
|
||||||
// If connection is down, try to re-connect.
|
// If connection is down, try to re-connect.
|
||||||
func (c *aliLSWriter) WriteMsg(lm *logs.LogMsg) error {
|
func (c *aliLSWriter) WriteMsg(lm *logs.LogMsg) error {
|
||||||
@ -117,20 +134,23 @@ func (c *aliLSWriter) WriteMsg(lm *logs.LogMsg) error {
|
|||||||
if len(strs) == 2 {
|
if len(strs) == 2 {
|
||||||
pos := strings.LastIndex(strs[0], " ")
|
pos := strings.LastIndex(strs[0], " ")
|
||||||
topic = strs[0][pos+1 : len(strs[0])]
|
topic = strs[0][pos+1 : len(strs[0])]
|
||||||
content = strs[0][0:pos] + strs[1]
|
|
||||||
lg = c.groupMap[topic]
|
lg = c.groupMap[topic]
|
||||||
}
|
}
|
||||||
|
|
||||||
// send to empty Topic
|
// send to empty Topic
|
||||||
if lg == nil {
|
if lg == nil {
|
||||||
content = lm.Msg
|
|
||||||
lg = c.group[0]
|
lg = c.group[0]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content = lm.Msg
|
|
||||||
lg = c.group[0]
|
lg = c.group[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.customFormatter != nil {
|
||||||
|
content = c.customFormatter(lm)
|
||||||
|
} else {
|
||||||
|
content = c.Format(lm)
|
||||||
|
}
|
||||||
|
|
||||||
c1 := &LogContent{
|
c1 := &LogContent{
|
||||||
Key: proto.String("msg"),
|
Key: proto.String("msg"),
|
||||||
Value: proto.String(content),
|
Value: proto.String(content),
|
||||||
|
@ -18,6 +18,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// connWriter implements LoggerInterface.
|
// connWriter implements LoggerInterface.
|
||||||
@ -25,6 +27,7 @@ import (
|
|||||||
type connWriter struct {
|
type connWriter struct {
|
||||||
lg *logWriter
|
lg *logWriter
|
||||||
innerWriter io.WriteCloser
|
innerWriter io.WriteCloser
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
ReconnectOnMsg bool `json:"reconnectOnMsg"`
|
ReconnectOnMsg bool `json:"reconnectOnMsg"`
|
||||||
Reconnect bool `json:"reconnect"`
|
Reconnect bool `json:"reconnect"`
|
||||||
Net string `json:"net"`
|
Net string `json:"net"`
|
||||||
@ -39,9 +42,24 @@ func NewConn() Logger {
|
|||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *connWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes a connection writer with json config.
|
// Init initializes a connection writer with json config.
|
||||||
// json config only needs they "level" key
|
// json config only needs they "level" key
|
||||||
func (c *connWriter) Init(jsonConfig string) error {
|
func (c *connWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return json.Unmarshal([]byte(jsonConfig), c)
|
return json.Unmarshal([]byte(jsonConfig), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +80,15 @@ func (c *connWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
defer c.innerWriter.Close()
|
defer c.innerWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.lg.writeln(lm)
|
msg := ""
|
||||||
|
if c.customFormatter != nil {
|
||||||
|
msg = c.customFormatter(lm)
|
||||||
|
} else {
|
||||||
|
msg = c.Format(lm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.lg.writeln(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
|
|
||||||
"github.com/shiena/ansicolor"
|
"github.com/shiena/ansicolor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,10 +50,21 @@ var colors = []brush{
|
|||||||
// consoleWriter implements LoggerInterface and writes messages to terminal.
|
// consoleWriter implements LoggerInterface and writes messages to terminal.
|
||||||
type consoleWriter struct {
|
type consoleWriter struct {
|
||||||
lg *logWriter
|
lg *logWriter
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color
|
Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *consoleWriter) Format(lm *LogMsg) string {
|
||||||
|
msg := lm.Msg
|
||||||
|
|
||||||
|
h, _, _ := formatTimeHeader(lm.When)
|
||||||
|
bytes := append(append(h, msg...), '\n')
|
||||||
|
|
||||||
|
return string(bytes)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// NewConsole creates ConsoleWriter returning as LoggerInterface.
|
// NewConsole creates ConsoleWriter returning as LoggerInterface.
|
||||||
func NewConsole() Logger {
|
func NewConsole() Logger {
|
||||||
cw := &consoleWriter{
|
cw := &consoleWriter{
|
||||||
@ -64,10 +77,22 @@ func NewConsole() Logger {
|
|||||||
|
|
||||||
// Init initianlizes the console logger.
|
// Init initianlizes the console logger.
|
||||||
// jsonConfig must be in the format '{"level":LevelTrace}'
|
// jsonConfig must be in the format '{"level":LevelTrace}'
|
||||||
func (c *consoleWriter) Init(jsonConfig string) error {
|
func (c *consoleWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(jsonConfig) == 0 {
|
if len(jsonConfig) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Unmarshal([]byte(jsonConfig), c)
|
return json.Unmarshal([]byte(jsonConfig), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +101,21 @@ func (c *consoleWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
if lm.Level > c.Level {
|
if lm.Level > c.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg := ""
|
||||||
|
|
||||||
if c.Colorful {
|
if c.Colorful {
|
||||||
lm.Msg = strings.Replace(lm.Msg, levelPrefix[lm.Level], colors[lm.Level](levelPrefix[lm.Level]), 1)
|
lm.Msg = strings.Replace(lm.Msg, levelPrefix[lm.Level], colors[lm.Level](levelPrefix[lm.Level]), 1)
|
||||||
}
|
}
|
||||||
c.lg.writeln(lm)
|
|
||||||
|
if c.customFormatter != nil {
|
||||||
|
msg = c.customFormatter(lm)
|
||||||
|
} else {
|
||||||
|
msg = c.Format(lm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
c.lg.writeln(msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/elastic/go-elasticsearch/v6/esapi"
|
"github.com/elastic/go-elasticsearch/v6/esapi"
|
||||||
|
|
||||||
"github.com/astaxie/beego/pkg/infrastructure/logs"
|
"github.com/astaxie/beego/pkg/infrastructure/logs"
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewES returns a LoggerInterface
|
// NewES returns a LoggerInterface
|
||||||
@ -33,11 +34,27 @@ type esLogger struct {
|
|||||||
*elasticsearch.Client
|
*elasticsearch.Client
|
||||||
DSN string `json:"dsn"`
|
DSN string `json:"dsn"`
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
|
customFormatter func(*logs.LogMsg) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *esLogger) Format(lm *logs.LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// {"dsn":"http://localhost:9200/","level":1}
|
// {"dsn":"http://localhost:9200/","level":1}
|
||||||
func (el *esLogger) Init(jsonconfig string) error {
|
func (el *esLogger) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
err := json.Unmarshal([]byte(jsonconfig), el)
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := logs.GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
el.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(jsonConfig), el)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -65,9 +82,16 @@ func (el *esLogger) WriteMsg(lm *logs.LogMsg) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg := ""
|
||||||
|
if el.customFormatter != nil {
|
||||||
|
msg = el.customFormatter(lm)
|
||||||
|
} else {
|
||||||
|
msg = el.Format(lm)
|
||||||
|
}
|
||||||
|
|
||||||
idx := LogDocument{
|
idx := LogDocument{
|
||||||
Timestamp: lm.When.Format(time.RFC3339),
|
Timestamp: lm.When.Format(time.RFC3339),
|
||||||
Msg: lm.Msg,
|
Msg: msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := json.Marshal(idx)
|
body, err := json.Marshal(idx)
|
||||||
|
@ -27,6 +27,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileLogWriter implements LoggerInterface.
|
// fileLogWriter implements LoggerInterface.
|
||||||
@ -60,6 +62,8 @@ type fileLogWriter struct {
|
|||||||
hourlyOpenDate int
|
hourlyOpenDate int
|
||||||
hourlyOpenTime time.Time
|
hourlyOpenTime time.Time
|
||||||
|
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
|
|
||||||
Rotate bool `json:"rotate"`
|
Rotate bool `json:"rotate"`
|
||||||
|
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
@ -89,6 +93,10 @@ func newFileWriter() Logger {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *fileLogWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// Init file logger with json config.
|
// Init file logger with json config.
|
||||||
// jsonConfig like:
|
// jsonConfig like:
|
||||||
// {
|
// {
|
||||||
@ -100,7 +108,18 @@ func newFileWriter() Logger {
|
|||||||
// "rotate":true,
|
// "rotate":true,
|
||||||
// "perm":"0600"
|
// "perm":"0600"
|
||||||
// }
|
// }
|
||||||
func (w *fileLogWriter) Init(jsonConfig string) error {
|
func (w *fileLogWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := json.Unmarshal([]byte(jsonConfig), w)
|
err := json.Unmarshal([]byte(jsonConfig), w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -149,7 +168,15 @@ func (w *fileLogWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
hd, d, h := formatTimeHeader(lm.When)
|
hd, d, h := formatTimeHeader(lm.When)
|
||||||
lm.Msg = string(hd) + lm.Msg + "\n"
|
msg := ""
|
||||||
|
|
||||||
|
if w.customFormatter != nil {
|
||||||
|
msg = w.customFormatter(lm)
|
||||||
|
} else {
|
||||||
|
msg = w.Format(lm)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = fmt.Sprintf("%s %s\n", string(hd), msg)
|
||||||
if w.Rotate {
|
if w.Rotate {
|
||||||
w.RLock()
|
w.RLock()
|
||||||
if w.needRotateHourly(len(lm.Msg), h) {
|
if w.needRotateHourly(len(lm.Msg), h) {
|
||||||
@ -176,10 +203,10 @@ func (w *fileLogWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Lock()
|
w.Lock()
|
||||||
_, err := w.fileWriter.Write([]byte(lm.Msg))
|
_, err := w.fileWriter.Write([]byte(msg))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.maxLinesCurLines++
|
w.maxLinesCurLines++
|
||||||
w.maxSizeCurSize += len(lm.Msg)
|
w.maxSizeCurSize += len(msg)
|
||||||
}
|
}
|
||||||
w.Unlock()
|
w.Unlock()
|
||||||
return err
|
return err
|
||||||
@ -302,7 +329,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
|||||||
|
|
||||||
_, err = os.Lstat(w.Filename)
|
_, err = os.Lstat(w.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//even if the file is not exist or other ,we should RESTART the logger
|
// even if the file is not exist or other ,we should RESTART the logger
|
||||||
goto RESTART_LOGGER
|
goto RESTART_LOGGER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
// JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
||||||
@ -15,6 +17,7 @@ type JLWriter struct {
|
|||||||
RedirectURL string `json:"redirecturl,omitempty"`
|
RedirectURL string `json:"redirecturl,omitempty"`
|
||||||
ImageURL string `json:"imageurl,omitempty"`
|
ImageURL string `json:"imageurl,omitempty"`
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newJLWriter creates jiaoliao writer.
|
// newJLWriter creates jiaoliao writer.
|
||||||
@ -23,8 +26,22 @@ func newJLWriter() Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init JLWriter with json config string
|
// Init JLWriter with json config string
|
||||||
func (s *JLWriter) Init(jsonconfig string) error {
|
func (s *JLWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
return json.Unmarshal([]byte(jsonconfig), s)
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal([]byte(jsonConfig), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JLWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg writes message in smtp writer.
|
// WriteMsg writes message in smtp writer.
|
||||||
@ -34,7 +51,14 @@ func (s *JLWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
text := fmt.Sprintf("%s %s", lm.When.Format("2006-01-02 15:04:05"), lm.Msg)
|
text := ""
|
||||||
|
|
||||||
|
if s.customFormatter != nil {
|
||||||
|
text = fmt.Sprintf("%s %s", lm.When.Format("2006-01-02 15:04:05"), s.customFormatter(lm))
|
||||||
|
} else {
|
||||||
|
text = fmt.Sprintf("%s %s", lm.When.Format("2006-01-02 15:04:05"), s.Format(lm))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Add("authorName", s.AuthorName)
|
form.Add("authorName", s.AuthorName)
|
||||||
|
@ -38,10 +38,13 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RFC5424 log message levels.
|
// RFC5424 log message levels.
|
||||||
@ -84,8 +87,9 @@ type newLoggerFunc func() Logger
|
|||||||
|
|
||||||
// Logger defines the behavior of a log provider.
|
// Logger defines the behavior of a log provider.
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Init(config string) error
|
Init(config string, opts ...utils.KV) error
|
||||||
WriteMsg(lm *LogMsg) error
|
WriteMsg(lm *LogMsg) error
|
||||||
|
Format(lm *LogMsg) string
|
||||||
Destroy()
|
Destroy()
|
||||||
Flush()
|
Flush()
|
||||||
}
|
}
|
||||||
@ -114,6 +118,7 @@ type BeeLogger struct {
|
|||||||
init bool
|
init bool
|
||||||
enableFuncCallDepth bool
|
enableFuncCallDepth bool
|
||||||
loggerFuncCallDepth int
|
loggerFuncCallDepth int
|
||||||
|
globalFormatter func(*LogMsg) string
|
||||||
enableFullFilePath bool
|
enableFullFilePath bool
|
||||||
asynchronous bool
|
asynchronous bool
|
||||||
prefix string
|
prefix string
|
||||||
@ -139,6 +144,10 @@ type LogMsg struct {
|
|||||||
LineNumber int
|
LineNumber int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogFormatter interface {
|
||||||
|
Format(lm *LogMsg) string
|
||||||
|
}
|
||||||
|
|
||||||
var logMsgPool *sync.Pool
|
var logMsgPool *sync.Pool
|
||||||
|
|
||||||
// NewLogger returns a new BeeLogger.
|
// NewLogger returns a new BeeLogger.
|
||||||
@ -179,6 +188,10 @@ func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger {
|
|||||||
return bl
|
return bl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// SetLogger provides a given logger adapter into BeeLogger with config string.
|
// SetLogger provides a given logger adapter into BeeLogger with config string.
|
||||||
// config must in in JSON format like {"interval":360}}
|
// config must in in JSON format like {"interval":360}}
|
||||||
func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
|
func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
|
||||||
@ -195,7 +208,16 @@ func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lg := logAdapter()
|
lg := logAdapter()
|
||||||
err := lg.Init(config)
|
var err error
|
||||||
|
|
||||||
|
// Global formatter overrides the default set formatter
|
||||||
|
// but not adapter specific formatters set with logs.SetLoggerWithOpts()
|
||||||
|
if bl.globalFormatter != nil {
|
||||||
|
err = lg.Init(config, &utils.SimpleKV{Key: "formatter", Value: bl.globalFormatter})
|
||||||
|
} else {
|
||||||
|
err = lg.Init(config)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error())
|
fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error())
|
||||||
return err
|
return err
|
||||||
@ -299,7 +321,7 @@ func (bl *BeeLogger) writeMsg(lm *LogMsg, v ...interface{}) error {
|
|||||||
lm.Msg = fmt.Sprintf("[%s:%d] %s", lm.FilePath, lm.LineNumber, lm.Msg)
|
lm.Msg = fmt.Sprintf("[%s:%d] %s", lm.FilePath, lm.LineNumber, lm.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set level info in front of filename info
|
// set level info in front of filename info
|
||||||
if lm.Level == levelLoggerImpl {
|
if lm.Level == levelLoggerImpl {
|
||||||
// set to emergency to ensure all log will be print out correctly
|
// set to emergency to ensure all log will be print out correctly
|
||||||
lm.Level = LevelEmergency
|
lm.Level = LevelEmergency
|
||||||
@ -382,6 +404,87 @@ func (bl *BeeLogger) startLogger() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the formatter from the opts common.SimpleKV structure
|
||||||
|
// Looks for a key: "formatter" with value: func(*LogMsg) string
|
||||||
|
func GetFormatter(opts utils.KV) (func(*LogMsg) string, error) {
|
||||||
|
if strings.ToLower(opts.GetKey().(string)) == "formatter" {
|
||||||
|
formatterInterface := reflect.ValueOf(opts.GetValue()).Interface()
|
||||||
|
formatterFunc := formatterInterface.(func(*LogMsg) string)
|
||||||
|
return formatterFunc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no \"formatter\" key given in simpleKV")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLoggerWithOpts sets a log adapter with a user defined logging format. Config must be valid JSON
|
||||||
|
// such as: {"interval":360}
|
||||||
|
func (bl *BeeLogger) setLoggerWithOpts(adapterName string, opts utils.KV, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.GetKey() == nil {
|
||||||
|
return fmt.Errorf("No SimpleKV struct set for %s log adapter", adapterName)
|
||||||
|
}
|
||||||
|
|
||||||
|
lg := logAdapter()
|
||||||
|
err := lg.Init(config, opts)
|
||||||
|
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.
|
||||||
|
func (bl *BeeLogger) SetLoggerWithOpts(adapterName string, opts utils.KV, configs ...string) error {
|
||||||
|
bl.lock.Lock()
|
||||||
|
defer bl.lock.Unlock()
|
||||||
|
if !bl.init {
|
||||||
|
bl.outputs = []*nameLogger{}
|
||||||
|
bl.init = true
|
||||||
|
}
|
||||||
|
return bl.setLoggerWithOpts(adapterName, opts, configs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLoggerWIthOpts sets a given log adapter with a custom log adapter.
|
||||||
|
// Log Adapter must be given in the form common.SimpleKV{Key: "formatter": Value: struct.FormatFunc}
|
||||||
|
// where FormatFunc has the signature func(*LogMsg) string
|
||||||
|
// func SetLoggerWithOpts(adapter string, config []string, formatterFunc func(*LogMsg) string) error {
|
||||||
|
func SetLoggerWithOpts(adapter string, config []string, opts utils.KV) error {
|
||||||
|
err := beeLogger.SetLoggerWithOpts(adapter, opts, config...)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl *BeeLogger) setGlobalFormatter(fmtter func(*LogMsg) string) error {
|
||||||
|
bl.globalFormatter = fmtter
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGlobalFormatter sets the global formatter for all log adapters
|
||||||
|
// This overrides and other individually set adapter
|
||||||
|
func SetGlobalFormatter(fmtter func(*LogMsg) string) error {
|
||||||
|
return beeLogger.setGlobalFormatter(fmtter)
|
||||||
|
}
|
||||||
|
|
||||||
// Emergency Log EMERGENCY level message.
|
// Emergency Log EMERGENCY level message.
|
||||||
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
|
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
|
||||||
if LevelEmergency > bl.level {
|
if LevelEmergency > bl.level {
|
||||||
@ -777,9 +880,9 @@ func formatLog(f interface{}, v ...interface{}) string {
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
|
if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
|
||||||
//format string
|
// format string
|
||||||
} else {
|
} else {
|
||||||
//do not contain format char
|
// do not contain format char
|
||||||
msg += strings.Repeat(" %v", len(v))
|
msg += strings.Repeat(" %v", len(v))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
35
pkg/infrastructure/logs/log_formatter_test.go
Normal file
35
pkg/infrastructure/logs/log_formatter_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func customFormatter(lm *LogMsg) string {
|
||||||
|
return fmt.Sprintf("[CUSTOM CONSOLE LOGGING] %s", lm.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func globalFormatter(lm *LogMsg) string {
|
||||||
|
return fmt.Sprintf("[GLOBAL] %s", lm.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomLoggingFormatter(t *testing.T) {
|
||||||
|
// beego.BConfig.Log.AccessLogs = true
|
||||||
|
|
||||||
|
SetLoggerWithOpts("console", []string{`{"color":true}`}, &utils.SimpleKV{Key: "formatter", Value: customFormatter})
|
||||||
|
|
||||||
|
// Message will be formatted by the customFormatter with colorful text set to true
|
||||||
|
Informational("Test message")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalLoggingFormatter(t *testing.T) {
|
||||||
|
SetGlobalFormatter(globalFormatter)
|
||||||
|
|
||||||
|
SetLogger("console", `{"color":true}`)
|
||||||
|
|
||||||
|
// Message will be formatted by globalFormatter
|
||||||
|
Informational("Test message")
|
||||||
|
|
||||||
|
}
|
@ -30,10 +30,10 @@ func newLogWriter(wr io.Writer) *logWriter {
|
|||||||
return &logWriter{writer: wr}
|
return &logWriter{writer: wr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lg *logWriter) writeln(lm *LogMsg) (int, error) {
|
func (lg *logWriter) writeln(msg string) (int, error) {
|
||||||
lg.Lock()
|
lg.Lock()
|
||||||
h, _, _ := formatTimeHeader(lm.When)
|
msg += "\n"
|
||||||
n, err := lg.writer.Write(append(append(h, lm.Msg...), '\n'))
|
n, err := lg.writer.Write([]byte(msg))
|
||||||
lg.Unlock()
|
lg.Unlock()
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ package logs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A filesLogWriter manages several fileLogWriter
|
// A filesLogWriter manages several fileLogWriter
|
||||||
@ -27,6 +29,7 @@ type multiFileLogWriter struct {
|
|||||||
writers [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter
|
writers [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter
|
||||||
fullLogWriter *fileLogWriter
|
fullLogWriter *fileLogWriter
|
||||||
Separate []string `json:"separate"`
|
Separate []string `json:"separate"`
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
}
|
}
|
||||||
|
|
||||||
var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"}
|
var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"}
|
||||||
@ -44,9 +47,19 @@ var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning
|
|||||||
// "separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"],
|
// "separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"],
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func (f *multiFileLogWriter) Init(config string) error {
|
func (f *multiFileLogWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writer := newFileWriter().(*fileLogWriter)
|
writer := newFileWriter().(*fileLogWriter)
|
||||||
err := writer.Init(config)
|
err := writer.Init(jsonConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -54,10 +67,10 @@ func (f *multiFileLogWriter) Init(config string) error {
|
|||||||
f.writers[LevelDebug+1] = writer
|
f.writers[LevelDebug+1] = writer
|
||||||
|
|
||||||
//unmarshal "separate" field to f.Separate
|
//unmarshal "separate" field to f.Separate
|
||||||
json.Unmarshal([]byte(config), f)
|
json.Unmarshal([]byte(jsonConfig), f)
|
||||||
|
|
||||||
jsonMap := map[string]interface{}{}
|
jsonMap := map[string]interface{}{}
|
||||||
json.Unmarshal([]byte(config), &jsonMap)
|
json.Unmarshal([]byte(jsonConfig), &jsonMap)
|
||||||
|
|
||||||
for i := LevelEmergency; i < LevelDebug+1; i++ {
|
for i := LevelEmergency; i < LevelDebug+1; i++ {
|
||||||
for _, v := range f.Separate {
|
for _, v := range f.Separate {
|
||||||
@ -74,10 +87,13 @@ func (f *multiFileLogWriter) Init(config string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *multiFileLogWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
func (f *multiFileLogWriter) Destroy() {
|
func (f *multiFileLogWriter) Destroy() {
|
||||||
for i := 0; i < len(f.writers); i++ {
|
for i := 0; i < len(f.writers); i++ {
|
||||||
if f.writers[i] != nil {
|
if f.writers[i] != nil {
|
||||||
|
@ -5,12 +5,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
||||||
type SLACKWriter struct {
|
type SLACKWriter struct {
|
||||||
WebhookURL string `json:"webhookurl"`
|
WebhookURL string `json:"webhookurl"`
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
|
UseCustomFormatter bool
|
||||||
|
CustomFormatter func(*LogMsg) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSLACKWriter creates jiaoliao writer.
|
// newSLACKWriter creates jiaoliao writer.
|
||||||
@ -18,9 +22,19 @@ func newSLACKWriter() Logger {
|
|||||||
return &SLACKWriter{Level: LevelTrace}
|
return &SLACKWriter{Level: LevelTrace}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SLACKWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// Init SLACKWriter with json config string
|
// Init SLACKWriter with json config string
|
||||||
func (s *SLACKWriter) Init(jsonconfig string) error {
|
func (s *SLACKWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
return json.Unmarshal([]byte(jsonconfig), s)
|
// if elem != nil {
|
||||||
|
// s.UseCustomFormatter = true
|
||||||
|
// s.CustomFormatter = elem
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return json.Unmarshal([]byte(jsonConfig), s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg write message in smtp writer.
|
// WriteMsg write message in smtp writer.
|
||||||
@ -29,8 +43,8 @@ func (s *SLACKWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
if lm.Level > s.Level {
|
if lm.Level > s.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
msg := s.Format(lm)
|
||||||
text := fmt.Sprintf("{\"text\": \"%s %s\"}", lm.When.Format("2006-01-02 15:04:05"), lm.Msg)
|
text := fmt.Sprintf("{\"text\": \"%s %s\"}", lm.When.Format("2006-01-02 15:04:05"), msg)
|
||||||
|
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Add("payload", text)
|
form.Add("payload", text)
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/pkg/infrastructure/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server.
|
// SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server.
|
||||||
@ -32,6 +34,7 @@ type SMTPWriter struct {
|
|||||||
FromAddress string `json:"fromAddress"`
|
FromAddress string `json:"fromAddress"`
|
||||||
RecipientAddresses []string `json:"sendTos"`
|
RecipientAddresses []string `json:"sendTos"`
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
|
customFormatter func(*LogMsg) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSMTPWriter creates the smtp writer.
|
// NewSMTPWriter creates the smtp writer.
|
||||||
@ -50,8 +53,19 @@ func newSMTPWriter() Logger {
|
|||||||
// "sendTos":["email1","email2"],
|
// "sendTos":["email1","email2"],
|
||||||
// "level":LevelError
|
// "level":LevelError
|
||||||
// }
|
// }
|
||||||
func (s *SMTPWriter) Init(jsonconfig string) error {
|
func (s *SMTPWriter) Init(jsonConfig string, opts ...utils.KV) error {
|
||||||
return json.Unmarshal([]byte(jsonconfig), s)
|
|
||||||
|
for _, elem := range opts {
|
||||||
|
if elem.GetKey() == "formatter" {
|
||||||
|
formatter, err := GetFormatter(elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.customFormatter = formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal([]byte(jsonConfig), s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth {
|
func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth {
|
||||||
@ -114,6 +128,10 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd
|
|||||||
return client.Quit()
|
return client.Quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SMTPWriter) Format(lm *LogMsg) string {
|
||||||
|
return lm.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// WriteMsg writes message in smtp writer.
|
// WriteMsg writes message in smtp writer.
|
||||||
// Sends an email with subject and only this message.
|
// Sends an email with subject and only this message.
|
||||||
func (s *SMTPWriter) WriteMsg(lm *LogMsg) error {
|
func (s *SMTPWriter) WriteMsg(lm *LogMsg) error {
|
||||||
@ -126,11 +144,13 @@ func (s *SMTPWriter) WriteMsg(lm *LogMsg) error {
|
|||||||
// Set up authentication information.
|
// Set up authentication information.
|
||||||
auth := s.getSMTPAuth(hp[0])
|
auth := s.getSMTPAuth(hp[0])
|
||||||
|
|
||||||
|
msg := s.Format(lm)
|
||||||
|
|
||||||
// Connect to the server, authenticate, set the sender and recipient,
|
// Connect to the server, authenticate, set the sender and recipient,
|
||||||
// and send the email all in one step.
|
// and send the email all in one step.
|
||||||
contentType := "Content-Type: text/plain" + "; charset=UTF-8"
|
contentType := "Content-Type: text/plain" + "; charset=UTF-8"
|
||||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
|
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
|
||||||
">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", lm.When.Format("2006-01-02 15:04:05")) + lm.Msg)
|
">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", lm.When.Format("2006-01-02 15:04:05")) + msg)
|
||||||
|
|
||||||
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user