Skip to content

Commit

Permalink
Merge pull request #4950 from Dokiys/feature/orm_otel_filter
Browse files Browse the repository at this point in the history
Feat: add orm opentelemetry filter
  • Loading branch information
flycash authored May 30, 2022
2 parents 809c0b4 + f2c28be commit 5a873ea
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [upgrade to Go 1.18](https://github.com/beego/beego/pull/4896)
- [make `PatternLogFormatter` handling the arguments](https://github.com/beego/beego/pull/4914/files)
- [Add httplib OpenTelemetry Filter](https://github.com/beego/beego/pull/4888, https://github.com/beego/beego/pull/4915)
- [Add orm OpenTelemetry Filter](https://github.com/beego/beego/issues/4944)
- [Support NewBeegoRequestWithCtx in httplib](https://github.com/beego/beego/pull/4895)
- [Support lifecycle callback](https://github.com/beego/beego/pull/4918)
- [Append column comments to create table sentence when using postgres](https://github.com/beego/beego/pull/4940)
Expand Down
90 changes: 90 additions & 0 deletions client/orm/filter/opentelemetry/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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 opentelemetry

import (
"context"
"strings"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"

"github.com/beego/beego/v2/client/orm"
)

type (
CustomSpanFunc func(ctx context.Context, span otelTrace.Span, inv *orm.Invocation)
FilterChainOption func(fcv *FilterChainBuilder)
)

// FilterChainBuilder provides an opentelemtry Filter
type FilterChainBuilder struct {
// customSpanFunc users are able to custom their span
customSpanFunc CustomSpanFunc
}

func NewFilterChainBuilder(options ...FilterChainOption) *FilterChainBuilder {
fcb := &FilterChainBuilder{}
for _, o := range options {
o(fcb)
}
return fcb
}

// WithCustomSpanFunc add function to custom span
func WithCustomSpanFunc(customSpanFunc CustomSpanFunc) FilterChainOption {
return func(fcv *FilterChainBuilder) {
fcv.customSpanFunc = customSpanFunc
}
}

// FilterChain traces invocation with opentelemetry
// Unless invocation.Method is Begin*, Commit or Rollback
func (builder *FilterChainBuilder) FilterChain(next orm.Filter) orm.Filter {
return func(ctx context.Context, inv *orm.Invocation) []interface{} {
if strings.HasPrefix(inv.Method, "Begin") || inv.Method == "Commit" || inv.Method == "Rollback" {
return next(ctx, inv)
}
spanCtx, span := otel.Tracer("beego_orm").Start(ctx, invOperationName(ctx, inv))
defer span.End()

res := next(spanCtx, inv)
builder.buildSpan(spanCtx, span, inv)
return res
}
}

// buildSpan add default span attributes and custom attributes with customSpanFunc
func (builder *FilterChainBuilder) buildSpan(ctx context.Context, span otelTrace.Span, inv *orm.Invocation) {
span.SetAttributes(attribute.String("orm.method", inv.Method))
span.SetAttributes(attribute.String("orm.table", inv.GetTableName()))
span.SetAttributes(attribute.Bool("orm.insideTx", inv.InsideTx))
v, _ := ctx.Value(orm.TxNameKey).(string)
span.SetAttributes(attribute.String("orm.txName", v))
span.SetAttributes(attribute.String("span.kind", "client"))
span.SetAttributes(attribute.String("component", "beego"))

if builder.customSpanFunc != nil {
builder.customSpanFunc(ctx,span, inv)
}
}

func invOperationName(ctx context.Context, inv *orm.Invocation) string {
if n, ok := ctx.Value(orm.TxNameKey).(string); ok {
return inv.Method + "#tx(" + n + ")"
}
return inv.Method + "#" + inv.GetTableName()
}
61 changes: 61 additions & 0 deletions client/orm/filter/opentelemetry/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 opentelemetry

import (
"bytes"
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/trace"
otelTrace "go.opentelemetry.io/otel/trace"

"github.com/beego/beego/v2/client/orm"
)

func TestFilterChainBuilderFilterChain(t *testing.T) {
// Init Trace
buf := bytes.NewBuffer([]byte{})
exp, err := stdouttrace.New(stdouttrace.WithWriter(buf))
if err != nil {
t.Error(err)
}
tp := trace.NewTracerProvider(trace.WithBatcher(exp))
otel.SetTracerProvider(tp)

// Build FilterChain
csf := func(ctx context.Context, span otelTrace.Span, inv *orm.Invocation) {
span.SetAttributes(attribute.String("hello", "work"))
}
builder := NewFilterChainBuilder(WithCustomSpanFunc(csf))

inv := &orm.Invocation{Method: "Hello"}
next := func(ctx context.Context, inv *orm.Invocation) []interface{} { return nil }

builder.FilterChain(next)(context.Background(), inv)

// Close tp
err = tp.Shutdown(context.Background())
if err != nil {
t.Error(err)
}

// Assert opentelemetry span name
assert.Equal(t, "Hello#", string(buf.Bytes()[9:15]))
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ require (
github.com/stretchr/testify v1.7.1
go.etcd.io/etcd/client/v3 v3.5.4
go.opentelemetry.io/otel v1.7.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0
go.opentelemetry.io/otel/sdk v1.7.0
go.opentelemetry.io/otel/trace v1.7.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
google.golang.org/grpc v1.38.0
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,10 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 h1:8hPcgCg0rUJiKE6VWahRvjgLUrNl7rW2hffUEPKXVEM=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0/go.mod h1:K4GDXPY6TjUiwbOh+DkKaEdCF8y+lvMoM6SeAPyfCCM=
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down Expand Up @@ -627,6 +631,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
Expand Down

0 comments on commit 5a873ea

Please sign in to comment.