mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #121970 from pohly/log-apimachinery-runtime
apimachinery runtime: support contextual logging
This commit is contained in:
commit
d35ba3635b
@ -127,6 +127,7 @@ linters-settings: # please keep this alphabetized
|
|||||||
# The following packages have been migrated to contextual logging.
|
# The following packages have been migrated to contextual logging.
|
||||||
# Packages matched here do not have to be listed above because
|
# Packages matched here do not have to be listed above because
|
||||||
# "contextual" implies "structured".
|
# "contextual" implies "structured".
|
||||||
|
contextual k8s.io/apimachinery/pkg/util/runtime/.*
|
||||||
contextual k8s.io/client-go/metadata/.*
|
contextual k8s.io/client-go/metadata/.*
|
||||||
contextual k8s.io/client-go/tools/events/.*
|
contextual k8s.io/client-go/tools/events/.*
|
||||||
contextual k8s.io/client-go/tools/record/.*
|
contextual k8s.io/client-go/tools/record/.*
|
||||||
|
@ -173,6 +173,7 @@ linters-settings: # please keep this alphabetized
|
|||||||
# The following packages have been migrated to contextual logging.
|
# The following packages have been migrated to contextual logging.
|
||||||
# Packages matched here do not have to be listed above because
|
# Packages matched here do not have to be listed above because
|
||||||
# "contextual" implies "structured".
|
# "contextual" implies "structured".
|
||||||
|
contextual k8s.io/apimachinery/pkg/util/runtime/.*
|
||||||
contextual k8s.io/client-go/metadata/.*
|
contextual k8s.io/client-go/metadata/.*
|
||||||
contextual k8s.io/client-go/tools/events/.*
|
contextual k8s.io/client-go/tools/events/.*
|
||||||
contextual k8s.io/client-go/tools/record/.*
|
contextual k8s.io/client-go/tools/record/.*
|
||||||
|
@ -176,6 +176,7 @@ linters-settings: # please keep this alphabetized
|
|||||||
# The following packages have been migrated to contextual logging.
|
# The following packages have been migrated to contextual logging.
|
||||||
# Packages matched here do not have to be listed above because
|
# Packages matched here do not have to be listed above because
|
||||||
# "contextual" implies "structured".
|
# "contextual" implies "structured".
|
||||||
|
contextual k8s.io/apimachinery/pkg/util/runtime/.*
|
||||||
contextual k8s.io/client-go/metadata/.*
|
contextual k8s.io/client-go/metadata/.*
|
||||||
contextual k8s.io/client-go/tools/events/.*
|
contextual k8s.io/client-go/tools/events/.*
|
||||||
contextual k8s.io/client-go/tools/record/.*
|
contextual k8s.io/client-go/tools/record/.*
|
||||||
|
@ -24,6 +24,7 @@ structured k8s.io/apiserver/pkg/server/options/encryptionconfig/.*
|
|||||||
# The following packages have been migrated to contextual logging.
|
# The following packages have been migrated to contextual logging.
|
||||||
# Packages matched here do not have to be listed above because
|
# Packages matched here do not have to be listed above because
|
||||||
# "contextual" implies "structured".
|
# "contextual" implies "structured".
|
||||||
|
contextual k8s.io/apimachinery/pkg/util/runtime/.*
|
||||||
contextual k8s.io/client-go/metadata/.*
|
contextual k8s.io/client-go/metadata/.*
|
||||||
contextual k8s.io/client-go/tools/events/.*
|
contextual k8s.io/client-go/tools/events/.*
|
||||||
contextual k8s.io/client-go/tools/record/.*
|
contextual k8s.io/client-go/tools/record/.*
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -35,7 +36,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PanicHandlers is a list of functions which will be invoked when a panic happens.
|
// PanicHandlers is a list of functions which will be invoked when a panic happens.
|
||||||
var PanicHandlers = []func(interface{}){logPanic}
|
var PanicHandlers = []func(context.Context, interface{}){logPanic}
|
||||||
|
|
||||||
// HandleCrash simply catches a crash and logs an error. Meant to be called via
|
// HandleCrash simply catches a crash and logs an error. Meant to be called via
|
||||||
// defer. Additional context-specific handlers can be provided, and will be
|
// defer. Additional context-specific handlers can be provided, and will be
|
||||||
@ -43,23 +44,54 @@ var PanicHandlers = []func(interface{}){logPanic}
|
|||||||
// handlers and logging the panic message.
|
// handlers and logging the panic message.
|
||||||
//
|
//
|
||||||
// E.g., you can provide one or more additional handlers for something like shutting down go routines gracefully.
|
// E.g., you can provide one or more additional handlers for something like shutting down go routines gracefully.
|
||||||
|
//
|
||||||
|
// TODO(pohly): logcheck:context // HandleCrashWithContext should be used instead of HandleCrash in code which supports contextual logging.
|
||||||
func HandleCrash(additionalHandlers ...func(interface{})) {
|
func HandleCrash(additionalHandlers ...func(interface{})) {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
for _, fn := range PanicHandlers {
|
additionalHandlersWithContext := make([]func(context.Context, interface{}), len(additionalHandlers))
|
||||||
fn(r)
|
for i, handler := range additionalHandlers {
|
||||||
}
|
handler := handler // capture loop variable
|
||||||
for _, fn := range additionalHandlers {
|
additionalHandlersWithContext[i] = func(_ context.Context, r interface{}) {
|
||||||
fn(r)
|
handler(r)
|
||||||
}
|
}
|
||||||
if ReallyCrash {
|
|
||||||
// Actually proceed to panic.
|
|
||||||
panic(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCrash(context.Background(), r, additionalHandlersWithContext...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleCrashWithContext simply catches a crash and logs an error. Meant to be called via
|
||||||
|
// defer. Additional context-specific handlers can be provided, and will be
|
||||||
|
// called in case of panic. HandleCrash actually crashes, after calling the
|
||||||
|
// handlers and logging the panic message.
|
||||||
|
//
|
||||||
|
// E.g., you can provide one or more additional handlers for something like shutting down go routines gracefully.
|
||||||
|
//
|
||||||
|
// The context is used to determine how to log.
|
||||||
|
func HandleCrashWithContext(ctx context.Context, additionalHandlers ...func(context.Context, interface{})) {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
handleCrash(ctx, r, additionalHandlers...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCrash is the common implementation of HandleCrash and HandleCrash.
|
||||||
|
// Having those call a common implementation ensures that the stack depth
|
||||||
|
// is the same regardless through which path the handlers get invoked.
|
||||||
|
func handleCrash(ctx context.Context, r any, additionalHandlers ...func(context.Context, interface{})) {
|
||||||
|
for _, fn := range PanicHandlers {
|
||||||
|
fn(ctx, r)
|
||||||
|
}
|
||||||
|
for _, fn := range additionalHandlers {
|
||||||
|
fn(ctx, r)
|
||||||
|
}
|
||||||
|
if ReallyCrash {
|
||||||
|
// Actually proceed to panic.
|
||||||
|
panic(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logPanic logs the caller tree when a panic occurs (except in the special case of http.ErrAbortHandler).
|
// logPanic logs the caller tree when a panic occurs (except in the special case of http.ErrAbortHandler).
|
||||||
func logPanic(r interface{}) {
|
func logPanic(ctx context.Context, r interface{}) {
|
||||||
if r == http.ErrAbortHandler {
|
if r == http.ErrAbortHandler {
|
||||||
// honor the http.ErrAbortHandler sentinel panic value:
|
// honor the http.ErrAbortHandler sentinel panic value:
|
||||||
// ErrAbortHandler is a sentinel panic value to abort a handler.
|
// ErrAbortHandler is a sentinel panic value to abort a handler.
|
||||||
@ -73,10 +105,20 @@ func logPanic(r interface{}) {
|
|||||||
const size = 64 << 10
|
const size = 64 << 10
|
||||||
stacktrace := make([]byte, size)
|
stacktrace := make([]byte, size)
|
||||||
stacktrace = stacktrace[:runtime.Stack(stacktrace, false)]
|
stacktrace = stacktrace[:runtime.Stack(stacktrace, false)]
|
||||||
|
|
||||||
|
// We don't really know how many call frames to skip because the Go
|
||||||
|
// panic handler is between us and the code where the panic occurred.
|
||||||
|
// If it's one function (as in Go 1.21), then skipping four levels
|
||||||
|
// gets us to the function which called the `defer HandleCrashWithontext(...)`.
|
||||||
|
logger := klog.FromContext(ctx).WithCallDepth(4)
|
||||||
|
|
||||||
|
// For backwards compatibility, conversion to string
|
||||||
|
// is handled here instead of defering to the logging
|
||||||
|
// backend.
|
||||||
if _, ok := r.(string); ok {
|
if _, ok := r.(string); ok {
|
||||||
klog.Errorf("Observed a panic: %s\n%s", r, stacktrace)
|
logger.Error(nil, "Observed a panic", "panic", r, "stacktrace", string(stacktrace))
|
||||||
} else {
|
} else {
|
||||||
klog.Errorf("Observed a panic: %#v (%v)\n%s", r, r, stacktrace)
|
logger.Error(nil, "Observed a panic", "panic", fmt.Sprintf("%v", r), "panicGoValue", fmt.Sprintf("%#v", r), "stacktrace", string(stacktrace))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,35 +126,76 @@ func logPanic(r interface{}) {
|
|||||||
// error occurs.
|
// error occurs.
|
||||||
// TODO(lavalamp): for testability, this and the below HandleError function
|
// TODO(lavalamp): for testability, this and the below HandleError function
|
||||||
// should be packaged up into a testable and reusable object.
|
// should be packaged up into a testable and reusable object.
|
||||||
var ErrorHandlers = []func(error){
|
var ErrorHandlers = []ErrorHandler{
|
||||||
logError,
|
logError,
|
||||||
(&rudimentaryErrorBackoff{
|
func(_ context.Context, _ error, _ string, _ ...interface{}) {
|
||||||
lastErrorTime: time.Now(),
|
(&rudimentaryErrorBackoff{
|
||||||
// 1ms was the number folks were able to stomach as a global rate limit.
|
lastErrorTime: time.Now(),
|
||||||
// If you need to log errors more than 1000 times a second you
|
// 1ms was the number folks were able to stomach as a global rate limit.
|
||||||
// should probably consider fixing your code instead. :)
|
// If you need to log errors more than 1000 times a second you
|
||||||
minPeriod: time.Millisecond,
|
// should probably consider fixing your code instead. :)
|
||||||
}).OnError,
|
minPeriod: time.Millisecond,
|
||||||
|
}).OnError()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorHandler func(ctx context.Context, err error, msg string, keysAndValues ...interface{})
|
||||||
|
|
||||||
// HandlerError is a method to invoke when a non-user facing piece of code cannot
|
// HandlerError is a method to invoke when a non-user facing piece of code cannot
|
||||||
// return an error and needs to indicate it has been ignored. Invoking this method
|
// return an error and needs to indicate it has been ignored. Invoking this method
|
||||||
// is preferable to logging the error - the default behavior is to log but the
|
// is preferable to logging the error - the default behavior is to log but the
|
||||||
// errors may be sent to a remote server for analysis.
|
// errors may be sent to a remote server for analysis.
|
||||||
|
//
|
||||||
|
// TODO(pohly): logcheck:context // HandleErrorWithContext should be used instead of HandleError in code which supports contextual logging.
|
||||||
func HandleError(err error) {
|
func HandleError(err error) {
|
||||||
// this is sometimes called with a nil error. We probably shouldn't fail and should do nothing instead
|
// this is sometimes called with a nil error. We probably shouldn't fail and should do nothing instead
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleError(context.Background(), err, "Unhandled Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerErrorWithContext is a method to invoke when a non-user facing piece of code cannot
|
||||||
|
// return an error and needs to indicate it has been ignored. Invoking this method
|
||||||
|
// is preferable to logging the error - the default behavior is to log but the
|
||||||
|
// errors may be sent to a remote server for analysis. The context is used to
|
||||||
|
// determine how to log the error.
|
||||||
|
//
|
||||||
|
// If contextual logging is enabled, the default log output is equivalent to
|
||||||
|
//
|
||||||
|
// logr.FromContext(ctx).WithName("UnhandledError").Error(err, msg, keysAndValues...)
|
||||||
|
//
|
||||||
|
// Without contextual logging, it is equivalent to:
|
||||||
|
//
|
||||||
|
// klog.ErrorS(err, msg, keysAndValues...)
|
||||||
|
//
|
||||||
|
// In contrast to HandleError, passing nil for the error is still going to
|
||||||
|
// trigger a log entry. Don't construct a new error or wrap an error
|
||||||
|
// with fmt.Errorf. Instead, add additional information via the mssage
|
||||||
|
// and key/value pairs.
|
||||||
|
//
|
||||||
|
// This variant should be used instead of HandleError because it supports
|
||||||
|
// structured, contextual logging.
|
||||||
|
func HandleErrorWithContext(ctx context.Context, err error, msg string, keysAndValues ...interface{}) {
|
||||||
|
handleError(ctx, err, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleError is the common implementation of HandleError and HandleErrorWithContext.
|
||||||
|
// Using this common implementation ensures that the stack depth
|
||||||
|
// is the same regardless through which path the handlers get invoked.
|
||||||
|
func handleError(ctx context.Context, err error, msg string, keysAndValues ...interface{}) {
|
||||||
for _, fn := range ErrorHandlers {
|
for _, fn := range ErrorHandlers {
|
||||||
fn(err)
|
fn(ctx, err, msg, keysAndValues...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logError prints an error with the call stack of the location it was reported
|
// logError prints an error with the call stack of the location it was reported.
|
||||||
func logError(err error) {
|
// It expects to be called as <caller> -> HandleError[WithContext] -> handleError -> logError.
|
||||||
klog.ErrorDepth(2, err)
|
func logError(ctx context.Context, err error, msg string, keysAndValues ...interface{}) {
|
||||||
|
logger := klog.FromContext(ctx).WithCallDepth(3)
|
||||||
|
logger = klog.LoggerWithName(logger, "UnhandledError")
|
||||||
|
logger.Error(err, msg, keysAndValues...) //nolint:logcheck // logcheck complains about unknown key/value pairs.
|
||||||
}
|
}
|
||||||
|
|
||||||
type rudimentaryErrorBackoff struct {
|
type rudimentaryErrorBackoff struct {
|
||||||
@ -125,7 +208,7 @@ type rudimentaryErrorBackoff struct {
|
|||||||
|
|
||||||
// OnError will block if it is called more often than the embedded period time.
|
// OnError will block if it is called more often than the embedded period time.
|
||||||
// This will prevent overly tight hot error loops.
|
// This will prevent overly tight hot error loops.
|
||||||
func (r *rudimentaryErrorBackoff) OnError(error) {
|
func (r *rudimentaryErrorBackoff) OnError() {
|
||||||
now := time.Now() // start the timer before acquiring the lock
|
now := time.Now() // start the timer before acquiring the lock
|
||||||
r.lastErrorTimeLock.Lock()
|
r.lastErrorTimeLock.Lock()
|
||||||
d := now.Sub(r.lastErrorTime)
|
d := now.Sub(r.lastErrorTime)
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:logcheck // Several functions are normally not okay in a package.
|
||||||
|
func ExampleHandleErrorWithContext() {
|
||||||
|
state := klog.CaptureState()
|
||||||
|
defer state.Restore()
|
||||||
|
var fs flag.FlagSet
|
||||||
|
klog.InitFlags(&fs)
|
||||||
|
for flag, value := range map[string]string{
|
||||||
|
"one_output": "true",
|
||||||
|
"logtostderr": "false",
|
||||||
|
} {
|
||||||
|
if err := fs.Set(flag, value); err != nil {
|
||||||
|
fmt.Printf("Unexpected error configuring klog: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
klog.SetOutput(&buffer)
|
||||||
|
|
||||||
|
logger := klog.Background()
|
||||||
|
logger = klog.LoggerWithValues(logger, "request", 42)
|
||||||
|
ctx := klog.NewContext(context.Background(), logger)
|
||||||
|
|
||||||
|
// The line number of the next call must be at line 60. Here are some
|
||||||
|
// blank lines that can be removed to keep the line unchanged.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
HandleErrorWithContext(ctx, errors.New("fake error"), "test")
|
||||||
|
|
||||||
|
klog.Flush()
|
||||||
|
// Strip varying header. Code location should be constant and something
|
||||||
|
// that needs to be tested.
|
||||||
|
output := buffer.String()
|
||||||
|
output = regexp.MustCompile(`^.* ([^[:space:]]*.go:[[:digit:]]*)\] `).ReplaceAllString(output, `xxx $1] `)
|
||||||
|
fmt.Print(output)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// xxx runtime_stack_test.go:60] "test" err="fake error" logger="UnhandledError" request=42
|
||||||
|
}
|
@ -18,6 +18,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -43,8 +44,8 @@ func TestCustomHandleCrash(t *testing.T) {
|
|||||||
old := PanicHandlers
|
old := PanicHandlers
|
||||||
defer func() { PanicHandlers = old }()
|
defer func() { PanicHandlers = old }()
|
||||||
var result interface{}
|
var result interface{}
|
||||||
PanicHandlers = []func(interface{}){
|
PanicHandlers = []func(context.Context, interface{}){
|
||||||
func(r interface{}) {
|
func(_ context.Context, r interface{}) {
|
||||||
result = r
|
result = r
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -66,8 +67,8 @@ func TestCustomHandleError(t *testing.T) {
|
|||||||
old := ErrorHandlers
|
old := ErrorHandlers
|
||||||
defer func() { ErrorHandlers = old }()
|
defer func() { ErrorHandlers = old }()
|
||||||
var result error
|
var result error
|
||||||
ErrorHandlers = []func(error){
|
ErrorHandlers = []ErrorHandler{
|
||||||
func(err error) {
|
func(_ context.Context, err error, msg string, keysAndValues ...interface{}) {
|
||||||
result = err
|
result = err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -101,7 +102,8 @@ func TestHandleCrashLog(t *testing.T) {
|
|||||||
if len(lines) < 4 {
|
if len(lines) < 4 {
|
||||||
t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call")
|
t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call")
|
||||||
}
|
}
|
||||||
if match, _ := regexp.MatchString("Observed a panic: test panic", lines[0]); !match {
|
t.Logf("Got log output:\n%s", strings.Join(lines, "\n"))
|
||||||
|
if match, _ := regexp.MatchString(`"Observed a panic" panic="test panic"`, lines[0]); !match {
|
||||||
t.Errorf("mismatch panic message: %s", lines[0])
|
t.Errorf("mismatch panic message: %s", lines[0])
|
||||||
}
|
}
|
||||||
// The following regexp's verify that Kubernetes panic log matches Golang stdlib
|
// The following regexp's verify that Kubernetes panic log matches Golang stdlib
|
||||||
@ -170,7 +172,7 @@ func Test_rudimentaryErrorBackoff_OnError_ParallelSleep(t *testing.T) {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
<-start
|
<-start
|
||||||
r.OnError(nil) // input error is ignored
|
r.OnError()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -194,8 +194,8 @@ func TestJitterUntilRecoversPanic(t *testing.T) {
|
|||||||
|
|
||||||
// Hook up a custom crash handler to ensure it is called when a jitter function panics
|
// Hook up a custom crash handler to ensure it is called when a jitter function panics
|
||||||
runtime.ReallyCrash = false
|
runtime.ReallyCrash = false
|
||||||
runtime.PanicHandlers = []func(interface{}){
|
runtime.PanicHandlers = []func(context.Context, interface{}){
|
||||||
func(p interface{}) {
|
func(_ context.Context, p interface{}) {
|
||||||
handled++
|
handled++
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -137,9 +137,7 @@ func (t *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metrics.RecordRequestPostTimeout(metrics.PostTimeoutSourceTimeoutHandler, status)
|
metrics.RecordRequestPostTimeout(metrics.PostTimeoutSourceTimeoutHandler, status)
|
||||||
err := fmt.Errorf("post-timeout activity - time-elapsed: %s, %v %q result: %v",
|
utilruntime.HandleErrorWithContext(r.Context(), nil, "Post-timeout activity", "timeElapsed", time.Since(timedOutAt), "method", r.Method, "path", r.URL.Path, "result", res)
|
||||||
time.Since(timedOutAt), r.Method, r.URL.Path, res)
|
|
||||||
utilruntime.HandleError(err)
|
|
||||||
}()
|
}()
|
||||||
}()
|
}()
|
||||||
httplog.SetStacktracePredicate(r.Context(), func(status int) bool {
|
httplog.SetStacktracePredicate(r.Context(), func(status int) bool {
|
||||||
|
@ -408,7 +408,9 @@ func TestErrConnKilled(t *testing.T) {
|
|||||||
if isStackTraceLoggedByRuntime(capturedOutput) {
|
if isStackTraceLoggedByRuntime(capturedOutput) {
|
||||||
t.Errorf("unexpected stack trace in log, actual = %v", capturedOutput)
|
t.Errorf("unexpected stack trace in log, actual = %v", capturedOutput)
|
||||||
}
|
}
|
||||||
if !strings.Contains(capturedOutput, `timeout or abort while handling: method=GET URI="/" audit-ID=""`) {
|
// For the sake of simplicity and clarity this matches the full log line.
|
||||||
|
// This is not part of the Kubernetes API and could change.
|
||||||
|
if !strings.Contains(capturedOutput, `"Timeout or abort while handling" logger="UnhandledError" method="GET" URI="/" auditID=""`) {
|
||||||
t.Errorf("unexpected output captured actual = %v", capturedOutput)
|
t.Errorf("unexpected output captured actual = %v", capturedOutput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,7 +503,9 @@ func TestErrConnKilledHTTP2(t *testing.T) {
|
|||||||
if isStackTraceLoggedByRuntime(capturedOutput) {
|
if isStackTraceLoggedByRuntime(capturedOutput) {
|
||||||
t.Errorf("unexpected stack trace in log, actual = %v", capturedOutput)
|
t.Errorf("unexpected stack trace in log, actual = %v", capturedOutput)
|
||||||
}
|
}
|
||||||
if !strings.Contains(capturedOutput, `timeout or abort while handling: method=GET URI="/" audit-ID=""`) {
|
// For the sake of simplicity and clarity this matches the full log line.
|
||||||
|
// This is not part of the Kubernetes API and could change.
|
||||||
|
if !strings.Contains(capturedOutput, `"Timeout or abort while handling" logger="UnhandledError" method="GET" URI="/" auditID=""`) {
|
||||||
t.Errorf("unexpected output captured actual = %v", capturedOutput)
|
t.Errorf("unexpected output captured actual = %v", capturedOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package filters
|
package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
@ -51,7 +50,7 @@ func WithPanicRecovery(handler http.Handler, resolver request.RequestInfoResolve
|
|||||||
// This call can have different handlers, but the default chain rate limits. Call it after the metrics are updated
|
// This call can have different handlers, but the default chain rate limits. Call it after the metrics are updated
|
||||||
// in case the rate limit delays it. If you outrun the rate for this one timed out requests, something has gone
|
// in case the rate limit delays it. If you outrun the rate for this one timed out requests, something has gone
|
||||||
// seriously wrong with your server, but generally having a logging signal for timeouts is useful.
|
// seriously wrong with your server, but generally having a logging signal for timeouts is useful.
|
||||||
runtime.HandleError(fmt.Errorf("timeout or abort while handling: method=%v URI=%q audit-ID=%q", req.Method, req.RequestURI, audit.GetAuditIDTruncated(req.Context())))
|
runtime.HandleErrorWithContext(req.Context(), nil, "Timeout or abort while handling", "method", req.Method, "URI", req.RequestURI, "auditID", audit.GetAuditIDTruncated(req.Context()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Error(w, "This request caused apiserver to panic. Look in the logs for details.", http.StatusInternalServerError)
|
http.Error(w, "This request caused apiserver to panic. Look in the logs for details.", http.StatusInternalServerError)
|
||||||
|
@ -79,7 +79,7 @@ func webSocketServerStreams(req *http.Request, w http.ResponseWriter, opts Optio
|
|||||||
if p := recover(); p != nil {
|
if p := recover(); p != nil {
|
||||||
// Standard panic logging.
|
// Standard panic logging.
|
||||||
for _, fn := range runtime.PanicHandlers {
|
for _, fn := range runtime.PanicHandlers {
|
||||||
fn(p)
|
fn(req.Context(), p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
Loading…
Reference in New Issue
Block a user