From ed1d2c7f6e2d8589daf69aedf9a9d6e7c5d76d86 Mon Sep 17 00:00:00 2001 From: IamCathal Date: Mon, 24 Aug 2020 20:22:38 +0100 Subject: [PATCH] Add custom logging format functionality and global formatter functionality --- pkg/logs/conn.go | 25 +++++++++---- pkg/logs/console.go | 30 ++++++++++++--- pkg/logs/file.go | 20 +++++++++- pkg/logs/jianliao.go | 23 ++++++++---- pkg/logs/log.go | 85 ++++++++++++++++++++++++++++++++++++++++--- pkg/logs/multifile.go | 18 ++++++--- pkg/logs/slack.go | 15 ++++++-- pkg/logs/smtp.go | 11 +++++- 8 files changed, 190 insertions(+), 37 deletions(-) diff --git a/pkg/logs/conn.go b/pkg/logs/conn.go index 79ab410c..e11909a0 100644 --- a/pkg/logs/conn.go +++ b/pkg/logs/conn.go @@ -23,13 +23,15 @@ import ( // connWriter implements LoggerInterface. // Writes messages in keep-live tcp connection. type connWriter struct { - lg *logWriter - innerWriter io.WriteCloser - ReconnectOnMsg bool `json:"reconnectOnMsg"` - Reconnect bool `json:"reconnect"` - Net string `json:"net"` - Addr string `json:"addr"` - Level int `json:"level"` + lg *logWriter + innerWriter io.WriteCloser + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string + ReconnectOnMsg bool `json:"reconnectOnMsg"` + Reconnect bool `json:"reconnect"` + Net string `json:"net"` + Addr string `json:"addr"` + Level int `json:"level"` } // NewConn creates new ConnWrite returning as LoggerInterface. @@ -45,7 +47,14 @@ func (c *connWriter) Format(lm *LogMsg) string { // Init initializes a connection writer with json config. // json config only needs they "level" key -func (c *connWriter) Init(jsonConfig string) error { +func (c *connWriter) Init(jsonConfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + c.UseCustomFormatter = true + c.CustomFormatter = elem + } + } + return json.Unmarshal([]byte(jsonConfig), c) } diff --git a/pkg/logs/console.go b/pkg/logs/console.go index 86db6178..a928de7d 100644 --- a/pkg/logs/console.go +++ b/pkg/logs/console.go @@ -47,9 +47,11 @@ var colors = []brush{ // consoleWriter implements LoggerInterface and writes messages to terminal. type consoleWriter struct { - lg *logWriter - Level int `json:"level"` - Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color + lg *logWriter + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string + 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 { @@ -62,7 +64,7 @@ func (c *consoleWriter) Format(lm *LogMsg) string { h, _, _ := formatTimeHeader(lm.When) bytes := append(append(h, msg...), '\n') - return "eee" + string(bytes) + return string(bytes) } @@ -78,10 +80,18 @@ func NewConsole() Logger { // Init initianlizes the console logger. // jsonConfig must be in the format '{"level":LevelTrace}' -func (c *consoleWriter) Init(jsonConfig string) error { +func (c *consoleWriter) Init(jsonConfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + c.UseCustomFormatter = true + c.CustomFormatter = elem + } + } + if len(jsonConfig) == 0 { return nil } + return json.Unmarshal([]byte(jsonConfig), c) } @@ -94,7 +104,15 @@ func (c *consoleWriter) WriteMsg(lm *LogMsg) error { if c.Colorful { lm.Msg = strings.Replace(lm.Msg, levelPrefix[lm.Level], colors[lm.Level](levelPrefix[lm.Level]), 1) } - msg := c.Format(lm) + + msg := "" + + if c.UseCustomFormatter { + msg = c.CustomFormatter(lm) + } else { + msg = c.Format(lm) + } + c.lg.writeln(msg) return nil } diff --git a/pkg/logs/file.go b/pkg/logs/file.go index 366fbcf2..4576e19d 100644 --- a/pkg/logs/file.go +++ b/pkg/logs/file.go @@ -60,6 +60,9 @@ type fileLogWriter struct { hourlyOpenDate int hourlyOpenTime time.Time + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string + Rotate bool `json:"rotate"` Level int `json:"level"` @@ -104,7 +107,14 @@ func (w *fileLogWriter) Format(lm *LogMsg) string { // "rotate":true, // "perm":"0600" // } -func (w *fileLogWriter) Init(jsonConfig string) error { +func (w *fileLogWriter) Init(jsonConfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + w.UseCustomFormatter = true + w.CustomFormatter = elem + } + } + err := json.Unmarshal([]byte(jsonConfig), w) if err != nil { return err @@ -153,7 +163,13 @@ func (w *fileLogWriter) WriteMsg(lm *LogMsg) error { return nil } hd, d, h := formatTimeHeader(lm.When) - msg := w.Format(lm) + msg := "" + if w.UseCustomFormatter { + msg = w.CustomFormatter(lm) + } else { + msg = w.Format(lm) + } + msg = fmt.Sprintf("%s %s\n", string(hd), msg) if w.Rotate { w.RLock() diff --git a/pkg/logs/jianliao.go b/pkg/logs/jianliao.go index 6830bade..9877bed6 100644 --- a/pkg/logs/jianliao.go +++ b/pkg/logs/jianliao.go @@ -9,12 +9,14 @@ import ( // JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook type JLWriter struct { - AuthorName string `json:"authorname"` - Title string `json:"title"` - WebhookURL string `json:"webhookurl"` - RedirectURL string `json:"redirecturl,omitempty"` - ImageURL string `json:"imageurl,omitempty"` - Level int `json:"level"` + AuthorName string `json:"authorname"` + Title string `json:"title"` + WebhookURL string `json:"webhookurl"` + RedirectURL string `json:"redirecturl,omitempty"` + ImageURL string `json:"imageurl,omitempty"` + Level int `json:"level"` + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string } // newJLWriter creates jiaoliao writer. @@ -23,7 +25,14 @@ func newJLWriter() Logger { } // Init JLWriter with json config string -func (s *JLWriter) Init(jsonconfig string) error { +func (s *JLWriter) Init(jsonconfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + s.UseCustomFormatter = true + s.CustomFormatter = elem + } + } + return json.Unmarshal([]byte(jsonconfig), s) } diff --git a/pkg/logs/log.go b/pkg/logs/log.go index d47173e5..fd8fca63 100644 --- a/pkg/logs/log.go +++ b/pkg/logs/log.go @@ -84,7 +84,7 @@ type newLoggerFunc func() Logger // Logger defines the behavior of a log provider. type Logger interface { - Init(config string) error + Init(config string, LogFormatter ...func(*LogMsg) string) error WriteMsg(lm *LogMsg) error Format(lm *LogMsg) string Destroy() @@ -115,6 +115,7 @@ type BeeLogger struct { init bool enableFuncCallDepth bool loggerFuncCallDepth int + globalFormatter func(*LogMsg) string enableFullFilePath bool asynchronous bool prefix string @@ -129,8 +130,6 @@ const defaultAsyncMsgLen = 1e3 type nameLogger struct { Logger - // Formatter func(*LogMsg) string - LogFormatter name string } @@ -206,7 +205,16 @@ func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error { } 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, bl.globalFormatter) + } else { + err = lg.Init(config) + } + if err != nil { fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error()) return err @@ -248,7 +256,6 @@ func (bl *BeeLogger) DelLogger(adapterName string) error { func (bl *BeeLogger) writeToLoggers(lm *LogMsg) { for _, l := range bl.outputs { - // fmt.Println("Formatted: ", l.Format(lm)) err := l.WriteMsg(lm) if err != nil { fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) @@ -394,6 +401,74 @@ func (bl *BeeLogger) startLogger() { } } +// 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, formatterFunc func(*LogMsg) 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) + } + + if formatterFunc == nil { + return fmt.Errorf("No formatter set for %s log adapter", adapterName) + } + + lg := logAdapter() + err := lg.Init(config, formatterFunc) + 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, formatterFunc func(*LogMsg) string, configs ...string) error { + bl.lock.Lock() + defer bl.lock.Unlock() + if !bl.init { + bl.outputs = []*nameLogger{} + bl.init = true + } + return bl.setLoggerWithOpts(adapterName, formatterFunc, 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 { + err := beeLogger.SetLoggerWithOpts(adapter, formatterFunc, 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. func (bl *BeeLogger) Emergency(format string, v ...interface{}) { if LevelEmergency > bl.level { diff --git a/pkg/logs/multifile.go b/pkg/logs/multifile.go index 0650c99d..bcd4dd4e 100644 --- a/pkg/logs/multifile.go +++ b/pkg/logs/multifile.go @@ -24,9 +24,11 @@ import ( // 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 type multiFileLogWriter struct { - writers [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter - fullLogWriter *fileLogWriter - Separate []string `json:"separate"` + writers [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter + fullLogWriter *fileLogWriter + Separate []string `json:"separate"` + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string } var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"} @@ -44,7 +46,14 @@ var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning // "separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"], // } -func (f *multiFileLogWriter) Init(config string) error { +func (f *multiFileLogWriter) Init(config string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + f.UseCustomFormatter = true + f.CustomFormatter = elem + } + } + writer := newFileWriter().(*fileLogWriter) err := writer.Init(config) if err != nil { @@ -74,7 +83,6 @@ func (f *multiFileLogWriter) Init(config string) error { } } } - return nil } diff --git a/pkg/logs/slack.go b/pkg/logs/slack.go index c0584f72..9407b48a 100644 --- a/pkg/logs/slack.go +++ b/pkg/logs/slack.go @@ -9,8 +9,10 @@ import ( // SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook type SLACKWriter struct { - WebhookURL string `json:"webhookurl"` - Level int `json:"level"` + WebhookURL string `json:"webhookurl"` + Level int `json:"level"` + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string } // newSLACKWriter creates jiaoliao writer. @@ -23,7 +25,14 @@ func (s *SLACKWriter) Format(lm *LogMsg) string { } // Init SLACKWriter with json config string -func (s *SLACKWriter) Init(jsonconfig string) error { +func (s *SLACKWriter) Init(jsonconfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + s.UseCustomFormatter = true + s.CustomFormatter = elem + } + } + return json.Unmarshal([]byte(jsonconfig), s) } diff --git a/pkg/logs/smtp.go b/pkg/logs/smtp.go index d992b279..b81be68f 100644 --- a/pkg/logs/smtp.go +++ b/pkg/logs/smtp.go @@ -32,6 +32,8 @@ type SMTPWriter struct { FromAddress string `json:"fromAddress"` RecipientAddresses []string `json:"sendTos"` Level int `json:"level"` + UseCustomFormatter bool + CustomFormatter func(*LogMsg) string } // NewSMTPWriter creates the smtp writer. @@ -50,7 +52,14 @@ func newSMTPWriter() Logger { // "sendTos":["email1","email2"], // "level":LevelError // } -func (s *SMTPWriter) Init(jsonconfig string) error { +func (s *SMTPWriter) Init(jsonconfig string, LogFormatter ...func(*LogMsg) string) error { + for _, elem := range LogFormatter { + if elem != nil { + s.UseCustomFormatter = true + s.CustomFormatter = elem + } + } + return json.Unmarshal([]byte(jsonconfig), s) }