summaryrefslogtreecommitdiff
path: root/internal/logging/logging.go
blob: 91a9734b09e7aefbfa069470273060bf8b76d1ce (plain)
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
		}
	}
}