Get name of function using reflection

19.4k views Asked by At

I'm trying to use Go's reflection system to retrieve the name of a function but I get an empty string when calling the Name method on its type. Is this the expected behavior?

This is a simple example of how I approach the problem:

package main

import "fmt"
import "reflect"

func main() {
    typ := reflect.TypeOf(main)
    name := typ.Name()
    fmt.Println("Name of function" + name)
}
4

There are 4 answers

2
Denys Séguret On BEST ANSWER

The solution is to use FuncForPc which returns a *Func.

This returns "main.main" :

package main

import "fmt"
import "reflect"
import "runtime"


func main() {
    name := runtime.FuncForPC(reflect.ValueOf(main).Pointer()).Name()
    fmt.Println("Name of function : " + name)
}

If you want "main", just tokenize it.

2
Sonia On
package main

import "fmt"
import "runtime"

func main() {
    pc, _, _, _ := runtime.Caller(0)
    fmt.Println("Name of function: " + runtime.FuncForPC(pc).Name())
    fmt.Println()

    // or, define a function for it
    fmt.Println("Name of function: " + funcName())
    x()
}

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    return runtime.FuncForPC(pc).Name()
}

func x() {
    fmt.Println("Name of function: " + funcName())
    y()
}

func y() {
    fmt.Println("Name of function: " + funcName())
    z()
}
func z() {
    fmt.Println("Name of function: " + funcName())
}

Output:

Name of function: main.main

Name of function: main.main
Name of function: main.x
Name of function: main.y
Name of function: main.z

1
Koala3 On
import runtime

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    nameFull := runtime.FuncForPC(pc).Name()    // main.foo
    nameEnd := filepath.Ext(nameFull)           // .foo
    name := strings.TrimPrefix(nameEnd, ".")    // foo
    return name
}
0
Rohanthewiz On

This is a tested production ready utility function for returning function name.

Note 1: We are handling the possibility of a nil pointer from FuncForPC

Note 2: optFuncLevel is just a friendly name for stack frame level. This gives us the flexibility of using this within another layer of utility functions. A direct call from say main would just pass 1 (or nothing since default), but if I am calling FunctionName in a log enriching function, say PrettyLog() that is called from regular code, I would call it as FunctionName(2) in the call from PrettyLog, so the function name returned is the name of the caller of PrettyLog, not PrettyLog itself.

// FunctionName returns the function name of the caller
// optFuncLevel passes the function level to go back up.
// The default is 1, referring to the caller of this function
func FunctionName(optFuncLevel ...int) (funcName string) {
    frameLevel := 1 // default to the caller's frame
    if len(optFuncLevel) > 0 {
        frameLevel = optFuncLevel[0]
    }

    if pc, _, _, ok := runtime.Caller(frameLevel); ok {
        fPtr := runtime.FuncForPC(pc)
        if fPtr == nil {
            return
        }
        // Shorten full function name a bit
        farr := strings.SplitN(fPtr.Name(), "/", 2)
        if len(farr) < 2 {
            return
        }
        return farr[1]
    }

    return
}