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