kubernetes源码阅读之工具函数runtime使用

在代码中经常会遇到异常等情况,可以自定义异常处理,甚至可以定义一串处理函数,非常方便

var (
    ReallyCrash = true
)

//默认的是记录日志
var PanicHandlers = []func(interface{}){logPanic}

// 先是通过PanicHandlers里面的函数处理
// 然后通过additionalHandlers里面函数处理,最后向上继续抛出
func HandleCrash(additionalHandlers ...func(interface{})) {
    if r := recover(); r != nil {
        for _, fn := range PanicHandlers {
            fn(r)
        }
        for _, fn := range additionalHandlers {
            fn(r)
        }
        if ReallyCrash {
            // Actually proceed to panic.
            panic(r)
        }
    }
}

// logPanic当异常发生后记录调用树
func logPanic(r interface{}) {
    callers := getCallers(r)
    glog.Errorf("Observed a panic: %#v (%v)\n%v", r, r, callers)
}

func getCallers(r interface{}) string {
    callers := ""
    for i := 0; true; i++ {
        _, file, line, ok := runtime.Caller(i)
        if !ok {
            break
        }
        callers = callers + fmt.Sprintf("%v:%v\n", file, line)
    }

    return callers
}


// ErrorHandlers保存一组处理函数,当遇到不能返回的错误时候被触发.
var ErrorHandlers = []func(error){
    logError,
    (&rudimentaryErrorBackoff{
        lastErrorTime: time.Now(),
        // 1ms was the number folks were able to stomach as a global rate limit.
        // If you need to log errors more than 1000 times a second you
        // should probably consider fixing your code instead. :)
        minPeriod: time.Millisecond,
    }).OnError,
}

// HandlerError 是一个error处理函数
func HandleError(err error) {
    if err == nil {
        return
    }

    for _, fn := range ErrorHandlers {
        fn(err)
    }
}

// logError prints an error with the call stack of the location it was reported
func logError(err error) {
    glog.ErrorDepth(2, err)
}

type rudimentaryErrorBackoff struct {
    minPeriod time.Duration // immutable
    // TODO(lavalamp): use the clock for testability. Need to move that
    // package for that to be accessible here.
    lastErrorTimeLock sync.Mutex
    lastErrorTime     time.Time
}

// OnError will block if it is called more often than the embedded period time.
// This will prevent overly tight hot error loops.
func (r *rudimentaryErrorBackoff) OnError(error) {
    r.lastErrorTimeLock.Lock()
    defer r.lastErrorTimeLock.Unlock()
    d := time.Since(r.lastErrorTime)
    if d < r.minPeriod {
        time.Sleep(r.minPeriod - d)
    }
    r.lastErrorTime = time.Now()
}

// GetCaller返回函数调用者
func GetCaller() string {
    var pc [1]uintptr
    runtime.Callers(3, pc[:])
    f := runtime.FuncForPC(pc[0])
    if f == nil {
        return fmt.Sprintf("Unable to find caller")
    }
    return f.Name()
}

// RecoverFromPanic替换并包含原来错误,并加上调用栈
func RecoverFromPanic(err *error) {
    if r := recover(); r != nil {
        callers := getCallers(r)

        *err = fmt.Errorf(
            "recovered from panic %q. (err=%v) Call stack:\n%v",
            r,
            *err,
            callers)
    }
}

下面有写个测试的程序


func TestCustomHandleCrash(t *testing.T) {
    old := PanicHandlers
    defer func() { PanicHandlers = old }()
    var result interface{}
    PanicHandlers = []func(interface{}){
        func(r interface{}) {
            t.Log("exec panichandlers")
            result = r
        },
    }
    func() {
        defer func() {
            t.Log("defer")
            if x := recover(); x == nil {
                t.Errorf("Expected a panic to recover from TestCustomHandleCrash")
            }
        }()
        defer HandleCrash()
        panic("test")
    }()
    fmt.Println(result)
    if result != "test" {
        t.Errorf("did not receive custom handler")
    }
}

自定义了一个处理方法,由于defer调用栈,会先执行PanicHandlers里面的方法,然后执行defer func方法。此时x仍然不为nil,因为在HandleCrash调用完方法后还会继续向上抛出异常的。

下面异常处理函数就更加简单了,


func TestCustomHandleError(t *testing.T) {
    old := ErrorHandlers
    defer func() { ErrorHandlers = old }()
    var result error
    ErrorHandlers = []func(error){
        func(err error) {
            result = err
        },
    }
    err := fmt.Errorf("test")
    HandleError(err)
    if result != err {
        t.Errorf("did not receive custom handler")
    }
}

通过HandleError处理异常,他会主意调用ErrorHandlers里面的方法,其实这个里面只有一个方法。

你可能感兴趣的:(Kubernetes)