diff --git a/log/README.md b/log/README.md new file mode 100644 index 0000000..a8cd764 --- /dev/null +++ b/log/README.md @@ -0,0 +1,98 @@ +# log 日志 + +安装: `go get git.blauwelle.com/go/crate/log` + +简单使用方式 + +```go +package main + +import ( + "context" + + "git.blauwelle.com/go/crate/log" + "git.blauwelle.com/go/crate/log/logsdk" + "git.blauwelle.com/go/crate/log/logsdk/logjson" +) + +func main() { + log.Logger().AddProcessor(logsdk.AllLevels, logjson.New()) // 添加日志处理器, 默认没有处理器(日志生成后会被忽略) + log.Info(context.Background(), "hello world") // 打印日志, Context 会被传递给日志处理器 +} +``` + +`log` 模块包含日志处理的代码, 由 3 个包组成: + +1. [logsdk](./logsdk): 日志实现; +2. [logjson](./logsdk/logjson): 控制台 JSON 日志处理器(`Processor`); +3. [log](.): 根目录, 提供全局 `Logger`, 把 `Logger` / `Entry` 相关的方法封装成函数. + +## 基本概念 + +`logsdk` 由 `Logger`, `Entry`, `Processor` 3 部分组成, 其中只有 `Processor` 是接口类型. +`Logger` 主要负责全局的日志配置, `Logger` 可以通过内嵌的 `Entry` 值对象实现日志处理. +`Entry` 负责保存局部的的日志配置, 比如覆盖 `Logger` 的 `Caller` 开关或设置日志的时间属性; `Entry` 生成单条日志(`ReadonlyEntry`), 并调用 `Processor` 输出日志. + +## 使用方式 + +### 获取全局 `Logger` 对象 + +一般只用来配置全局 `Logger` 对象(通过 `Set*` 或 `AddProcessor` 方法) + +```go +log.Logger() +``` + +### 修改 `Logger` 配置 + +`Logger` 提供以下方法用来修改全局配置 + +- `AddProcessor` 新增处理器; +- `SetLevel` 设置全局日志等级, 只有小于 `Logger` 上设置的日志等级的日志才会被生成; +- `SetCallerSkip` 设置从 `runtime` 包获取调用信息时的 `skip` 参数, 为了方便使用, 0 表示调用 `Logger` / `Entry` 的日志方法处; +- `SetReportCaller` 设置生成调用信息; +- `SetReportStack` 设置生成调用栈; +- `SetReportStackLevel` 当日志等级小于设定值时强制生成调用栈; +- `Reset` 把 Logger 恢复到初始状态; +- `SetExit` 设置 `Logger` 的退出函数(`Logger.Exit`), 当日志等级为 `LevelFatal` 时调用这个函数; + +### 日志生成 + +`Entry` 负责生成日志, `Logger` 通过内嵌 `Entry` 值对象的方式获得 `Entry` 的方法. +`Entry` 提供以下方法来修改局部配置, `log` 包提供了对应的函数调用 + +- `AddCallerSkip` 增加通过 `runtime` 获取调用信息时的 `skip` 值, `skip` 可以是负值; +- `WithField` 添加1组键值对; +- `WithFields` 添加键值对; +- `WithTime` 设置生成的日志的时间; +- `WithReportCaller` 覆盖 `Logger` 上的生成调用信息的设置; +- `WithReportStack` 覆盖 `Logger` 上的生成调用栈的设置; + +`Entry` 提供以下方法来生成日志, 所有方法的第 1 个参数都是 `context.Context`, `Log` 方法的参数包含日志等级和日志消息, +其他方法的参数是日志消息. +所有方法除了 `Log` 外日志等级从高到底(严重程度从低到高), 这些方法有对应的格式化方法(如 `Info` 和 `Infof`). +`log` 包提供了对应的函数调用 + +- `Log` +- `Trace` +- `Debug` +- `Info` +- `Warn` +- `Error` +- `Fatal` +- `Panic` + +### 其他用法 + +关闭日志生成 `log.Logger().SetLevel(logsdk.LevelDisabled)` + +关闭强制生成调用栈 `log.Logger().SetReportStackLevel(logsdk.LevelDisabled)` + +mock 实现 mock 日志处理器对生成的日志进行处理 + +--- + +## 日志处理器 + +- [logsdk/logjson](logsdk/logjson) 控制台 JSON 日志 +- [go get git.blauwelle.com/go/crate/logotel](../logotel) OpenTelemetry 日志 diff --git a/log/logsdk/entry.go b/log/logsdk/entry.go index 8eddc4f..1bfa0ec 100644 --- a/log/logsdk/entry.go +++ b/log/logsdk/entry.go @@ -198,7 +198,7 @@ func (entry Entry) log(ctx context.Context, level Level, message string) { if newEntry.GetReportCaller() { readonlyEntry.Caller = getCaller(newEntry.callerSkip) } - if newEntry.GetReportStack() { + if newEntry.GetReportStack() || level <= newEntry.logger.GetReportStackLevel() { readonlyEntry.Stack = getStack(newEntry.callerSkip, 32) } for _, processor := range newEntry.logger.getLevelProcessors(level) { diff --git a/log/logsdk/logger.go b/log/logsdk/logger.go index 9ed981a..5889efb 100644 --- a/log/logsdk/logger.go +++ b/log/logsdk/logger.go @@ -10,6 +10,7 @@ import ( func New() *Logger { logger := &Logger{} logger.entry.logger = logger + logger.Reset() return logger } @@ -22,11 +23,12 @@ 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 + lock sync.RWMutex + level atomic.Int32 + reportStackLevel atomic.Int32 + callerSkip atomic.Int32 + reportCaller atomic.Bool + reportStack atomic.Bool } // AddProcessor 把日志处理器增加到 Logger @@ -82,6 +84,16 @@ func (logger *Logger) SetReportStack(reportStack bool) { logger.reportStack.Store(reportStack) } +// GetReportStackLevel 获取自动添加调用栈对应的日志等级 +func (logger *Logger) GetReportStackLevel() Level { + return Level(logger.reportStackLevel.Load()) +} + +// SetReportStackLevel 设置日志等级小于等于 level 时自动添加调用栈 +func (logger *Logger) SetReportStackLevel(level Level) { + logger.reportStackLevel.Store(int32(level)) +} + // Reset 把 Logger 重置到初始状态 func (logger *Logger) Reset() { logger.lock.Lock() @@ -90,6 +102,7 @@ func (logger *Logger) Reset() { logger.lock.Unlock() logger.SetLevel(LevelInfo) + logger.SetReportStackLevel(LevelWarn) logger.SetCallerSkip(0) logger.SetReportCaller(false) logger.SetReportStack(false)