github.com/zhongdalu/gf@v1.0.0/g/errors/gerror/gerror_error.go (about)

     1  // Copyright 2019 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  package gerror
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io"
    13  	"runtime"
    14  	"strings"
    15  )
    16  
    17  // Error is custom error for additional features.
    18  type Error struct {
    19  	error error  // Wrapped error.
    20  	stack stack  // Stack array, which records the stack information when this error is created or wrapped.
    21  	text  string // Error text, which is created by New* functions.
    22  }
    23  
    24  const (
    25  	gFILTER_KEY = "/g/errors/gerror/gerror"
    26  )
    27  
    28  var (
    29  	// goRootForFilter is used for stack filtering purpose.
    30  	goRootForFilter = runtime.GOROOT()
    31  )
    32  
    33  func init() {
    34  	if goRootForFilter != "" {
    35  		goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
    36  	}
    37  }
    38  
    39  // Error implements the interface of Error, it returns the error as string.
    40  func (err *Error) Error() string {
    41  	if err.text != "" {
    42  		if err.error != nil {
    43  			return err.text + ": " + err.error.Error()
    44  		}
    45  		return err.text
    46  	}
    47  	return err.error.Error()
    48  }
    49  
    50  // Cause returns the root cause error.
    51  func (err *Error) Cause() error {
    52  	loop := err
    53  	for loop != nil {
    54  		if loop.error != nil {
    55  			if e, ok := loop.error.(*Error); ok {
    56  				loop = e
    57  			} else {
    58  				return loop.error
    59  			}
    60  		} else {
    61  			return loop
    62  		}
    63  	}
    64  	return nil
    65  }
    66  
    67  // Format formats the frame according to the fmt.Formatter interface.
    68  //
    69  // %v, %s   : Print the error string;
    70  // %-v, %-s : Print current error string;
    71  // %+s      : Print full stack error list;
    72  // %+v      : Print the error string and full stack error list;
    73  func (err *Error) Format(s fmt.State, verb rune) {
    74  	switch verb {
    75  	case 's', 'v':
    76  		switch {
    77  		case s.Flag('-'):
    78  			if err.text != "" {
    79  				io.WriteString(s, err.text)
    80  			} else {
    81  				io.WriteString(s, err.Error())
    82  			}
    83  		case s.Flag('+'):
    84  			if verb == 's' {
    85  				io.WriteString(s, err.Stack())
    86  			} else {
    87  				io.WriteString(s, err.Error()+"\n"+err.Stack())
    88  			}
    89  		default:
    90  			io.WriteString(s, err.Error())
    91  		}
    92  	}
    93  }
    94  
    95  // Stack returns the stack callers as string.
    96  // It returns an empty string id the <err> does not support stacks.
    97  func (err *Error) Stack() string {
    98  	if err == nil {
    99  		return ""
   100  	}
   101  	loop := err
   102  	index := 1
   103  	buffer := bytes.NewBuffer(nil)
   104  	for loop != nil {
   105  		buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
   106  		index++
   107  		formatSubStack(loop.stack, buffer)
   108  		if loop.error != nil {
   109  			if e, ok := loop.error.(*Error); ok {
   110  				loop = e
   111  			} else {
   112  				buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error()))
   113  				index++
   114  				break
   115  			}
   116  		} else {
   117  			break
   118  		}
   119  	}
   120  	return buffer.String()
   121  }
   122  
   123  // formatSubStack formats the stack for error.
   124  func formatSubStack(st stack, buffer *bytes.Buffer) {
   125  	index := 1
   126  	space := "  "
   127  	for _, p := range st {
   128  		if fn := runtime.FuncForPC(p - 1); fn != nil {
   129  			file, line := fn.FileLine(p - 1)
   130  			if strings.Contains(file, gFILTER_KEY) {
   131  				continue
   132  			}
   133  			if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
   134  				continue
   135  			}
   136  			if index > 9 {
   137  				space = " "
   138  			}
   139  			buffer.WriteString(fmt.Sprintf("   %d).%s%s\n    \t%s:%d\n", index, space, fn.Name(), file, line))
   140  			index++
   141  		}
   142  	}
   143  }