182 lines
5.1 KiB
Go
182 lines
5.1 KiB
Go
|
|
package starlog
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"regexp"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestCustomRedactor(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactor(RedactorFunc(func(ctx context.Context, entry *Entry) error {
|
||
|
|
_ = ctx
|
||
|
|
entry.Message = "masked-message"
|
||
|
|
if entry.Fields == nil {
|
||
|
|
entry.Fields = make(Fields)
|
||
|
|
}
|
||
|
|
entry.Fields["token"] = "***"
|
||
|
|
entry.Err = nil
|
||
|
|
return nil
|
||
|
|
}))
|
||
|
|
|
||
|
|
logger.WithField("token", "raw-token").WithError(errors.New("boom")).Error("origin message")
|
||
|
|
got := buf.String()
|
||
|
|
|
||
|
|
if !strings.Contains(got, "masked-message") || !strings.Contains(got, "token=***") {
|
||
|
|
t.Fatalf("expected custom redactor output, got %q", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(got, "origin message") || strings.Contains(got, "raw-token") || strings.Contains(got, "boom") {
|
||
|
|
t.Fatalf("expected original sensitive values to be hidden, got %q", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSensitiveFieldRule(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.AddRedactRule(NewSensitiveFieldRule("", "password", "token"))
|
||
|
|
|
||
|
|
logger.WithFields(Fields{
|
||
|
|
"password": "p@ssw0rd",
|
||
|
|
"token": "abc123",
|
||
|
|
"user": "alice",
|
||
|
|
}).Info("login")
|
||
|
|
|
||
|
|
got := buf.String()
|
||
|
|
if !strings.Contains(got, "password=[REDACTED]") || !strings.Contains(got, "token=[REDACTED]") {
|
||
|
|
t.Fatalf("expected sensitive fields to be redacted, got %q", got)
|
||
|
|
}
|
||
|
|
if !strings.Contains(got, "user=alice") {
|
||
|
|
t.Fatalf("non-sensitive field should remain, got %q", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(got, "p@ssw0rd") || strings.Contains(got, "abc123") {
|
||
|
|
t.Fatalf("sensitive values leaked in output: %q", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMessageRegexRule(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.AddRedactRule(NewMessageRegexRule(regexp.MustCompile(`\d{11}`), "***"))
|
||
|
|
|
||
|
|
logger.Info("phone=13812345678")
|
||
|
|
got := buf.String()
|
||
|
|
if !strings.Contains(got, "phone=***") {
|
||
|
|
t.Fatalf("expected phone number to be masked, got %q", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(got, "13812345678") {
|
||
|
|
t.Fatalf("phone number should not appear in output: %q", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRedactFailMaskAllDefault(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactor(RedactorFunc(func(context.Context, *Entry) error {
|
||
|
|
return errors.New("redactor failed")
|
||
|
|
}))
|
||
|
|
|
||
|
|
logger.WithField("password", "secret").Info("hello")
|
||
|
|
got := buf.String()
|
||
|
|
if !strings.Contains(got, "[REDACTED]") {
|
||
|
|
t.Fatalf("expected fallback mask token in output, got %q", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(got, "hello") || strings.Contains(got, "secret") {
|
||
|
|
t.Fatalf("raw content should be masked on redaction failure, got %q", got)
|
||
|
|
}
|
||
|
|
if logger.GetRedactErrorCount() == 0 {
|
||
|
|
t.Fatalf("redaction error count should increase")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRedactFailDrop(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactFailMode(RedactFailDrop)
|
||
|
|
logger.SetRedactor(RedactorFunc(func(context.Context, *Entry) error {
|
||
|
|
return errors.New("drop this log")
|
||
|
|
}))
|
||
|
|
|
||
|
|
logger.Info("should disappear")
|
||
|
|
if got := buf.String(); got != "" {
|
||
|
|
t.Fatalf("log should be dropped on redaction failure, got %q", got)
|
||
|
|
}
|
||
|
|
if logger.GetRedactErrorCount() == 0 {
|
||
|
|
t.Fatalf("redaction error count should increase")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRedactFailOpen(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactFailMode(RedactFailOpen)
|
||
|
|
logger.SetRedactor(RedactorFunc(func(context.Context, *Entry) error {
|
||
|
|
return errors.New("open mode")
|
||
|
|
}))
|
||
|
|
|
||
|
|
logger.Info("keep raw")
|
||
|
|
got := buf.String()
|
||
|
|
if !strings.Contains(got, "keep raw") {
|
||
|
|
t.Fatalf("log should keep original content on open mode, got %q", got)
|
||
|
|
}
|
||
|
|
if logger.GetRedactErrorCount() == 0 {
|
||
|
|
t.Fatalf("redaction error count should increase")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSetRedactMaskToken(t *testing.T) {
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactMaskToken("***")
|
||
|
|
logger.SetRedactor(RedactorFunc(func(context.Context, *Entry) error {
|
||
|
|
return errors.New("mask token test")
|
||
|
|
}))
|
||
|
|
|
||
|
|
logger.WithField("token", "v").Info("raw")
|
||
|
|
got := buf.String()
|
||
|
|
if !strings.Contains(got, "***") {
|
||
|
|
t.Fatalf("expected custom mask token in output, got %q", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(got, "raw") || strings.Contains(got, "v") {
|
||
|
|
t.Fatalf("expected raw values to be hidden by custom mask token, got %q", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRedactionFailureReportsWriteError(t *testing.T) {
|
||
|
|
resetAsyncMetricsForTest()
|
||
|
|
defer resetAsyncMetricsForTest()
|
||
|
|
|
||
|
|
var buf bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&buf)
|
||
|
|
logger.SetRedactor(RedactorFunc(func(context.Context, *Entry) error {
|
||
|
|
return errors.New("redactor failed")
|
||
|
|
}))
|
||
|
|
|
||
|
|
observed := make(chan error, 1)
|
||
|
|
SetWriteErrorHandler(func(err error, data LogData) {
|
||
|
|
if err == nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
select {
|
||
|
|
case observed <- err:
|
||
|
|
default:
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
logger.Info("check redaction error report")
|
||
|
|
if GetWriteErrorCount() == 0 {
|
||
|
|
t.Fatalf("write error count should increase when redaction fails")
|
||
|
|
}
|
||
|
|
select {
|
||
|
|
case err := <-observed:
|
||
|
|
if !errors.Is(err, ErrRedactionFailed) {
|
||
|
|
t.Fatalf("expected ErrRedactionFailed, got %v", err)
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
t.Fatalf("write error handler should be invoked on redaction failure")
|
||
|
|
}
|
||
|
|
}
|