github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/runtime/runtime.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package runtime
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"runtime"
    23  	"sync"
    24  	"time"
    25  
    26  	"k8s.io/klog/v2"
    27  )
    28  
    29  var (
    30  	// ReallyCrash controls the behavior of HandleCrash and defaults to
    31  	// true. It's exposed so components can optionally set to false
    32  	// to restore prior behavior. This flag is mostly used for tests to validate
    33  	// crash conditions.
    34  	ReallyCrash = true
    35  )
    36  
    37  // PanicHandlers is a list of functions which will be invoked when a panic happens.
    38  var PanicHandlers = []func(interface{}){logPanic}
    39  
    40  // HandleCrash simply catches a crash and logs an error. Meant to be called via
    41  // defer.  Additional context-specific handlers can be provided, and will be
    42  // called in case of panic.  HandleCrash actually crashes, after calling the
    43  // handlers and logging the panic message.
    44  //
    45  // E.g., you can provide one or more additional handlers for something like shutting down go routines gracefully.
    46  func HandleCrash(additionalHandlers ...func(interface{})) {
    47  	if r := recover(); r != nil {
    48  		for _, fn := range PanicHandlers {
    49  			fn(r)
    50  		}
    51  		for _, fn := range additionalHandlers {
    52  			fn(r)
    53  		}
    54  		if ReallyCrash {
    55  			// Actually proceed to panic.
    56  			panic(r)
    57  		}
    58  	}
    59  }
    60  
    61  // logPanic logs the caller tree when a panic occurs (except in the special case of http.ErrAbortHandler).
    62  func logPanic(r interface{}) {
    63  	if r == http.ErrAbortHandler {
    64  		// honor the http.ErrAbortHandler sentinel panic value:
    65  		//   ErrAbortHandler is a sentinel panic value to abort a handler.
    66  		//   While any panic from ServeHTTP aborts the response to the client,
    67  		//   panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log.
    68  		return
    69  	}
    70  
    71  	// Same as stdlib http server code. Manually allocate stack trace buffer size
    72  	// to prevent excessively large logs
    73  	const size = 64 << 10
    74  	stacktrace := make([]byte, size)
    75  	stacktrace = stacktrace[:runtime.Stack(stacktrace, false)]
    76  	if _, ok := r.(string); ok {
    77  		klog.Errorf("Observed a panic: %s\n%s", r, stacktrace)
    78  	} else {
    79  		klog.Errorf("Observed a panic: %#v (%v)\n%s", r, r, stacktrace)
    80  	}
    81  }
    82  
    83  // ErrorHandlers is a list of functions which will be invoked when a nonreturnable
    84  // error occurs.
    85  // TODO(lavalamp): for testability, this and the below HandleError function
    86  // should be packaged up into a testable and reusable object.
    87  var ErrorHandlers = []func(error){
    88  	logError,
    89  	(&rudimentaryErrorBackoff{
    90  		lastErrorTime: time.Now(),
    91  		// 1ms was the number folks were able to stomach as a global rate limit.
    92  		// If you need to log errors more than 1000 times a second you
    93  		// should probably consider fixing your code instead. :)
    94  		minPeriod: time.Millisecond,
    95  	}).OnError,
    96  }
    97  
    98  // HandlerError is a method to invoke when a non-user facing piece of code cannot
    99  // return an error and needs to indicate it has been ignored. Invoking this method
   100  // is preferable to logging the error - the default behavior is to log but the
   101  // errors may be sent to a remote server for analysis.
   102  func HandleError(err error) {
   103  	// this is sometimes called with a nil error.  We probably shouldn't fail and should do nothing instead
   104  	if err == nil {
   105  		return
   106  	}
   107  
   108  	for _, fn := range ErrorHandlers {
   109  		fn(err)
   110  	}
   111  }
   112  
   113  // logError prints an error with the call stack of the location it was reported
   114  func logError(err error) {
   115  	klog.ErrorDepth(2, err)
   116  }
   117  
   118  type rudimentaryErrorBackoff struct {
   119  	minPeriod time.Duration // immutable
   120  	// TODO(lavalamp): use the clock for testability. Need to move that
   121  	// package for that to be accessible here.
   122  	lastErrorTimeLock sync.Mutex
   123  	lastErrorTime     time.Time
   124  }
   125  
   126  // OnError will block if it is called more often than the embedded period time.
   127  // This will prevent overly tight hot error loops.
   128  func (r *rudimentaryErrorBackoff) OnError(error) {
   129  	r.lastErrorTimeLock.Lock()
   130  	defer r.lastErrorTimeLock.Unlock()
   131  	d := time.Since(r.lastErrorTime)
   132  	if d < r.minPeriod {
   133  		// If the time moves backwards for any reason, do nothing
   134  		time.Sleep(r.minPeriod - d)
   135  	}
   136  	r.lastErrorTime = time.Now()
   137  }
   138  
   139  // GetCaller returns the caller of the function that calls it.
   140  func GetCaller() string {
   141  	var pc [1]uintptr
   142  	runtime.Callers(3, pc[:])
   143  	f := runtime.FuncForPC(pc[0])
   144  	if f == nil {
   145  		return "Unable to find caller"
   146  	}
   147  	return f.Name()
   148  }
   149  
   150  // RecoverFromPanic replaces the specified error with an error containing the
   151  // original error, and  the call tree when a panic occurs. This enables error
   152  // handlers to handle errors and panics the same way.
   153  func RecoverFromPanic(err *error) {
   154  	if r := recover(); r != nil {
   155  		// Same as stdlib http server code. Manually allocate stack trace buffer size
   156  		// to prevent excessively large logs
   157  		const size = 64 << 10
   158  		stacktrace := make([]byte, size)
   159  		stacktrace = stacktrace[:runtime.Stack(stacktrace, false)]
   160  
   161  		*err = fmt.Errorf(
   162  			"recovered from panic %q. (err=%v) Call stack:\n%s",
   163  			r,
   164  			*err,
   165  			stacktrace)
   166  	}
   167  }
   168  
   169  // Must panics on non-nil errors. Useful to handling programmer level errors.
   170  func Must(err error) {
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  }