1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-25 21:11:29 +00:00

Merge pull request #4216 from flycash/ftr/log_format

Log format support
This commit is contained in:
Ming Deng 2020-09-11 00:11:27 +08:00 committed by GitHub
commit 93736a8e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 413 additions and 68 deletions

View File

@ -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"
) )
@ -32,11 +33,12 @@ type Config struct {
// aliLSWriter implements LoggerInterface. // aliLSWriter implements LoggerInterface.
// Writes messages in keep-live tcp connection. // Writes messages in keep-live tcp connection.
type aliLSWriter struct { type aliLSWriter struct {
store *LogStore store *LogStore
group []*LogGroup group []*LogGroup
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),

View File

@ -18,18 +18,21 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net" "net"
"github.com/astaxie/beego/pkg/infrastructure/utils"
) )
// connWriter implements LoggerInterface. // connWriter implements LoggerInterface.
// Writes messages in keep-live tcp connection. // Writes messages in keep-live tcp connection.
type connWriter struct { type connWriter struct {
lg *logWriter lg *logWriter
innerWriter io.WriteCloser innerWriter io.WriteCloser
ReconnectOnMsg bool `json:"reconnectOnMsg"` customFormatter func(*LogMsg) string
Reconnect bool `json:"reconnect"` ReconnectOnMsg bool `json:"reconnectOnMsg"`
Net string `json:"net"` Reconnect bool `json:"reconnect"`
Addr string `json:"addr"` Net string `json:"net"`
Level int `json:"level"` Addr string `json:"addr"`
Level int `json:"level"`
} }
// NewConn creates new ConnWrite returning as LoggerInterface. // NewConn creates new ConnWrite returning as LoggerInterface.
@ -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
} }

View File

@ -19,6 +19,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/astaxie/beego/pkg/infrastructure/utils"
"github.com/shiena/ansicolor" "github.com/shiena/ansicolor"
) )
@ -47,9 +49,20 @@ 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
Level int `json:"level"` customFormatter func(*LogMsg) string
Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color Level int `json:"level"`
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.
@ -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
} }

View File

@ -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
@ -31,13 +32,29 @@ func NewES() logs.Logger {
// import _ "github.com/astaxie/beego/logs/es" // import _ "github.com/astaxie/beego/logs/es"
type esLogger struct { 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)

View File

@ -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
} }

View File

@ -5,16 +5,19 @@ 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
type JLWriter struct { type JLWriter struct {
AuthorName string `json:"authorname"` AuthorName string `json:"authorname"`
Title string `json:"title"` Title string `json:"title"`
WebhookURL string `json:"webhookurl"` WebhookURL string `json:"webhookurl"`
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)

View File

@ -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:

View 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")
}

View File

@ -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
} }

View File

@ -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
@ -24,9 +26,10 @@ import (
// and write the error-level logs to project.error.log and write the debug-level logs to project.debug.log // and write the error-level logs to project.error.log and write the debug-level logs to project.debug.log
// the rotate attribute also acts like fileLogWriter // the rotate attribute also acts like fileLogWriter
type multiFileLogWriter struct { 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 {

View File

@ -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)

View File

@ -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)
} }