Golang中如何获取函数名称

Golang中如何获取函数名称 我有一个函数,在该函数中调用了另一个函数,我想在内部函数中获取外部函数的名称:

func MyFunc() {
  CallFunc()
}

func CallFunc() {
  // 我想打印出 MyFunc
}

简单的解决方案是将函数名作为参数传递。但这需要手动操作:

CallFunc("MyFunc")

但我认为应该存在某种无需手动传递函数名的方法。有办法实现吗?

5 回复

哇。我之前都不知道这个。太好了!

更多关于Golang中如何获取函数名称的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我也是来建议这个解决方案的。它可以在(不那么)野外找到:appliedgo.net/what 是一个临时的故障排除工具,它通过这种方式获取当前函数名

(抱歉自我推销一下。^-^)

如果你想要一个对熟悉Go堆栈跟踪的人来说格式良好的调用栈,Dean的建议非常棒。如果你只想获取调用函数的名称,可以这样做:

package main

import (
        "fmt"
        "runtime"
)

func main() {
        fmt.Println("from main:", CallerName(0))
        func2()
}

func func2() {
        fmt.Println("from func2:", CallerName(0))
        fmt.Println(CallerName(0), "called by", CallerName(1))
}

func CallerName(skip int) string {
        pc, _, _, ok := runtime.Caller(skip + 1)
        if !ok {
                return ""
        }
        f := runtime.FuncForPC(pc)
        if f == nil {
                return ""
        }
        return f.Name()
}

请注意,这将包含“完全限定”的函数名。main包中的函数名是main.<函数名>,但非main包中的函数名将包含完整的包路径(例如(*github.com/account/project/package.Type).Func)。

或许可以使用类似 runtime.Stack 的方法?

func CallFunc() {
	stack := make([]byte, 512)
	runtime.Stack(stack, false)
	fmt.Println("Stack is:")
	fmt.Println(string(stack))
}

在 Go Playground 中运行 会打印出:

Stack is:
goroutine 1 [running]:
main.CallFunc()
	/tmp/sandbox549134487/prog.go:20 +0x45
main.MyFunc(...)
	/tmp/sandbox549134487/prog.go:15
main.main()
	/tmp/sandbox549134487/prog.go:11 +0x18

你可以在这里查看更多信息:

favicon.ico

runtime 包 - runtime - Go Packages

runtime 包包含与 Go 运行时系统交互的操作,例如控制 goroutine 的函数。

反射可能也会让你感兴趣:

favicon.ico

reflect 包 - reflect - Go Packages

reflect 包实现了运行时反射,允许程序操作任意类型的对象。

不过,听起来你想要的似乎是基于 CallFunc 被调用的位置来提供不同的功能。这听起来确实像是通过参数来实现会更合理。

在Go中可以通过runtime.Caller获取调用栈信息,进而获取函数名称。以下是实现方法:

package main

import (
    "fmt"
    "runtime"
    "strings"
)

func MyFunc() {
    CallFunc()
}

func CallFunc() {
    // 获取调用者的函数名称
    pc, _, _, ok := runtime.Caller(1)
    if !ok {
        fmt.Println("无法获取调用者信息")
        return
    }
    
    fn := runtime.FuncForPC(pc)
    if fn == nil {
        fmt.Println("无法获取函数信息")
        return
    }
    
    // 获取完整的函数名(包含包路径)
    fullName := fn.Name()
    
    // 提取最后的函数名部分
    parts := strings.Split(fullName, ".")
    funcName := parts[len(parts)-1]
    
    fmt.Printf("调用者函数名: %s\n", funcName) // 输出: MyFunc
}

func main() {
    MyFunc()
}

如果需要更通用的工具函数:

func GetCallerFuncName(skip int) string {
    pc, _, _, ok := runtime.Caller(skip + 1)
    if !ok {
        return ""
    }
    
    fn := runtime.FuncForPC(pc)
    if fn == nil {
        return ""
    }
    
    fullName := fn.Name()
    parts := strings.Split(fullName, ".")
    return parts[len(parts)-1]
}

func CallFunc() {
    callerName := GetCallerFuncName(1) // 1表示跳过CallFunc本身
    fmt.Printf("调用者函数名: %s\n", callerName)
}

对于嵌套调用的情况:

func OuterFunc() {
    MiddleFunc()
}

func MiddleFunc() {
    CallFunc()
}

func CallFunc() {
    // 获取不同层级的调用者
    for i := 1; i <= 3; i++ {
        pc, _, _, ok := runtime.Caller(i)
        if !ok {
            break
        }
        fn := runtime.FuncForPC(pc)
        if fn != nil {
            fullName := fn.Name()
            parts := strings.Split(fullName, ".")
            fmt.Printf("层级 %d: %s\n", i, parts[len(parts)-1])
        }
    }
}
// 输出:
// 层级 1: CallFunc
// 层级 2: MiddleFunc  
// 层级 3: OuterFunc

注意:runtime.Caller的参数表示要跳过的调用栈帧数。传递1表示跳过CallFunc本身,获取其调用者。

回到顶部