28

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)
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Laserallan
  • 11,072
  • 10
  • 46
  • 67
  • 1
    It seems to me that the type of main is `function`. What would you expect as a name ? – Denys Séguret May 24 '12 at 18:02
  • That's a very point. The code example should probably not work, but I think the name of the question is valid. – Laserallan May 24 '12 at 18:03
  • Possible duplicate of [How to get the name of a function in Go?](https://stackoverflow.com/questions/7052693/how-to-get-the-name-of-a-function-in-go) – satoru Apr 12 '19 at 02:02

4 Answers4

40

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.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 11
    Or, without reflect, pc, _, _, _ := runtime.Caller(0) fmt.Println("Name of function: " + runtime.FuncForPC(pc).Name()) – Sonia May 24 '12 at 18:25
  • 1
    Interesting. But I was assuming OP was looking for a more general solution, with any function. – Denys Séguret May 24 '12 at 18:27
33
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

Sonia
  • 27,135
  • 8
  • 52
  • 54
  • Personally, I prefer your solution (or @Koala3's extension on your own solution), mostly because it allows to encapsulate the runtime calls inside a function which can then be 'safely' deployed elsewhere; if the Go runtime functions for some reason change, then the whole code needs to be changed at just one spot. Also, avoiding reflection is a neat trick, well done! :) – Gwyneth Llewelyn Jul 08 '17 at 18:13
  • Great answer Sonia! One caution is that `FuncForPC(pc)` returns a pointer, so you should check for nil always. I will share as an answer a full production-level utility function based on the core principles above. Thanks! – Rohanthewiz Oct 22 '21 at 16:04
4
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
}
Koala3
  • 2,203
  • 4
  • 20
  • 15
  • The advantage of keeping the package name with the function name is, of course, that it's possible to have several functions with the same name in different packages... and then you have no idea which is which :) In fact, the main reason for me to search for this answer on SO was because I was unsure which of my functions was causing an error, and I needed to track down the precise package name besides the function name... Note that you need to import `strings`and `filepath`as well for your solution to work! – Gwyneth Llewelyn Jul 08 '17 at 18:16
0

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
}
Rohanthewiz
  • 947
  • 9
  • 9