Skip to content

Commit

Permalink
chore: logging improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
moshloop committed Aug 18, 2024
1 parent bf5e272 commit 6e5cb46
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 47 deletions.
15 changes: 15 additions & 0 deletions logger/caller.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package logger

import (
"path"
"runtime"
"strconv"
"strings"
Expand Down Expand Up @@ -67,6 +68,20 @@ func skipFrame(frame runtime.Frame) bool {
return false
}

func GetCaller(pc ...uintptr) string {
frames := runtime.CallersFrames(pc)
for {
frame, more := frames.Next()
if !skipFrame(frame) {

return path.Base(path.Dir(frame.File)) + "/" + path.Base(frame.File)
}
if !more {
return ""
}
}
}

func CallerPC(skip ...int) uintptr {
pcs := [13]uintptr{}
len := runtime.Callers(1, pcs[:])
Expand Down
3 changes: 2 additions & 1 deletion logger/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

var currentLogger Logger
var color, reportCaller, jsonLogs bool
var color, reportCaller, jsonLogs, logToStderr bool
var level int

func init() {
Expand All @@ -27,6 +27,7 @@ func BindFlags(flags *pflag.FlagSet) {
flags.BoolVar(&jsonLogs, "json-logs", false, "Print logs in json format to stderr")
flags.BoolVar(&color, "color", true, "Print logs using color")
flags.BoolVar(&reportCaller, "report-caller", false, "Report log caller info")
flags.BoolVar(&logToStderr, "log-to-stderr", false, "Log to stderr instead of stdout")
}

func BindGoFlags() {
Expand Down
28 changes: 28 additions & 0 deletions logger/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,34 @@ const (
Silent LogLevel = 10
)

func (l LogLevel) String() string {
switch l {
case Debug:
return "debug"
case Trace:
return "trace"
case Trace1:
return "trace1"
case Trace2:
return "trace2"
case Trace3:
return "trace3"
case Trace4:
return "trace4"
case Info:
return "info"
case Warn:
return "warn"
case Error:
return "error"
case Fatal:
return "fatal"
case Silent:
return "silent"
}
return ""
}

const (
cyan = "\x1b[36"
Cyan = cyan + Normal
Expand Down
137 changes: 91 additions & 46 deletions logger/slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
"strconv"
"strings"
"time"
"unicode"

"github.com/flanksource/commons/is"
"github.com/flanksource/commons/properties"
"github.com/kr/pretty"
"github.com/lmittmann/tint"
"github.com/lrita/cmap"
"github.com/samber/lo"
)

var (
Expand All @@ -28,14 +28,14 @@ var todo = context.TODO()
func GetNamedLoggingLevels() (levels map[string]string) {
levels = make(map[string]string)
namedLoggers.Range(func(key string, value *SlogLogger) bool {
levels[key] = value.Level.String()
levels[key] = FromSlogLevel(value.Level.Level()).String()
return true
})
return levels
}

func BrightF(msg string, args ...interface{}) string {
if !color || isTTY && !jsonLogs {
if isTTY && color && !jsonLogs {
return DarkWhite + fmt.Sprintf(msg, args...) + Reset
}
return fmt.Sprintf(msg, args...)
Expand All @@ -54,7 +54,6 @@ func GetSlogLogger() SlogLogger {

func onPropertyUpdate(props *properties.Properties) {
for k, v := range props.GetAll() {

if k == "log.level" || k == "log.json" || k == "log.caller" || k == "log.color" {
root := New("root")
existing := GetLogger()
Expand All @@ -69,35 +68,50 @@ func onPropertyUpdate(props *properties.Properties) {
reportCaller, _ = strconv.ParseBool(v)
}
}

if props.On(false, "log.json") && props.On(false, "log.color") {
// disable color logs when json logs are enabled
properties.Set("log.color", "false")
}
}

func New(prefix string) *SlogLogger {
// create a new slogger
var slogger *slog.Logger
var logger *SlogLogger
var lvl = &slog.LevelVar{}
var l any

reportCaller := properties.On(false, fmt.Sprintf("log.caller.%s", prefix), "log.caller")
logJson := properties.On(false, fmt.Sprintf("log.json.%s", prefix), "log.json")
logColor := properties.On(false, fmt.Sprintf("log.color.%s", prefix), "log.color")
reportCaller := properties.On(reportCaller, fmt.Sprintf("log.caller.%s", prefix), "log.caller")
logJson := properties.On(jsonLogs, "log.json")
logColor := properties.On(color, fmt.Sprintf("log.color.%s", prefix), "log.color")
logLevel := properties.String("", fmt.Sprintf("log.level.%s", prefix), "log.level")
if logJson {
slogger = slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
AddSource: reportCaller,
Level: lvl,
}))
} else {
slogger = slog.New(tint.NewHandler(os.Stderr, &tint.Options{
Level: lvl,
NoColor: !logColor,
AddSource: reportCaller,
TimeFormat: properties.String("15:04:05.999", fmt.Sprintf("log.time.format.%s", prefix), "log.time.format"),
}))
logStderr := properties.On(logToStderr, "log.stderr")
destination := os.Stdout
if logStderr {
destination = os.Stderr
}

logger := SlogLogger{
Logger: slogger,
Level: lvl,
if logJson {
color = false
jsonLogs = true
logger = &SlogLogger{
Level: lvl,
Logger: slog.New(slog.NewJSONHandler(destination, &slog.HandlerOptions{
AddSource: reportCaller,
Level: lvl,
})),
}

} else {
logger = &SlogLogger{
Logger: slog.New(tint.NewHandler(destination, &tint.Options{
Level: lvl,
NoColor: !logColor,
AddSource: reportCaller,
TimeFormat: properties.String("15:04:05.999", fmt.Sprintf("log.time.format.%s", prefix), "log.time.format"),
})),
Level: lvl,
}
}

if logLevel != "" {
Expand All @@ -106,11 +120,12 @@ func New(prefix string) *SlogLogger {
l = level
}

if prefix != "" {
logger.Prefix = fmt.Sprintf("[%s] ", BrightF(prefix))
if prefix != "" && prefix != "root" {
logger.Prefix = prefix
}

logger.SetLogLevel(l)
return &logger
return logger
}
func UseSlog() {
if currentLogger != nil {
Expand All @@ -126,26 +141,39 @@ func UseSlog() {
properties.RegisterListener(onPropertyUpdate)
}

func camelCaseWords(s string) []string {
var result strings.Builder
for _, r := range s {
if unicode.IsUpper(r) {
result.WriteRune(' ')
result.WriteRune(r)

} else {
result.WriteRune(r)
}
}
return strings.Fields(result.String())
}

func GetLogger(names ...string) *SlogLogger {
parent, _ := namedLoggers.Load("root")
if len(names) == 0 {
return parent
}

path := ""
for _, name := range names {
if !strings.Contains(name, " ") {
name = strings.ToLower(strings.Join(lo.Words(name), " "))
} else {
name = strings.ToLower(name)
}
for i, name := range names {
name = strings.ToLower(strings.Join(camelCaseWords(name), " "))
if path != "" {
path += "."
}
path = path + name
path = path + strings.TrimSpace(name)
if v, ok := namedLoggers.Load(path); ok {
return v
}
if i == 0 {
break
}
}
child, _ := namedLoggers.LoadOrStore(path, New(path))
return child
Expand All @@ -163,8 +191,8 @@ func (s SlogLogger) Warnf(format string, args ...interface{}) {
if !s.Logger.Enabled(todo, slog.LevelWarn) {
return
}
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
_ = s.Logger.Handler().Handle(context.Background(), r)
s.handle(slog.NewRecord(time.Now(), slog.LevelWarn, "", CallerPC()), format, args...)

}

func (s SlogLogger) GetSlogLogger() *slog.Logger {
Expand All @@ -175,8 +203,7 @@ func (s SlogLogger) Infof(format string, args ...interface{}) {
if !s.Logger.Enabled(todo, slog.LevelInfo) {
return
}
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
_ = s.Logger.Handler().Handle(context.Background(), r)
s.handle(slog.NewRecord(time.Now(), slog.LevelInfo, "", CallerPC()), format, args...)
}

func (s SlogLogger) Secretf(format string, args ...interface{}) {
Expand All @@ -191,29 +218,47 @@ func (s SlogLogger) Errorf(format string, args ...interface{}) {
if !s.Logger.Enabled(todo, slog.LevelError) {
return
}
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
_ = s.Logger.Handler().Handle(context.Background(), r)
s.handle(slog.NewRecord(time.Now(), slog.LevelError, "", CallerPC()), format, args...)
}

func (s SlogLogger) Debugf(format string, args ...interface{}) {
if !s.Logger.Enabled(context.Background(), slog.LevelDebug) {
return
}
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
s.handle(slog.NewRecord(time.Now(), slog.LevelDebug, "", CallerPC()), format, args...)

}

func (s SlogLogger) handle(r slog.Record, format string, args ...interface{}) {
caller := GetCaller(r.PC)
if fileLogger, ok := namedLoggers.Load(caller); ok {
if !fileLogger.IsLevelEnabled(FromSlogLevel(r.Level)) {
return
}
}
if jsonLogs {
if s.Prefix != "" {
r.Add("logger", s.Prefix)
}
r.Message = fmt.Sprintf(format, args...)
} else if s.Prefix != "" {
r.Message = fmt.Sprintf(fmt.Sprintf("(%s) ", BrightF(s.Prefix))+format, args...)
} else {
r.Message = fmt.Sprintf(format, args...)
}
_ = s.Logger.Handler().Handle(context.Background(), r)
}

func (s SlogLogger) Tracef(format string, args ...interface{}) {
if !s.Logger.Enabled(todo, SlogTraceLevel) {
return
}
r := slog.NewRecord(time.Now(), SlogTraceLevel, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
_ = s.Logger.Handler().Handle(context.Background(), r)
s.handle(slog.NewRecord(time.Now(), SlogTraceLevel, "", CallerPC()), format, args...)

}

func (s SlogLogger) Fatalf(format string, args ...interface{}) {
r := slog.NewRecord(time.Now(), SlogFatal, fmt.Sprintf(s.Prefix+format, args...), CallerPC())
_ = s.Logger.Handler().Handle(context.Background(), r)
s.handle(slog.NewRecord(time.Now(), SlogFatal, "", CallerPC()), format, args...)
}

type slogVerbose struct {
Expand All @@ -226,8 +271,8 @@ func (v slogVerbose) Infof(format string, args ...interface{}) {
return
}

r := slog.NewRecord(time.Now(), v.level, fmt.Sprintf(v.Prefix+format, args...), CallerPC())
_ = v.Logger.Handler().Handle(context.Background(), r)
v.handle(slog.NewRecord(time.Now(), v.level, "", CallerPC()), format, args...)

}

func (v slogVerbose) Enabled() bool {
Expand Down

0 comments on commit 6e5cb46

Please sign in to comment.