Skip to content

runtime.Caller的使用

在日志库中通常可以看到runtime.Caller 。它被用来提供当前日志操作的源文件信息。

func Caller(skip int) (pc uintptr, file string, line int, ok bool)

Caller当前调用的goroutine栈的文件和行号信息。参数skip由当前向上层的stack frame的号码,用0来表示当前调用,1表示调用当前函数的上层函数,如此递推。(需要注意的是,由于历史原因Caller和Callers中的skip的含义是不同的)。

返回值为对应调用的程序的计数器,文件名以及行号。如果无法获取这些信息,那么ok的值就会是false。

package rt

import (
	"runtime"
	"testing"
)

func TestRuntimeCaller(t *testing.T) {
	pc, file, line, ok := runtime.Caller(0)
	if ok {
		pcName := runtime.FuncForPC(pc).Name()
		t.Log("pc:", pc, "pcName:", pcName, "file:", file, "line:", line)
	}
}

输出的结果为

pc: 5175028 pcName: learning-go/basic/runtime.TestRuntimeCaller file: /home/daomin/projects/learning-go/basic/runtime/caller_test.go line: 9

这是一个开销很大的函数,golang自带的log里,在使用这个函数的时候,专门做了特殊处理来优化性能。

go/src/log/log.go

func (l *Logger) Output(calldepth int, s string) error {

    now := time.Now() // get this early.

    var file string

    var line int

    l.mu.Lock()

    defer l.mu.Unlock()

    if l.flag&(Lshortfile|Llongfile) != 0 {

        // Release lock while getting caller info - it's expensive.

        l.mu.Unlock()

        var ok bool

        _, file, line, ok = runtime.Caller(calldepth)

        if !ok {

            file = "???"

            line = 0

        }

        l.mu.Lock()

    }

    ...

    return er

}

推荐使用runtime.CallersFrames() TODO

https://stackoverflow.com/questions/64135896/runtime-callers-print-different-program-counters-depending-on-where-its-run-from

Published ingolang

Be First to Comment

Leave a Reply

Your email address will not be published.