Get Name of Current Module in Go

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.HandlerA
  • github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerB
  • github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerC

And I'd like to get values like:

  • long.HandlerA
  • long.nested.path.HandlerB
  • long.nested.path.HandlerC

编辑: Go 似乎没有模块的运行时表示,这没关系,如果我可以在编译时做到这一点,那也很好。我已经看过 codegen 文档,并且很难弄清楚如何编写可以从go generate.

icz*_*cza 6

模块信息包含在可执行二进制文件中,可以使用该函数获取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 - 如何从代码内部显示模块版本


bla*_*een 4

如果您的目标只是程序中可用的模块名称,并且您可以在链接时设置此值,那么您可以使用-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更简单。