Naf*_*Kay 5 reflection go go-modules
I am attempting to create named loggers automatically for HTTP handlers that I'm writing, where I am passed a function (pointer).
I'm using the code mentioned in this question to get the name of a function:
package utils
import (
"reflect"
"runtime"
)
func GetFunctionName(fn interface{}) string {
value := reflect.ValueOf(fn)
ptr := value.Pointer()
ffp := runtime.FuncForPC(ptr)
return ffp.Name()
}
Run Code Online (Sandbox Code Playgroud)
I'm using this in my main function to try it out like so:
package main
import (
"github.com/naftulikay/golang-webapp/experiments/functionname/long"
"github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path"
"github.com/naftulikay/golang-webapp/experiments/functionname/utils"
"log"
)
type Empty struct{}
func main() {
a := long.HandlerA
b := path.HandlerB
c := path.HandlerC
log.Printf("long.HandlerA: %s", utils.GetFunctionName(a))
log.Printf("long.nested.path.HandlerB: %s", utils.GetFunctionName(b))
log.Printf("long.nested.path.HandlerC: %s", utils.GetFunctionName(c))
}
Run Code Online (Sandbox Code Playgroud)
I see output like this:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerA
Run Code Online (Sandbox Code Playgroud)
This is okay but I'd like an output such as long.HandlerA, long.nested.path.HandlerB, etc.
If I could get the Go module name (github.com/naftulikay/golang-webapp/experiments/functionname), I can then use strings.Replace to remove the module name to arrive at long/nested/path.HandlerB, then strings.Replace to replace / with . to finally get to my desired value, which is long.nested.path.HandlerB.
The first question is: can I do better than runtime.FuncForPC(reflect.ValueOf(fn).Pointer()) for getting the qualified path to a function?
If the answer is no, is there a way to get the current Go module name using runtime or reflect so that I can transform the output of runtime.FuncForPC into what I need?
Once again, I'm getting values like:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerAgithub.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerBgithub.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerCAnd I'd like to get values like:
long.HandlerAlong.nested.path.HandlerBlong.nested.path.HandlerC编辑: Go 似乎没有模块的运行时表示,这没关系,如果我可以在编译时做到这一点,那也很好。我已经看过 codegen 文档,并且很难弄清楚如何编写可以从go generate.
模块信息包含在可执行二进制文件中,可以使用该函数获取debug.ReadBuildInfo()(唯一的要求是必须使用模块支持构建可执行文件,但这是当前版本中的默认设置,并且可能是未来版本中的唯一设置) 。
BuildInfo.Path是当前模块的路径。
假设您有以下go.mod文件:
module example.com/foo
Run Code Online (Sandbox Code Playgroud)
读取构建信息的示例:
bi, ok := debug.ReadBuildInfo()
if !ok {
log.Printf("Failed to read build info")
return
}
fmt.Println(bi.Main.Path)
// or
fmt.Println(bi.Path)
Run Code Online (Sandbox Code Playgroud)
这将输出(在Go Playground上尝试):
example.com/foo
example.com/foo
Run Code Online (Sandbox Code Playgroud)
请参阅相关内容:Golang - 如何从代码内部显示模块版本
如果您的目标只是让程序中可用的模块名称,并且您可以在链接时设置此值,那么您可以使用-ldflags构建选项。
go list -m您可以从模块目录中获取模块的名称。
您可以将所有内容放入 Makefile 或 shell 脚本中:
MOD_NAME=$(go list -m)
go build -ldflags="-X 'main.MODNAME=$MOD_NAME'" -o main ./...
Run Code Online (Sandbox Code Playgroud)
main.go 看起来像:
package main
import "fmt"
var MODNAME string
func main() {
fmt.Println(MODNAME) // example.com
}
Run Code Online (Sandbox Code Playgroud)
对于上述"golang.org/x/mod/modfile"包,示例可能如下所示:
package main
import (
"fmt"
"golang.org/x/mod/modfile"
_ "embed"
)
//go:embed go.mod
var gomod []byte
func main() {
f, err := modfile.Parse("go.mod", gomod, nil)
if err != nil {
panic(err)
}
fmt.Println(f.Module.Mod.Path) // example.com
}
Run Code Online (Sandbox Code Playgroud)
然而,将整个go.mod文件嵌入到您的用例中似乎有点矫枉过正。当然,您也可以在运行时打开该文件,但这意味着您必须go.mod与可执行文件一起部署。在我看来,设置模块名称-ldflags更简单。