Skip to content
This repository has been archived by the owner on Jun 14, 2023. It is now read-only.

feat: add gorm plugin #37

Merged
merged 3 commits into from
Nov 20, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/plugin_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
sql/test/sql_plugin_test.yaml: sql/**
kafkareporter/test/go_kafka_reporter_plugin_test.yaml: kafkareporter/**
dubbo-go/test/dubbo_go_plugin_test.yaml: dubbo-go/**
gorm/test/gorm_go_plugin_test.yaml: gorm/**
wu-sheng marked this conversation as resolved.
Show resolved Hide resolved

PluginsTest:
name: Plugin
Expand Down
40 changes: 40 additions & 0 deletions gorm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Go2Sky with Gorm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this doc should be linked from https://github.com/SkyAPM/go2sky-plugins#trace-plugins


## Installation

```bash
go get -u github.com/SkyAPM/go2sky-plugins/gorm
```

## Usage

```go
import (
gormPlugin "github.com/SkyAPM/go2sky-plugins/gorm"

"github.com/SkyAPM/go2sky"
"github.com/SkyAPM/go2sky/reporter"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

// init reporter
re, err := reporter.NewLogReporter()
defer re.Close()

// init tracer
tracer, err := go2sky.NewTracer("service-name", go2sky.WithReporter(re))
if err != nil {
log.Fatalf("init tracer error: %v", err)
}

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

if err != nil {
log.Fatalf("open db error: %v \n", err)
}
db.Use(gormPlugin.New(tracer, "127.0.0.1:3306", gormPlugin.MYSQL))

// use with context
dbWithCtx := db.WithContext(ctx)
```
17 changes: 17 additions & 0 deletions gorm/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Copyright 2021 SkyAPM org
//
// 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 gorm
20 changes: 20 additions & 0 deletions gorm/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module github.com/SkyAPM/go2sky-plugins/gorm

go 1.16

require (
github.com/SkyAPM/go2sky v1.2.0
gorm.io/gorm v1.22.3
skywalking.apache.org/repo/goapi v0.0.0-20211014145040-b215a7f7b270
)

require (
github.com/google/uuid v1.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20211112145013-271947fe86fd // indirect
google.golang.org/grpc v1.42.0 // indirect
gorm.io/driver/mysql v1.2.0
)
230 changes: 230 additions & 0 deletions gorm/go.sum

Large diffs are not rendered by default.

144 changes: 144 additions & 0 deletions gorm/gorm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//
// Copyright 2021 SkyAPM org
//
// 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 gorm

import (
"fmt"
"time"

"github.com/SkyAPM/go2sky"
"gorm.io/gorm"
agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
)

var (
_ gorm.Plugin = &SkyWalking{}
)

const (
componentIDUnknown = 0
componentIDMySQL = 5012
)

type DBType string

const (
UNKNOWN DBType = "unknown"
MYSQL DBType = "mysql"
)

const spanKey = "spanKey"

type SkyWalking struct {
tracer *go2sky.Tracer
peer string
sqlType DBType
}

func New(tracer *go2sky.Tracer, peer string, sqlType DBType) *SkyWalking {
return &SkyWalking{tracer, peer, sqlType}
}

func (s *SkyWalking) Name() string {
return "gorm:skyWalking"
}

func (s *SkyWalking) Initialize(db *gorm.DB) (err error) {
// before database operation
db.Callback().Create().Before("gorm:create").Register("sky_create_span", s.BeforeCallback("create"))
db.Callback().Query().Before("gorm:query").Register("sky_create_span", s.BeforeCallback("query"))
db.Callback().Update().Before("gorm:update").Register("sky_create_span", s.BeforeCallback("update"))
db.Callback().Delete().Before("gorm:delete").Register("sky_create_span", s.BeforeCallback("delete"))
db.Callback().Row().Before("gorm:row").Register("sky_create_span", s.BeforeCallback("row"))
db.Callback().Raw().Before("gorm:raw").Register("sky_create_span", s.BeforeCallback("raw"))

// after database operation
db.Callback().Create().After("gorm:create").Register("sky_end_span", s.AfterCallback())
db.Callback().Query().After("gorm:query").Register("sky_end_span", s.AfterCallback())
db.Callback().Update().After("gorm:update").Register("sky_end_span", s.AfterCallback())
db.Callback().Delete().After("gorm:delete").Register("sky_end_span", s.AfterCallback())
db.Callback().Row().After("gorm:row").Register("sky_end_span", s.AfterCallback())
db.Callback().Raw().After("gorm:raw").Register("sky_end_span", s.AfterCallback())

return
}

func (s *SkyWalking) BeforeCallback(operation string) func(db *gorm.DB) {
tracer := s.tracer
peer := s.peer

if tracer == nil {
return func(db *gorm.DB) {}
}

return func(db *gorm.DB) {
tableName := db.Statement.Table
operation := fmt.Sprintf("%s/%s", tableName, operation)

span, err := tracer.CreateExitSpan(db.Statement.Context, operation, peer, func(key, value string) error {
return nil
})
if err != nil {
db.Logger.Error(db.Statement.Context, "gorm:skyWalking failed to create exit span, got error: %v", err)
}

// set span from db instance's context to pass span
db.Set(spanKey, span)
}
}

func (s *SkyWalking) AfterCallback() func(db *gorm.DB) {
tracer := s.tracer
if tracer == nil {
return func(db *gorm.DB) {}
}

return func(db *gorm.DB) {
// get span from db instance's context
spanInterface, _ := db.Get(spanKey)
span := spanInterface.(go2sky.Span)

defer span.End()

sql := db.Statement.SQL.String()
vars := db.Statement.Vars
err := db.Statement.Error

span.SetComponent(s.getComponent())
span.SetSpanLayer(agentv3.SpanLayer_Database)
span.Tag(go2sky.TagDBType, string(s.sqlType))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the tag key should look like this, and please help update go2sky :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to change this tag TagDBBindVariables in go2sky into tagDbSqlParameters = "db.sql.parameters"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

span.Tag(go2sky.TagDBStatement, sql)

if len(vars) != 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add reportParam and reportQuery options like this, let the user choose. WDYT?

varsStr := fmt.Sprintf("%v", vars)
span.Tag(go2sky.TagDBBindVariables, varsStr)
}

if err != nil {
span.Error(time.Now(), err.Error())
}
}
}

func (s *SkyWalking) getComponent() int32 {
switch s.sqlType {
case MYSQL:
return componentIDMySQL
default:
return componentIDUnknown
}
}
26 changes: 26 additions & 0 deletions gorm/test/Dockerfile.client
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# Copyright 2021 SkyAPM org
#
# 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.
#

FROM golang:1.16

ADD . /go2sky
WORKDIR /go2sky

EXPOSE 8080

ENTRYPOINT ["go"]

CMD ["run", "test/client.go"]
Loading