Is it possible get information about caller function in Golang?

huangapple go评论87阅读模式
英文:

Is it possible get information about caller function in Golang?

问题

在Golang中,可以获取关于调用函数的信息吗?例如,如果我有以下代码:

func foo() {
    //做一些事情
}
func main() {
    foo()
}

如何获取foo是从main函数中调用的呢?在其他语言中,我可以轻松实现这个功能(例如在C#中,只需要使用CallerMemberName类属性)。

英文:

Is it possible get information about caller function in Golang? For example if I have

func foo() {
    //Do something
}
func main() {
    foo() 
}

How can I get that foo has been called from main?
I'm able to this in other language (for example in C# I just need to use CallerMemberName class attribute)

答案1

得分: 120

你可以使用runtime.Caller来轻松获取关于调用者的信息:

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

示例 #1: 打印调用者的文件名和行号:https://play.golang.org/p/cdO4Z4ApHS

package main

import (
	"fmt"
	"runtime"
)

func foo() {
	_, file, no, ok := runtime.Caller(1)
	if ok {
		fmt.Printf("called from %s#%d\n", file, no)
	}
}

func main() {
	foo()
}

示例 #2: 使用runtime.FuncForPC获取更多信息:https://play.golang.org/p/y8mpQq2mAv

package main

import (
	"fmt"
	"runtime"
)

func foo() {
	pc, _, _, ok := runtime.Caller(1)
	details := runtime.FuncForPC(pc)
	if ok && details != nil {
		fmt.Printf("called from %s\n", details.Name())
	}
}

func main() {
	foo()
}
英文:

<!-- language-all: lang-go -->

You can use runtime.Caller for easily retrieving information about the caller:

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

Example #1: Print caller file name and line number: https://play.golang.org/p/cdO4Z4ApHS

package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;
)

func foo() {
	_, file, no, ok := runtime.Caller(1)
	if ok {
		fmt.Printf(&quot;called from %s#%d\n&quot;, file, no)
	}
}

func main() {
	foo()
}

Example #2: Get more information with runtime.FuncForPC: https://play.golang.org/p/y8mpQq2mAv

package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;
)

func foo() {
	pc, _, _, ok := runtime.Caller(1)
	details := runtime.FuncForPC(pc)
	if ok &amp;&amp; details != nil {
		fmt.Printf(&quot;called from %s\n&quot;, details.Name())
	}
}

func main() {
	foo()
}

答案2

得分: 32

扩展我的评论,这里是一些返回当前函数调用者的代码:

import (
	"fmt"
	"runtime"
)

func getFrame(skipFrames int) runtime.Frame {
	// 我们需要索引为 skipFrames+2 的帧,因为我们不想要 runtime.Callers 和 getFrame 自身的帧
	targetFrameIndex := skipFrames + 2

	// 设置大小为 targetFrameIndex+2,以确保我们有足够的空间来存储比我们需要的多一个调用者
	programCounters := make([]uintptr, targetFrameIndex+2)
	n := runtime.Callers(0, programCounters)

	frame := runtime.Frame{Function: "unknown"}
	if n > 0 {
		frames := runtime.CallersFrames(programCounters[:n])
		for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
			var frameCandidate runtime.Frame
			frameCandidate, more = frames.Next()
			if frameIndex == targetFrameIndex {
				frame = frameCandidate
			}
		}
	}

	return frame
}

// MyCaller 返回调用它的函数的调用者 :)
func MyCaller() string {
	// 跳过 GetCallerFunctionName 和获取调用者的函数
	return getFrame(2).Function
}

// foo 调用 MyCaller
func foo() {
	fmt.Println(MyCaller())
}

// bar 是我们想要在输出中看到的 - 它是我们的 "caller"
func bar() {
	foo()
}

func main() {
	bar()
}

更多示例请参考:https://play.golang.org/p/cv-SpkvexuM

英文:

expanding on my comment, here's some code that returns the current func's caller

import(
	&quot;fmt&quot;
    &quot;runtime&quot;
)

func getFrame(skipFrames int) runtime.Frame {
    // We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
    targetFrameIndex := skipFrames + 2

    // Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
    programCounters := make([]uintptr, targetFrameIndex+2)
    n := runtime.Callers(0, programCounters)

    frame := runtime.Frame{Function: &quot;unknown&quot;}
    if n &gt; 0 {
        frames := runtime.CallersFrames(programCounters[:n])
        for more, frameIndex := true, 0; more &amp;&amp; frameIndex &lt;= targetFrameIndex; frameIndex++ {
            var frameCandidate runtime.Frame
            frameCandidate, more = frames.Next()
            if frameIndex == targetFrameIndex {
                frame = frameCandidate
            }
        }
    }

    return frame
}

// MyCaller returns the caller of the function that called it :)
func MyCaller() string {
    	// Skip GetCallerFunctionName and the function to get the caller of
    	return getFrame(2).Function
}

// foo calls MyCaller
func foo() {
    fmt.Println(MyCaller())
}

// bar is what we want to see in the output - it is our &quot;caller&quot;
func bar() {
    foo()
}

func main(){
	bar()
}

For more examples: https://play.golang.org/p/cv-SpkvexuM

huangapple
  • 本文由 发表于 2016年2月5日 06:34:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/35212985.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定