add metrics, tracing, validation interceptors, add options: WithInterceptors, With{Client,Server}Interceptors

This commit is contained in:
Adphi 2021-11-21 14:58:49 +01:00
parent 9ae20eab1e
commit dc78a2c688
15 changed files with 401 additions and 41 deletions

View File

@ -17,6 +17,7 @@ Features:
- [ ] mdns
- [ ] kubernetes
- [ ] default interceptors implementation:
- [ ] default
- [ ] validation
- [ ] health
- [ ] context logger
@ -24,8 +25,8 @@ Features:
- [ ] rate-limiting
- [ ] auth claim in context
- [ ] recovery
- [ ] tracing (open-tracing)
- [ ] metrics (prometheus)
- [x] tracing (open-tracing)
- [x] metrics (prometheus)
- [ ] retries
- [ ] context DB / transaction
- ...

View File

@ -14,6 +14,8 @@ import (
"github.com/sirupsen/logrus"
"go.linka.cloud/grpc/client"
metrics2 "go.linka.cloud/grpc/interceptors/metrics"
validation2 "go.linka.cloud/grpc/interceptors/validation"
"go.linka.cloud/grpc/logger"
"go.linka.cloud/grpc/registry/mdns"
"go.linka.cloud/grpc/service"
@ -32,6 +34,7 @@ func (g *GreeterHandler) SayHello(ctx context.Context, req *HelloRequest) (*Hell
}
func (g *GreeterHandler) SayHelloStream(req *HelloStreamRequest, s Greeter_SayHelloStreamServer) error {
for i := int64(0); i < req.Count; i++ {
if err := s.Send(&HelloReply{Message: fmt.Sprintf("Hello %s (%d)!", req.Name, i+1)}); err != nil {
return err
@ -64,6 +67,8 @@ func main() {
defer cancel()
var svc service.Service
var err error
metrics := metrics2.NewInterceptors()
validation := validation2.NewInterceptors(true)
address := "0.0.0.0:9991"
svc, err = service.New(
service.WithContext(ctx),
@ -88,6 +93,7 @@ func main() {
service.WithGRPCWeb(true),
service.WithGRPCWebPrefix("/grpc"),
service.WithMiddlewares(httpLogger),
service.WithInterceptors(validation, metrics),
)
if err != nil {
panic(err)
@ -115,6 +121,10 @@ func main() {
logrus.Fatal(err)
}
logrus.Infof("received message: %s", res.Message)
res, err = g.SayHello(context.Background(), &HelloRequest{})
if err == nil {
logrus.Fatal("expected validation error")
}
stream, err := g.SayHelloStream(context.Background(), &HelloStreamRequest{Name: "test", Count: 10})
if err != nil {
logrus.Fatal(err)

View File

@ -10,6 +10,7 @@ import (
reflect "reflect"
sync "sync"
_ "github.com/envoyproxy/protoc-gen-validate/validate"
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@ -178,30 +179,33 @@ var file_example_example_proto_rawDesc = []byte{
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f,
0x72, 0x6c, 0x64, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x3e, 0x0a,
0x12, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01,
0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72,
0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c,
0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22,
0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72,
0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01, 0x2a, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65,
0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65,
0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x6f, 0x2e, 0x6c, 0x69,
0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x0c, 0x48, 0x65,
0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10,
0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c,
0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x22, 0x49, 0x0a, 0x12, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28,
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01, 0x0a,
0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,
0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c,
0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f,
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01, 0x2a, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x6c,
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c,
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,
0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e,
0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (

View File

@ -46,7 +46,16 @@ func (m *HelloRequest) Validate(all bool) error {
var errors []error
// no validation rules for Name
if l := utf8.RuneCountInString(m.GetName()); l < 2 || l > 40 {
err := HelloRequestValidationError{
field: "Name",
reason: "value length must be between 2 and 40 runes, inclusive",
}
if !all {
return err
}
errors = append(errors, err)
}
if len(errors) > 0 {
return HelloRequestMultiError(errors)
@ -229,7 +238,16 @@ func (m *HelloStreamRequest) Validate(all bool) error {
var errors []error
// no validation rules for Name
if l := utf8.RuneCountInString(m.GetName()); l < 2 || l > 40 {
err := HelloStreamRequestValidationError{
field: "Name",
reason: "value length must be between 2 and 40 runes, inclusive",
}
if !all {
return err
}
errors = append(errors, err)
}
// no validation rules for Count

View File

@ -5,6 +5,7 @@ package helloworld;
option go_package = "go.linka.cloud/grpc/example;main";
import "google/api/annotations.proto";
import "validate/validate.proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
@ -17,7 +18,7 @@ service Greeter {
}
message HelloRequest {
string name = 1;
string name = 1 [(validate.rules).string = {min_len: 2, max_len: 40}];
}
message HelloReply {
@ -25,6 +26,6 @@ message HelloReply {
}
message HelloStreamRequest {
string name = 1;
string name = 1 [(validate.rules).string = {min_len: 2, max_len: 40}];
int64 count = 2;
}

3
go.mod
View File

@ -10,7 +10,9 @@ require (
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.1.2
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/improbable-eng/grpc-web v0.14.1
github.com/jinzhu/gorm v1.9.12
@ -19,6 +21,7 @@ require (
github.com/lyft/protoc-gen-star v0.6.0 // indirect
github.com/miekg/dns v1.1.35
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/opentracing/opentracing-go v1.1.0
github.com/planetscale/vtprotobuf v0.2.0
github.com/rs/cors v1.7.0
github.com/sirupsen/logrus v1.8.1

11
go.sum
View File

@ -91,6 +91,7 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
@ -102,7 +103,9 @@ github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oD
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -299,6 +302,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@ -306,6 +310,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@ -429,6 +435,7 @@ github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lL
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -475,6 +482,7 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
@ -504,12 +512,14 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@ -528,6 +538,7 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=

View File

@ -0,0 +1,20 @@
package interceptors
import (
"google.golang.org/grpc"
)
type ServerInterceptors interface {
UnaryServerInterceptor() grpc.UnaryServerInterceptor
StreamServerInterceptor() grpc.StreamServerInterceptor
}
type ClientInterceptors interface {
UnaryClientInterceptor() grpc.UnaryClientInterceptor
StreamClientInterceptor() grpc.StreamClientInterceptor
}
type Interceptors interface {
ServerInterceptors
ClientInterceptors
}

View File

@ -0,0 +1,57 @@
package metrics
import (
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"google.golang.org/grpc"
"go.linka.cloud/grpc/interceptors"
)
type metrics struct {
s *grpc_prometheus.ServerMetrics
c *grpc_prometheus.ClientMetrics
}
func NewInterceptors(opts ...grpc_prometheus.CounterOption) interceptors.Interceptors {
s := grpc_prometheus.NewServerMetrics(opts...)
c := grpc_prometheus.NewClientMetrics(opts...)
return &metrics{s: s, c: c}
}
func NewServerInterceptors(opts ...grpc_prometheus.CounterOption) interceptors.ServerInterceptors {
s := grpc_prometheus.NewServerMetrics(opts...)
return &metrics{s: s}
}
func NewClientInterceptors(opts ...grpc_prometheus.CounterOption) interceptors.ClientInterceptors {
c := grpc_prometheus.NewClientMetrics(opts...)
return &metrics{c: c}
}
func DefaultInterceptors() interceptors.Interceptors {
return &metrics{s: grpc_prometheus.DefaultServerMetrics, c: grpc_prometheus.DefaultClientMetrics}
}
func DefaultServerInterceptors() interceptors.ServerInterceptors {
return &metrics{s: grpc_prometheus.DefaultServerMetrics}
}
func DefaultClientInterceptors() interceptors.ClientInterceptors {
return &metrics{c: grpc_prometheus.DefaultClientMetrics}
}
func (m *metrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return m.s.UnaryServerInterceptor()
}
func (m *metrics) StreamServerInterceptor() grpc.StreamServerInterceptor {
return m.s.StreamServerInterceptor()
}
func (m *metrics) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
return m.c.UnaryClientInterceptor()
}
func (m *metrics) StreamClientInterceptor() grpc.StreamClientInterceptor {
return m.c.StreamClientInterceptor()
}

View File

@ -0,0 +1,41 @@
package tracing
import (
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/opentracing/opentracing-go"
"google.golang.org/grpc"
"go.linka.cloud/grpc/interceptors"
)
type tracing struct {
opts []otgrpc.Option
}
func NewInterceptors(opts ...otgrpc.Option) interceptors.Interceptors {
return tracing{opts: opts}
}
func NewClientInterceptors(opts ...otgrpc.Option) interceptors.ClientInterceptors {
return tracing{opts: opts}
}
func (t tracing) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
return otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer(), t.opts...)
}
func (t tracing) StreamClientInterceptor() grpc.StreamClientInterceptor {
return otgrpc.OpenTracingStreamClientInterceptor(opentracing.GlobalTracer(), t.opts...)
}
func NewServerInterceptors(opts ...otgrpc.Option) interceptors.ServerInterceptors {
return tracing{opts: opts}
}
func (t tracing) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return otgrpc.OpenTracingServerInterceptor(opentracing.GlobalTracer(), t.opts...)
}
func (t tracing) StreamServerInterceptor() grpc.StreamServerInterceptor {
return otgrpc.OpenTracingStreamServerInterceptor(opentracing.GlobalTracer(), t.opts...)
}

View File

@ -0,0 +1 @@
package validation

View File

@ -0,0 +1,161 @@
package validation
import (
"context"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
"go.linka.cloud/grpc/errors"
"go.linka.cloud/grpc/interceptors"
)
// The validate interface starting with protoc-gen-validate v0.6.0.
// See https://github.com/envoyproxy/protoc-gen-validate/pull/455.
type validator interface {
Validate(all bool) error
}
// The validate interface prior to protoc-gen-validate v0.6.0.
type validatorLegacy interface {
Validate() error
}
type validatorMultiError interface {
AllErrors() []error
}
type validatorError interface {
Field() string
Reason() string
Key() bool
Cause() error
ErrorName() string
}
func validatorErrorToGrpc(e validatorError) *errdetails.BadRequest_FieldViolation {
return &errdetails.BadRequest_FieldViolation{
Field: e.Field(),
Description: e.Reason(),
}
}
func errToStatus(err error) error {
switch v := err.(type) {
case validatorError:
return errors.InvalidArgumentD(err, validatorErrorToGrpc(v))
case validatorMultiError:
var details []proto.Message
for _, v := range v.AllErrors() {
if d, ok := v.(validatorError); ok {
details = append(details, validatorErrorToGrpc(d))
}
}
return errors.InvalidArgumentd(err, details...)
default:
return errors.InvalidArgument(err)
}
}
func (i interceptor) validate(req interface{}) error {
switch v := req.(type) {
case validatorLegacy:
if err := v.Validate(); err != nil {
return errToStatus(err)
}
case validator:
if err := v.Validate(i.all); err != nil {
return errToStatus(err)
}
}
return nil
}
type interceptor struct {
all bool
}
func NewInterceptors(validateAll bool) interceptors.Interceptors {
return &interceptor{all: validateAll}
}
// UnaryServerInterceptor returns a new unary server interceptor that validates incoming messages.
//
// Invalid messages will be rejected with `InvalidArgument` before reaching any userspace handlers.
func (i interceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if err := i.validate(req); err != nil {
return nil, err
}
return handler(ctx, req)
}
}
// UnaryClientInterceptor returns a new unary client interceptor that validates outgoing messages.
//
// Invalid messages will be rejected with `InvalidArgument` before sending the request to server.
func (i interceptor) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
if err := i.validate(req); err != nil {
return err
}
return invoker(ctx, method, req, reply, cc, opts...)
}
}
// StreamServerInterceptor returns a new streaming server interceptor that validates incoming messages.
//
// The stage at which invalid messages will be rejected with `InvalidArgument` varies based on the
// type of the RPC. For `ServerStream` (1:m) requests, it will happen before reaching any userspace
// handlers. For `ClientStream` (n:1) or `BidiStream` (n:m) RPCs, the messages will be rejected on
// calls to `stream.Recv()`.
func (i interceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
wrapper := &recvWrapper{ServerStream: stream, i: i}
return handler(srv, wrapper)
}
}
func (i interceptor) StreamClientInterceptor() grpc.StreamClientInterceptor {
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
desc.Handler = (&sendWrapper{handler: desc.Handler, i: i}).Handler()
return streamer(ctx, desc, cc, method)
}
}
type recvWrapper struct {
i interceptor
grpc.ServerStream
}
func (s *recvWrapper) RecvMsg(m interface{}) error {
if err := s.ServerStream.RecvMsg(m); err != nil {
return err
}
if err := s.i.validate(m); err != nil {
return err
}
return nil
}
type sendWrapper struct {
i interceptor
grpc.ServerStream
handler grpc.StreamHandler
}
func (s *sendWrapper) Handler() grpc.StreamHandler {
return func(srv interface{}, stream grpc.ServerStream) error {
return s.handler(srv, s)
}
}
func (s *sendWrapper) SendMsg(m interface{}) error {
if err := s.i.validate(m); err != nil {
return err
}
return s.ServerStream.SendMsg(m)
}

View File

@ -17,6 +17,7 @@ import (
"google.golang.org/grpc"
"go.linka.cloud/grpc/certs"
"go.linka.cloud/grpc/interceptors"
"go.linka.cloud/grpc/registry"
"go.linka.cloud/grpc/transport"
"go.linka.cloud/grpc/utils/addr"
@ -228,16 +229,45 @@ func WithAfterStop(fn ...func() error) Option {
}
}
func WithInterceptors(i ...interceptors.Interceptors) Option {
return func(o *options) {
for _, v := range i {
o.unaryServerInterceptors = append(o.unaryServerInterceptors, v.UnaryServerInterceptor())
o.streamServerInterceptors = append(o.streamServerInterceptors, v.StreamServerInterceptor())
o.unaryClientInterceptors = append(o.unaryClientInterceptors, v.UnaryClientInterceptor())
o.streamClientInterceptors = append(o.streamClientInterceptors, v.StreamClientInterceptor())
}
}
}
func WithServerInterceptors(i ...interceptors.ServerInterceptors) Option {
return func(o *options) {
for _, v := range i {
o.unaryServerInterceptors = append(o.unaryServerInterceptors, v.UnaryServerInterceptor())
o.streamServerInterceptors = append(o.streamServerInterceptors, v.StreamServerInterceptor())
}
}
}
func WithClientInterceptors(i ...interceptors.ClientInterceptors) Option {
return func(o *options) {
for _, v := range i {
o.unaryClientInterceptors = append(o.unaryClientInterceptors, v.UnaryClientInterceptor())
o.streamClientInterceptors = append(o.streamClientInterceptors, v.StreamClientInterceptor())
}
}
}
func WithUnaryClientInterceptor(i ...grpc.UnaryClientInterceptor) Option {
return func(o *options) {
o.clientInterceptors = append(o.clientInterceptors, i...)
o.unaryClientInterceptors = append(o.unaryClientInterceptors, i...)
}
}
// WithUnaryServerInterceptor adds unary Wrapper interceptors to the options passed into the server
func WithUnaryServerInterceptor(i ...grpc.UnaryServerInterceptor) Option {
return func(o *options) {
o.serverInterceptors = append(o.serverInterceptors, i...)
o.unaryServerInterceptors = append(o.unaryServerInterceptors, i...)
}
}
@ -340,10 +370,11 @@ type options struct {
serverOpts []grpc.ServerOption
serverInterceptors []grpc.UnaryServerInterceptor
unaryServerInterceptors []grpc.UnaryServerInterceptor
streamServerInterceptors []grpc.StreamServerInterceptor
clientInterceptors []grpc.UnaryClientInterceptor
unaryClientInterceptors []grpc.UnaryClientInterceptor
streamClientInterceptors []grpc.StreamClientInterceptor
mux ServeMux
@ -428,7 +459,7 @@ func (o *options) ServerOpts() []grpc.ServerOption {
}
func (o *options) ServerInterceptors() []grpc.UnaryServerInterceptor {
return o.serverInterceptors
return o.unaryServerInterceptors
}
func (o *options) StreamServerInterceptors() []grpc.StreamServerInterceptor {
@ -436,7 +467,7 @@ func (o *options) StreamServerInterceptors() []grpc.StreamServerInterceptor {
}
func (o *options) ClientInterceptors() []grpc.UnaryClientInterceptor {
return o.clientInterceptors
return o.unaryClientInterceptors
}
func (o *options) StreamClientInterceptors() []grpc.StreamClientInterceptor {

View File

@ -107,10 +107,10 @@ func newService(opts ...Option) (*service, error) {
}
return s.run()
}
ui := grpcmiddleware.ChainUnaryServer(s.opts.serverInterceptors...)
ui := grpcmiddleware.ChainUnaryServer(s.opts.unaryServerInterceptors...)
s.inproc = s.inproc.WithServerUnaryInterceptor(ui)
si := grpcmiddleware.ChainStreamServer(/*TODO(adphi): add to options*/)
si := grpcmiddleware.ChainStreamServer( /*TODO(adphi): add to options*/ )
s.inproc = s.inproc.WithServerStreamInterceptor(si)
gopts := []grpc.ServerOption{
@ -181,8 +181,8 @@ func (s *service) run() error {
if reflect.DeepEqual(s.opts.cors, cors.Options{}) {
s.opts.cors = cors.Options{
AllowedHeaders: []string{"*"},
AllowedMethods: []string{
AllowedHeaders: []string{"*"},
AllowedMethods: []string{
http.MethodGet,
http.MethodPost,
http.MethodPut,
@ -235,7 +235,7 @@ func (s *service) run() error {
logrus.Warnf("received %v", sig)
return s.Close()
case err := <-errs:
if err != nil && !ignoreMuxError(err){
if err != nil && !ignoreMuxError(err) {
logrus.Error(err)
return err
}

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*