Skip to content

Commit

Permalink
plugin grpc Unary (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alipebt authored Aug 8, 2023
1 parent e5fbe62 commit fd87ffe
Show file tree
Hide file tree
Showing 27 changed files with 1,264 additions and 8 deletions.
1 change: 1 addition & 0 deletions .github/workflows/plugin-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ jobs:
- plugin_exclusion
- runtime_metrics
- mux
- grpc
steps:
- uses: actions/checkout@v2
with:
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Release Notes.

#### Plugins
* Support [mux](https://github.com/gorilla/mux) HTTP server framework.
* Support [grpc](https://github.com/grpc/grpc-go) Unary server and client framework

#### Documentation

Expand Down
1 change: 1 addition & 0 deletions docs/en/agent/support-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ metrics based on the tracing data.
* `dubbo`: [Dubbo](https://github.com/apache/dubbo-go) tested v3.0.1 to v3.0.5.
* `kratosv2`: [Kratos](https://github.com/go-kratos/kratos) tested v2.3.1 to v2.6.2.
* `microv4`: [Go-Micro](https://github.com/go-micro/go-micro) tested v4.6.0 to v4.10.2.
* `grpc` : [gRPC](https://github.com/grpc/grpc-go) tested v1.55.0 to v1.57.0.
* Database Client
* `gorm`: [GORM](https://github.com/go-gorm/gorm) tested v1.22.0 to v1.25.1.
* [MySQL Driver](https://github.com/go-gorm/mysql)
Expand Down
2 changes: 2 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use (
./plugins/sql
./plugins/runtimemetrics
./plugins/mux
./plugins/grpc

./test/benchmark-codebase/consumer
./test/benchmark-codebase/provider
Expand All @@ -37,6 +38,7 @@ use (
./test/plugins/scenarios/logrus
./test/plugins/scenarios/zap
./test/plugins/scenarios/mux
./test/plugins/scenarios/grpc

./test/plugins/scenarios/plugin_exclusion
./test/plugins/scenarios/runtime_metrics
Expand Down
89 changes: 89 additions & 0 deletions go.work.sum

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions plugins/grpc/client_recvmsg_interceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 grpc

import (
"strings"

"github.com/apache/skywalking-go/plugins/core/operator"
"github.com/apache/skywalking-go/plugins/core/tracing"
)

type ClientRecvMsgInterceptor struct {
}

func (h *ClientRecvMsgInterceptor) BeforeInvoke(invocation operator.Invocation) error {
cs := invocation.CallerInstance().(*nativeclientStream)
method := cs.callHdr.Method
if strings.HasPrefix(method, "/skywalking") {
return nil
}
s, err := tracing.CreateLocalSpan(formatOperationName(method, "/Client/Response/RecvMsg"),
tracing.WithLayer(tracing.SpanLayerRPCFramework),
tracing.WithTag(tracing.TagURL, method),
tracing.WithComponent(23),
)
if err != nil {
return err
}
invocation.SetContext(s)
return nil
}

func (h *ClientRecvMsgInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
if invocation.GetContext() != nil {
span := invocation.GetContext().(tracing.Span)
if err, ok := result[0].(error); ok && err != nil {
span.Error(err.Error())
}
span.End()
}
return nil
}
57 changes: 57 additions & 0 deletions plugins/grpc/client_sendmsg_interceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 grpc

import (
"strings"

"github.com/apache/skywalking-go/plugins/core/operator"
"github.com/apache/skywalking-go/plugins/core/tracing"
)

type ClientSendMsgInterceptor struct {
}

func (h *ClientSendMsgInterceptor) BeforeInvoke(invocation operator.Invocation) error {
cs := invocation.CallerInstance().(*nativeclientStream)
method := cs.callHdr.Method
if strings.HasPrefix(method, "/skywalking") {
return nil
}
s, err := tracing.CreateLocalSpan(formatOperationName(method, "/Client/Request/SendMsg"),
tracing.WithLayer(tracing.SpanLayerRPCFramework),
tracing.WithTag(tracing.TagURL, method),
tracing.WithComponent(23),
)
if err != nil {
return err
}
invocation.SetContext(s)
return nil
}

func (h *ClientSendMsgInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
if invocation.GetContext() != nil {
span := invocation.GetContext().(tracing.Span)
if err, ok := result[0].(error); ok && err != nil {
span.Error(err.Error())
}
span.End()
}
return nil
}
68 changes: 68 additions & 0 deletions plugins/grpc/client_unary_interceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 grpc

import (
"context"
"strings"

"google.golang.org/grpc/metadata"

"github.com/apache/skywalking-go/plugins/core/operator"
"github.com/apache/skywalking-go/plugins/core/tracing"
)

type ClientUnaryInterceptor struct {
}

func (h *ClientUnaryInterceptor) BeforeInvoke(invocation operator.Invocation) error {
ctx := invocation.Args()[0].(context.Context)
method := invocation.Args()[1].(string)
clientconn := invocation.CallerInstance().(*nativeClientConn)
remoteAddr := clientconn.Target()
if strings.HasPrefix(method, "/skywalking") {
return nil
}
s, err := tracing.CreateExitSpan(formatOperationName(method, ""), remoteAddr, func(headerKey, headerValue string) error {
ctx = metadata.AppendToOutgoingContext(ctx, headerKey, headerValue)
invocation.ChangeArg(0, ctx)

return nil
},
tracing.WithLayer(tracing.SpanLayerRPCFramework),
tracing.WithTag(tracing.TagURL, method),
tracing.WithTag("RPCType", "Unary"),
tracing.WithComponent(23),
)
if err != nil {
return err
}
invocation.SetContext(s)
return nil
}

func (h *ClientUnaryInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
if invocation.GetContext() != nil {
span := invocation.GetContext().(tracing.Span)
if err, ok := result[0].(error); ok && err != nil {
span.Error(err.Error())
}
span.End()
}
return nil
}
27 changes: 27 additions & 0 deletions plugins/grpc/formatOperationName.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 grpc

import "strings"

func formatOperationName(service, method string) string {
service = service[1:]
service = strings.ReplaceAll(service, "/", ".")
operationName := service + method
return operationName
}
7 changes: 7 additions & 0 deletions plugins/grpc/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/apache/skywalking-go/plugins/grpc

go 1.20

require google.golang.org/grpc v1.56.2

require github.com/dave/dst v0.27.2 // indirect
13 changes: 13 additions & 0 deletions plugins/grpc/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/apache/skywalking-go/plugins/core v0.0.0-20230804112709-e5fbe6214220 h1:TS64/Lujq4HKtNrbchN6F8aW87oGl4Oiy8IscyPE1vc=
github.com/apache/skywalking-go/plugins/core v0.0.0-20230804112709-e5fbe6214220/go.mod h1:P0tAFNAYJUNsiTlFQgxmCcRcu+7Y0MP8GBZfSI449CM=
github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc=
github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
119 changes: 119 additions & 0 deletions plugins/grpc/instrument.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 grpc

import (
"embed"

"github.com/apache/skywalking-go/plugins/core/instrument"
)

//go:embed *
var fs embed.FS

//skywalking:nocopy
type Instrument struct {
}

func NewInstrument() *Instrument {
return &Instrument{}
}

func (i *Instrument) Name() string {
return "grpc"
}

func (i *Instrument) BasePackage() string {
return "google.golang.org/grpc"
}

func (i *Instrument) VersionChecker(version string) bool {
return true
}

func (i *Instrument) Points() []*instrument.Point {
return []*instrument.Point{
{
PackagePath: "",
At: instrument.NewMethodEnhance("*ClientConn", "Invoke",
instrument.WithArgType(0, "context.Context"),
instrument.WithArgType(1, "string"),
instrument.WithResultCount(1),
instrument.WithResultType(0, "error")),
Interceptor: "ClientUnaryInterceptor",
},
{
PackagePath: "",
At: instrument.NewMethodEnhance("*Server", "handleStream",
instrument.WithArgsCount(3),
instrument.WithArgType(0, "transport.ServerTransport"),
instrument.WithArgType(1, "*transport.Stream"),
instrument.WithArgType(2, "*traceInfo")),
Interceptor: "ServerHandleStreamInterceptor ",
},
{
PackagePath: "",
At: instrument.NewMethodEnhance("*Server", "processUnaryRPC",
instrument.WithArgsCount(5),
instrument.WithArgType(0, "transport.ServerTransport"),
instrument.WithArgType(1, "*transport.Stream"),
instrument.WithArgType(2, "*serviceInfo"),
instrument.WithArgType(3, "*MethodDesc"),
instrument.WithArgType(4, "*traceInfo"),
instrument.WithResultCount(1),
instrument.WithResultType(0, "error")),
Interceptor: "ServerUnaryInterceptor",
},
{
PackagePath: "",
At: instrument.NewMethodEnhance("*clientStream", "SendMsg",
instrument.WithArgsCount(1),
instrument.WithArgType(0, "interface{}"),
instrument.WithResultCount(1),
instrument.WithResultType(0, "error")),
Interceptor: "ClientSendMsgInterceptor",
},
{
PackagePath: "",
At: instrument.NewMethodEnhance("*clientStream", "RecvMsg",
instrument.WithArgsCount(1),
instrument.WithArgType(0, "interface{}"),
instrument.WithResultCount(1),
instrument.WithResultType(0, "error")),
Interceptor: "ClientRecvMsgInterceptor",
},
{
PackagePath: "",
At: instrument.NewMethodEnhance("*Server", "sendResponse",
instrument.WithArgsCount(6),
instrument.WithArgType(0, "transport.ServerTransport"),
instrument.WithArgType(1, "*transport.Stream"),
instrument.WithArgType(2, "interface{}"),
instrument.WithArgType(3, "Compressor"),
instrument.WithArgType(4, "*transport.Options"),
instrument.WithArgType(5, "encoding.Compressor"),
instrument.WithResultCount(1),
instrument.WithResultType(0, "error")),
Interceptor: "ServerSendResponseInterceptor",
},
}
}

func (i *Instrument) FS() *embed.FS {
return &fs
}
Loading

0 comments on commit fd87ffe

Please sign in to comment.