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{} } func (l Logger) WithFields(data map[string]interface{}) Logger { newLogger := Logger{ level: l.level, writer: l.writer, data: map[string]interface{}{}, } 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)) 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 // }