2015-10-10 07:29:18 +00:00
|
|
|
package flog
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2015-10-11 00:08:49 +00:00
|
|
|
"sort"
|
2015-10-10 07:29:18 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2015-10-10 23:46:41 +00:00
|
|
|
// PrettyWritingRouter returns an initialized PrettyWritingRouter
|
2015-10-10 23:41:58 +00:00
|
|
|
func NewPrettyWritingRouter(writer io.Writer) *PrettyWritingRouter {
|
|
|
|
router := PrettyWritingRouter{
|
2015-10-10 07:29:18 +00:00
|
|
|
writer:writer,
|
|
|
|
}
|
|
|
|
|
|
|
|
return &router
|
|
|
|
}
|
|
|
|
|
2015-10-10 23:46:41 +00:00
|
|
|
// PrettyWritingRouter is a router that writes a pretty version of
|
|
|
|
// the log it was give (including COLORS!) to the writer it was
|
|
|
|
// given when it was created.
|
|
|
|
//
|
2016-09-20 00:40:13 +00:00
|
|
|
// A PrettyWritingRouter is appropriate for a development (i.e., "DEV")
|
2015-10-10 23:46:41 +00:00
|
|
|
// deployment enviornment. (And probably not appropriate a production
|
|
|
|
// (i.e., "PROD") deployment environment.)
|
2015-10-10 23:41:58 +00:00
|
|
|
type PrettyWritingRouter struct {
|
2015-10-10 07:29:18 +00:00
|
|
|
writer io.Writer
|
|
|
|
}
|
|
|
|
|
2015-10-10 23:41:58 +00:00
|
|
|
func (router *PrettyWritingRouter) Route(message string, context map[string]interface{}) error {
|
2016-09-20 18:03:35 +00:00
|
|
|
if nil == router {
|
|
|
|
return errNilReceiver
|
|
|
|
}
|
2015-10-10 07:29:18 +00:00
|
|
|
|
2015-10-15 22:28:20 +00:00
|
|
|
const STYLE_FATAL = "\x1b[40;33;1m" // BG BLACK, FG YELLOW, BOLD
|
|
|
|
const STYLE_PANIC = "\x1b[40;31;1m" // BG BLACK, FG RED, BOLD
|
|
|
|
const STYLE_ERROR = "\x1b[41;37;1m" // BG RED, FG WHITE, BOLD
|
|
|
|
const STYLE_WARNING = "\x1b[43;37;1m" // BG YELLOW, FG WHITE, BOLD
|
|
|
|
const STYLE_NOTICE = "\x1b[42;33;1m" // BG GREEN, FG YELLOW, BOLD
|
2015-10-10 23:39:33 +00:00
|
|
|
const STYLE_TIMESTAMP = "\x1b[2m" // FAINT
|
2015-10-15 22:28:20 +00:00
|
|
|
const STYLE_MESSAGE = "\x1b[44;37;1m" // BG BLUE, FG WHITE, BOLD
|
2015-10-10 23:39:33 +00:00
|
|
|
const STYLE_DEFAULT = "\033[95m" // HEADER
|
|
|
|
const STYLE_RESET = "\033[0m" // RESET
|
2019-07-12 04:27:37 +00:00
|
|
|
const STYLE_CALLTRACE = "\033[40;36;1m" // BG BLACK, FG CYAN, BOLD
|
2015-10-10 23:39:33 +00:00
|
|
|
|
|
|
|
str := ""
|
|
|
|
|
|
|
|
if nil != context {
|
2015-10-15 22:28:20 +00:00
|
|
|
if _, ok := context["~fatal"]; ok {
|
|
|
|
str = fmt.Sprintf("%s 💀 💀 💀 💀 💀 %s\t%s", STYLE_FATAL, STYLE_RESET, str)
|
|
|
|
} else if _, ok := context["~panic"]; ok {
|
2015-10-10 23:39:33 +00:00
|
|
|
str = fmt.Sprintf("%s ☠ ☠ ☠ ☠ ☠ %s\t%s", STYLE_PANIC, STYLE_RESET, str)
|
2015-10-15 22:28:20 +00:00
|
|
|
} else if _, ok := context["~error"]; ok {
|
2015-10-10 23:39:33 +00:00
|
|
|
str = fmt.Sprintf("%s 😨 😨 😨 😨 😨 %s\t%s", STYLE_ERROR, STYLE_RESET, str)
|
2015-10-15 22:28:20 +00:00
|
|
|
} else if _, ok := context["~warn"]; ok {
|
2015-10-10 23:39:33 +00:00
|
|
|
str = fmt.Sprintf("%s 😟 😟 😟 😟 😟 %s\t%s", STYLE_WARNING, STYLE_RESET, str)
|
2015-10-15 22:28:20 +00:00
|
|
|
} else if _, ok := context["~print"]; ok {
|
2015-10-10 23:39:33 +00:00
|
|
|
str = fmt.Sprintf("%s 😮 😮 😮 😮 😮 %s\t%s", STYLE_NOTICE, STYLE_RESET, str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
str = fmt.Sprintf("%s%s%s%s\t(%s%v%s)", str, STYLE_MESSAGE, message, STYLE_RESET, STYLE_TIMESTAMP, time.Now(), STYLE_RESET)
|
2015-10-11 00:08:49 +00:00
|
|
|
|
2015-10-15 22:28:20 +00:00
|
|
|
// If we have an error, then get the error.Error() into the log too.
|
|
|
|
if errorFieldValue, ok := context["~error"]; ok {
|
|
|
|
if err, ok := errorFieldValue.(error); ok {
|
2015-10-16 00:59:07 +00:00
|
|
|
context["~~error"] = fmt.Sprintf("%s, {{%T}}", err.Error(), err)
|
2015-10-15 22:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-11 00:08:49 +00:00
|
|
|
//@TODO: This is a potential heavy operation. Is there a better way
|
|
|
|
// to get the ultimate result this is trying to archive?
|
|
|
|
//
|
|
|
|
sortedKeys := make([]string, len(context))
|
|
|
|
i := 0
|
|
|
|
for key, _ := range context {
|
|
|
|
sortedKeys[i] = key
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
sort.Strings(sortedKeys)
|
|
|
|
|
|
|
|
for _, key := range sortedKeys {
|
|
|
|
|
|
|
|
value := context[key]
|
|
|
|
|
2015-10-10 23:39:33 +00:00
|
|
|
style := STYLE_DEFAULT
|
|
|
|
|
|
|
|
switch key {
|
2015-10-15 22:28:20 +00:00
|
|
|
case "~fatal", "~fatals":
|
|
|
|
style = STYLE_FATAL
|
|
|
|
case "~panic", "~panics":
|
2015-10-10 23:39:33 +00:00
|
|
|
style = STYLE_PANIC
|
2015-10-16 00:59:07 +00:00
|
|
|
case "~error", "~errors", "~~error":
|
2015-10-10 23:39:33 +00:00
|
|
|
style = STYLE_ERROR
|
2015-10-15 22:28:20 +00:00
|
|
|
case "~warning", "~warnings":
|
2015-10-10 23:39:33 +00:00
|
|
|
style = STYLE_WARNING
|
2015-10-15 22:28:20 +00:00
|
|
|
case "~print", "~prints":
|
2015-10-10 23:39:33 +00:00
|
|
|
style = STYLE_NOTICE
|
|
|
|
}
|
|
|
|
|
|
|
|
str = fmt.Sprintf("%s\t%s%s%s=%s%#v%s", str, style, key, STYLE_RESET, style, value, STYLE_RESET)
|
2015-10-10 07:29:18 +00:00
|
|
|
}
|
|
|
|
|
2019-07-12 04:27:37 +00:00
|
|
|
if trace := calltrace(); nil != trace {
|
|
|
|
str = fmt.Sprintf("%s\t⟨⟨", str)
|
|
|
|
for _, s := range trace {
|
|
|
|
str = fmt.Sprintf("%s %s%s%s,", str, STYLE_CALLTRACE, s, STYLE_RESET)
|
|
|
|
}
|
|
|
|
str = fmt.Sprintf("%s ⟩⟩", str)
|
|
|
|
}
|
|
|
|
|
2015-10-10 07:29:18 +00:00
|
|
|
fmt.Fprintln(router.writer, str)
|
|
|
|
|
|
|
|
//@TODO: Should this be checking for errors from fmt.Fprintln()?
|
|
|
|
return nil
|
|
|
|
}
|