李伟乐 2 years ago
parent 36be0d28da
commit a02810700c
  1. 80
      log/filter.go
  2. 31
      log/filter_test.go
  3. 114
      log/global.go
  4. 9
      log/global_test.go
  5. 121
      log/helper.go
  6. 9
      log/helper_test.go
  7. 49
      log/level.go
  8. 65
      log/log.go
  9. 12
      log/log_test.go
  10. 42
      log/std.go
  11. 55
      log/value.go

@ -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…
Cancel
Save