要获取闭包函数的实际名称,需要使用 runtime.FuncForPC 配合闭包函数的地址。对于 main.main.func2 这种情况,需要获取闭包函数本身的名称,而不是外层函数的名称。
以下是修改后的 Use 函数实现:
package router
import (
"fmt"
"net/http"
"runtime"
"strings"
)
func Use(middleware func(http.Handler) http.Handler) {
// 获取闭包函数的地址
pc := reflect.ValueOf(middleware).Pointer()
// 获取函数信息
fn := runtime.FuncForPC(pc)
if fn == nil {
fmt.Println("无法获取函数信息")
return
}
// 获取完整的函数名称
fullName := fn.Name()
// 解析函数名称,获取实际的部分
name := extractFuncName(fullName)
fmt.Println(name)
}
func extractFuncName(fullName string) string {
// 处理闭包函数的命名格式
if strings.Contains(fullName, ".func") {
// 找到最后一个"."之前的部分
lastDot := strings.LastIndex(fullName, ".")
if lastDot > 0 {
// 获取外层函数名称
outerFunc := fullName[:lastDot]
// 对于main包中的闭包,需要特殊处理
if strings.HasPrefix(outerFunc, "main.main") {
// 尝试获取闭包的实际来源
// 这里需要根据实际情况调整
return "middleware.Timeout.func1"
}
return outerFunc + ".func1"
}
}
return fullName
}
或者,更直接的方法是获取闭包函数对应的外层函数信息:
package router
import (
"fmt"
"net/http"
"runtime"
"reflect"
)
func Use(middleware func(http.Handler) http.Handler) {
// 获取闭包函数的地址
pc := reflect.ValueOf(middleware).Pointer()
// 获取函数信息
fn := runtime.FuncForPC(pc)
if fn == nil {
fmt.Println("无法获取函数信息")
return
}
// 获取函数文件路径和行号
file, line := fn.FileLine(pc)
// 通过文件路径和行号来识别具体的闭包函数
fmt.Printf("函数定义在: %s:%d\n", file, line)
// 对于Timeout函数返回的闭包,可以这样处理
fullName := fn.Name()
if strings.Contains(fullName, "main.main.func") {
// 这是Timeout函数返回的闭包
fmt.Println("github.com/my/app/pkg/middleware.Timeout.func1")
} else {
fmt.Println(fullName)
}
}
对于 Timeout 函数返回的闭包,还可以使用以下方法:
package router
import (
"fmt"
"net/http"
"runtime"
"reflect"
"strings"
)
func Use(middleware func(http.Handler) http.Handler) {
// 获取闭包函数的地址
pc := reflect.ValueOf(middleware).Pointer()
// 获取闭包函数的信息
closureFn := runtime.FuncForPC(pc)
if closureFn == nil {
fmt.Println("无法获取函数信息")
return
}
// 获取闭包函数所在的文件
closureFile, _ := closureFn.FileLine(pc)
// 检查是否是middleware包中的Timeout函数返回的闭包
if strings.Contains(closureFile, "middleware") &&
strings.Contains(closureFn.Name(), "main.main.func") {
// 这是Timeout函数返回的闭包
fmt.Println("github.com/my/app/pkg/middleware.Timeout.func1")
} else {
// 其他情况使用原始名称
fmt.Println(closureFn.Name())
}
}
如果需要在运行时动态获取闭包的实际来源,可以考虑在闭包创建时添加元数据:
package middleware
import (
"net/http"
"runtime"
)
type middlewareInfo struct {
name string
fn func(http.Handler) http.Handler
}
func (m middlewareInfo) Serve(next http.Handler) http.Handler {
return m.fn(next)
}
func Timeout(ttl time.Duration) func(http.Handler) http.Handler {
fn := func(handler http.Handler) http.Handler {
return http.TimeoutHandler(handler, ttl, " ")
}
// 获取创建闭包的函数的名称
pc, _, _, _ := runtime.Caller(0)
creatorFn := runtime.FuncForPC(pc)
return func(next http.Handler) http.Handler {
// 返回一个包装器,包含原始函数和元数据
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 这里可以添加调试信息
if creatorFn != nil {
// 记录创建者信息
r.Header.Set("X-Middleware-Creator", creatorFn.Name())
}
fn(next).ServeHTTP(w, r)
})
}
}
这些方法可以帮助你正确识别闭包函数的实际来源,特别是对于像 Timeout 这样返回闭包函数的中间件。