Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
package sentry
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"slices"
|
|
)
|
|
|
|
const (
|
|
MechanismTypeGeneric string = "generic"
|
|
MechanismTypeChained string = "chained"
|
|
MechanismTypeUnwrap string = "unwrap"
|
|
MechanismSourceCause string = "cause"
|
|
)
|
|
|
|
func convertErrorToExceptions(err error, maxErrorDepth int) []Exception {
|
|
var exceptions []Exception
|
|
visited := make(map[error]bool)
|
|
convertErrorDFS(err, &exceptions, nil, "", visited, maxErrorDepth, 0)
|
|
|
|
// mechanism type is used for debugging purposes, but since we can't really distinguish the origin of who invoked
|
|
// captureException, we set it to nil if the error is not chained.
|
|
if len(exceptions) == 1 {
|
|
exceptions[0].Mechanism = nil
|
|
}
|
|
|
|
slices.Reverse(exceptions)
|
|
|
|
// Add a trace of the current stack to the top level(outermost) error in a chain if
|
|
// it doesn't have a stack trace yet.
|
|
// We only add to the most recent error to avoid duplication and because the
|
|
// current stack is most likely unrelated to errors deeper in the chain.
|
|
if len(exceptions) > 0 && exceptions[len(exceptions)-1].Stacktrace == nil {
|
|
exceptions[len(exceptions)-1].Stacktrace = NewStacktrace()
|
|
}
|
|
|
|
return exceptions
|
|
}
|
|
|
|
func convertErrorDFS(err error, exceptions *[]Exception, parentID *int, source string, visited map[error]bool, maxErrorDepth int, currentDepth int) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
if visited[err] {
|
|
return
|
|
}
|
|
visited[err] = true
|
|
|
|
_, isExceptionGroup := err.(interface{ Unwrap() []error })
|
|
|
|
exception := Exception{
|
|
Value: err.Error(),
|
|
Type: reflect.TypeOf(err).String(),
|
|
Stacktrace: ExtractStacktrace(err),
|
|
}
|
|
|
|
currentID := len(*exceptions)
|
|
|
|
var mechanismType string
|
|
|
|
if parentID == nil {
|
|
mechanismType = MechanismTypeGeneric
|
|
source = ""
|
|
} else {
|
|
mechanismType = MechanismTypeChained
|
|
}
|
|
|
|
exception.Mechanism = &Mechanism{
|
|
Type: mechanismType,
|
|
ExceptionID: currentID,
|
|
ParentID: parentID,
|
|
Source: source,
|
|
IsExceptionGroup: isExceptionGroup,
|
|
}
|
|
|
|
*exceptions = append(*exceptions, exception)
|
|
|
|
if maxErrorDepth >= 0 && currentDepth >= maxErrorDepth {
|
|
return
|
|
}
|
|
|
|
switch v := err.(type) {
|
|
case interface{ Unwrap() []error }:
|
|
unwrapped := v.Unwrap()
|
|
for i := range unwrapped {
|
|
if unwrapped[i] != nil {
|
|
childSource := fmt.Sprintf("errors[%d]", i)
|
|
convertErrorDFS(unwrapped[i], exceptions, ¤tID, childSource, visited, maxErrorDepth, currentDepth+1)
|
|
}
|
|
}
|
|
case interface{ Unwrap() error }:
|
|
unwrapped := v.Unwrap()
|
|
if unwrapped != nil {
|
|
convertErrorDFS(unwrapped, exceptions, ¤tID, MechanismTypeUnwrap, visited, maxErrorDepth, currentDepth+1)
|
|
}
|
|
case interface{ Cause() error }:
|
|
cause := v.Cause()
|
|
if cause != nil {
|
|
convertErrorDFS(cause, exceptions, ¤tID, MechanismSourceCause, visited, maxErrorDepth, currentDepth+1)
|
|
}
|
|
}
|
|
}
|