1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
package logging
import (
"errors"
"io"
"log/slog"
"os"
"strings"
"time"
"github.com/ChausseBenjamin/rafta/internal/util"
"github.com/charmbracelet/log"
)
const (
ErrKey = "error_message"
)
var (
ErrInvalidLevel = errors.New("invalid log level")
ErrInvalidFormat = errors.New("invalid log format")
)
func Setup(lvlStr, fmtStr, outStr string) error {
output, outputErr := setOutput(outStr)
format, formatErr := setFormat(fmtStr)
level, levelErr := setLevel(lvlStr)
prefixStr := ""
if format != log.JSONFormatter {
prefixStr = "Rafta 🚢"
}
var h slog.Handler = log.NewWithOptions(
output,
log.Options{
TimeFormat: time.DateTime,
Prefix: prefixStr,
Level: level,
ReportCaller: true,
Formatter: format,
},
)
h = withTrackedContext(h, util.ReqIDKey, "request_id")
h = withStackTrace(h)
slog.SetDefault(slog.New(h))
return errors.Join(outputErr, formatErr, levelErr)
}
func setLevel(target string) (log.Level, error) {
for _, l := range []struct {
prefix string
level log.Level
}{
{"deb", log.DebugLevel},
{"inf", log.InfoLevel},
{"warn", log.WarnLevel},
{"err", log.ErrorLevel},
} {
if strings.HasPrefix(strings.ToLower(target), l.prefix) {
return l.level, nil
}
}
return log.InfoLevel, ErrInvalidLevel
}
func setFormat(f string) (log.Formatter, error) {
switch f {
case "plain", "text":
return log.TextFormatter, nil
case "json", "structured":
return log.JSONFormatter, nil
}
return log.TextFormatter, ErrInvalidFormat
}
func setOutput(path string) (io.Writer, error) {
switch path {
case "stdout":
return os.Stdout, nil
case "stderr":
return os.Stderr, nil
default:
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return os.Stdout, err
} else {
return f, nil
}
}
}
|