This commit is contained in:
yusing
2026-02-16 08:59:01 +08:00
parent 15b9635ee1
commit e4e6f6b3e8
242 changed files with 3953 additions and 3502 deletions

View File

@@ -1,11 +1,11 @@
package accesslog
import (
"errors"
"net/http"
"time"
"github.com/yusing/godoxy/internal/serialization"
gperr "github.com/yusing/goutils/errs"
)
type (
@@ -58,9 +58,9 @@ var (
ReqLoggerFormats = []Format{FormatCommon, FormatCombined, FormatJSON}
)
func (cfg *ConfigBase) Validate() gperr.Error {
func (cfg *ConfigBase) Validate() error {
if cfg.Path == "" && !cfg.Stdout {
return gperr.New("path or stdout is required")
return errors.New("path or stdout is required")
}
return nil
}

View File

@@ -59,8 +59,8 @@ func (l *ConsoleLogger) LogError(req *http.Request, err error) {
l.formatter.LogRequestZeroLog(&log, req, internalErrorResponse)
}
func (l *ConsoleLogger) LogACL(info *maxmind.IPInfo, blocked bool) {
ConsoleACLFormatter{}.LogACLZeroLog(stdoutLogger, info, blocked)
func (l *ConsoleLogger) LogACL(info *maxmind.IPInfo, blocked bool, reason string) {
ConsoleACLFormatter{}.LogACLZeroLog(stdoutLogger, info, blocked, reason)
}
func (l *ConsoleLogger) Flush() {

View File

@@ -1,6 +1,7 @@
package accesslog
import (
"fmt"
"io"
"net/http"
"sync"
@@ -11,7 +12,6 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
gperr "github.com/yusing/goutils/errs"
ioutils "github.com/yusing/goutils/io"
strutils "github.com/yusing/goutils/strings"
"github.com/yusing/goutils/synk"
@@ -131,7 +131,7 @@ func (l *fileAccessLogger) LogError(req *http.Request, err error) {
l.LogRequest(req, internalErrorResponse)
}
func (l *fileAccessLogger) LogACL(info *maxmind.IPInfo, blocked bool) {
func (l *fileAccessLogger) LogACL(info *maxmind.IPInfo, blocked bool, reason string) {
line := bytesPool.GetBuffer()
defer bytesPool.PutBuffer(line)
l.AppendACLLog(line, info, blocked)
@@ -161,9 +161,9 @@ func (l *fileAccessLogger) Rotate(result *RotateResult) (rotated bool, err error
func (l *fileAccessLogger) handleErr(err error) {
if l.errRateLimiter.Allow() {
gperr.LogError("failed to write access log", err, &l.logger)
l.logger.Err(err).Msg("failed to write access log")
} else {
gperr.LogError("too many errors, stopping access log", err, &l.logger)
l.logger.Err(err).Msg("too many errors, stopping access log")
l.task.Finish(err)
}
}
@@ -234,7 +234,7 @@ func (l *fileAccessLogger) write(data []byte) {
if err != nil {
l.handleErr(err)
} else if n < len(data) {
l.handleErr(gperr.Errorf("%w, writing %d bytes, only %d written", io.ErrShortWrite, len(data), n))
l.handleErr(fmt.Errorf("%w, writing %d bytes, only %d written", io.ErrShortWrite, len(data), n))
}
atomic.AddInt64(&l.writeCount, int64(n))
}

View File

@@ -1,12 +1,13 @@
package accesslog
import (
"errors"
"fmt"
"net"
"net/http"
"strings"
nettypes "github.com/yusing/godoxy/internal/net/types"
gperr "github.com/yusing/goutils/errs"
strutils "github.com/yusing/goutils/strings"
)
@@ -29,7 +30,7 @@ type (
} // @name CIDR
)
var ErrInvalidHTTPHeaderFilter = gperr.New("invalid http header filter")
var ErrInvalidHTTPHeaderFilter = errors.New("invalid http header filter")
func (f *LogFilter[T]) CheckKeep(req *http.Request, res *http.Response) bool {
if len(f.Values) == 0 {
@@ -59,7 +60,7 @@ func (k *HTTPHeader) Parse(v string) error {
split = append(split, "")
case 2:
default:
return ErrInvalidHTTPHeaderFilter.Subject(v)
return fmt.Errorf("%w: %s", ErrInvalidHTTPHeaderFilter, v)
}
k.Key = split[0]
k.Value = split[1]

View File

@@ -171,7 +171,7 @@ func (f ACLLogFormatter) LogACLZeroLog(logger *zerolog.Logger, info *maxmind.IPI
event.Send()
}
func (f ConsoleACLFormatter) LogACLZeroLog(logger *zerolog.Logger, info *maxmind.IPInfo, blocked bool) {
func (f ConsoleACLFormatter) LogACLZeroLog(logger *zerolog.Logger, info *maxmind.IPInfo, blocked bool, reason string) {
event := logger.Info()
if info.City != nil {
if isoCode := info.City.Country.IsoCode; isoCode != "" {
@@ -186,6 +186,10 @@ func (f ConsoleACLFormatter) LogACLZeroLog(logger *zerolog.Logger, info *maxmind
action = "denied"
}
if reason != "" {
event.Str("reason", reason)
}
// NOTE: zerolog will append a newline to the buffer
event.Msgf("request %s from %s", action, info.Str)
}

View File

@@ -50,9 +50,9 @@ func (m *MultiAccessLogger) LogError(req *http.Request, err error) {
}
}
func (m *MultiAccessLogger) LogACL(info *maxmind.IPInfo, blocked bool) {
func (m *MultiAccessLogger) LogACL(info *maxmind.IPInfo, blocked bool, reason string) {
for _, accessLogger := range m.accessLoggers {
accessLogger.LogACL(info, blocked)
accessLogger.LogACL(info, blocked, reason)
}
}

View File

@@ -116,7 +116,7 @@ func TestMultiAccessLoggerLogACL(t *testing.T) {
Str: "192.168.1.1",
}
logger.LogACL(info, false)
logger.LogACL(info, false, "test reason")
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
@@ -252,7 +252,7 @@ func TestMultiAccessLoggerMixedOperations(t *testing.T) {
cfg2 := DefaultACLLoggerConfig()
cfg2.LogAllowed = true
aclLogger := NewMultiAccessLogger(testTask, cfg2, writers)
aclLogger.LogACL(info, false)
aclLogger.LogACL(info, false, "test reason")
logger.Flush()

View File

@@ -1,10 +1,10 @@
package accesslog
import (
"errors"
"fmt"
"strconv"
gperr "github.com/yusing/goutils/errs"
strutils "github.com/yusing/goutils/strings"
)
@@ -15,8 +15,8 @@ type Retention struct {
} // @name LogRetention
var (
ErrInvalidSyntax = gperr.New("invalid syntax")
ErrZeroValue = gperr.New("zero value")
ErrInvalidSyntax = errors.New("invalid syntax")
ErrZeroValue = errors.New("zero value")
)
// see back_scanner_test.go#L210 for benchmarks
@@ -34,7 +34,7 @@ var defaultChunkSize = 32 * kilobyte
func (r *Retention) Parse(v string) (err error) {
split := strutils.SplitSpace(v)
if len(split) != 2 {
return ErrInvalidSyntax.Subject(v)
return fmt.Errorf("%w: %s", ErrInvalidSyntax, v)
}
switch split[0] {
case "last":
@@ -64,7 +64,7 @@ func (r *Retention) Parse(v string) (err error) {
case "GB":
r.KeepSize = n * gigabyte
default:
return ErrInvalidSyntax.Subject("unit " + split[1])
return fmt.Errorf("%w: unit %s", ErrInvalidSyntax, split[1])
}
}
if !r.IsValid() {

View File

@@ -1,6 +1,8 @@
package accesslog
import (
"errors"
"fmt"
"strconv"
gperr "github.com/yusing/goutils/errs"
@@ -12,7 +14,7 @@ type StatusCodeRange struct {
End int
} // @name StatusCodeRange
var ErrInvalidStatusCodeRange = gperr.New("invalid status code range")
var ErrInvalidStatusCodeRange = errors.New("invalid status code range")
func (r *StatusCodeRange) Includes(code int) bool {
return r.Start <= code && code <= r.End
@@ -25,7 +27,7 @@ func (r *StatusCodeRange) Parse(v string) error {
case 1:
start, err := strconv.Atoi(split[0])
if err != nil {
return gperr.Wrap(err)
return err
}
r.Start = start
r.End = start
@@ -40,7 +42,7 @@ func (r *StatusCodeRange) Parse(v string) error {
r.End = end
return nil
default:
return ErrInvalidStatusCodeRange.Subject(v)
return fmt.Errorf("%w: %s", ErrInvalidStatusCodeRange, v)
}
}

View File

@@ -13,7 +13,7 @@ type (
AccessLogger interface {
LogRequest(req *http.Request, res *http.Response)
LogError(req *http.Request, err error)
LogACL(info *maxmind.IPInfo, blocked bool)
LogACL(info *maxmind.IPInfo, blocked bool, reason string)
Config() *Config
@@ -35,9 +35,9 @@ type (
}
ACLFormatter interface {
// AppendACLLog appends a log line to line with or without a trailing newline
AppendACLLog(line *bytes.Buffer, info *maxmind.IPInfo, blocked bool)
AppendACLLog(line *bytes.Buffer, info *maxmind.IPInfo, blocked bool, reason string)
// LogACLZeroLog logs an ACL log to the logger
LogACLZeroLog(logger *zerolog.Logger, info *maxmind.IPInfo, blocked bool)
LogACLZeroLog(logger *zerolog.Logger, info *maxmind.IPInfo, blocked bool, reason string)
}
)

View File

@@ -87,8 +87,28 @@ func multiWriter(out ...io.Writer) io.Writer {
func NewLogger(out ...io.Writer) zerolog.Logger {
writer := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
w.Out = diodeMultiWriter(out...)
if !common.IsTest {
w.Out = diodeMultiWriter(out...)
} else {
w.Out = multiWriter(out...)
}
w.TimeFormat = timeFmt
w.FormatPrepare = func(evt map[string]any) error {
// move error field to join message if it's multiline
if err, ok := evt[zerolog.ErrorFieldName].(string); ok {
if strings.Count(err, "\n") == 0 {
return nil
}
msg, ok := evt[zerolog.MessageFieldName].(string)
if ok && msg != "" {
evt[zerolog.MessageFieldName] = msg + "\n" + err
} else {
evt[zerolog.MessageFieldName] = err
}
delete(evt, zerolog.ErrorFieldName)
}
return nil
}
w.FormatMessage = func(msgI any) string { // pad spaces for each line
if msgI == nil {
return ""