Files
Xray-core/common/errors/errors.go
T
2026-09-20 17:09:02 +08:00

176 lines
3.7 KiB
Go

// Package errors is a drop-in replacement for Golang lib 'errors'.
package errors // import "github.com/xtls/xray-core/common/errors"
import (
"context"
"runtime"
"strings"
c "github.com/xtls/xray-core/common/ctx"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/serial"
)
const trim = len("github.com/xtls/xray-core/")
type hasInnerError interface {
// Unwrap returns the underlying error of this one.
Unwrap() error
}
// Error is an error object with underlying error.
type Error struct {
prefix []interface{}
message []interface{}
caller string
inner error
}
// Error implements error.Error().
func (err *Error) Error() string {
builder := strings.Builder{}
for _, prefix := range err.prefix {
builder.WriteByte('[')
builder.WriteString(serial.ToString(prefix))
builder.WriteString("] ")
}
if len(err.caller) > 0 {
builder.WriteString(err.caller)
builder.WriteString(": ")
}
msg := serial.Concat(err.message...)
builder.WriteString(msg)
if err.inner != nil {
builder.WriteString(" > ")
builder.WriteString(err.inner.Error())
}
return builder.String()
}
// Unwrap implements hasInnerError.Unwrap()
func (err *Error) Unwrap() error {
if err.inner == nil {
return nil
}
return err.inner
}
func (err *Error) Base(e error) *Error {
err.inner = e
return err
}
// String returns the string representation of this error.
func (err *Error) String() string {
return err.Error()
}
type ExportOptionHolder struct {
SessionID uint32
}
type ExportOption func(*ExportOptionHolder)
// New returns a new error object with message formed from given arguments.
func New(msg ...interface{}) *Error {
pc, _, _, _ := runtime.Caller(1)
details := runtime.FuncForPC(pc).Name()
if len(details) >= trim {
details = details[trim:]
}
i := strings.Index(details, ".")
if i > 0 {
details = details[:i]
}
return &Error{
message: msg,
caller: details,
}
}
func LogDebug(ctx context.Context, msg ...interface{}) {
doLog(ctx, nil, log.Severity_Debug, msg...)
}
func LogDebugInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Debug, msg...)
}
func LogInfo(ctx context.Context, msg ...interface{}) {
doLog(ctx, nil, log.Severity_Info, msg...)
}
func LogInfoInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Info, msg...)
}
func LogWarning(ctx context.Context, msg ...interface{}) {
doLog(ctx, nil, log.Severity_Warning, msg...)
}
func LogWarningInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Warning, msg...)
}
func LogError(ctx context.Context, msg ...interface{}) {
doLog(ctx, nil, log.Severity_Error, msg...)
}
func LogErrorInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Error, msg...)
}
func doLog(ctx context.Context, inner error, severity log.Severity, msg ...interface{}) {
if log.GetSeverity() < severity {
return
}
pc, _, _, _ := runtime.Caller(2)
details := runtime.FuncForPC(pc).Name()
if len(details) >= trim {
details = details[trim:]
}
i := strings.Index(details, ".")
if i > 0 {
details = details[:i]
}
err := &Error{
message: msg,
caller: details,
inner: inner,
}
if ctx != nil && ctx != context.Background() {
id := uint32(c.IDFromContext(ctx))
if id > 0 {
err.prefix = append(err.prefix, id)
}
}
log.Record(&log.GeneralMessage{
Severity: severity,
Content: err,
})
}
// Cause returns the root cause of this error.
func Cause(err error) error {
if err == nil {
return nil
}
L:
for {
switch inner := err.(type) {
case hasInnerError:
if inner.Unwrap() == nil {
break L
}
err = inner.Unwrap()
default:
break L
}
}
return err
}