...

Source file src/github.com/palantir/go-githubapp/githubapp/errors.go

Documentation: github.com/palantir/go-githubapp/githubapp

     1  // Copyright 2020 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package githubapp
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"runtime"
    21  
    22  	"github.com/rcrowley/go-metrics"
    23  )
    24  
    25  const (
    26  	MetricsKeyHandlerError = "github.handler.error"
    27  )
    28  
    29  var (
    30  	// HandlerRecoverStackDepth is the max depth of stack trace to recover on a
    31  	// handler panic.
    32  	HandlerRecoverStackDepth = 32
    33  )
    34  
    35  func errorCounter(r metrics.Registry, event string) metrics.Counter {
    36  	if r == nil {
    37  		return metrics.NilCounter{}
    38  	}
    39  
    40  	key := MetricsKeyHandlerError
    41  	if event != "" {
    42  		key = fmt.Sprintf("%s[event:%s]", key, event)
    43  	}
    44  	return metrics.GetOrRegisterCounter(key, r)
    45  }
    46  
    47  // HandlerPanicError is an error created from a recovered handler panic.
    48  type HandlerPanicError struct {
    49  	value interface{}
    50  	stack []runtime.Frame
    51  }
    52  
    53  // Value returns the exact value with which panic() was called.
    54  func (e HandlerPanicError) Value() interface{} {
    55  	return e.value
    56  }
    57  
    58  // StackTrace returns the stack of the panicking goroutine.
    59  func (e HandlerPanicError) StackTrace() []runtime.Frame {
    60  	return e.stack
    61  }
    62  
    63  // Format formats the error optionally including the stack trace.
    64  //
    65  //	%s    the error message
    66  //	%v    the error message and the source file and line number for each stack frame
    67  //
    68  // Format accepts the following flags:
    69  //
    70  //	%+v   the error message and the function, file, and line for each stack frame
    71  func (e HandlerPanicError) Format(s fmt.State, verb rune) {
    72  	switch verb {
    73  	case 's':
    74  		_, _ = io.WriteString(s, e.Error())
    75  	case 'v':
    76  		_, _ = io.WriteString(s, e.Error())
    77  		for _, f := range e.stack {
    78  			_, _ = io.WriteString(s, "\n")
    79  			if s.Flag('+') {
    80  				_, _ = fmt.Fprintf(s, "%s\n\t", f.Function)
    81  			}
    82  			_, _ = fmt.Fprintf(s, "%s:%d", f.File, f.Line)
    83  		}
    84  	}
    85  }
    86  
    87  func (e HandlerPanicError) Error() string {
    88  	v := e.value
    89  	if err, ok := v.(error); ok {
    90  		v = err.Error()
    91  	}
    92  	return fmt.Sprintf("panic: %v", v)
    93  }
    94  
    95  func getStack(skip int) []runtime.Frame {
    96  	rpc := make([]uintptr, HandlerRecoverStackDepth)
    97  
    98  	n := runtime.Callers(skip+2, rpc)
    99  	frames := runtime.CallersFrames(rpc[0:n])
   100  
   101  	var stack []runtime.Frame
   102  	for {
   103  		f, more := frames.Next()
   104  		if !more {
   105  			break
   106  		}
   107  		stack = append(stack, f)
   108  	}
   109  	return stack
   110  }
   111  

View as plain text