golang zap日志模块封装sentry

我们自己写个log日志包,把zap和sentry封装到一起。

下面直接贴上主要部分代码(两个模块初始化部分的代码请自行查阅官方文档):

  • logger.go
go 复制代码
package log

import (
	"github.com/getsentry/sentry-go"
	"go.uber.org/zap"
)

type Logger struct {
	*zap.Logger
}

// Debug logs a message at DebugLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (log *Logger) Debug(msg string, fields ...zap.Field) {
	log.Logger.Debug(msg, fields...)
	NewMessage(sentry.LevelDebug, msg, fields)
}

// Info logs a message at InfoLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (log *Logger) Info(msg string, fields ...zap.Field) {
	log.Logger.Info(msg, fields...)
	NewMessage(sentry.LevelInfo, msg, fields)
}

// Warn logs a message at WarnLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (log *Logger) Warn(msg string, fields ...zap.Field) {
	log.Logger.Warn(msg, fields...)
	NewMessage(sentry.LevelWarning, msg, fields)
}

// Error logs a message at ErrorLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (log *Logger) Error(msg string, fields ...zap.Field) {
	log.Logger.Error(msg, fields...)
	NewMessage(sentry.LevelError, msg, fields)
}

// Fatal logs a message at FatalLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then calls os.Exit(1), even if logging at FatalLevel is
// disabled.
func (log *Logger) Fatal(msg string, fields ...zap.Field) {
	log.Logger.Fatal(msg, fields...)
	NewMessage(sentry.LevelFatal, msg, fields)
}
  • sentry.go
go 复制代码
package log

import (
	"fmt"
	"utils/stime"
	"github.com/getsentry/sentry-go"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"math"
	"runtime"
	"time"
)

type CustomError struct {
	msg        string
	stackTrace []uintptr
}

func (e *CustomError) Error() string {
	return e.msg
}
func (e *CustomError) StackTrace() []uintptr {
	return e.stackTrace
}

func NewMessage(level sentry.Level, msg string, fields []zap.Field) {
	if sentryUsable {
		sentry.WithScope(func(scope *sentry.Scope) {
			scope.SetLevel(level)
			scope.SetExtra("msg", msg)
			if len(fields) > 0 {
				for _, field := range fields {
					switch field.Type {
					case zapcore.StringType:
						scope.SetExtra(field.Key, field.String)
					case zapcore.ErrorType:
						scope.SetExtra(field.Key, field.Interface.(error).Error())
					case zapcore.StringerType:
						scope.SetExtra(field.Key, field.Interface.(fmt.Stringer).String())
					case zapcore.Int8Type, zapcore.Int16Type, zapcore.Int32Type, zapcore.Int64Type, zapcore.Uint8Type, zapcore.Uint16Type, zapcore.Uint32Type, zapcore.Uint64Type:
						scope.SetExtra(field.Key, field.Integer)
					case zapcore.Float32Type, zapcore.Float64Type:
						floatVal := math.Float32frombits(uint32(field.Integer))
						scope.SetExtra(field.Key, floatVal)
					case zapcore.BoolType:
						scope.SetExtra(field.Key, field.Integer == 1)
					case zapcore.TimeFullType:
						if ts, ok := field.Interface.(time.Time); ok {
							scope.SetExtra(field.Key, ts.Format(stime.Format_Normal_YMDhms))
						}
					case zapcore.TimeType:
						scope.SetExtra(field.Key, time.Unix(0, field.Integer).UTC())
					default:
						scope.SetExtra(field.Key, fmt.Sprintf("%+v", field.Interface))
					}
				}
			}
			scope.SetFingerprint([]string{msg})
			if level == sentry.LevelError {
				stackTrace := make([]uintptr, 20)
				runtime.Callers(6, stackTrace)
				err := &CustomError{
					msg:        msg,
					stackTrace: stackTrace,
				}
				sentry.CaptureException(err)
			} else {
				sentry.CaptureMessage(msg)
			}
		})
	}
}

效果展示:

错误消息会展示错误类型,其它的debug、info等会直接展示消息名称

错误消息详情:

  • 详情中包含错误位置和调用方法,我就不单独写调用示例了。
  • 消息详情中可已看到zap.Field中携带的消息内容
相关推荐
FungLeo3 小时前
成为全栈·Go 后端篇·契约一致性与差分测试:两套实现如何互相校验
golang·node.js·go·合约测试·差分测试·成为全栈·api兼容
老王IT3 小时前
Go开发环境搭建(window操作系统)
开发语言·后端·golang
利威尔xu8 小时前
Go IM 源码实战(7):断线三天重连,水位算错就漏消息
redis·后端·微服务·面试·golang
行思理18 小时前
go语言学习难度及适合学习的人群
开发语言·学习·golang
@大迁世界1 天前
告别 Go,拥抱 Rust
开发语言·golang·rust
喵了几个咪1 天前
GoWind ShortPlay 拆解:不用模型的推荐闭环——埋点回流、三路召回与已看去重
开发语言·后端·微服务·golang·gowind
喵了几个咪1 天前
GoWind UBA 拆解:把 Doris UNIQUE KEY 翻译成 ClickHouse——读数正确性对齐实录
开发语言·后端·clickhouse·微服务·golang·gowind
FungLeo1 天前
成为全栈·Go 后端篇·用 Go 重写分层架构:领域包、装配根与最小抽象
golang·go·领域驱动设计·gorm·分层架构·成为全栈
北冥you鱼1 天前
Go 语言 Ticker 定时任务全解析:原理、陷阱与最佳实践
开发语言·数据库·golang
我不会起名字3221 天前
Go 服务升级 Go 1.2x 后 P99 变差:GMP 调度相关的 3 个变化
golang·go·gmp·调度·gomaxprocs