Support opentracing filter for Orm

This commit is contained in:
Ming Deng 2020-08-09 12:05:10 +00:00
parent 26b016a3a4
commit dec98f004c
5 changed files with 127 additions and 14 deletions

View File

@ -18,8 +18,12 @@ import (
"context"
)
// FilterChain is used to build a Filter
// don't forget to call next(...) inside your Filter
type FilterChain func(next Filter) Filter
// Filter's behavior is a little big strang.
// it's only be called when users call methods of Ormer
type Filter func(ctx context.Context, inv *Invocation)
var globalFilterChains = make([]FilterChain, 0, 4)

View File

@ -0,0 +1,59 @@
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package opentracing
import (
"context"
"github.com/opentracing/opentracing-go"
"github.com/astaxie/beego/pkg/orm"
)
// FilterChainBuilder provides an extension point
// this Filter's behavior looks a little bit strange
// for example:
// if we want to trace QuerySetter
// actually we trace invoking "QueryTable" and "QueryTableWithCtx"
type FilterChainBuilder struct {
// CustomSpanFunc users are able to custom their span
CustomSpanFunc func(span opentracing.Span, ctx context.Context, inv *orm.Invocation)
}
func (builder *FilterChainBuilder) FilterChain(next orm.Filter) orm.Filter {
return func(ctx context.Context, inv *orm.Invocation) {
operationName := builder.operationName(ctx, inv)
span, spanCtx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
next(spanCtx, inv)
span.SetTag("Method", inv.Method)
span.SetTag("Table", inv.GetTableName())
span.SetTag("InsideTx", inv.InsideTx)
span.SetTag("TxName", spanCtx.Value(orm.TxNameKey))
if builder.CustomSpanFunc != nil {
builder.CustomSpanFunc(span, spanCtx, inv)
}
}
}
func (builder *FilterChainBuilder) operationName(ctx context.Context, inv *orm.Invocation) string {
if n, ok := ctx.Value(orm.TxNameKey).(string); ok {
return inv.Method + "#" + n
}
return inv.Method + "#" + inv.GetTableName()
}

View File

@ -0,0 +1,43 @@
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package opentracing
import (
"context"
"testing"
"time"
"github.com/opentracing/opentracing-go"
"github.com/astaxie/beego/pkg/orm"
)
func TestFilterChainBuilder_FilterChain(t *testing.T) {
next := func(ctx context.Context, inv *orm.Invocation) {
inv.TxName = "Hello"
}
builder := &FilterChainBuilder{
CustomSpanFunc: func(span opentracing.Span, ctx context.Context, inv *orm.Invocation) {
span.SetTag("hello", "hell")
},
}
inv := &orm.Invocation{
Method: "Hello",
TxStartTime: time.Now(),
}
builder.FilterChain(next)(context.Background(), inv)
}

View File

@ -29,6 +29,10 @@ import (
// FilterChainBuilder is an extension point,
// when we want to support some configuration,
// please use this structure
// this Filter's behavior looks a little bit strange
// for example:
// if we want to records the metrics of QuerySetter
// actually we only records metrics of invoking "QueryTable" and "QueryTableWithCtx"
type FilterChainBuilder struct {
summaryVec prometheus.ObserverVec
}

View File

@ -30,22 +30,14 @@ type FilterChainBuilder struct {
func (builder *FilterChainBuilder) FilterChain(next beego.FilterFunc) beego.FilterFunc {
return func(ctx *context.Context) {
span := opentracing.SpanFromContext(ctx.Request.Context())
spanCtx := ctx.Request.Context()
if span == nil {
operationName := ctx.Input.URL()
// it means that there is not any span, so we create a span as the root span.
// TODO, if we support multiple servers, this need to be changed
route, found := beego.BeeApp.Handlers.FindRouter(ctx)
if found {
operationName = route.GetPattern()
}
span, spanCtx = opentracing.StartSpanFromContext(spanCtx, operationName)
newReq := ctx.Request.Clone(spanCtx)
ctx.Reset(ctx.ResponseWriter.ResponseWriter, newReq)
}
operationName := builder.operationName(ctx)
span, spanCtx := opentracing.StartSpanFromContext(ctx.Request.Context(), operationName)
defer span.Finish()
newReq := ctx.Request.Clone(spanCtx)
ctx.Reset(ctx.ResponseWriter.ResponseWriter, newReq)
next(ctx)
// if you think we need to do more things, feel free to create an issue to tell us
span.SetTag("status", ctx.Output.Status)
@ -56,3 +48,14 @@ func (builder *FilterChainBuilder) FilterChain(next beego.FilterFunc) beego.Filt
}
}
}
func (builder *FilterChainBuilder) operationName(ctx *context.Context) string {
operationName := ctx.Input.URL()
// it means that there is not any span, so we create a span as the root span.
// TODO, if we support multiple servers, this need to be changed
route, found := beego.BeeApp.Handlers.FindRouter(ctx)
if found {
operationName = route.GetPattern()
}
return operationName
}