github.com/searKing/golang/go@v1.2.117/runtime/panic.go (about)

     1  package runtime
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net/http"
     7  )
     8  
     9  var (
    10  	DefaultPanic     = Panic{}
    11  	NeverPanic       = Panic{IgnoreCrash: true}
    12  	LogPanic         = Panic{PanicHandlers: []func(any){logPanic}}
    13  	NeverPanicButLog = Panic{IgnoreCrash: true, PanicHandlers: []func(any){logPanic}}
    14  )
    15  
    16  // Panic simply catches a panic and logs an error. Meant to be called via
    17  // defer.  Additional context-specific handlers can be provided, and will be
    18  // called in case of panic.
    19  type Panic struct {
    20  	// IgnoreCrash controls the behavior of Recover and now defaults false.
    21  	// if false, crash immediately, rather than eating panics.
    22  	IgnoreCrash bool
    23  
    24  	// PanicHandlers for something like logging the panic message, shutting down go routines gracefully.
    25  	PanicHandlers []func(any)
    26  }
    27  
    28  // Recover actually crashes if IgnoreCrash is false, after calling PanicHandlers.
    29  func (p Panic) Recover() {
    30  	if r := recover(); r != nil {
    31  		for _, fn := range p.PanicHandlers {
    32  			fn(r)
    33  		}
    34  		if p.IgnoreCrash {
    35  			return
    36  		}
    37  		// Actually proceed to panic.
    38  		panic(r)
    39  	}
    40  }
    41  
    42  func (p *Panic) AppendHandler(handlers ...func(any)) *Panic {
    43  	p.PanicHandlers = append(p.PanicHandlers, handlers...)
    44  	return p
    45  }
    46  
    47  func HandlePanicWith(handlers ...func(any)) Panic {
    48  	p := Panic{}
    49  	p.AppendHandler(handlers...)
    50  	return p
    51  }
    52  
    53  // RecoverFromPanic replaces the specified error with an error containing the
    54  // original error, and the call tree when a panic occurs. This enables error
    55  // handlers to handle errors and panics the same way.
    56  func RecoverFromPanic(err error) error {
    57  	if r := recover(); r != nil {
    58  		const size = 64 << 10
    59  		stacktrace := GetCallStack(size)
    60  		if err == nil {
    61  			return fmt.Errorf(
    62  				"recovered from panic %q. Call stack:\n%s",
    63  				r,
    64  				stacktrace)
    65  		}
    66  
    67  		return fmt.Errorf(
    68  			"recovered from panic %q. (err=%w) Call stack:\n%s",
    69  			r,
    70  			err,
    71  			stacktrace)
    72  	}
    73  	return err
    74  }
    75  
    76  // logPanic logs the caller tree when a panic occurs (except in the special case of http.ErrAbortHandler).
    77  func logPanic(r any) {
    78  	if r == http.ErrAbortHandler {
    79  		// honor the http.ErrAbortHandler sentinel panic value:
    80  		//   ErrAbortHandler is a sentinel panic value to abort a handler.
    81  		//   While any panic from ServeHTTP aborts the response to the client,
    82  		//   panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log.
    83  		return
    84  	}
    85  
    86  	const size = 64 << 10
    87  	stacktrace := GetCallStack(size)
    88  	if _, ok := r.(string); ok {
    89  		log.Printf("Observed a panic: %s\n%s", r, stacktrace)
    90  	} else {
    91  		log.Printf("Observed a panic: %#v (%v)\n%s", r, r, stacktrace)
    92  	}
    93  }