parent
36be0d28da
commit
a02810700c
11 changed files with 587 additions and 0 deletions
@ -0,0 +1,80 @@ |
||||
package log |
||||
|
||||
type FilterOption func(*Filter) |
||||
|
||||
const fuzzyStr = "***" |
||||
|
||||
func FilterLevel(level Level) FilterOption { |
||||
return func(opts *Filter) { |
||||
opts.level = level |
||||
} |
||||
} |
||||
func FilterKey(key ...string) FilterOption { |
||||
return func(opts *Filter) { |
||||
for _, v := range key { |
||||
opts.key[v] = struct{}{} |
||||
} |
||||
} |
||||
} |
||||
func FilterValue(value ...string) FilterOption { |
||||
return func(opts *Filter) { |
||||
for _, v := range value { |
||||
opts.value[v] = struct{}{} |
||||
} |
||||
} |
||||
} |
||||
func FilterFunc(f func(level Level, keyvals ...any) bool) FilterOption { |
||||
return func(opts *Filter) { |
||||
opts.filter = f |
||||
} |
||||
} |
||||
|
||||
type Filter struct { |
||||
logger Logger |
||||
level Level |
||||
key map[any]struct{} |
||||
value map[any]struct{} |
||||
filter func(level Level, keyvals ...any) bool |
||||
} |
||||
|
||||
func NewFilter(logger Logger, opts ...FilterOption) *Filter { |
||||
options := &Filter{ |
||||
logger: logger, |
||||
key: make(map[any]struct{}), |
||||
value: make(map[any]struct{}), |
||||
} |
||||
for _, o := range opts { |
||||
o(options) |
||||
} |
||||
return options |
||||
} |
||||
func (f *Filter) Log(level Level, keyvals ...any) error { |
||||
if level < f.level { |
||||
return nil |
||||
} |
||||
var prefixkv []any |
||||
l, ok := f.logger.(*logger) |
||||
if ok && len(l.prefix) > 0 { |
||||
prefixkv = make([]any, 0, len(l.prefix)) |
||||
prefixkv = append(prefixkv, l.prefix...) |
||||
} |
||||
if f.filter != nil && (f.filter(level, prefixkv...) || f.filter(level, keyvals)) { |
||||
return nil |
||||
} |
||||
if len(f.key) > 0 || len(f.value) > 0 { |
||||
for i := 0; i < len(keyvals); i += 2 { |
||||
v := i + 1 |
||||
if v >= len(keyvals) { |
||||
continue |
||||
} |
||||
if _, ok := f.key[keyvals[i]]; ok { |
||||
keyvals[v] = fuzzyStr |
||||
} |
||||
|
||||
if _, ok := f.key[keyvals[v]]; ok { |
||||
keyvals[v] = fuzzyStr |
||||
} |
||||
} |
||||
} |
||||
return f.logger.Log(level, keyvals...) |
||||
} |
@ -0,0 +1,31 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func TestNewFilter(t *testing.T) { |
||||
logger := With(DefaultLogger, |
||||
//"ts", DefaultTimestamp, "caller", DefaultCaller
|
||||
) |
||||
log := NewHelper(NewFilter(logger, |
||||
FilterKey("username"), |
||||
FilterValue("hello"), |
||||
FilterFunc(func(level Level, keyvals ...any) bool { |
||||
if level == LevelWarn { |
||||
return true |
||||
} |
||||
for i := 0; i < len(keyvals); i++ { |
||||
if keyvals[i] == "password" { |
||||
keyvals[i+1] = fuzzyStr |
||||
} |
||||
} |
||||
return false |
||||
}), |
||||
)) |
||||
log.Log(LevelDebug, "msg", "test debug") |
||||
log.Info("hello") |
||||
log.Infow("password", "123456") |
||||
log.Infow("username", "kratos") |
||||
log.Warn("warn log") |
||||
} |
@ -0,0 +1,114 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"os" |
||||
"sync" |
||||
) |
||||
|
||||
var global = &loggerAppliance{} |
||||
|
||||
type loggerAppliance struct { |
||||
lock sync.Mutex |
||||
Logger |
||||
} |
||||
|
||||
func init() { |
||||
global.SetLogger(DefaultLogger) |
||||
} |
||||
func (l *loggerAppliance) SetLogger(in Logger) { |
||||
l.lock.Lock() |
||||
defer l.lock.Unlock() |
||||
l.Logger = in |
||||
} |
||||
func SetLogger(logger Logger) { |
||||
global.SetLogger(logger) |
||||
} |
||||
func GetLogger() Logger { |
||||
return global |
||||
} |
||||
func Log(level Level, keyvals ...any) { |
||||
_ = global.Log(level, keyvals...) |
||||
} |
||||
func Context(ctx context.Context) *Helper { |
||||
return NewHelper(WithContext(ctx, global.Logger)) |
||||
} |
||||
|
||||
// Debug logs a message at debug level.
|
||||
func Debug(a ...interface{}) { |
||||
_ = global.Log(LevelDebug, DefaultMessageKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Debugf logs a message at debug level.
|
||||
func Debugf(format string, a ...interface{}) { |
||||
_ = global.Log(LevelDebug, DefaultMessageKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Debugw logs a message at debug level.
|
||||
func Debugw(keyvals ...interface{}) { |
||||
_ = global.Log(LevelDebug, keyvals...) |
||||
} |
||||
|
||||
// Info logs a message at info level.
|
||||
func Info(a ...interface{}) { |
||||
_ = global.Log(LevelInfo, DefaultMessageKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Infof logs a message at info level.
|
||||
func Infof(format string, a ...interface{}) { |
||||
_ = global.Log(LevelInfo, DefaultMessageKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Infow logs a message at info level.
|
||||
func Infow(keyvals ...interface{}) { |
||||
_ = global.Log(LevelInfo, keyvals...) |
||||
} |
||||
|
||||
// Warn logs a message at warn level.
|
||||
func Warn(a ...interface{}) { |
||||
_ = global.Log(LevelWarn, DefaultMessageKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Warnf logs a message at warnf level.
|
||||
func Warnf(format string, a ...interface{}) { |
||||
_ = global.Log(LevelWarn, DefaultMessageKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Warnw logs a message at warnf level.
|
||||
func Warnw(keyvals ...interface{}) { |
||||
_ = global.Log(LevelWarn, keyvals...) |
||||
} |
||||
|
||||
// Error logs a message at error level.
|
||||
func Error(a ...interface{}) { |
||||
_ = global.Log(LevelError, DefaultMessageKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Errorf logs a message at error level.
|
||||
func Errorf(format string, a ...interface{}) { |
||||
_ = global.Log(LevelError, DefaultMessageKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Errorw logs a message at error level.
|
||||
func Errorw(keyvals ...interface{}) { |
||||
_ = global.Log(LevelError, keyvals...) |
||||
} |
||||
|
||||
// Fatal logs a message at fatal level.
|
||||
func Fatal(a ...interface{}) { |
||||
_ = global.Log(LevelFatal, DefaultMessageKey, fmt.Sprint(a...)) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalf logs a message at fatal level.
|
||||
func Fatalf(format string, a ...interface{}) { |
||||
_ = global.Log(LevelFatal, DefaultMessageKey, fmt.Sprintf(format, a...)) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalw logs a message at fatal level.
|
||||
func Fatalw(keyvals ...interface{}) { |
||||
_ = global.Log(LevelFatal, keyvals...) |
||||
os.Exit(1) |
||||
} |
@ -0,0 +1,9 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func TestDebug(t *testing.T) { |
||||
Debug("debug") |
||||
} |
@ -0,0 +1,121 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"os" |
||||
) |
||||
|
||||
var DefaultMessageKey = "msg" |
||||
|
||||
type Option func(*Helper) |
||||
type Helper struct { |
||||
logger Logger |
||||
msgKey string |
||||
} |
||||
|
||||
func WithMessageKey(k string) Option { |
||||
return func(opts *Helper) { |
||||
opts.msgKey = k |
||||
} |
||||
} |
||||
|
||||
func NewHelper(logger Logger, opts ...Option) *Helper { |
||||
options := &Helper{ |
||||
msgKey: DefaultMessageKey, |
||||
logger: logger, |
||||
} |
||||
for _, o := range opts { |
||||
o(options) |
||||
} |
||||
return options |
||||
} |
||||
func (h *Helper) WithContext(ctx context.Context) *Helper { |
||||
return &Helper{ |
||||
logger: WithContext(ctx, h.logger), |
||||
msgKey: h.msgKey, |
||||
} |
||||
} |
||||
|
||||
// Log Print log by level and keyvals.
|
||||
func (h *Helper) Log(level Level, keyvals ...interface{}) { |
||||
_ = h.logger.Log(level, keyvals...) |
||||
} |
||||
|
||||
// Debug logs a message at debug level.
|
||||
func (h *Helper) Debug(a ...interface{}) { |
||||
_ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Debugf logs a message at debug level.
|
||||
func (h *Helper) Debugf(format string, a ...interface{}) { |
||||
_ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Debugw logs a message at debug level.
|
||||
func (h *Helper) Debugw(keyvals ...interface{}) { |
||||
_ = h.logger.Log(LevelDebug, keyvals...) |
||||
} |
||||
|
||||
// Info logs a message at info level.
|
||||
func (h *Helper) Info(a ...interface{}) { |
||||
_ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Infof logs a message at info level.
|
||||
func (h *Helper) Infof(format string, a ...interface{}) { |
||||
_ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Infow logs a message at info level.
|
||||
func (h *Helper) Infow(keyvals ...interface{}) { |
||||
_ = h.logger.Log(LevelInfo, keyvals...) |
||||
} |
||||
|
||||
// Warn logs a message at warn level.
|
||||
func (h *Helper) Warn(a ...interface{}) { |
||||
_ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Warnf logs a message at warnf level.
|
||||
func (h *Helper) Warnf(format string, a ...interface{}) { |
||||
_ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Warnw logs a message at warnf level.
|
||||
func (h *Helper) Warnw(keyvals ...interface{}) { |
||||
_ = h.logger.Log(LevelWarn, keyvals...) |
||||
} |
||||
|
||||
// Error logs a message at error level.
|
||||
func (h *Helper) Error(a ...interface{}) { |
||||
_ = h.logger.Log(LevelError, h.msgKey, fmt.Sprint(a...)) |
||||
} |
||||
|
||||
// Errorf logs a message at error level.
|
||||
func (h *Helper) Errorf(format string, a ...interface{}) { |
||||
_ = h.logger.Log(LevelError, h.msgKey, fmt.Sprintf(format, a...)) |
||||
} |
||||
|
||||
// Errorw logs a message at error level.
|
||||
func (h *Helper) Errorw(keyvals ...interface{}) { |
||||
_ = h.logger.Log(LevelError, keyvals...) |
||||
} |
||||
|
||||
// Fatal logs a message at fatal level.
|
||||
func (h *Helper) Fatal(a ...interface{}) { |
||||
_ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprint(a...)) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalf logs a message at fatal level.
|
||||
func (h *Helper) Fatalf(format string, a ...interface{}) { |
||||
_ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprintf(format, a...)) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalw logs a message at fatal level.
|
||||
func (h *Helper) Fatalw(keyvals ...interface{}) { |
||||
_ = h.logger.Log(LevelFatal, keyvals...) |
||||
os.Exit(1) |
||||
} |
@ -0,0 +1,9 @@ |
||||
package log |
||||
|
||||
import "testing" |
||||
|
||||
func TestNewHelper(t *testing.T) { |
||||
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller) |
||||
log := NewHelper(logger) |
||||
log.Debug("debug") |
||||
} |
@ -0,0 +1,49 @@ |
||||
package log |
||||
|
||||
import "strings" |
||||
|
||||
type Level int8 |
||||
|
||||
const LevelKey = "level" |
||||
const ( |
||||
LevelDebug Level = iota - 1 |
||||
LevelInfo |
||||
LevelWarn |
||||
LevelError |
||||
LevelFatal |
||||
) |
||||
|
||||
func (l Level) Key() string { |
||||
return LevelKey |
||||
} |
||||
func (l Level) String() string { |
||||
switch l { |
||||
case LevelDebug: |
||||
return "DEBUG" |
||||
case LevelInfo: |
||||
return "INFO" |
||||
case LevelWarn: |
||||
return "WARN" |
||||
case LevelError: |
||||
return "ERROR" |
||||
case LevelFatal: |
||||
return "FATAL" |
||||
default: |
||||
return "" |
||||
} |
||||
} |
||||
func ParseLevel(s string) Level { |
||||
switch strings.ToUpper(s) { |
||||
case "DEBUG": |
||||
return LevelDebug |
||||
case "INFO": |
||||
return LevelInfo |
||||
case "WARN": |
||||
return LevelWarn |
||||
case "ERROR": |
||||
return LevelError |
||||
case "FATAL": |
||||
return LevelFatal |
||||
} |
||||
return LevelInfo |
||||
} |
@ -0,0 +1,65 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"context" |
||||
"log" |
||||
) |
||||
|
||||
var DefaultLogger = NewStdLogger(log.Writer()) |
||||
|
||||
type Logger interface { |
||||
Log(level Level, keyvals ...any) error |
||||
} |
||||
|
||||
type logger struct { |
||||
logger Logger |
||||
prefix []any |
||||
hasValuer bool |
||||
ctx context.Context |
||||
} |
||||
|
||||
func (l *logger) Log(level Level, keyvals ...any) error { |
||||
kvs := make([]any, 0, len(l.prefix)+len(keyvals)) |
||||
kvs = append(kvs, l.prefix...) |
||||
if l.hasValuer { |
||||
bindValues(l.ctx, kvs) |
||||
} |
||||
kvs = append(kvs, keyvals...) |
||||
return l.logger.Log(level, kvs...) |
||||
} |
||||
|
||||
func With(l Logger, kv ...any) Logger { |
||||
c, ok := l.(*logger) |
||||
if !ok { |
||||
return &logger{ |
||||
logger: l, |
||||
prefix: kv, |
||||
hasValuer: containsValuer(kv), |
||||
ctx: context.Background(), |
||||
} |
||||
} |
||||
kvs := make([]any, 0, len(c.prefix)+len(kv)) |
||||
kvs = append(kvs, c.prefix...) |
||||
kvs = append(kvs, kv...) |
||||
return &logger{ |
||||
logger: c.logger, |
||||
prefix: kvs, |
||||
hasValuer: containsValuer(kvs), |
||||
ctx: c.ctx, |
||||
} |
||||
} |
||||
func WithContext(ctx context.Context, l Logger) Logger { |
||||
c, ok := l.(*logger) |
||||
if !ok { |
||||
return &logger{ |
||||
logger: l, |
||||
ctx: ctx, |
||||
} |
||||
} |
||||
return &logger{ |
||||
logger: c.logger, |
||||
prefix: c.prefix, |
||||
hasValuer: c.hasValuer, |
||||
ctx: ctx, |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func Test_logger_Log(t *testing.T) { |
||||
logger := DefaultLogger |
||||
logger = With(logger, "ts", DefaultTimestamp) |
||||
logger = With(logger, "call", DefaultCaller) |
||||
_ = logger.Log(LevelInfo, "key1", "val1") |
||||
} |
@ -0,0 +1,42 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"log" |
||||
"sync" |
||||
) |
||||
|
||||
type stdLogger struct { |
||||
log *log.Logger |
||||
pool *sync.Pool |
||||
} |
||||
|
||||
func NewStdLogger(w io.Writer) Logger { |
||||
return &stdLogger{ |
||||
log: log.New(w, "", 0), |
||||
pool: &sync.Pool{ |
||||
New: func() any { |
||||
return new(bytes.Buffer) |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
func (l *stdLogger) Log(level Level, keyvals ...any) error { |
||||
if len(keyvals) == 0 { |
||||
return nil |
||||
} |
||||
if len(keyvals)&1 == 1 { |
||||
keyvals = append(keyvals, "KEYVALS UNPAIRED") |
||||
} |
||||
buf := l.pool.Get().(*bytes.Buffer) |
||||
buf.WriteString(level.String()) |
||||
for i := 0; i < len(keyvals); i += 2 { |
||||
_, _ = fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1]) |
||||
} |
||||
_ = l.log.Output(4, buf.String()) |
||||
buf.Reset() |
||||
l.pool.Put(buf) |
||||
return nil |
||||
} |
@ -0,0 +1,55 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"context" |
||||
"runtime" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
DefaultCaller = Caller(4) |
||||
DefaultTimestamp = Timestamp(time.RFC3339) |
||||
) |
||||
|
||||
type Valuer func(ctx context.Context) any |
||||
|
||||
func Value(ctx context.Context, v any) any { |
||||
if v, ok := v.(Valuer); ok { |
||||
return v(ctx) |
||||
} |
||||
return v |
||||
} |
||||
func Caller(depth int) Valuer { |
||||
return func(ctx context.Context) any { |
||||
_, file, line, _ := runtime.Caller(depth) |
||||
idx := strings.LastIndexByte(file, '/') |
||||
if idx == -1 { |
||||
return file[idx+1:] + ":" + strconv.Itoa(line) |
||||
} |
||||
idx = strings.LastIndexByte(file[:idx], '/') |
||||
return file[idx+1:] + ":" + strconv.Itoa(line) |
||||
} |
||||
} |
||||
func Timestamp(layout string) Valuer { |
||||
return func(ctx context.Context) any { |
||||
return time.Now().Format(layout) |
||||
} |
||||
} |
||||
|
||||
func bindValues(ctx context.Context, keyvals []any) { |
||||
for i := 1; i < len(keyvals); i += 2 { |
||||
if v, ok := keyvals[i].(Valuer); ok { |
||||
keyvals[i] = v(ctx) |
||||
} |
||||
} |
||||
} |
||||
func containsValuer(keyvals []any) bool { |
||||
for i := 1; i < len(keyvals); i += 2 { |
||||
if _, ok := keyvals[i].(Valuer); ok { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
Loading…
Reference in new issue