All checks were successful
Server Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
146 lines
2.8 KiB
Go
146 lines
2.8 KiB
Go
package exception
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"nixcn-cms/tracer"
|
|
"runtime"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/codes"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
// context passthrough
|
|
type ctxKey string
|
|
|
|
const (
|
|
ctxKeyService ctxKey = "exception_service"
|
|
ctxKeyEndpoint ctxKey = "exception_endpoint"
|
|
)
|
|
|
|
func ContextWithService(ctx context.Context, service string) context.Context {
|
|
return context.WithValue(ctx, ctxKeyService, service)
|
|
}
|
|
|
|
func ContextWithEndpoint(ctx context.Context, endpoint string) context.Context {
|
|
return context.WithValue(ctx, ctxKeyEndpoint, endpoint)
|
|
}
|
|
|
|
func extractFromCtx(ctx context.Context, key ctxKey, fallback string) string {
|
|
if val, ok := ctx.Value(key).(string); ok && val != "" {
|
|
return val
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
// 13 chars len
|
|
// :1=status
|
|
// :3=endpoint
|
|
// :3=service
|
|
// :1=common/specific
|
|
// :5=original
|
|
type Builder struct {
|
|
Status string
|
|
Endpoint string
|
|
Service string
|
|
Type string
|
|
Original string
|
|
Error error
|
|
Span trace.Span
|
|
ErrorCode string
|
|
}
|
|
|
|
// functional option
|
|
type Option func(*Builder)
|
|
|
|
func New(opts ...Option) *Builder {
|
|
b := &Builder{}
|
|
for _, opt := range opts {
|
|
opt(b)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func WithStatus(s string) Option {
|
|
return func(b *Builder) {
|
|
b.Status = s
|
|
}
|
|
}
|
|
|
|
func WithType(s string) Option {
|
|
return func(b *Builder) {
|
|
b.Type = s
|
|
}
|
|
}
|
|
|
|
func WithOriginal(s string) Option {
|
|
return func(b *Builder) {
|
|
b.Original = s
|
|
}
|
|
}
|
|
|
|
func WithError(e error) Option {
|
|
return func(b *Builder) {
|
|
b.Error = e
|
|
}
|
|
}
|
|
|
|
func WithSpan(s trace.Span) Option {
|
|
return func(b *Builder) {
|
|
b.Span = s
|
|
}
|
|
}
|
|
|
|
// exception builder
|
|
func (self *Builder) build() {
|
|
self.ErrorCode = fmt.Sprintf("%s%s%s%s%s",
|
|
self.Status,
|
|
self.Endpoint,
|
|
self.Service,
|
|
self.Type,
|
|
self.Original,
|
|
)
|
|
}
|
|
|
|
func (self *Builder) Throw(ctx context.Context) *Builder {
|
|
self.Service = extractFromCtx(ctx, ctxKeyService, "svc")
|
|
self.Endpoint = extractFromCtx(ctx, ctxKeyEndpoint, "ep")
|
|
|
|
self.build()
|
|
|
|
pc, file, line, _ := runtime.Caller(1)
|
|
function := runtime.FuncForPC(pc)
|
|
|
|
if self.Span == nil {
|
|
self.Span = trace.SpanFromContext(ctx)
|
|
}
|
|
|
|
if self.Original != CommonSuccess {
|
|
if self.Span != nil && self.Span.IsRecording() {
|
|
self.Span.SetAttributes(
|
|
attribute.String("code.function", function.Name()),
|
|
attribute.String("code.file", tracer.ShortPath(file)),
|
|
attribute.Int("code.line", line),
|
|
attribute.String("exception.code", self.ErrorCode),
|
|
)
|
|
|
|
if self.Error != nil {
|
|
self.Span.RecordError(self.Error)
|
|
self.Span.SetStatus(codes.Error, self.Error.Error())
|
|
} else {
|
|
self.Span.SetStatus(codes.Error, fmt.Sprintf("ServiceException: %s", self.ErrorCode))
|
|
}
|
|
}
|
|
|
|
ErrorHandler(ctx, self.Status, self.ErrorCode, self.Error)
|
|
}
|
|
|
|
return self
|
|
}
|
|
|
|
func (self *Builder) String() string {
|
|
self.build()
|
|
return self.ErrorCode
|
|
}
|