You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
3.6 KiB
155 lines
3.6 KiB
7 years ago
|
/*
|
||
|
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package errors
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// Package path of the project.
|
||
|
pkgPath string
|
||
|
)
|
||
|
|
||
|
// Init - initialize package path.
|
||
|
func Init(gopath string, p string) {
|
||
|
pkgPath = filepath.Join(gopath, "src", p) + string(os.PathSeparator)
|
||
|
}
|
||
|
|
||
|
// stackInfo - Represents a stack frame in the stack trace.
|
||
|
type stackInfo struct {
|
||
|
Filename string `json:"fileName"` // File where error occurred
|
||
|
Line int `json:"line"` // Line where error occurred
|
||
|
Name string `json:"name"` // Name of the function where error occurred
|
||
|
}
|
||
|
|
||
|
// Error - error type containing cause and the stack trace.
|
||
|
type Error struct {
|
||
|
Cause error // Holds the cause error
|
||
|
stack []stackInfo // Stack trace info.
|
||
|
errs []error // Useful for XL to hold errors from all disks
|
||
|
}
|
||
|
|
||
|
// Implement error interface.
|
||
|
func (e Error) Error() string {
|
||
|
return e.Cause.Error()
|
||
|
}
|
||
|
|
||
|
// Stack - returns slice of stack trace.
|
||
|
func (e Error) Stack() []string {
|
||
|
var stack []string
|
||
|
for _, info := range e.stack {
|
||
|
stack = append(stack, fmt.Sprintf("%s:%d:%s()", info.Filename, info.Line, info.Name))
|
||
|
}
|
||
|
return stack
|
||
|
}
|
||
|
|
||
|
// Trace - return new Error type.
|
||
|
func Trace(e error, errs ...error) error {
|
||
|
// Error is nil nothing to do return nil.
|
||
|
if e == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Already a trace error should be returned as is.
|
||
|
if _, ok := e.(*Error); ok {
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
err := &Error{}
|
||
|
err.Cause = e
|
||
|
err.errs = errs
|
||
|
|
||
|
stack := make([]uintptr, 40)
|
||
|
length := runtime.Callers(2, stack)
|
||
|
if length > len(stack) {
|
||
|
length = len(stack)
|
||
|
}
|
||
|
stack = stack[:length]
|
||
|
|
||
|
for _, pc := range stack {
|
||
|
pc = pc - 1
|
||
|
fn := runtime.FuncForPC(pc)
|
||
|
file, line := fn.FileLine(pc)
|
||
|
|
||
|
var suffixFound bool
|
||
|
for _, ignoreName := range []string{
|
||
|
"runtime.",
|
||
|
"testing.",
|
||
|
} {
|
||
|
if strings.HasPrefix(fn.Name(), ignoreName) {
|
||
|
suffixFound = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if suffixFound {
|
||
|
continue
|
||
|
}
|
||
|
_, name := filepath.Split(fn.Name())
|
||
|
name = strings.SplitN(name, ".", 2)[1]
|
||
|
file = filepath.FromSlash(strings.TrimPrefix(filepath.ToSlash(file), filepath.ToSlash(pkgPath)))
|
||
|
err.stack = append(err.stack, stackInfo{
|
||
|
Filename: file,
|
||
|
Line: line,
|
||
|
Name: name,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Cause - Returns the underlying cause error.
|
||
|
func Cause(err error) error {
|
||
|
if e, ok := err.(*Error); ok {
|
||
|
err = e.Cause
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Causes - Returns slice of underlying cause error.
|
||
|
func Causes(errs []error) (cerrs []error) {
|
||
|
for _, err := range errs {
|
||
|
cerrs = append(cerrs, Cause(err))
|
||
|
}
|
||
|
return cerrs
|
||
|
}
|
||
|
|
||
|
// IsErrIgnored returns whether given error is ignored or not.
|
||
|
func IsErrIgnored(err error, ignoredErrs ...error) bool {
|
||
|
return IsErr(err, ignoredErrs...)
|
||
|
}
|
||
|
|
||
|
// IsErr returns whether given error is exact error.
|
||
|
func IsErr(err error, errs ...error) bool {
|
||
|
err = Cause(err)
|
||
|
for _, exactErr := range errs {
|
||
|
if err == exactErr {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// Tracef behaves like fmt.Errorf but adds traces to the returned error.
|
||
|
func Tracef(format string, args ...interface{}) error {
|
||
|
return Trace(fmt.Errorf(format, args...))
|
||
|
}
|