-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathconverter.go
80 lines (68 loc) · 2.35 KB
/
converter.go
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
package slogloki
import (
"fmt"
"log/slog"
"strconv"
"strings"
"github.com/prometheus/common/model"
slogcommon "github.com/samber/slog-common"
)
var SourceKey = "source"
var ErrorKeys = []string{"error", "err"}
// See:
// - /~https://github.com/samber/slog-loki/issues/10
// - /~https://github.com/samber/slog-loki/issues/11
var SubAttributeSeparator = "__"
var AttributeKeyInvalidCharReplacement = "_"
type Converter func(addSource bool, replaceAttr func(groups []string, a slog.Attr) slog.Attr, loggerAttr []slog.Attr, groups []string, record *slog.Record) model.LabelSet
func DefaultConverter(addSource bool, replaceAttr func(groups []string, a slog.Attr) slog.Attr, loggerAttr []slog.Attr, groups []string, record *slog.Record) model.LabelSet {
// aggregate all attributes
attrs := slogcommon.AppendRecordAttrsToAttrs(loggerAttr, groups, record)
// developer formatters
attrs = slogcommon.ReplaceError(attrs, ErrorKeys...)
if addSource {
attrs = append(attrs, slogcommon.Source(SourceKey, record))
}
attrs = append(attrs, slog.String("level", record.Level.String()))
attrs = slogcommon.ReplaceAttrs(replaceAttr, []string{}, attrs...)
attrs = slogcommon.RemoveEmptyAttrs(attrs)
// handler formatter
output := slogcommon.AttrsToMap(attrs...)
labelSet := model.LabelSet{}
flatten("", output, labelSet)
return labelSet
}
// https://stackoverflow.com/questions/64419565/how-to-efficiently-flatten-a-map
func flatten(prefix string, src map[string]any, dest model.LabelSet) {
if len(prefix) > 0 {
prefix += SubAttributeSeparator
}
for k, v := range src {
switch child := v.(type) {
case map[string]any:
flatten(prefix+k, child, dest)
case []any:
for i := 0; i < len(child); i++ {
dest[model.LabelName(stripIvalidChars(prefix+k+SubAttributeSeparator+strconv.Itoa(i)))] = model.LabelValue(fmt.Sprintf("%v", child[i]))
}
default:
dest[model.LabelName(stripIvalidChars(prefix+k))] = model.LabelValue(fmt.Sprintf("%v", v))
}
}
}
func stripIvalidChars(s string) string {
// it would be more performent with a proper caching strategy+LRU
var result strings.Builder
for i := 0; i < len(s); i++ {
b := s[i]
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == '_' || b == ':' {
result.WriteByte(b)
} else {
result.WriteString(AttributeKeyInvalidCharReplacement)
}
}
return result.String()
}