merge: access log rotation and enhancements

This commit is contained in:
yusing
2025-04-24 15:29:18 +08:00
parent d668b03175
commit 31812430f1
29 changed files with 1600 additions and 581 deletions

View File

@@ -1,8 +1,11 @@
package accesslog
import (
"iter"
"net/http"
"net/url"
"github.com/rs/zerolog"
)
type (
@@ -21,83 +24,181 @@ const (
RedactedValue = "REDACTED"
)
func processMap[V any](cfg *FieldConfig, m map[string]V, redactedV V) map[string]V {
type mapStringStringIter interface {
Iter(yield func(k string, v []string) bool)
MarshalZerologObject(e *zerolog.Event)
}
type mapStringStringSlice struct {
m map[string][]string
}
func (m mapStringStringSlice) Iter(yield func(k string, v []string) bool) {
for k, v := range m.m {
if !yield(k, v) {
return
}
}
}
func (m mapStringStringSlice) MarshalZerologObject(e *zerolog.Event) {
for k, v := range m.m {
e.Strs(k, v)
}
}
type mapStringStringRedacted struct {
m map[string][]string
}
func (m mapStringStringRedacted) Iter(yield func(k string, v []string) bool) {
for k := range m.m {
if !yield(k, []string{RedactedValue}) {
return
}
}
}
func (m mapStringStringRedacted) MarshalZerologObject(e *zerolog.Event) {
for k, v := range m.Iter {
e.Strs(k, v)
}
}
type mapStringStringSliceWithConfig struct {
m map[string][]string
cfg *FieldConfig
}
func (m mapStringStringSliceWithConfig) Iter(yield func(k string, v []string) bool) {
var mode FieldMode
var ok bool
for k, v := range m.m {
if mode, ok = m.cfg.Config[k]; !ok {
mode = m.cfg.Default
}
switch mode {
case FieldModeKeep:
if !yield(k, v) {
return
}
case FieldModeRedact:
if !yield(k, []string{RedactedValue}) {
return
}
}
}
}
func (m mapStringStringSliceWithConfig) MarshalZerologObject(e *zerolog.Event) {
for k, v := range m.Iter {
e.Strs(k, v)
}
}
type mapStringStringDrop struct{}
func (m mapStringStringDrop) Iter(yield func(k string, v []string) bool) {}
func (m mapStringStringDrop) MarshalZerologObject(e *zerolog.Event) {}
var mapStringStringDropIter mapStringStringIter = mapStringStringDrop{}
func mapIter[Map http.Header | url.Values](cfg *FieldConfig, m Map) mapStringStringIter {
if len(cfg.Config) == 0 {
switch cfg.Default {
case FieldModeKeep:
return m
return mapStringStringSlice{m: m}
case FieldModeDrop:
return nil
return mapStringStringDropIter
case FieldModeRedact:
redacted := make(map[string]V)
for k := range m {
redacted[k] = redactedV
}
return redacted
return mapStringStringRedacted{m: m}
}
}
return mapStringStringSliceWithConfig{m: m, cfg: cfg}
}
if len(m) == 0 {
return m
}
type slice[V any] struct {
s []V
getKey func(V) string
getVal func(V) string
cfg *FieldConfig
}
newMap := make(map[string]V, len(m))
for k := range m {
type sliceIter interface {
Iter(yield func(k string, v string) bool)
MarshalZerologObject(e *zerolog.Event)
}
func (s *slice[V]) Iter(yield func(k string, v string) bool) {
for _, v := range s.s {
k := s.getKey(v)
var mode FieldMode
var ok bool
if mode, ok = cfg.Config[k]; !ok {
mode = cfg.Default
if mode, ok = s.cfg.Config[k]; !ok {
mode = s.cfg.Default
}
switch mode {
case FieldModeKeep:
newMap[k] = m[k]
if !yield(k, s.getVal(v)) {
return
}
case FieldModeRedact:
newMap[k] = redactedV
if !yield(k, RedactedValue) {
return
}
}
}
return newMap
}
func processSlice[V any, VReturn any](cfg *FieldConfig, s []V, getKey func(V) string, convert func(V) VReturn, redact func(V) VReturn) map[string]VReturn {
type sliceDrop struct{}
func (s sliceDrop) Iter(yield func(k string, v string) bool) {}
func (s sliceDrop) MarshalZerologObject(e *zerolog.Event) {}
var sliceDropIter sliceIter = sliceDrop{}
func (s *slice[V]) MarshalZerologObject(e *zerolog.Event) {
for k, v := range s.Iter {
e.Str(k, v)
}
}
func iterSlice[V any](cfg *FieldConfig, s []V, getKey func(V) string, getVal func(V) string) sliceIter {
if len(s) == 0 ||
len(cfg.Config) == 0 && cfg.Default == FieldModeDrop {
return nil
return sliceDropIter
}
newMap := make(map[string]VReturn, len(s))
for _, v := range s {
var mode FieldMode
var ok bool
k := getKey(v)
if mode, ok = cfg.Config[k]; !ok {
mode = cfg.Default
}
switch mode {
case FieldModeKeep:
newMap[k] = convert(v)
case FieldModeRedact:
newMap[k] = redact(v)
}
}
return newMap
return &slice[V]{s: s, getKey: getKey, getVal: getVal, cfg: cfg}
}
func (cfg *FieldConfig) ProcessHeaders(headers http.Header) http.Header {
return processMap(cfg, headers, []string{RedactedValue})
func (cfg *FieldConfig) IterHeaders(headers http.Header) iter.Seq2[string, []string] {
return mapIter(cfg, headers).Iter
}
func (cfg *FieldConfig) ProcessQuery(q url.Values) url.Values {
return processMap(cfg, q, []string{RedactedValue})
func (cfg *FieldConfig) ZerologHeaders(headers http.Header) zerolog.LogObjectMarshaler {
return mapIter(cfg, headers)
}
func (cfg *FieldConfig) ProcessCookies(cookies []*http.Cookie) map[string]string {
return processSlice(cfg, cookies,
func(c *http.Cookie) string {
return c.Name
},
func(c *http.Cookie) string {
return c.Value
},
func(c *http.Cookie) string {
return RedactedValue
})
func (cfg *FieldConfig) IterQuery(q url.Values) iter.Seq2[string, []string] {
return mapIter(cfg, q).Iter
}
func (cfg *FieldConfig) ZerologQuery(q url.Values) zerolog.LogObjectMarshaler {
return mapIter(cfg, q)
}
func cookieGetKey(c *http.Cookie) string {
return c.Name
}
func cookieGetValue(c *http.Cookie) string {
return c.Value
}
func (cfg *FieldConfig) IterCookies(cookies []*http.Cookie) iter.Seq2[string, string] {
return iterSlice(cfg, cookies, cookieGetKey, cookieGetValue).Iter
}
func (cfg *FieldConfig) ZerologCookies(cookies []*http.Cookie) zerolog.LogObjectMarshaler {
return iterSlice(cfg, cookies, cookieGetKey, cookieGetValue)
}