add log;
							parent
							
								
									e484aaa1f9
								
							
						
					
					
						commit
						179e00910d
					
				@ -0,0 +1,8 @@
 | 
			
		||||
module git.blauwelle.com/go/crate/log
 | 
			
		||||
 | 
			
		||||
go 1.20
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	git.blauwelle.com/go/crate/runtimehelper v0.1.0
 | 
			
		||||
	git.blauwelle.com/go/crate/synchelper v0.1.0
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,4 @@
 | 
			
		||||
git.blauwelle.com/go/crate/runtimehelper v0.1.0 h1:qNhtnt9YmHXNHKsGRbwD3AZ3pezpOwrbmX1o9Bz532I=
 | 
			
		||||
git.blauwelle.com/go/crate/runtimehelper v0.1.0/go.mod h1:yVMA0GkO9AS7iuPmalHKeWyv9en0JWj25rY1vpTuHhk=
 | 
			
		||||
git.blauwelle.com/go/crate/synchelper v0.1.0 h1:4yEXpshkklaws/57P94xN5bA3NmyyKGcZqYmzd6QIK4=
 | 
			
		||||
git.blauwelle.com/go/crate/synchelper v0.1.0/go.mod h1:2JkfH+7sF0Q0wiIaDOqG42ZLO5JxpcMfSoyy7db4Y2g=
 | 
			
		||||
@ -0,0 +1,150 @@
 | 
			
		||||
// log 实现了全局的日志处理
 | 
			
		||||
//
 | 
			
		||||
// 这个包里定义的函数都是对全局的 [logsdk.Logger] 对象的封装.
 | 
			
		||||
// 全局的 Logger 不能重新赋值,
 | 
			
		||||
// Logger 的配置方法和日志方法可以并发调用.
 | 
			
		||||
 | 
			
		||||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.blauwelle.com/go/crate/log/logsdk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	Level = logsdk.Level
 | 
			
		||||
	Field = logsdk.Field
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	LevelPanic = logsdk.LevelPanic
 | 
			
		||||
	LevelFatal = logsdk.LevelFatal
 | 
			
		||||
	LevelError = logsdk.LevelError
 | 
			
		||||
	LevelWarn  = logsdk.LevelWarn
 | 
			
		||||
	LevelInfo  = logsdk.LevelInfo
 | 
			
		||||
	LevelDebug = logsdk.LevelDebug
 | 
			
		||||
	LevelTrace = logsdk.LevelTrace
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AddCallerSkip 增加调用 [runtime.Callers] 时的 skip 参数,
 | 
			
		||||
// 当通过装饰器等方式封装 Entry 导致增加调用 Entry 方法的深度时使用 AddCallerSkip 调整 skip,
 | 
			
		||||
// 直接在需要日志的地方调用 Entry 的方法时不需要 AddCallerSkip.
 | 
			
		||||
func AddCallerSkip(n int) logsdk.Entry {
 | 
			
		||||
	return globalLogger.AddCallerSkip(n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithField 增加1组键值对
 | 
			
		||||
func WithField(key string, value any) logsdk.Entry {
 | 
			
		||||
	return globalLogger.WithField(key, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithFields 增加键值对
 | 
			
		||||
func WithFields(fields ...logsdk.Field) logsdk.Entry {
 | 
			
		||||
	return globalLogger.WithFields(fields...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithTime 设置日志的时间,
 | 
			
		||||
// Entry 默认使用调用 Log 等最终方法的时间作为日志的时间.
 | 
			
		||||
func WithTime(t time.Time) logsdk.Entry {
 | 
			
		||||
	return globalLogger.WithTime(t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithReportCaller 设置 [logsdk.Entry] 是否收集调用记录
 | 
			
		||||
func WithReportCaller(reportCaller bool) logsdk.Entry {
 | 
			
		||||
	return globalLogger.WithReportCaller(reportCaller)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithReportStack 设置 [logsdk.Entry] 是否收集调用栈
 | 
			
		||||
func WithReportStack(reportStack bool) logsdk.Entry {
 | 
			
		||||
	return globalLogger.WithReportStack(reportStack)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log 输出日志
 | 
			
		||||
func Log(ctx context.Context, level Level, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Log(ctx, level, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace 输出 LevelTrace 等级的日志
 | 
			
		||||
func Trace(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Trace(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug 输出 LevelDebug 等级的日志
 | 
			
		||||
func Debug(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Debug(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info 输出 LevelInfo 等级的日志
 | 
			
		||||
func Info(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Info(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warn 输出 LevelWarn 等级的日志
 | 
			
		||||
func Warn(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Warn(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error 输出 LevelError 等级的日志
 | 
			
		||||
func Error(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Error(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatal 输出 LevelFatal 等级的日志并调用 Logger.Exit
 | 
			
		||||
func Fatal(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Fatal(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Panic 输出 LevelPanic 等级的日志后执行 panic,
 | 
			
		||||
// 即使 Logger 日志等级高于 LevelPanic 也会 panic.
 | 
			
		||||
func Panic(ctx context.Context, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Panic(ctx, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logf 格式化输出日志
 | 
			
		||||
func Logf(ctx context.Context, level Level, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Logf(ctx, level, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tracef 格式化输出 LevelTrace 等级的日志
 | 
			
		||||
func Tracef(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Tracef(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugf 格式化输出 LevelDebug 等级的日志
 | 
			
		||||
func Debugf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Debugf(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Infof 格式化输出 LevelInfo 等级的日志
 | 
			
		||||
func Infof(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Infof(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warnf 格式化输出 LevelWarn 等级的日志
 | 
			
		||||
func Warnf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Warnf(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errorf 格式化输出 LevelError 等级的日志
 | 
			
		||||
func Errorf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Errorf(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatalf 格式化输出 LevelFatal 等级的日志并调用 Logger.Exit
 | 
			
		||||
func Fatalf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Fatalf(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Panicf 格式化输出 LevelPanic 等级的日志
 | 
			
		||||
func Panicf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	globalLogger.AddCallerSkip(1).Panicf(ctx, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logger 返回全局 logger, 通常用来在程序启动时对全局 logger 进行配置,
 | 
			
		||||
// 业务代码处理日志时直接使用这个包里定义的日志函数.
 | 
			
		||||
func Logger() *logsdk.Logger {
 | 
			
		||||
	return globalLogger
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,220 @@
 | 
			
		||||
package logsdk
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.blauwelle.com/go/crate/runtimehelper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Entry 包含日志所需的全部中间信息并负责输出日志
 | 
			
		||||
type Entry struct {
 | 
			
		||||
	logger            *Logger
 | 
			
		||||
	time              time.Time
 | 
			
		||||
	fields            []Field
 | 
			
		||||
	callerSkip        int
 | 
			
		||||
	reportCaller      bool
 | 
			
		||||
	isReportCallerSet bool
 | 
			
		||||
	reportStack       bool
 | 
			
		||||
	isReportStackSet  bool
 | 
			
		||||
	initialized       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry Entry) copy() Entry {
 | 
			
		||||
	if entry.initialized {
 | 
			
		||||
		return entry
 | 
			
		||||
	}
 | 
			
		||||
	return entry.logger.newEntry()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddCallerSkip 增加调用 [runtime.Callers] 时的 skip 参数,
 | 
			
		||||
// 当通过装饰器等方式封装 Entry 导致增加调用 Entry 方法的深度时使用 AddCallerSkip 调整 skip,
 | 
			
		||||
// 直接在需要日志的地方调用 Entry 的方法时不需要 AddCallerSkip.
 | 
			
		||||
func (entry Entry) AddCallerSkip(n int) Entry {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	newEntry.callerSkip += n
 | 
			
		||||
	return newEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithField 增加1组键值对
 | 
			
		||||
func (entry Entry) WithField(key string, value any) Entry {
 | 
			
		||||
	return entry.WithFields(Field{Key: key, Value: value})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithFields 增加键值对
 | 
			
		||||
func (entry Entry) WithFields(fields ...Field) Entry {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	newEntry.fields = make([]Field, len(entry.fields)+len(fields))
 | 
			
		||||
	copy(newEntry.fields, entry.fields)
 | 
			
		||||
	copy(newEntry.fields, fields)
 | 
			
		||||
	return newEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithTime 设置日志的时间,
 | 
			
		||||
// Entry 默认使用调用 Log 等最终方法的时间作为日志的时间.
 | 
			
		||||
func (entry Entry) WithTime(t time.Time) Entry {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	newEntry.time = t
 | 
			
		||||
	return newEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReportCaller 获取是否收集调用记录,
 | 
			
		||||
// 默认使用 Logger 上的对应配置.
 | 
			
		||||
func (entry Entry) GetReportCaller() bool {
 | 
			
		||||
	if entry.isReportCallerSet {
 | 
			
		||||
		return entry.reportCaller
 | 
			
		||||
	}
 | 
			
		||||
	return entry.logger.GetReportCaller()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithReportCaller 设置是否收集调用记录
 | 
			
		||||
func (entry Entry) WithReportCaller(reportCaller bool) Entry {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	newEntry.reportCaller = reportCaller
 | 
			
		||||
	newEntry.isReportCallerSet = true
 | 
			
		||||
	return newEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReportStack 获取是否收集调用栈,
 | 
			
		||||
// 默认使用 Logger 上的对应配置.
 | 
			
		||||
func (entry Entry) GetReportStack() bool {
 | 
			
		||||
	if entry.isReportStackSet {
 | 
			
		||||
		return entry.reportStack
 | 
			
		||||
	}
 | 
			
		||||
	return entry.logger.GetReportStack()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithReportStack 设置是否收集调用栈
 | 
			
		||||
func (entry Entry) WithReportStack(reportStack bool) Entry {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	newEntry.reportStack = reportStack
 | 
			
		||||
	newEntry.isReportStackSet = true
 | 
			
		||||
	return newEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log 输出日志
 | 
			
		||||
func (entry Entry) Log(ctx context.Context, level Level, args ...any) {
 | 
			
		||||
	entry.log(ctx, level, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace 输出 LevelTrace 等级的日志
 | 
			
		||||
func (entry Entry) Trace(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelTrace, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug 输出 LevelDebug 等级的日志
 | 
			
		||||
func (entry Entry) Debug(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelDebug, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info 输出 LevelInfo 等级的日志
 | 
			
		||||
func (entry Entry) Info(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelInfo, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warn 输出 LevelWarn 等级的日志
 | 
			
		||||
func (entry Entry) Warn(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelWarn, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error 输出 LevelError 等级的日志
 | 
			
		||||
func (entry Entry) Error(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelError, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatal 输出 LevelFatal 等级的日志并调用 Logger.Exit
 | 
			
		||||
func (entry Entry) Fatal(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelFatal, fmt.Sprint(args...))
 | 
			
		||||
	entry.logger.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Panic 输出 LevelPanic 等级的日志后执行 panic,
 | 
			
		||||
// 即使 Logger 日志等级高于 LevelPanic 也会 panic.
 | 
			
		||||
func (entry Entry) Panic(ctx context.Context, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelPanic, fmt.Sprint(args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logf 格式化输出日志
 | 
			
		||||
func (entry Entry) Logf(ctx context.Context, level Level, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, level, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tracef 格式化输出 LevelTrace 等级的日志
 | 
			
		||||
func (entry Entry) Tracef(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelTrace, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugf 格式化输出 LevelDebug 等级的日志
 | 
			
		||||
func (entry Entry) Debugf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelDebug, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Infof 格式化输出 LevelInfo 等级的日志
 | 
			
		||||
func (entry Entry) Infof(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelInfo, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warnf 格式化输出 LevelWarn 等级的日志
 | 
			
		||||
func (entry Entry) Warnf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelWarn, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errorf 格式化输出 LevelError 等级的日志
 | 
			
		||||
func (entry Entry) Errorf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelError, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatalf 格式化输出 LevelFatal 等级的日志并调用 Logger.Exit
 | 
			
		||||
func (entry Entry) Fatalf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelFatal, fmt.Sprintf(format, args...))
 | 
			
		||||
	entry.logger.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Panicf 格式化输出 LevelPanic 等级的日志
 | 
			
		||||
// 即使 Logger 日志等级高于 LevelPanic 也会 panic.
 | 
			
		||||
func (entry Entry) Panicf(ctx context.Context, format string, args ...any) {
 | 
			
		||||
	entry.log(ctx, LevelPanic, fmt.Sprintf(format, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry Entry) log(ctx context.Context, level Level, message string) {
 | 
			
		||||
	newEntry := entry.copy()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if level == LevelPanic {
 | 
			
		||||
			panic(message)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	if newEntry.logger.GetLevel() < level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	readonlyEntry := ReadonlyEntry{
 | 
			
		||||
		Context: ctx,
 | 
			
		||||
		Fields:  newEntry.fields,
 | 
			
		||||
		Message: message,
 | 
			
		||||
		Time:    newEntry.time,
 | 
			
		||||
		Level:   level,
 | 
			
		||||
	}
 | 
			
		||||
	if readonlyEntry.Time.IsZero() {
 | 
			
		||||
		readonlyEntry.Time = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
	if newEntry.GetReportCaller() {
 | 
			
		||||
		readonlyEntry.Caller = runtimehelper.Caller(newEntry.callerSkip)
 | 
			
		||||
	}
 | 
			
		||||
	if newEntry.GetReportStack() {
 | 
			
		||||
		readonlyEntry.Stack = runtimehelper.Stack(newEntry.callerSkip, 32)
 | 
			
		||||
	}
 | 
			
		||||
	for _, processor := range newEntry.logger.getLevelProcessors(level) {
 | 
			
		||||
		processor.Process(readonlyEntry)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadonlyEntry 是日志系统收集到1条日志记录
 | 
			
		||||
type ReadonlyEntry struct {
 | 
			
		||||
	Context context.Context `json:"-"`
 | 
			
		||||
	Caller  runtimehelper.Frame
 | 
			
		||||
	Stack   []runtimehelper.Frame
 | 
			
		||||
	Time    time.Time
 | 
			
		||||
	Message string
 | 
			
		||||
	Fields  []Field
 | 
			
		||||
	Level   Level
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
package logsdk
 | 
			
		||||
 | 
			
		||||
// Field 是日志记录中的键值对
 | 
			
		||||
type Field struct {
 | 
			
		||||
	Value any    `json:"v"`
 | 
			
		||||
	Key   string `json:"k"`
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,166 @@
 | 
			
		||||
package jsonlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.blauwelle.com/go/crate/log/logsdk"
 | 
			
		||||
	"git.blauwelle.com/go/crate/runtimehelper"
 | 
			
		||||
	"git.blauwelle.com/go/crate/synchelper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var _ logsdk.EntryProcessor = &Processor{}
 | 
			
		||||
 | 
			
		||||
// New 返回日志处理对象,
 | 
			
		||||
// 返回的对象是 [logsdk.EntryProcessor].
 | 
			
		||||
func New(opts ...Option) *Processor {
 | 
			
		||||
	cfg := defaultConfig()
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		opt.apply(cfg)
 | 
			
		||||
	}
 | 
			
		||||
	return &Processor{
 | 
			
		||||
		bufferPool:        cfg.pool,
 | 
			
		||||
		output:            cfg.output,
 | 
			
		||||
		timeFormat:        cfg.timestampFormat,
 | 
			
		||||
		disableTime:       cfg.disableTime,
 | 
			
		||||
		disableHTMLEscape: cfg.disableHTMLEscape,
 | 
			
		||||
		prettyPrint:       cfg.prettyPrint,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Processor 日志处理对象, 把日志处理成 JSON.
 | 
			
		||||
type Processor struct {
 | 
			
		||||
	bufferPool        synchelper.BytesBufferPool
 | 
			
		||||
	output            io.Writer
 | 
			
		||||
	timeFormat        string
 | 
			
		||||
	disableTime       bool
 | 
			
		||||
	disableHTMLEscape bool
 | 
			
		||||
	prettyPrint       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Process 处理日志
 | 
			
		||||
func (processor *Processor) Process(entry logsdk.ReadonlyEntry) {
 | 
			
		||||
	m := Entry{
 | 
			
		||||
		Stack:   entry.Stack,
 | 
			
		||||
		Fields:  entry.Fields,
 | 
			
		||||
		Level:   entry.Level,
 | 
			
		||||
		Message: entry.Message,
 | 
			
		||||
	}
 | 
			
		||||
	if !processor.disableTime {
 | 
			
		||||
		m.Time = entry.Time.Format(processor.timeFormat) // 1次分配
 | 
			
		||||
	}
 | 
			
		||||
	if entry.Caller.IsValid() {
 | 
			
		||||
		// 1次分配
 | 
			
		||||
		// 直接取 &entry.Caller 会增加堆内存分配
 | 
			
		||||
		m.Caller = &runtimehelper.Frame{
 | 
			
		||||
			Function: entry.Caller.Function,
 | 
			
		||||
			File:     entry.Caller.File,
 | 
			
		||||
			Line:     entry.Caller.Line,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := processor.bufferPool.Get()
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	defer processor.bufferPool.Put(buf)
 | 
			
		||||
 | 
			
		||||
	encoder := json.NewEncoder(buf)
 | 
			
		||||
	if processor.prettyPrint {
 | 
			
		||||
		encoder.SetIndent("", "  ")
 | 
			
		||||
	}
 | 
			
		||||
	encoder.SetEscapeHTML(!processor.disableHTMLEscape)
 | 
			
		||||
 | 
			
		||||
	// Encode 2次分配
 | 
			
		||||
	if err := encoder.Encode(m); err != nil {
 | 
			
		||||
		_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot encode log %#v: %s\n", m, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := buf.WriteTo(processor.output); err != nil {
 | 
			
		||||
		_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot write log: %s\n", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Option 配置日志处理对象
 | 
			
		||||
type Option interface {
 | 
			
		||||
	apply(cfg *config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithBufferPool 配置缓冲池
 | 
			
		||||
func WithBufferPool(pool synchelper.BytesBufferPool) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.pool = pool
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithOutput 配置输出
 | 
			
		||||
func WithOutput(w io.Writer) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.output = w
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithTimeFormat 配置时间格式
 | 
			
		||||
func WithTimeFormat(format string) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.timestampFormat = format
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDisableTime 配置仅用时间输出
 | 
			
		||||
func WithDisableTime(disable bool) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.disableTime = disable
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDisableHTMLEscape 配置禁止 HTML 转义
 | 
			
		||||
func WithDisableHTMLEscape(disable bool) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.disableHTMLEscape = disable
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithPrettyPrint 配置 JSON 多行缩进输出
 | 
			
		||||
func WithPrettyPrint(pretty bool) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.prettyPrint = pretty
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entry 被用来 JSON 序列化
 | 
			
		||||
type Entry struct {
 | 
			
		||||
	Message string                `json:"msg"`
 | 
			
		||||
	Time    string                `json:"time,omitempty"`
 | 
			
		||||
	Caller  *runtimehelper.Frame  `json:"caller,omitempty"`
 | 
			
		||||
	Stack   []runtimehelper.Frame `json:"stack,omitempty"`
 | 
			
		||||
	Fields  []logsdk.Field        `json:"fields,omitempty"`
 | 
			
		||||
	Level   logsdk.Level          `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultConfig() *config {
 | 
			
		||||
	return &config{
 | 
			
		||||
		pool:              synchelper.NewBytesBufferPool(512, 2048),
 | 
			
		||||
		output:            synchelper.NewSyncWriter(os.Stderr),
 | 
			
		||||
		timestampFormat:   time.RFC3339Nano,
 | 
			
		||||
		disableTime:       false,
 | 
			
		||||
		disableHTMLEscape: false,
 | 
			
		||||
		prettyPrint:       false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type config struct {
 | 
			
		||||
	pool              synchelper.BytesBufferPool
 | 
			
		||||
	output            io.Writer
 | 
			
		||||
	timestampFormat   string
 | 
			
		||||
	disableTime       bool
 | 
			
		||||
	disableHTMLEscape bool
 | 
			
		||||
	prettyPrint       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type optionFunc func(cfg *config)
 | 
			
		||||
 | 
			
		||||
func (fn optionFunc) apply(cfg *config) {
 | 
			
		||||
	fn(cfg)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,74 @@
 | 
			
		||||
package logsdk
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
// Level 日志等级
 | 
			
		||||
type Level int
 | 
			
		||||
 | 
			
		||||
func (level Level) String() string {
 | 
			
		||||
	if b, err := level.MarshalText(); err == nil {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
	return "unknown"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (level Level) MarshalText() ([]byte, error) {
 | 
			
		||||
	switch level {
 | 
			
		||||
	case LevelPanic:
 | 
			
		||||
		return []byte(LevelPanicValue), nil
 | 
			
		||||
	case LevelFatal:
 | 
			
		||||
		return []byte(LevelFatalValue), nil
 | 
			
		||||
	case LevelError:
 | 
			
		||||
		return []byte(LevelErrorValue), nil
 | 
			
		||||
	case LevelWarn:
 | 
			
		||||
		return []byte(LevelWarnValue), nil
 | 
			
		||||
	case LevelInfo:
 | 
			
		||||
		return []byte(LevelInfoValue), nil
 | 
			
		||||
	case LevelDebug:
 | 
			
		||||
		return []byte(LevelDebugValue), nil
 | 
			
		||||
	case LevelTrace:
 | 
			
		||||
		return []byte(LevelTraceValue), nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("not a valid log level %d", level)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// LevelCount 日志等级的个数
 | 
			
		||||
	LevelCount = 7
 | 
			
		||||
 | 
			
		||||
	// LevelOffset 是把 LevelInfo 等级的日志值偏移到零值的偏移量
 | 
			
		||||
	LevelOffset = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// LevelDisabled 表示不处理任何等级的日志,
 | 
			
		||||
	// 其他日志等级的值越小表示日志严重程度越高.
 | 
			
		||||
	LevelDisabled Level = iota - LevelOffset - 1
 | 
			
		||||
	LevelPanic
 | 
			
		||||
	LevelFatal
 | 
			
		||||
	LevelError
 | 
			
		||||
	LevelWarn
 | 
			
		||||
	LevelInfo
 | 
			
		||||
	LevelDebug
 | 
			
		||||
	LevelTrace
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	LevelPanicValue = "panic"
 | 
			
		||||
	LevelFatalValue = "fatal"
 | 
			
		||||
	LevelErrorValue = "error"
 | 
			
		||||
	LevelWarnValue  = "warn"
 | 
			
		||||
	LevelInfoValue  = "info"
 | 
			
		||||
	LevelDebugValue = "debug"
 | 
			
		||||
	LevelTraceValue = "trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var AllLevels = []Level{
 | 
			
		||||
	LevelPanic,
 | 
			
		||||
	LevelFatal,
 | 
			
		||||
	LevelError,
 | 
			
		||||
	LevelWarn,
 | 
			
		||||
	LevelInfo,
 | 
			
		||||
	LevelDebug,
 | 
			
		||||
	LevelTrace,
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,129 @@
 | 
			
		||||
package logsdk
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New 返回初始未经过参数调整的 Logger
 | 
			
		||||
func New() *Logger {
 | 
			
		||||
	logger := &Logger{}
 | 
			
		||||
	logger.entry.logger = logger
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// entry 仅被用来嵌入 Logger
 | 
			
		||||
type entry = Entry
 | 
			
		||||
 | 
			
		||||
// Logger 中保存了日志所需的全局配置,
 | 
			
		||||
// 使用 Logger 处理日志.
 | 
			
		||||
type Logger struct {
 | 
			
		||||
	exit            func(code int)  // protected by lock
 | 
			
		||||
	levelProcessors levelProcessors // protected by lock
 | 
			
		||||
	entry
 | 
			
		||||
	lock         sync.RWMutex
 | 
			
		||||
	level        atomic.Int32
 | 
			
		||||
	callerSkip   atomic.Int32
 | 
			
		||||
	reportCaller atomic.Bool
 | 
			
		||||
	reportStack  atomic.Bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddProcessor 把日志处理器增加到 Logger
 | 
			
		||||
func (logger *Logger) AddProcessor(levels []Level, processor EntryProcessor) {
 | 
			
		||||
	logger.lock.Lock()
 | 
			
		||||
	for _, level := range levels {
 | 
			
		||||
		logger.levelProcessors[level+LevelOffset] = append(logger.levelProcessors[level+LevelOffset], processor)
 | 
			
		||||
	}
 | 
			
		||||
	logger.lock.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLevel 返回日志系统的等级, 严重程度低于返回等级的日志不会被处理.
 | 
			
		||||
func (logger *Logger) GetLevel() Level {
 | 
			
		||||
	return Level(logger.level.Load())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLevel 设置日志系统的等级
 | 
			
		||||
func (logger *Logger) SetLevel(level Level) {
 | 
			
		||||
	logger.level.Store(int32(level))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCallerSkip 获取调用 [runtime.Callers] 时的 skip 参数,
 | 
			
		||||
// skip 已经被偏移到从调用 Logger 相关方法处获取调用信息.
 | 
			
		||||
// 0 表示从调用 Logger 相关方法处获取调用信息.
 | 
			
		||||
func (logger *Logger) GetCallerSkip() int {
 | 
			
		||||
	return int(logger.callerSkip.Load())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCallerSkip 设置调用 [runtime.Callers] 时的 skip 参数,
 | 
			
		||||
// skip 已经被偏移到从调用 Logger 相关方法处获取调用信息.
 | 
			
		||||
// 0 表示从调用 Logger 相关方法处获取调用信息.
 | 
			
		||||
func (logger *Logger) SetCallerSkip(callerSkip int) {
 | 
			
		||||
	logger.callerSkip.Store(int32(callerSkip))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReportCaller 返回是否收集调用信息
 | 
			
		||||
func (logger *Logger) GetReportCaller() bool {
 | 
			
		||||
	return logger.reportCaller.Load()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetReportCaller 设置是否收集调用信息
 | 
			
		||||
func (logger *Logger) SetReportCaller(reportCaller bool) {
 | 
			
		||||
	logger.reportCaller.Store(reportCaller)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReportStack 返回是否收集调用栈信息
 | 
			
		||||
func (logger *Logger) GetReportStack() bool {
 | 
			
		||||
	return logger.reportStack.Load()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetReportStack 设置是否收集调用栈信息
 | 
			
		||||
func (logger *Logger) SetReportStack(reportStack bool) {
 | 
			
		||||
	logger.reportStack.Store(reportStack)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset 把 Logger 重置到初始状态
 | 
			
		||||
func (logger *Logger) Reset() {
 | 
			
		||||
	logger.lock.Lock()
 | 
			
		||||
	logger.exit = nil
 | 
			
		||||
	logger.levelProcessors = levelProcessors{}
 | 
			
		||||
	logger.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	logger.SetLevel(LevelInfo)
 | 
			
		||||
	logger.SetCallerSkip(0)
 | 
			
		||||
	logger.SetReportCaller(false)
 | 
			
		||||
	logger.SetReportStack(false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exit 退出程序, 执行的具体过程可以通过 SetExit 指定
 | 
			
		||||
func (logger *Logger) Exit(code int) {
 | 
			
		||||
	logger.lock.RLock()
 | 
			
		||||
	defer logger.lock.RUnlock()
 | 
			
		||||
	if logger.exit == nil {
 | 
			
		||||
		os.Exit(code)
 | 
			
		||||
	}
 | 
			
		||||
	logger.exit(code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetExit 指定退出程序时执行的函数
 | 
			
		||||
func (logger *Logger) SetExit(fn func(code int)) {
 | 
			
		||||
	logger.lock.Lock()
 | 
			
		||||
	logger.exit = fn
 | 
			
		||||
	logger.lock.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) getLevelProcessors(level Level) []EntryProcessor {
 | 
			
		||||
	logger.lock.RLock()
 | 
			
		||||
	defer logger.lock.RUnlock()
 | 
			
		||||
	return logger.levelProcessors[level+LevelOffset]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) newEntry() Entry {
 | 
			
		||||
	return Entry{
 | 
			
		||||
		logger:       logger,
 | 
			
		||||
		callerSkip:   logger.GetCallerSkip() + 2,
 | 
			
		||||
		reportCaller: logger.GetReportCaller(),
 | 
			
		||||
		reportStack:  logger.GetReportStack(),
 | 
			
		||||
		initialized:  true,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
package logsdk
 | 
			
		||||
 | 
			
		||||
// EntryProcessor 处理日志记录
 | 
			
		||||
type EntryProcessor interface {
 | 
			
		||||
	Process(entry ReadonlyEntry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type levelProcessors [LevelCount][]EntryProcessor
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.blauwelle.com/go/crate/log/logsdk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// globalLogger 不能重新赋值
 | 
			
		||||
var globalLogger = logsdk.New()
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue