外网网站,工商注册地址查询系统,二次元网站开发的意义,如何盗用网站模板快速体验
以下是 项目中 已经用slog替换 zap 后的 logger 使用方法,无任何感知,与之前一模一样
package mainimport github.com/webws/go-moda/loggerfunc main() {// 格式化打印 {time:2023-09-08T01:25:21.31346308:00,levelgithub.com/webws/go-moda/loggerfunc main() {// 格式化打印 {time:2023-09-08T01:25:21.31346308:00,level:INFO,msg:info hello slog,key:value,file:/Users/xxx/w/pro/go-moda/example/logger/main.go,line:6}logger.Infow(info hello slog, key, value) // 打印jsonlogger.Debugw(debug hello slog, key, value) // 不展示logger.SetLevel(logger.DebugLevel) // 设置等级logger.Debugw(debug hello slog, key, value) // 设置了等级之后展示 debug// withnewLog : logger.With(newkey, newValue)newLog.Debugw(new hello slog) // 会打印 newkey:newValuelogger.Debugw(old hello slog) // 不会打印 newkey:newValue
}slog 基础使用
Go 1.21版本中 将 golang.org/x/exp/slog 引入了go标准库 路径为 log/slog。 新项目的 如果不使用第三方包可以直接用slog当你的 logger
slog 简单示例: slog.Info(finished, key, value)slog.Debug(finished, key1, value1)以下是打印日志 默认slog 输出级别是info以上,所以debug是打印不出来.
2023/09/08 00:27:24 INFO finished keyvaluejson格式化,设置日志等级,并打印调用函数和文件
opts : slog.HandlerOptions{AddSource: true, Level: slog.LevelInfo}logger : slog.New(slog.NewJSONHandler(os.Stdout, opts))logger.Info(finished, key, value)输出
{time:2023-09-08T00:34:22.03596208:00,level:INFO,source:{function:callvis/slog.TestLogJsonHandler,file:/Users/websong/w/pro/go-note/slog/main_test.go,line:39},msg:finished,key:value}
原有 logger zap实现
原有的项目已经实现了一套logger,使用zap log 实现接口
原有代码示例
logger interface LoggerInterface
package loggertype LoggerInterface interface {Debugw(msg string, keysAndValues ...interface{})Infow(msg string, keysAndValues ...interface{})Errorw(msg string, keysAndValues ...interface{})Fatalw(msg string, keysAndValues ...interface{})SetLevel(level Level)With(keyValues ...interface{}) LoggerInterface
}zap log 实现 LoggerInterface
type ZapSugaredLogger struct {logger *zap.SugaredLoggerzapConfig *zap.Config
}func buildZapLog(level Level) LoggerInterface {encoderConfig : zapcore.EncoderConfig{TimeKey: ts,LevelKey: level,NameKey: logger,CallerKey: caller,MessageKey: msg,StacktraceKey: stacktrace,LineEnding: zapcore.DefaultLineEnding,EncodeDuration: zapcore.SecondsDurationEncoder,EncodeTime: zapcore.ISO8601TimeEncoder,EncodeLevel: zapcore.LowercaseLevelEncoder,EncodeCaller: zapcore.ShortCallerEncoder,}zapConfig : zap.Config{Level: zap.NewAtomicLevelAt(zapcore.Level(level)),Development: true,DisableCaller: false,DisableStacktrace: true,Sampling: zap.SamplingConfig{Initial: 100, Thereafter: 100},Encoding: json,EncoderConfig: encoderConfig,OutputPaths: []string{stderr},ErrorOutputPaths: []string{stderr},}l, err : zapConfig.Build(zap.AddCallerSkip(2))if err ! nil {fmt.Printf(zap build logger fail err%v, err)return nil}return ZapSugaredLogger{logger: l.Sugar(),zapConfig: zapConfig,}func (l *ZapSugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {l.logger.Debugw(msg, keysAndValues...)}func (l *ZapSugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {l.logger.Errorw(msg, keysAndValues...)}// ...省略info 之类其他实现接口的方法
}全局初始化logger,因代码量太大,以下是伪代码,主要提供思路,为下文 slog 无侵入替换zap 预热
package logger// 全局 log,也可以单独 NewLogger 获取新的实例
var globalog newlogger(DebugLevel)func newlogger(level Level) *Logger {l : Logger{logger: buildZapLog(level)}return l
}
func Infow(msg string, keysAndValues ...interface{}) {globalog.logger.Infow(msg, keysAndValues...)
}
// ...省略其他全局方法,比如DebugW 之类在项目里就可以通过logger 使用日志 logger.Debugw(msg1, k1, v1) // debuglogger.SetLevel(DebugLevel) //设置等级logger.Debugw(msg3, k3, v3) newLogger : logger.With(name, song)logger.Infow(msg4, k4, v4) // printslog 不侵入业务 替换zap
logger interface 接口保持不变
slog 实现 代码
package loggerimport (log/slogosruntime
)var _ LoggerInterface (*SlogLogger)(nil)type SlogLogger struct {logger *slog.Loggerlevel *slog.LevelVar// true 代表使用slog打印文件路径,false 会使用自定的方法给日志 增加字段 file lineaddSource bool
}// newSlog
func newSlog(level Level, addSource bool) LoggerInterface {levelVar : slog.LevelVar{}levelVar.Set(slog.LevelInfo)opts : slog.HandlerOptions{AddSource: addSource, Level: levelVar}logger : slog.New(slog.NewJSONHandler(os.Stdout, opts))return SlogLogger{logger: logger,level: levelVar,}
}
func (l *SlogLogger) Fatalw(msg string, keysAndValues ...interface{}) {keysAndValues l.ApppendFileLine(keysAndValues...)l.logger.Error(msg, keysAndValues...)os.Exit(1)
}func (l *SlogLogger) Infow(msg string, keysAndValues ...interface{}) {keysAndValues l.ApppendFileLine(keysAndValues...)l.logger.Info(msg, keysAndValues...)
}
// 省略继承接口的其他方法 DebugW 之类的
func (l *SlogLogger) SetLevel(level Level) {zapLevelToSlogLevel(level)l.level.Set(slog.Level(zapLevelToSlogLevel(level)))
}
//
func (l *SlogLogger) With(keyValues ...interface{}) LoggerInterface {newLog : l.logger.With(keyValues...)return SlogLogger{logger: newLog,level: l.level,}
}// ApppendFileLine 获取调用方的文件和文件号
// slog 原生 暂不支持 callerSkip,使用此函数啃根会有性能问题,最好等slog提供 CallerSkip 的参数
func (l *SlogLogger) ApppendFileLine(keyValues ...interface{}) []interface{} {l.addSource falseif !l.addSource {var pc uintptrvar pcs [1]uintptr// skip [runtime.Callers, this function, this functions caller]runtime.Callers(4, pcs[:])pc pcs[0]fs : runtime.CallersFrames([]uintptr{pc})f, _ : fs.Next()keyValues append(keyValues, file, f.File, line, f.Line)return keyValues}return keyValues
}全局初始化logger,以下伪代码
package logger
// 全局 log,也可以单独 NewLogger 获取新的实例
var globalog newlogger(DebugLevel)func newlogger(level Level) *Logger {l : Logger{logger: newSlog(level, false)}return l
}
func Infow(msg string, keysAndValues ...interface{}) {globalog.logger.Infow(msg, keysAndValues...)
}
// ...省略其他全局方法,比如DebugW 之类slog 实现 callerSkip 功能
slog 的 addsource 参数 会打印文件名和行号,但 并不能像 zap 那样支持 callerSkip,也就是说 如果将 slog 封装在 logger 目录的log.go 文件下,使用logger进行打印,展示的文件会一只是log.go
看了 slog 的源码,其实slog 使用了 runtime.Callers 在内部实现了 callerSkip 功能,但是没有对外暴露 callerSkip 参数
我就封装了 ApppendFileLine 方法,使用 runtime.Callers 获取到 文件名 和 行号,增加 file 和 line 的key value到日志
可能会有性能问题,希望slog能对外提供一个 callerSkip 参数 var pc uintptrvar pcs [1]uintptr// skip [runtime.Callers, this function, this functions caller]runtime.Callers(4, pcs[:])pc pcs[0]fs : runtime.CallersFrames([]uintptr{pc})f, _ : fs.Next()keyValues append(keyValues, file, f.File, line, f.Line)说明
文章中贴的代码不多,主要提供思路,虽然省略了一些方法和 全局logger的实现方式,也写了两个多小时
如要查看logger实现细节,可查看 在文章开头 快速体验 引用的包 github.com/webws/go-moda/logger
也可以直接看下我这个 仓库 go-moda 里使用 slog 和 zap 的封装