Select Git revision
logger.go 7.17 KiB
package logger
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/fatih/color"
"gitlab.com/uafrica/go-utils/errors"
)
type ILogger interface {
Fatalf(format string, args ...interface{})
Fatal(args ...interface{})
Errorf(format string, args ...interface{})
Error(args ...interface{})
Warnf(format string, args ...interface{})
Warn(args ...interface{})
Infof(format string, args ...interface{})
Info(args ...interface{})
Debugf(format string, args ...interface{})
Debug(args ...interface{})
}
type Logger struct {
//apiRequest *events.APIGatewayProxyRequest
//currentRequestID *string
level Level
writer io.Writer
data map[string]interface{}
fg color.Attribute
bg color.Attribute
}
func (l Logger) WithForeground(fg color.Attribute) Logger {
l.fg = fg
return l
}
func (l Logger) WithBackground(bg color.Attribute) Logger {
l.bg = bg
return l
}
type textColor struct {
fg color.Attribute
bg color.Attribute
}
var (
fgColors = []color.Attribute{
color.FgWhite,
color.FgRed,
color.FgGreen,
color.FgYellow,
color.FgBlue,
color.FgMagenta,
color.FgCyan,
color.FgBlack,
}
bgColors = []color.Attribute{
color.BgBlack,
color.BgWhite,
color.BgRed,
color.BgGreen,
color.BgYellow,
color.BgBlue,
color.BgMagenta,
color.BgCyan,
}
nextFg = 0
nextBg = 0
)
func (l Logger) NextColor() Logger {
l.fg = 0
l.bg = 0
for l.fg == l.bg {
l.bg = bgColors[nextBg]
l.fg = fgColors[nextFg]
incColor()
}
return l
}
func incColor() {
nextFg++
if nextFg >= len(fgColors) {
nextFg = 0
nextBg++
if nextBg >= len(bgColors) {
nextBg++
}
}
}
func (l Logger) WithFields(data map[string]interface{}) Logger {
newLogger := Logger{
level: l.level,
writer: l.writer,
data: map[string]interface{}{},
fg: l.fg,
bg: l.bg,
}
for n, v := range l.data {
newLogger.data[n] = v
}
for n, v := range data {
newLogger.data[n] = v
}
return newLogger
}
func (l Logger) Fatalf(format string, args ...interface{}) {
l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprintf(format, args...))
}
func (l Logger) Fatal(args ...interface{}) {
l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprint(args...))
}
func (l Logger) Errorf(format string, args ...interface{}) {
l.log(LevelError, 1, fmt.Sprintf(format, args...))
}
func (l Logger) Error(args ...interface{}) {
l.log(LevelError, 1, fmt.Sprint(args...))
}
func (l Logger) Warnf(format string, args ...interface{}) {
l.log(LevelWarn, 1, fmt.Sprintf(format, args...))
}
func (l Logger) Warn(args ...interface{}) {
l.log(LevelWarn, 1, fmt.Sprint(args...))
}
func (l Logger) Infof(format string, args ...interface{}) {
l.log(LevelInfo, 1, fmt.Sprintf(format, args...))
}
func (l Logger) Info(args ...interface{}) {
l.log(LevelInfo, 1, fmt.Sprint(args...))
}
func (l Logger) Debugf(format string, args ...interface{}) {
l.log(LevelDebug, 1, fmt.Sprintf(format, args...))
}
func (l Logger) Debug(args ...interface{}) {
l.log(LevelDebug, 1, fmt.Sprint(args...))
}
func (l Logger) log(level Level, skip int, msg string) {
if level <= l.level && l.writer != nil {
entry := Entry{
Timestamp: time.Now(),
Level: level,
Caller: errors.GetCaller(skip + 2).Info(),
Data: l.data,
Message: strings.ReplaceAll(msg, "\n", ";"),
}
// jsonEntry, err := json.Marshal(entry)
// if err != nil {
// l.writer.Write([]byte(fmt.Sprintf("failed to marshal entry: %v: %+v\n", err, entry)))
// }
// l.writer.Write(append(jsonEntry, []byte("\n")...))
source := fmt.Sprintf("%s/%s:%d", entry.Caller.Package, entry.Caller.File, entry.Caller.Line)
if len(source) > 40 {
source = source[len(source)-40:]
}
buffer := bytes.NewBuffer(nil)
red := color.New(color.FgRed).FprintfFunc()
magenta := color.New(color.FgMagenta).FprintfFunc()
yellow := color.New(color.FgYellow).FprintfFunc()
blue := color.New(color.FgBlue).FprintfFunc()
green := color.New(color.FgGreen).FprintfFunc()
cyan := color.New(color.FgCyan).FprintfFunc()
blue(buffer, entry.Timestamp.Format("2006-01-02 15:04:05"))
levelString := fmt.Sprintf(" %5.5s", entry.Level)
switch entry.Level {
case LevelFatal:
red(buffer, levelString)
case LevelError:
red(buffer, levelString)
case LevelWarn:
magenta(buffer, levelString)
case LevelInfo:
yellow(buffer, levelString)
case LevelDebug:
green(buffer, levelString)
}
cyan(buffer, fmt.Sprintf(" %-40.40s| ", source))
base := color.New(l.fg, l.bg).FprintfFunc()
base(buffer, entry.Message) //buffer.Write([]byte(entry.Message))
if len(entry.Data) > 0 {
jsonData, _ := json.Marshal(entry.Data)
green(buffer, " "+string(jsonData))
}
buffer.WriteString("\n")
l.writer.Write(buffer.Bytes())
}
}
type Entry struct {
Timestamp time.Time `json:"time"`
Level Level `json:"level"`
Caller errors.CallerInfo `json:"caller"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
}
// func sendRaygunError(fields map[string]interface{}, errToSend error) {
// if os.Getenv("DEBUGGING") == "true" {
// // Don't log raygun errors on debug
// return
// }
// raygun, err := RaygunReporter()
// if err != nil || raygun == nil {
// logger.Errorf("Unable to create Raygun client: " + err.Error())
// return
// }
// env := getEnvironment()
// tags := []string{env}
// //todo: raygun.Version(globals.BuildVersion)
// //todo: tags = append(tags, globals.BuildVersion)
// if logger.apiRequest != nil {
// methodAndPath := logger.apiRequest.HTTPMethod + ": " + logger.apiRequest.Path
// tags = append(tags, methodAndPath)
// fields["body"] = logger.apiRequest.Body
// fields["query"] = logger.apiRequest.QueryStringParameters
// fields["identity"] = logger.apiRequest.RequestContext.Identity
// }
// raygun.Tags(tags)
// if logger.currentRequestID != nil {
// fields["request_id"] = logger.currentRequestID
// }
// fields["env"] = env
// raygun.CustomData(fields)
// raygun.Request(fakeHttpRequest())
// if errToSend == nil {
// errToSend = errors.New("")
// }
// err = raygun.SendError(errToSend)
// if err != nil {
// logger.Errorf("Failed to send raygun error: " + err.Error())
// }
// }
// func fakeHttpRequest() *http.Request {
// if logger.apiRequest == nil {
// return nil
// }
// requestURL := url.URL{
// Path: logger.apiRequest.Path,
// Host: logger.apiRequest.Headers["Host"],
// }
// request := http.Request{
// Method: logger.apiRequest.HTTPMethod,
// URL: &requestURL,
// Header: logger.apiRequest.MultiValueHeaders,
// }
// return &request
// }
// func RaygunReporter() (*raygun4go.Client, error) {
// key := "AwdpHhwOF1lTT6AppyEHA"
// // TODO Raygun per environment
// //env := getEnvironment()
// //if env == "dev" {
// // key = "q98iTyE5CcF7raZsIiLA"
// //} else if env == "stage" {
// // key = "TA54mzcv9cBWBLmfwIQMDg"
// //} else if env == "prod" {
// // key = "BXdraqiPKBXImqP4siK1w"
// //}
// raygun, err := raygun4go.New("uAfrica V3", key)
// if err != nil || raygun == nil {
// logger.Errorf("Unable to create Raygun client:" + err.Error())
// return nil, err
// }
// return raygun, nil
// }