英文:
How can I pass a context.Context via Gin's context?
问题
我正在尝试找出在使用Gin时,使用OpenTelemetry进行跟踪时传播context.Context
的正确方法。
我目前有一个gin
处理程序,它调用一个函数并传递一个*gin.Context
,如下所示:
func (m Handler) doSomething(ginCtx *gin.Context, name string) {
ctx, span := otel.Tracer("mytracer").Start(ginCtx.Request.Context(), "doSomething")
defer span.End()
// ...
}
实际上,这会导致不正确的跟踪范围,因为在调用doSomething()
之前,我在调用函数中创建了一个新的context.Context
,其中包含父跟踪范围的信息(类似于上面的代码片段)。
在我的大部分代码中,我都在传递context.Context
,但我想我可以利用gin的Context
。否则,我必须同时传递这两种类型的上下文:
func (m Handler) doSomething(ctx context.Context, ginCtx *gin.Context, name string) {
ctx, span := otel.Tracer("mytracer").Start(ctx, "doSomething")
defer span.End()
// ...
}
这种做法感觉不对,因为存储在*gin.Context
中的Request
与我通过context.Context
作为参数传递的内容不同步。然而,我不敢使用具有更新的Context
的新Request
设置*gin.Context
上的Request
,因为:
- 它只适用于此函数,我必须取消设置它(也许通过
defer()
实现?) - 它似乎不是线程安全的(尽管我假设在这种情况下,我需要在gin上下文中进行
Copy()
)
处理这个问题的正确方法是仅Copy
*gin.Context
并使用新的context.Context
修改Request,然后传递*gin.Context
而不是context.Context
吗?
我不知道从文本中Copy
一个gin.Context
的影响是什么:
> Copy返回一个当前上下文的副本,可以在请求范围之外安全使用。当上下文必须传递给goroutine时,必须使用此方法。
我是否仍然可以通过复制的*gin.Context
进行Abort()
操作,复制后的旧上下文是否仍然可用?我只需要像传递context.Context
一样,使其行为类似于堆栈,即从函数中简单返回即可将上下文弹出,然后我就可以使用旧的上下文。
英文:
I'm trying to figure out the proper way to propagate a context.Context
for the purposes of tracing with OpenTelemetry when using Gin.
I currently have a gin
handler that calls a function and passes a *gin.Context
, like so:
func (m Handler) doSomething(ginCtx *gin.Context, name string) {
ctx, span := otel.Tracer("mytracer").Start(ginCtx.Request.Context(), "doSomething")
defer span.End()
// ...
}
This actually results in incorrect spans, since before doSomething()
is called, I create a new context.Context
in the calling function with the parent span information (similar to the snippet above).
In most of my code, I'm passing around a context.Context
, but figured I could make use of gin's Context
. Otherwise I have to pass both types of contexts:
func (m Handler) doSomething(ctx context.Context, ginCtx *gin.Context, name string) {
ctx, span := otel.Tracer("mytracer").Start(ctx, "doSomething")
defer span.End()
// ...
}
This feels wrong since the Request
stored in the *gin.Context
is out of sync with what I'm passing around as a parameter via context.Context
. However, I'm afraid to set the Request
on the *gin.Context
with a new Request
that has the updated Context
because
- It's only for this function and I'd have to un-set it (maybe through a
defer()
?) - It doesn't seem like it'd be thread-safe (though I assume I'd need to
Copy()
the gin Context in this scenario anyway)
Is the proper way to handle this just Copy
ing the *gin.Context
and modifying the Request with the new context.Context
, then passing the *gin.Context
around instead of context.Context
?
I don't know what the implications are of Copy
ing a gin.Context
from the text:
> Copy returns a copy of the current context that can be safely used outside the request's scope. This has to be used when the context has to be passed to a goroutine.
Can I still Abort()
through a copied *gin.Context
, and is the old one still usable after copying? I just need something that behaves like a stack in the same way that passing context.Context
s around does, where simply returning from the function "pops" the context off and I'm left with the old one.
答案1
得分: 1
我对gin
包不是特别熟悉,但是我有一些类似的要求需要在labstack/echo
中解决,我通过以下方式将所需内容注入到函数中来解决:
type Router struct {
Inner *gin.Gin
}
func (router *Router) Handle(ctx context.Context, method string, route string, handler func(context.Context, *gin.Context) gin.IRoutes) {
router.Inner.Handle(method, route, func(gCtx *gin.Context) error {
return handler(ctx, gCtx)
})
}
这样可以在声明路由时将context.Context
注入进去。所以,不再需要这样做:
g := gin.Default()
g.POST("/some/route", myHandler) // myHandler接受*gin.Context
而是可以这样做:
r := Router{Inner: gin.Default()}
r.Handle("POST", "/some/route", myHandlerV2) // myHandlerV2接受*gin.Context和context.Context
这种模式还可以允许你注入其他依赖,比如数据库连接、日志记录器等。
英文:
I'm not overly familiar with the gin
package, but I had some similar requirements for labstack/echo
and I solved it by injecting what I needed into the function this way:
type Router struct {
Inner *gin.Gin
}
func (router *Router) Handle(ctx context.Context, method string, route string, handler(context.Context, *gin.Context) gin.IRoutes {
return router.Inner.Handle(method, route, func(gCtx *gin.Context) error {
return handler(ctx, gCtx)
})
}
This works by allowing you to inject the context.Context
in when you declare the route. So, instead of doing this:
g := gin.Default()
g.POST("/some/route", myHandler) // myHandler accepts *gin.Context
you can now do:
r := Router{Inner: gin.Default()}
r.Handle("POST", "/some/route", myHandlerV2) // myHandlerV2 accepts *gin.Context and context.Context
This pattern would also allow you to inject other dependencies such as database connections, loggers, etc.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论