英文:
Get Name of Current Module in Go
问题
我正在尝试为我编写的HTTP处理程序自动创建命名的日志记录器,其中我传递了一个函数(指针)。
我正在使用这个问题中提到的代码来获取函数的名称:
package utils
import (
"reflect"
"runtime"
)
func GetFunctionName(fn interface{}) string {
value := reflect.ValueOf(fn)
ptr := value.Pointer()
ffp := runtime.FuncForPC(ptr)
return ffp.Name()
}
我在我的main
函数中使用它进行测试,代码如下:
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))
}
我看到的输出如下:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerA
这样做没问题,但我希望输出结果为long.HandlerA
、long.nested.path.HandlerB
等。
如果我能获取到Go模块的名称(github.com/naftulikay/golang-webapp/experiments/functionname
),我可以使用strings.Replace
来删除模块名称,得到long/nested/path.HandlerB
,然后再使用strings.Replace
将/
替换为.
,最终得到我想要的值long.nested.path.HandlerB
。
第一个问题是:有没有比runtime.FuncForPC(reflect.ValueOf(fn).Pointer())
更好的方法来获取函数的限定路径?
如果答案是否定的,有没有办法使用runtime
或reflect
来获取当前的Go模块名称,以便我可以将runtime.FuncForPC
的输出转换为我需要的格式?
再次强调,我得到的值如下:
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
我希望得到的值如下:
long.HandlerA
long.nested.path.HandlerB
long.nested.path.HandlerC
编辑: 看起来Go没有模块的运行时表示,这没关系,如果我可以在编译时做到这一点也可以。我看过代码生成文档,但很难弄清楚如何编写自己的自定义代码生成器,以便可以从go generate
中使用。
英文:
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()
}
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))
}
I see output like this:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerA
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
EDIT: It appears that Go does not have a runtime representation of modules, and that's okay, if I can do it at compile time that would be fine too. I've seen the codegen documentation and I'm having a hard time figuring out how to write my own custom codegen that can be used from go generate
.
答案1
得分: 5
模块信息包含在可执行二进制文件中,并可以使用debug.ReadBuildInfo()
函数获取(唯一的要求是可执行文件必须使用模块支持构建,但这是当前版本的默认设置,也可能是未来版本的唯一设置)。
BuildInfo.Path
是当前模块的路径。
假设你有以下的go.mod
文件:
module example.com/foo
示例读取构建信息:
bi, ok := debug.ReadBuildInfo()
if !ok {
log.Printf("Failed to read build info")
return
}
fmt.Println(bi.Main.Path)
// 或者
fmt.Println(bi.Path)
这将输出(在Go Playground上尝试一下):
example.com/foo
example.com/foo
参考链接:https://stackoverflow.com/questions/62009264/golang-how-to-display-modules-version-from-inside-of-code/62009359#62009359
英文:
The module info is included in the executable binary, and can be acquired using the debug.ReadBuildInfo()
function (the only requirement is that the executable must be built using module support, but this is the default in the current version, and likely the only in future versions).
BuildInfo.Path
is the current module's path.
Let's say you have the following go.mod
file:
module example.com/foo
Example reading the build info:
bi, ok := debug.ReadBuildInfo()
if !ok {
log.Printf("Failed to read build info")
return
}
fmt.Println(bi.Main.Path)
// or
fmt.Println(bi.Path)
This will output (try it on the Go Playground):
example.com/foo
example.com/foo
答案2
得分: 3
如果你的目标只是在程序中使用模块的名称,并且如果你可以接受在链接时设置该值,那么你可以使用-ldflags
构建选项。
你可以使用go list -m
命令在模块目录中获取模块的名称。
你可以将所有内容放在一个Makefile或者一个shell脚本中:
MOD_NAME=$(go list -m)
go build -ldflags="-X 'main.MODNAME=$MOD_NAME'" -o main ./...
其中main.go
的内容如下:
package main
import "fmt"
var MODNAME string
func main() {
fmt.Println(MODNAME) // example.com
}
使用提到的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
}
然而,在你的使用情况下,将整个go.mod
文件嵌入可能有些过度。当然,你也可以在运行时打开文件,但这意味着你必须将go.mod
与可执行文件一起部署。使用-ldflags
设置模块名称更加直接明了。
英文:
If your goal is to just have the name of the module available in your program, and if you are okay with setting this value at link time, then you may use the -ldflags
build option.
You can get the name of the module with go list -m
from within the module directory.
You can place everything in a Makefile or in a shell script:
MOD_NAME=$(go list -m)
go build -ldflags="-X 'main.MODNAME=$MOD_NAME'" -o main ./...
With main.go looking like:
package main
import "fmt"
var MODNAME string
func main() {
fmt.Println(MODNAME) // example.com
}
<hr>
With the mentioned "golang.org/x/mod/modfile"
package, an example might look like:
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
}
However embedding the entire go.mod
file in your use case seems overkill. Of course you could also open the file at runtime, but that means you have to deploy go.mod
along with your executable. Setting the module name with -ldflags
is more straightforward IMO.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论