Bye bye openapi (#1081)

* add DateTime sans mgo

* change all uses of strfmt.DateTime to common.DateTime, remove test strfmt usage

* remove api tests, system-test dep on api test

multiple reasons to remove the api tests:

* awkward dependency with fn_go meant generating bindings on a branched fn to
vendor those to test new stuff. this is at a minimum not at all intuitive,
worth it, nor a fun way to spend the finite amount of time we have to live.
* api tests only tested a subset of functionality that the server/ api tests
already test, and we risk having tests where one tests some thing and the
other doesn't. let's not. we have too many test suites as it is, and these
pretty much only test that we updated the fn_go bindings, which is actually a
hassle as noted above and the cli will pretty quickly figure out anyway.
* fn_go relies on openapi, which relies on mgo, which is deprecated and we'd
like to remove as a dependency. openapi is a _huge_ dep built in a NIH
fashion, that cannot simply remove the mgo dep as users may be using it.
we've now stolen their date time and otherwise killed usage of it in fn core,
for fn_go it still exists but that's less of a problem.

* update deps

removals:

* easyjson
* mgo
* go-openapi
* mapstructure
* fn_go
* purell
* go-validator

also, had to lock docker. we shouldn't use docker on master anyway, they
strongly advise against that. had no luck with latest version rev, so i locked
it to what we were using before. until next time.

the rest is just playing dep roulette, those end up removing a ton tho

* fix exec test to work

* account for john le cache
This commit is contained in:
Reed Allman
2018-06-21 11:09:16 -07:00
committed by GitHub
parent aa5d7169f4
commit 51ff7caeb2
2635 changed files with 440440 additions and 402994 deletions

View File

@@ -0,0 +1,182 @@
# grpc_ctxtags
`import "github.com/grpc-ecosystem/go-grpc-middleware/tags"`
* [Overview](#pkg-overview)
* [Imported Packages](#pkg-imports)
* [Index](#pkg-index)
* [Examples](#pkg-examples)
## <a name="pkg-overview">Overview</a>
`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request.
### Request Context Tags
Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used
for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack.
You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads.
For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can
use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server.
Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message
establishes the meta-data for the stream.
If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)`
will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used.
Tags fields are typed, and shallow and should follow the OpenTracing semantics convention:
<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a>
#### Example:
<details>
<summary>Click to expand code.</summary>
```go
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")),
}
_ = grpc.NewServer(
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
)
```
</details>
#### Example:
<details>
<summary>Click to expand code.</summary>
```go
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")),
}
_ = grpc.NewServer(
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
)
```
</details>
## <a name="pkg-imports">Imported Packages</a>
- [github.com/grpc-ecosystem/go-grpc-middleware](./..)
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc)
- [google.golang.org/grpc/peer](https://godoc.org/google.golang.org/grpc/peer)
## <a name="pkg-index">Index</a>
* [Variables](#pkg-variables)
* [func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}](#CodeGenRequestFieldExtractor)
* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor)
* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor)
* [type Option](#Option)
* [func WithFieldExtractor(f RequestFieldExtractorFunc) Option](#WithFieldExtractor)
* [func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option](#WithFieldExtractorForInitialReq)
* [type RequestFieldExtractorFunc](#RequestFieldExtractorFunc)
* [func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc](#TagBasedRequestFieldExtractor)
* [type Tags](#Tags)
* [func Extract(ctx context.Context) Tags](#Extract)
#### <a name="pkg-examples">Examples</a>
* [Package (InitialisationWithOptions)](#example__initialisationWithOptions)
* [Package (Initialization)](#example__initialization)
#### <a name="pkg-files">Package files</a>
[context.go](./context.go) [doc.go](./doc.go) [fieldextractor.go](./fieldextractor.go) [interceptors.go](./interceptors.go) [options.go](./options.go)
## <a name="pkg-variables">Variables</a>
``` go
var (
// NoopTags is a trivial, minimum overhead implementation of Tags for which all operations are no-ops.
NoopTags = &noopTags{}
)
```
## <a name="CodeGenRequestFieldExtractor">func</a> [CodeGenRequestFieldExtractor](./fieldextractor.go#L23)
``` go
func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}
```
CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests.
These are usually coming from a protoc-plugin that generates additional information based on custom field options.
## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L26)
``` go
func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor
```
StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags.
## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L14)
``` go
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor
```
UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags.
## <a name="Option">type</a> [Option](./options.go#L26)
``` go
type Option func(*options)
```
### <a name="WithFieldExtractor">func</a> [WithFieldExtractor](./options.go#L30)
``` go
func WithFieldExtractor(f RequestFieldExtractorFunc) Option
```
WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for
unary and server-streamed methods only.
### <a name="WithFieldExtractorForInitialReq">func</a> [WithFieldExtractorForInitialReq](./options.go#L39)
``` go
func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option
```
WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages,
for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be
extracted from the first message from the client.
## <a name="RequestFieldExtractorFunc">type</a> [RequestFieldExtractorFunc](./fieldextractor.go#L13)
``` go
type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{}
```
RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request.
It is called from tags middleware on arrival of unary request or a server-stream request.
Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil.
### <a name="TagBasedRequestFieldExtractor">func</a> [TagBasedRequestFieldExtractor](./fieldextractor.go#L43)
``` go
func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc
```
TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests.
These are usually coming from a protoc-plugin, such as Gogo protobuf.
message Metadata {
repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ];
}
The tagName is configurable using the tagName variable. Here it would be "log_field".
## <a name="Tags">type</a> [Tags](./context.go#L19-L27)
``` go
type Tags interface {
// Set sets the given key in the metadata tags.
Set(key string, value interface{}) Tags
// Has checks if the given key exists.
Has(key string) bool
// Values returns a map of key to values.
// Do not modify the underlying map, please use Set instead.
Values() map[string]interface{}
}
```
Tags is the interface used for storing request tags between Context calls.
The default implementation is *not* thread safe, and should be handled only in the context of the request.
### <a name="Extract">func</a> [Extract](./context.go#L63)
``` go
func Extract(ctx context.Context) Tags
```
Extracts returns a pre-existing Tags object in the Context.
If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context.
- - -
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)

View File

@@ -0,0 +1 @@
DOC.md

View File

@@ -0,0 +1,78 @@
package grpc_ctxtags
import (
"context"
)
type ctxMarker struct{}
var (
// ctxMarkerKey is the Context value marker used by *all* logging middleware.
// The logging middleware object must interf
ctxMarkerKey = &ctxMarker{}
// NoopTags is a trivial, minimum overhead implementation of Tags for which all operations are no-ops.
NoopTags = &noopTags{}
)
// Tags is the interface used for storing request tags between Context calls.
// The default implementation is *not* thread safe, and should be handled only in the context of the request.
type Tags interface {
// Set sets the given key in the metadata tags.
Set(key string, value interface{}) Tags
// Has checks if the given key exists.
Has(key string) bool
// Values returns a map of key to values.
// Do not modify the underlying map, please use Set instead.
Values() map[string]interface{}
}
type mapTags struct {
values map[string]interface{}
}
func (t *mapTags) Set(key string, value interface{}) Tags {
t.values[key] = value
return t
}
func (t *mapTags) Has(key string) bool {
_, ok := t.values[key]
return ok
}
func (t *mapTags) Values() map[string]interface{} {
return t.values
}
type noopTags struct{}
func (t *noopTags) Set(key string, value interface{}) Tags {
return t
}
func (t *noopTags) Has(key string) bool {
return false
}
func (t *noopTags) Values() map[string]interface{} {
return nil
}
// Extracts returns a pre-existing Tags object in the Context.
// If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context.
func Extract(ctx context.Context) Tags {
t, ok := ctx.Value(ctxMarkerKey).(Tags)
if !ok {
return NoopTags
}
return t
}
func setInContext(ctx context.Context, tags Tags) context.Context {
return context.WithValue(ctx, ctxMarkerKey, tags)
}
func newTags() Tags {
return &mapTags{values: make(map[string]interface{})}
}

View File

@@ -0,0 +1,22 @@
/*
`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request.
Request Context Tags
Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used
for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack.
You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads.
For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can
use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server.
Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message
establishes the meta-data for the stream.
If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)`
will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used.
Tags fields are typed, and shallow and should follow the OpenTracing semantics convention:
https://github.com/opentracing/specification/blob/master/semantic_conventions.md
*/
package grpc_ctxtags

View File

@@ -0,0 +1,28 @@
package grpc_ctxtags_test
import (
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
"google.golang.org/grpc"
)
// Simple example of server initialization code, with data automatically populated from `log_fields` Golang tags.
func Example_initialization() {
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")),
}
_ = grpc.NewServer(
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
)
}
// Example using WithFieldExtractorForInitialReq
func Example_initialisationWithOptions() {
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")),
}
_ = grpc.NewServer(
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
)
}

View File

@@ -0,0 +1,85 @@
// Copyright 2017 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_ctxtags
import (
"reflect"
)
// RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request.
// It is called from tags middleware on arrival of unary request or a server-stream request.
// Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil.
type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{}
type requestFieldsExtractor interface {
// ExtractRequestFields is a method declared on a Protobuf message that extracts fields from the interface.
// The values from the extracted fields should be set in the appendToMap, in order to avoid allocations.
ExtractRequestFields(appendToMap map[string]interface{})
}
// CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests.
// These are usually coming from a protoc-plugin that generates additional information based on custom field options.
func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} {
if ext, ok := req.(requestFieldsExtractor); ok {
retMap := make(map[string]interface{})
ext.ExtractRequestFields(retMap)
if len(retMap) == 0 {
return nil
}
return retMap
}
return nil
}
// TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests.
// These are usually coming from a protoc-plugin, such as Gogo protobuf.
//
// message Metadata {
// repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ];
// }
//
// The tagName is configurable using the tagName variable. Here it would be "log_field".
func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc {
return func(fullMethod string, req interface{}) map[string]interface{} {
retMap := make(map[string]interface{})
reflectMessageTags(req, retMap, tagName)
if len(retMap) == 0 {
return nil
}
return retMap
}
}
func reflectMessageTags(msg interface{}, existingMap map[string]interface{}, tagName string) {
v := reflect.ValueOf(msg)
// Only deal with pointers to structs.
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
return
}
// Deref the pointer get to the struct.
v = v.Elem()
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
kind := field.Kind()
// Only recurse down direct pointers, which should only be to nested structs.
if kind == reflect.Ptr {
reflectMessageTags(field.Interface(), existingMap, tagName)
}
// In case of arrays/splices (repeated fields) go down to the concrete type.
if kind == reflect.Array || kind == reflect.Slice {
if field.Len() == 0 {
continue
}
kind = field.Index(0).Kind()
}
// Only be interested in
if (kind >= reflect.Bool && kind <= reflect.Float64) || kind == reflect.String {
if tag := t.Field(i).Tag.Get(tagName); tag != "" {
existingMap[tag] = field.Interface()
}
}
}
return
}

View File

@@ -0,0 +1,52 @@
// Copyright 2017 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_ctxtags_test
import "testing"
import (
pb_gogotestproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/gogotestproto"
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCodeGenRequestLogFieldExtractor_ManualIsDeclared(t *testing.T) {
req := &pb_testproto.PingRequest{Value: "my_value"}
valMap := grpc_ctxtags.CodeGenRequestFieldExtractor("", req)
require.Len(t, valMap, 1, "PingRequest should have a ExtractLogFields method declared in test.manual_extractfields.pb")
require.EqualValues(t, valMap, map[string]interface{}{"value": "my_value"})
}
func TestTaggedRequestFiledExtractor_PingRequest(t *testing.T) {
req := &pb_gogotestproto.PingRequest{
Ping: &pb_gogotestproto.Ping{
Id: &pb_gogotestproto.PingId{
Id: 1337, // logfield is ping_id
},
Value: "something",
},
Meta: &pb_gogotestproto.Metadata{
Tags: []string{"tagone", "tagtwo"}, // logfield is meta_tags
},
}
valMap := grpc_ctxtags.TagBasedRequestFieldExtractor("log_field")("", req)
assert.EqualValues(t, 1337, valMap["ping_id"])
assert.EqualValues(t, []string{"tagone", "tagtwo"}, valMap["meta_tags"])
}
func TestTaggedRequestFiledExtractor_PongRequest(t *testing.T) {
req := &pb_gogotestproto.PongRequest{
Pong: &pb_gogotestproto.Pong{
Id: "some_id",
},
Meta: &pb_gogotestproto.Metadata{
Tags: []string{"tagone", "tagtwo"}, // logfield is meta_tags
},
}
valMap := grpc_ctxtags.TagBasedRequestFieldExtractor("log_field")("", req)
assert.EqualValues(t, "some_id", valMap["pong_id"])
assert.EqualValues(t, []string{"tagone", "tagtwo"}, valMap["meta_tags"])
}

View File

@@ -0,0 +1,83 @@
// Copyright 2017 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_ctxtags
import (
"github.com/grpc-ecosystem/go-grpc-middleware"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
)
// UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags.
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {
o := evaluateOptions(opts)
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
newCtx := newTagsForCtx(ctx)
if o.requestFieldsFunc != nil {
setRequestFieldTags(newCtx, o.requestFieldsFunc, info.FullMethod, req)
}
return handler(newCtx, req)
}
}
// StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags.
func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor {
o := evaluateOptions(opts)
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
newCtx := newTagsForCtx(stream.Context())
if o.requestFieldsFunc == nil {
// Short-circuit, don't do the expensive bit of allocating a wrappedStream.
wrappedStream := grpc_middleware.WrapServerStream(stream)
wrappedStream.WrappedContext = newCtx
return handler(srv, wrappedStream)
}
wrapped := &wrappedStream{stream, info, o, newCtx, true}
err := handler(srv, wrapped)
return err
}
}
// wrappedStream is a thin wrapper around grpc.ServerStream that allows modifying context and extracts log fields from the initial message.
type wrappedStream struct {
grpc.ServerStream
info *grpc.StreamServerInfo
opts *options
// WrappedContext is the wrapper's own Context. You can assign it.
WrappedContext context.Context
initial bool
}
// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context()
func (w *wrappedStream) Context() context.Context {
return w.WrappedContext
}
func (w *wrappedStream) RecvMsg(m interface{}) error {
err := w.ServerStream.RecvMsg(m)
// We only do log fields extraction on the single-request of a server-side stream.
if !w.info.IsClientStream || w.opts.requestFieldsFromInitial && w.initial {
w.initial = false
setRequestFieldTags(w.Context(), w.opts.requestFieldsFunc, w.info.FullMethod, m)
}
return err
}
func newTagsForCtx(ctx context.Context) context.Context {
t := newTags()
if peer, ok := peer.FromContext(ctx); ok {
t.Set("peer.address", peer.Addr.String())
}
return setInContext(ctx, t)
}
func setRequestFieldTags(ctx context.Context, f RequestFieldExtractorFunc, fullMethodName string, req interface{}) {
if valMap := f(fullMethodName, req); valMap != nil {
t := Extract(ctx)
for k, v := range valMap {
t.Set("grpc.request."+k, v)
}
}
}

View File

@@ -0,0 +1,204 @@
package grpc_ctxtags_test
import (
"encoding/json"
"io"
"testing"
"time"
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
goodPing = &pb_testproto.PingRequest{Value: "something", SleepTimeMs: 9999}
anotherPing = &pb_testproto.PingRequest{Value: "else", SleepTimeMs: 9999}
)
func tagsToJson(value map[string]interface{}) string {
str, _ := json.Marshal(value)
return string(str)
}
func tagsFromJson(t *testing.T, jstring string) map[string]interface{} {
var msgMapTemplate interface{}
err := json.Unmarshal([]byte(jstring), &msgMapTemplate)
if err != nil {
t.Fatalf("failed unmarshaling tags from response %v", err)
}
return msgMapTemplate.(map[string]interface{})
}
type tagPingBack struct {
pb_testproto.TestServiceServer
}
func (s *tagPingBack) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
return &pb_testproto.PingResponse{Value: tagsToJson(grpc_ctxtags.Extract(ctx).Values())}, nil
}
func (s *tagPingBack) PingError(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.Empty, error) {
return s.TestServiceServer.PingError(ctx, ping)
}
func (s *tagPingBack) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
out := &pb_testproto.PingResponse{Value: tagsToJson(grpc_ctxtags.Extract(stream.Context()).Values())}
return stream.Send(out)
}
func (s *tagPingBack) PingEmpty(ctx context.Context, empty *pb_testproto.Empty) (*pb_testproto.PingResponse, error) {
return s.TestServiceServer.PingEmpty(ctx, empty)
}
func (s *tagPingBack) PingStream(stream pb_testproto.TestService_PingStreamServer) error {
for {
_, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
out := &pb_testproto.PingResponse{Value: tagsToJson(grpc_ctxtags.Extract(stream.Context()).Values())}
err = stream.Send(out)
if err != nil {
return err
}
}
}
func TestTaggingSuite(t *testing.T) {
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor),
}
s := &TaggingSuite{
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
TestService: &tagPingBack{&grpc_testing.TestPingService{T: t}},
ServerOpts: []grpc.ServerOption{
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
},
},
}
suite.Run(t, s)
}
type TaggingSuite struct {
*grpc_testing.InterceptorTestSuite
}
func (s *TaggingSuite) SetupTest() {
}
func (s *TaggingSuite) TestPing_WithCustomTags() {
resp, err := s.Client.Ping(s.SimpleCtx(), goodPing)
require.NoError(s.T(), err, "must not be an error on a successful call")
tags := tagsFromJson(s.T(), resp.Value)
require.Len(s.T(), tags, 2, "the tags should contain only two values")
assert.Equal(s.T(), tags["grpc.request.value"], "something", "the tags should contain the correct request value")
assert.Contains(s.T(), tags, "peer.address", "the tags should contain a peer address")
}
func (s *TaggingSuite) TestPing_WithDeadline() {
ctx, _ := context.WithDeadline(context.TODO(), time.Now().AddDate(0, 0, 5))
resp, err := s.Client.Ping(ctx, goodPing)
require.NoError(s.T(), err, "must not be an error on a successful call")
tags := tagsFromJson(s.T(), resp.Value)
require.Len(s.T(), tags, 2, "the tags should contain only two values")
assert.Equal(s.T(), tags["grpc.request.value"], "something", "the tags should contain the correct request value")
assert.Contains(s.T(), tags, "peer.address", "the tags should contain a peer address")
}
func (s *TaggingSuite) TestPing_WithNoDeadline() {
ctx := context.TODO()
resp, err := s.Client.Ping(ctx, goodPing)
require.NoError(s.T(), err, "must not be an error on a successful call")
tags := tagsFromJson(s.T(), resp.Value)
require.Len(s.T(), tags, 2, "the tags should contain only two values")
assert.Equal(s.T(), tags["grpc.request.value"], "something", "the tags should contain the correct request value")
assert.Contains(s.T(), tags, "peer.address", "the tags should contain a peer address")
}
func (s *TaggingSuite) TestPingList_WithCustomTags() {
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
require.NoError(s.T(), err, "should not fail on establishing the stream")
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading stream should not fail")
tags := tagsFromJson(s.T(), resp.Value)
require.Len(s.T(), tags, 2, "the tags should contain only two values")
assert.Contains(s.T(), tags, "peer.address", "the tags should contain a peer address")
assert.Equal(s.T(), tags["grpc.request.value"], "something", "the tags should contain the correct request value")
}
}
func TestTaggingOnInitialRequestSuite(t *testing.T) {
opts := []grpc_ctxtags.Option{
grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.CodeGenRequestFieldExtractor),
}
// Embeds TaggingSuite as the behaviour should be identical in
// the case of unary and server-streamed calls
s := &ClientStreamedTaggingSuite{
TaggingSuite: &TaggingSuite{
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
TestService: &tagPingBack{&grpc_testing.TestPingService{T: t}},
ServerOpts: []grpc.ServerOption{
grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)),
grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)),
},
},
},
}
suite.Run(t, s)
}
type ClientStreamedTaggingSuite struct {
*TaggingSuite
}
func (s *ClientStreamedTaggingSuite) TestPingStream_WithCustomTagsFirstRequest() {
stream, err := s.Client.PingStream(s.SimpleCtx())
require.NoError(s.T(), err, "should not fail on establishing the stream")
count := 0
for {
switch {
case count == 0:
err = stream.Send(goodPing)
case count < 3:
err = stream.Send(anotherPing)
default:
err = stream.CloseSend()
}
resp, err := stream.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading stream should not fail")
tags := tagsFromJson(s.T(), resp.Value)
require.Len(s.T(), tags, 2, "the tags should contain only two values")
assert.Equal(s.T(), tags["grpc.request.value"], "something", "the tags should contain the correct request value")
assert.Contains(s.T(), tags, "peer.address", "the tags should contain a peer address")
count++
}
assert.Equal(s.T(), count, 3)
}

View File

@@ -0,0 +1,25 @@
package ctx_logrus
import (
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
)
// AddFields adds logrus fields to the logger.
// Deprecated: should use the ctxlogrus.Extract instead
func AddFields(ctx context.Context, fields logrus.Fields) {
ctxlogrus.AddFields(ctx, fields)
}
// Extract takes the call-scoped logrus.Entry from grpc_logrus middleware.
// Deprecated: should use the ctxlogrus.Extract instead
func Extract(ctx context.Context) *logrus.Entry {
return ctxlogrus.Extract(ctx)
}
// ToContext adds the logrus.Entry to the context for extraction later.
// Depricated: should use ctxlogrus.ToContext instead
func ToContext(ctx context.Context, entry *logrus.Entry) context.Context {
return ctxlogrus.ToContext(ctx, entry)
}

View File

@@ -0,0 +1,44 @@
// Copyright 2017 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_ctxtags
var (
defaultOptions = &options{
requestFieldsFunc: nil,
}
)
type options struct {
requestFieldsFunc RequestFieldExtractorFunc
requestFieldsFromInitial bool
}
func evaluateOptions(opts []Option) *options {
optCopy := &options{}
*optCopy = *defaultOptions
for _, o := range opts {
o(optCopy)
}
return optCopy
}
type Option func(*options)
// WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for
// unary and server-streamed methods only.
func WithFieldExtractor(f RequestFieldExtractorFunc) Option {
return func(o *options) {
o.requestFieldsFunc = f
}
}
// WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages,
// for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be
// extracted from the first message from the client.
func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option {
return func(o *options) {
o.requestFieldsFunc = f
o.requestFieldsFromInitial = true
}
}

View File

@@ -0,0 +1,33 @@
package ctx_zap
import (
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/net/context"
)
// AddFields adds zap fields to the logger.
// Deprecated: should use the ctxzap.AddFields instead
func AddFields(ctx context.Context, fields ...zapcore.Field) {
ctxzap.AddFields(ctx, fields...)
}
// Extract takes the call-scoped Logger from grpc_zap middleware.
// Deprecated: should use the ctxzap.Extract instead
func Extract(ctx context.Context) *zap.Logger {
return ctxzap.Extract(ctx)
}
// TagsToFields transforms the Tags on the supplied context into zap fields.
// Deprecated: use ctxzap.TagsToFields
func TagsToFields(ctx context.Context) []zapcore.Field {
return ctxzap.TagsToFields(ctx)
}
// ToContext adds the zap.Logger to the context for extraction later.
// Returning the new context that has been created.
// Deprecated: use ctxzap.ToContext
func ToContext(ctx context.Context, logger *zap.Logger) context.Context {
return ctxzap.ToContext(ctx, logger)
}