mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Adding a way to inject a request ID (#1046)
* Adding a way to inject a request ID It is very useful to associate a request ID to each incoming request, this change allows to provide a function to do that via Server Option. The change comes with a default function which will generate a new request ID. The request ID is put in the request context along with a common logger which always logs the request-id We add gRPC interceptors to the server so it can get the request ID out of the gRPC metadata and put it in the common logger stored in the context so as all the log lines using the common logger from the context will have the request ID logged
This commit is contained in:
18
Gopkg.lock
generated
18
Gopkg.lock
generated
@@ -302,6 +302,24 @@
|
||||
packages = ["."]
|
||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
||||
version = "v1.6.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
packages = ["."]
|
||||
revision = "c250d6563d4d4c20252cd865923440e829844f4e"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
|
||||
@@ -81,3 +81,4 @@ ignored = ["github.com/fnproject/fn/cli",
|
||||
[[constraint]]
|
||||
name = "github.com/dchest/siphash"
|
||||
version = "1.1.0"
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/fnproject/fn/api/common"
|
||||
"github.com/fnproject/fn/api/models"
|
||||
"github.com/fnproject/fn/fnext"
|
||||
"github.com/fnproject/fn/grpcutil"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -206,6 +207,7 @@ func (ch *callHandle) enqueueCallResponse(err error) {
|
||||
var details string
|
||||
var errCode int
|
||||
var errStr string
|
||||
log := common.Logger(ch.ctx)
|
||||
|
||||
if err != nil {
|
||||
errCode = models.GetAPIErrorCode(err)
|
||||
@@ -215,8 +217,7 @@ func (ch *callHandle) enqueueCallResponse(err error) {
|
||||
if ch.c != nil {
|
||||
details = ch.c.Model().ID
|
||||
}
|
||||
|
||||
common.Logger(ch.ctx).Debugf("Sending Call Finish details=%v", details)
|
||||
log.Debugf("Sending Call Finish details=%v", details)
|
||||
|
||||
errTmp := ch.enqueueMsgStrict(&runner.RunnerMsg{
|
||||
Body: &runner.RunnerMsg_Finished{Finished: &runner.CallFinished{
|
||||
@@ -227,13 +228,13 @@ func (ch *callHandle) enqueueCallResponse(err error) {
|
||||
}}})
|
||||
|
||||
if errTmp != nil {
|
||||
common.Logger(ch.ctx).WithError(errTmp).Infof("enqueueCallResponse Send Error details=%v err=%v:%v", details, errCode, errStr)
|
||||
log.WithError(errTmp).Infof("enqueueCallResponse Send Error details=%v err=%v:%v", details, errCode, errStr)
|
||||
return
|
||||
}
|
||||
|
||||
errTmp = ch.finalize()
|
||||
if errTmp != nil {
|
||||
common.Logger(ch.ctx).WithError(errTmp).Infof("enqueueCallResponse Finalize Error details=%v err=%v:%v", details, errCode, errStr)
|
||||
log.WithError(errTmp).Infof("enqueueCallResponse Finalize Error details=%v err=%v:%v", details, errCode, errStr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,22 +554,21 @@ func (pr *pureRunner) handleTryCall(tc *runner.TryCall, state *callHandle) error
|
||||
// Handles a client engagement
|
||||
func (pr *pureRunner) Engage(engagement runner.RunnerProtocol_EngageServer) error {
|
||||
grpc.EnableTracing = false
|
||||
|
||||
ctx := engagement.Context()
|
||||
log := common.Logger(ctx)
|
||||
// Keep lightweight tabs on what this runner is doing: for draindown tests
|
||||
atomic.AddInt32(&pr.inflight, 1)
|
||||
defer atomic.AddInt32(&pr.inflight, -1)
|
||||
|
||||
log := common.Logger(engagement.Context())
|
||||
pv, ok := peer.FromContext(engagement.Context())
|
||||
pv, ok := peer.FromContext(ctx)
|
||||
log.Debug("Starting engagement")
|
||||
if ok {
|
||||
log.Debug("Peer is ", pv)
|
||||
}
|
||||
md, ok := metadata.FromIncomingContext(engagement.Context())
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if ok {
|
||||
log.Debug("MD is ", md)
|
||||
}
|
||||
|
||||
state := NewCallHandle(engagement)
|
||||
|
||||
tryMsg := state.getTryMsg()
|
||||
@@ -713,11 +713,16 @@ func creds(cert string, key string, ca string) (credentials.TransportCredentials
|
||||
|
||||
func createPureRunner(addr string, a Agent, creds credentials.TransportCredentials) (*pureRunner, error) {
|
||||
var srv *grpc.Server
|
||||
var opts []grpc.ServerOption
|
||||
|
||||
sInterceptor := grpc.StreamInterceptor(grpcutil.RIDStreamServerInterceptor)
|
||||
uInterceptor := grpc.UnaryInterceptor(grpcutil.RIDUnaryServerInterceptor)
|
||||
opts = append(opts, sInterceptor)
|
||||
opts = append(opts, uInterceptor)
|
||||
if creds != nil {
|
||||
srv = grpc.NewServer(grpc.Creds(creds))
|
||||
} else {
|
||||
srv = grpc.NewServer()
|
||||
opts = append(opts, grpc.Creds(creds))
|
||||
}
|
||||
srv = grpc.NewServer(opts...)
|
||||
|
||||
pr := &pureRunner{
|
||||
gRPCServer: srv,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
pb "github.com/fnproject/fn/api/agent/grpc"
|
||||
@@ -118,6 +119,12 @@ func (r *gRPCRunner) TryExec(ctx context.Context, call pool.RunnerCall) (bool, e
|
||||
return true, err
|
||||
}
|
||||
|
||||
rid := common.RequestIDFromContext(ctx)
|
||||
if rid != "" {
|
||||
// Create a new gRPC metadata where we store the request ID
|
||||
mp := metadata.Pairs(common.RequestIDContextKey, rid)
|
||||
ctx = metadata.NewOutgoingContext(ctx, mp)
|
||||
}
|
||||
runnerConnection, err := r.client.Engage(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Unable to create client to runner node")
|
||||
|
||||
@@ -9,6 +9,14 @@ import (
|
||||
|
||||
type contextKey string
|
||||
|
||||
// RequestIDContextKey is the name of the key used to store the request ID into the context
|
||||
const RequestIDContextKey = "fn_request_id"
|
||||
|
||||
//WithRequestID stores a request ID into the context
|
||||
func WithRequestID(ctx context.Context, rid string) context.Context {
|
||||
return context.WithValue(ctx, contextKey(RequestIDContextKey), rid)
|
||||
}
|
||||
|
||||
// WithLogger stores the logger.
|
||||
func WithLogger(ctx context.Context, l logrus.FieldLogger) context.Context {
|
||||
return context.WithValue(ctx, contextKey("logger"), l)
|
||||
|
||||
21
api/common/request_id_util.go
Normal file
21
api/common/request_id_util.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/fnproject/fn/api/id"
|
||||
)
|
||||
|
||||
// FnRequestID returns the passed value if that is not empty otherwise it generates a new unique ID
|
||||
func FnRequestID(ridFound string) string {
|
||||
if ridFound == "" {
|
||||
return id.New().String()
|
||||
}
|
||||
return ridFound
|
||||
}
|
||||
|
||||
//RequestIDFromContext extract the request id from the context
|
||||
func RequestIDFromContext(ctx context.Context) string {
|
||||
rid, _ := ctx.Value(contextKey(RequestIDContextKey)).(string)
|
||||
return rid
|
||||
}
|
||||
@@ -62,6 +62,8 @@ const (
|
||||
EnvCert = "FN_NODE_CERT"
|
||||
EnvCertKey = "FN_NODE_CERT_KEY"
|
||||
EnvCertAuth = "FN_NODE_CERT_AUTHORITY"
|
||||
// The header name of the incoming request which holds the request ID
|
||||
EnvRidHeader = "FN_RID_HEADER"
|
||||
|
||||
EnvProcessCollectorList = "FN_PROCESS_COLLECTOR_LIST"
|
||||
EnvLBPlacementAlg = "FN_PLACER"
|
||||
|
||||
@@ -5,11 +5,38 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/fnproject/fn/api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ServerOption func(context.Context, *Server) error
|
||||
|
||||
//RIDProvider is used to manage request ID
|
||||
type RIDProvider struct {
|
||||
HeaderName string //The name of the header where the reques id is stored in the incoming request
|
||||
RIDGenerator func(string) string // Function to generate the requestID
|
||||
}
|
||||
|
||||
func WithRIDProvider(ridProvider *RIDProvider) ServerOption {
|
||||
return func(ctx context.Context, s *Server) error {
|
||||
s.Router.Use(withRIDProvider(ridProvider))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func withRIDProvider(ridp *RIDProvider) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
rid := ridp.RIDGenerator(c.Request.Header.Get(ridp.HeaderName))
|
||||
ctx := common.WithRequestID(c.Request.Context(), rid)
|
||||
// We set the rid in the common logger so it is always logged when the common logger is used
|
||||
l := common.Logger(ctx).WithFields(logrus.Fields{common.RequestIDContextKey: rid})
|
||||
ctx = common.WithLogger(ctx, l)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func EnableShutdownEndpoint(ctx context.Context, halt context.CancelFunc) ServerOption {
|
||||
return func(ctx context.Context, s *Server) error {
|
||||
s.Router.GET("/shutdown", s.handleShutdown(halt))
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"github.com/fnproject/fn/api/agent/drivers/docker"
|
||||
"github.com/fnproject/fn/api/logs/s3"
|
||||
"github.com/fnproject/fn/api/server"
|
||||
// The trace package is imported in several places by different dependencies and if we don't import explicity here it is
|
||||
// initialized every time it is imported and that creates a panic at run time as we register multiple time the handler for
|
||||
// /debug/requests. For example see: https://github.com/GoogleCloudPlatform/google-cloud-go/issues/663 and https://github.com/bradleyfalzon/gopherci/issues/101
|
||||
_ "golang.org/x/net/trace"
|
||||
// EXTENSIONS: Add extension imports here or use `fn build-server`. Learn more: https://github.com/fnproject/fn/blob/master/docs/operating/extending.md
|
||||
|
||||
_ "github.com/fnproject/fn/api/server/defaultexts"
|
||||
|
||||
@@ -11,13 +11,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/api/common"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// DialWithBackoff creates a grpc connection using backoff strategy for reconnections
|
||||
func DialWithBackoff(ctx context.Context, address string, creds credentials.TransportCredentials, timeout time.Duration, backoffCfg grpc.BackoffConfig) (*grpc.ClientConn, error) {
|
||||
return dial(ctx, address, creds, timeout, grpc.WithBackoffConfig(backoffCfg))
|
||||
func DialWithBackoff(ctx context.Context, address string, creds credentials.TransportCredentials, timeout time.Duration, backoffCfg grpc.BackoffConfig, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
opts = append(opts, grpc.WithBackoffConfig(backoffCfg))
|
||||
return dial(ctx, address, creds, timeout, opts...)
|
||||
}
|
||||
|
||||
// uses grpc connection backoff protocol https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
|
||||
@@ -81,3 +85,31 @@ func CreateCredentials(certPath, keyPath, caCertPath, certCommonName string) (cr
|
||||
RootCAs: certPool,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// RIDStreamServerInterceptor is a gRPC stream interceptor which gets the request ID out of the context and put a logger with request ID logged into the common logger in the context
|
||||
func RIDStreamServerInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
newStream := grpc_middleware.WrapServerStream(stream)
|
||||
rid := ridFromMetadata(stream.Context())
|
||||
if rid != "" {
|
||||
newStream.WrappedContext, _ = common.LoggerWithFields(newStream.WrappedContext, logrus.Fields{common.RequestIDContextKey: rid})
|
||||
}
|
||||
return handler(srv, newStream)
|
||||
}
|
||||
|
||||
// RIDUnaryServerInterceptor is an unary gRPC interceptor which gets the request ID out of the context and put a logger with request ID logged into the common logger in the context
|
||||
func RIDUnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
rid := ridFromMetadata(ctx)
|
||||
if rid != "" {
|
||||
ctx, _ = common.LoggerWithFields(ctx, logrus.Fields{common.RequestIDContextKey: rid})
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func ridFromMetadata(ctx context.Context) string {
|
||||
rid := ""
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if ok && len(md[common.RequestIDContextKey]) > 0 {
|
||||
rid = md[common.RequestIDContextKey][0]
|
||||
}
|
||||
return rid
|
||||
}
|
||||
|
||||
33
grpcutil/dial_test.go
Normal file
33
grpcutil/dial_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package grpcutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/fnproject/fn/api/common"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestRIDFoundInMetadata(t *testing.T) {
|
||||
expected := "request-id-test"
|
||||
ctx := context.Background()
|
||||
m := make(map[string]string)
|
||||
m[common.RequestIDContextKey] = expected
|
||||
md := metadata.New(m)
|
||||
incomingCtx := metadata.NewIncomingContext(ctx, md)
|
||||
actual := ridFromMetadata(incomingCtx)
|
||||
if actual != expected {
|
||||
t.Fatalf("Wrong request ID expected '%s' got '%s'", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRIDNotFoundInMetadata(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
m := make(map[string]string)
|
||||
md := metadata.New(m)
|
||||
incomingCtx := metadata.NewIncomingContext(ctx, md)
|
||||
actual := ridFromMetadata(incomingCtx)
|
||||
if actual != "" {
|
||||
t.Fatalf("Expected empty request ID got '%s'", actual)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/fnproject/fn/api/agent"
|
||||
"github.com/fnproject/fn/api/agent/hybrid"
|
||||
"github.com/fnproject/fn/api/common"
|
||||
pool "github.com/fnproject/fn/api/runnerpool"
|
||||
"github.com/fnproject/fn/api/server"
|
||||
_ "github.com/fnproject/fn/api/server/defaultexts"
|
||||
@@ -197,6 +198,11 @@ func SetUpLBNode(ctx context.Context) (*server.Server, error) {
|
||||
opts = append(opts, server.WithMQURL(""))
|
||||
opts = append(opts, server.WithLogURL(""))
|
||||
opts = append(opts, server.EnableShutdownEndpoint(ctx, func() {})) // TODO: do it properly
|
||||
ridProvider := &server.RIDProvider{
|
||||
HeaderName: "fn_request_id",
|
||||
RIDGenerator: common.FnRequestID,
|
||||
}
|
||||
opts = append(opts, server.WithRIDProvider(ridProvider))
|
||||
opts = append(opts, server.WithPrometheus())
|
||||
|
||||
apiURL := "http://127.0.0.1:8085"
|
||||
|
||||
202
vendor/github.com/grpc-ecosystem/go-grpc-middleware/.gitignore
generated
vendored
Normal file
202
vendor/github.com/grpc-ecosystem/go-grpc-middleware/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Go template
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
### Windows template
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
### Kate template
|
||||
# Swap Files #
|
||||
.*.kate-swp
|
||||
.swp.*
|
||||
### SublimeText template
|
||||
# cache files for sublime text
|
||||
*.tmlanguage.cache
|
||||
*.tmPreferences.cache
|
||||
*.stTheme.cache
|
||||
|
||||
# workspace files are user-specific
|
||||
*.sublime-workspace
|
||||
|
||||
# project files should be checked into the repository, unless a significant
|
||||
# proportion of contributors will probably not be using SublimeText
|
||||
# *.sublime-project
|
||||
|
||||
# sftp configuration file
|
||||
sftp-config.json
|
||||
### Linux template
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
### Xcode template
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData/
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata/
|
||||
|
||||
## Other
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprint
|
||||
### Eclipse template
|
||||
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
|
||||
coverage.txt
|
||||
|
||||
#vendor
|
||||
vendor/
|
||||
22
vendor/github.com/grpc-ecosystem/go-grpc-middleware/.travis.yml
generated
vendored
Normal file
22
vendor/github.com/grpc-ecosystem/go-grpc-middleware/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.8.x
|
||||
env:
|
||||
- DEP_VERSION="0.3.2"
|
||||
|
||||
before_install:
|
||||
# Download the binary to bin folder in $GOPATH
|
||||
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
|
||||
# Make the binary executable
|
||||
- chmod +x $GOPATH/bin/dep
|
||||
|
||||
install:
|
||||
- dep ensure
|
||||
|
||||
script:
|
||||
- make checkdocs
|
||||
- make test
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/CONTRIBUTING.md
generated
vendored
Normal file
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Contributing
|
||||
|
||||
We would love to have people submit pull requests and help make `grpc-ecosystem/go-grpc-middleware` even better 👍.
|
||||
|
||||
Fork, then clone the repo:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:your-username/go-grpc-middleware.git
|
||||
```
|
||||
|
||||
Before checking in please run the following:
|
||||
|
||||
```bash
|
||||
make all
|
||||
```
|
||||
|
||||
This will `vet`, `fmt`, regenerate documentation and run all tests.
|
||||
|
||||
|
||||
Push to your fork and open a pull request.
|
||||
166
vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md
generated
vendored
Normal file
166
vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
# grpc_middleware
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools.
|
||||
|
||||
### Middleware
|
||||
gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the
|
||||
upstream gRPC codebase is relatively bare bones.
|
||||
|
||||
This package, and most of its child packages provides commonly needed middleware for gRPC:
|
||||
client-side interceptors for retires, server-side interceptors for input validation and auth,
|
||||
functions for chaining said interceptors, metadata convenience methods and more.
|
||||
|
||||
### Chaining
|
||||
By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on
|
||||
the server side. `grpc_middleware` provides convenient chaining methods
|
||||
|
||||
Simple way of turning a multiple interceptors into a single interceptor. Here's an example for
|
||||
server chaining:
|
||||
|
||||
myServer := grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)),
|
||||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary),
|
||||
)
|
||||
|
||||
These interceptors will be executed from left to right: logging, monitoring and auth.
|
||||
|
||||
Here's an example for client side chaining:
|
||||
|
||||
clientConn, err = grpc.Dial(
|
||||
address,
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)),
|
||||
grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)),
|
||||
)
|
||||
client = pb_testproto.NewTestServiceClient(clientConn)
|
||||
resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"})
|
||||
|
||||
These interceptors will be executed from left to right: monitoring and then retry logic.
|
||||
|
||||
The retry interceptor will call every interceptor that follows it whenever when a retry happens.
|
||||
|
||||
### Writing Your Own
|
||||
Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting
|
||||
bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design.
|
||||
For example, you may want to pass the identity of the caller from the auth interceptor all the way
|
||||
to the handling function.
|
||||
|
||||
For example, a client side interceptor example for auth looks like:
|
||||
|
||||
func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
newCtx := context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(newCtx, req)
|
||||
}
|
||||
|
||||
Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within
|
||||
the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is
|
||||
needed. For example:
|
||||
|
||||
func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
newStream := grpc_middleware.WrapServerStream(stream)
|
||||
newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(srv, stream)
|
||||
}
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor](#ChainStreamClient)
|
||||
* [func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor](#ChainStreamServer)
|
||||
* [func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor](#ChainUnaryClient)
|
||||
* [func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor](#ChainUnaryServer)
|
||||
* [func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption](#WithStreamServerChain)
|
||||
* [func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption](#WithUnaryServerChain)
|
||||
* [type WrappedServerStream](#WrappedServerStream)
|
||||
* [func WrapServerStream(stream grpc.ServerStream) \*WrappedServerStream](#WrapServerStream)
|
||||
* [func (w \*WrappedServerStream) Context() context.Context](#WrappedServerStream.Context)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[chain.go](./chain.go) [doc.go](./doc.go) [wrappers.go](./wrappers.go)
|
||||
|
||||
## <a name="ChainStreamClient">func</a> [ChainStreamClient](./chain.go#L136)
|
||||
``` go
|
||||
func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor
|
||||
```
|
||||
ChainStreamClient creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainStreamClient(one, two, three) will execute one before two before three.
|
||||
|
||||
## <a name="ChainStreamServer">func</a> [ChainStreamServer](./chain.go#L58)
|
||||
``` go
|
||||
func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor
|
||||
```
|
||||
ChainStreamServer creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryServer(one, two, three) will execute one before two before three.
|
||||
If you want to pass context between interceptors, use WrapServerStream.
|
||||
|
||||
## <a name="ChainUnaryClient">func</a> [ChainUnaryClient](./chain.go#L97)
|
||||
``` go
|
||||
func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor
|
||||
```
|
||||
ChainUnaryClient creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryClient(one, two, three) will execute one before two before three.
|
||||
|
||||
## <a name="ChainUnaryServer">func</a> [ChainUnaryServer](./chain.go#L18)
|
||||
``` go
|
||||
func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor
|
||||
```
|
||||
ChainUnaryServer creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryServer(one, two, three) will execute one before two before three, and three
|
||||
will see context changes of one and two.
|
||||
|
||||
## <a name="WithStreamServerChain">func</a> [WithStreamServerChain](./chain.go#L181)
|
||||
``` go
|
||||
func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption
|
||||
```
|
||||
WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors.
|
||||
Basically syntactic sugar.
|
||||
|
||||
## <a name="WithUnaryServerChain">func</a> [WithUnaryServerChain](./chain.go#L175)
|
||||
``` go
|
||||
func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption
|
||||
```
|
||||
Chain creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors.
|
||||
Basically syntactic sugar.
|
||||
|
||||
## <a name="WrappedServerStream">type</a> [WrappedServerStream](./wrappers.go#L12-L16)
|
||||
``` go
|
||||
type WrappedServerStream struct {
|
||||
grpc.ServerStream
|
||||
// WrappedContext is the wrapper's own Context. You can assign it.
|
||||
WrappedContext context.Context
|
||||
}
|
||||
```
|
||||
WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context.
|
||||
|
||||
### <a name="WrapServerStream">func</a> [WrapServerStream](./wrappers.go#L24)
|
||||
``` go
|
||||
func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream
|
||||
```
|
||||
WrapServerStream returns a ServerStream that has the ability to overwrite context.
|
||||
|
||||
### <a name="WrappedServerStream.Context">func</a> (\*WrappedServerStream) [Context](./wrappers.go#L19)
|
||||
``` go
|
||||
func (w *WrappedServerStream) Context() context.Context
|
||||
```
|
||||
Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context()
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
123
vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock
generated
vendored
Normal file
123
vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
revision = "2d3a6656c17a60b0815b7e06ab0be04eacb6e613"
|
||||
version = "v0.16.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = ["gogoproto","proto","protoc-gen-gogo/descriptor"]
|
||||
revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02"
|
||||
version = "v0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["jsonpb","proto","ptypes","ptypes/any","ptypes/duration","ptypes/struct","ptypes/timestamp"]
|
||||
revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
packages = [".","ext","log","mocktracer"]
|
||||
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert","require","suite"]
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/atomic"
|
||||
packages = ["."]
|
||||
revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/multierr"
|
||||
packages = ["."]
|
||||
revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/zap"
|
||||
packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"]
|
||||
revision = "35aad584952c3e7020db7b839f6b102de6271f89"
|
||||
version = "v1.7.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
|
||||
revision = "a8b9294777976932365dabb6640cf1468d95c70f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [".","google","internal","jws","jwt"]
|
||||
revision = "f95fa95eaa936d9d87489b15d1d18b97c1ba9c28"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "13fcbd661c8ececa8807a29b48407d674b1d8ed8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "75cc3cad82b5f47d3fb229ddda8c5167da14f294"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
|
||||
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "7f0da29060c682909f650ad8ed4e515bd74fa12a"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [".","balancer","balancer/roundrobin","codes","connectivity","credentials","credentials/oauth","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"]
|
||||
revision = "5a9f7b402fe85096d2e1d0383435ee1876e863d0"
|
||||
version = "v1.8.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b24c6670412eb0bc44ed1db77fecc52333f8725f3e3272bdc568f5683a63031f"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml
generated
vendored
Normal file
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
[[constraint]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
version = "0.5.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
version = "1.0.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
version = "1.0.3"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.1.4"
|
||||
|
||||
[[constraint]]
|
||||
name = "go.uber.org/zap"
|
||||
version = "1.7.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "1.8.0"
|
||||
201
vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE
generated
vendored
Normal file
201
vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
86
vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md
generated
vendored
Normal file
86
vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
# Go gRPC Middleware
|
||||
|
||||
[](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware)
|
||||
[](https://goreportcard.com/report/github.com/grpc-ecosystem/go-grpc-middleware)
|
||||
[](https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware)
|
||||
[](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/?badge)
|
||||
[](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware)
|
||||
[](LICENSE)
|
||||
[](#status)
|
||||
[](https://join.slack.com/t/improbable-eng/shared_invite/enQtMzQ1ODcyMzQ5MjM4LWY5ZWZmNGM2ODc5MmViNmQ3ZTA3ZTY3NzQwOTBlMTkzZmIxZTIxODk0OWU3YjZhNWVlNDU3MDlkZGViZjhkMjc)
|
||||
|
||||
[gRPC Go](https://github.com/grpc/grpc-go) Middleware: interceptors, helpers, utilities.
|
||||
|
||||
**Important** The repo recently moved to `github.com/grpc-ecosystem/go-grpc-middleware`, please update your import paths.
|
||||
|
||||
## Middleware
|
||||
|
||||
[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for
|
||||
Interceptors, i.e. [middleware](https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.gv7tdlghs)
|
||||
that is executed either on the gRPC Server before the request is passed onto the user's application logic, or on the gRPC client either around the user call. It is a perfect way to implement
|
||||
common patterns: auth, logging, message, validation, retries or monitoring.
|
||||
|
||||
These are generic building blocks that make it easy to build multiple microservices easily.
|
||||
The purpose of this repository is to act as a go-to point for such reusable functionality. It contains
|
||||
some of them itself, but also will link to useful external repos.
|
||||
|
||||
`grpc_middleware` itself provides support for chaining interceptors. See [Documentation](DOC.md), but here's an example:
|
||||
|
||||
```go
|
||||
import "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
|
||||
myServer := grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_opentracing.StreamServerInterceptor(),
|
||||
grpc_prometheus.StreamServerInterceptor,
|
||||
grpc_zap.StreamServerInterceptor(zapLogger),
|
||||
grpc_auth.StreamServerInterceptor(myAuthFunction),
|
||||
grpc_recovery.StreamServerInterceptor(),
|
||||
)),
|
||||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_opentracing.UnaryServerInterceptor(),
|
||||
grpc_prometheus.UnaryServerInterceptor,
|
||||
grpc_zap.UnaryServerInterceptor(zapLogger),
|
||||
grpc_auth.UnaryServerInterceptor(myAuthFunction),
|
||||
grpc_recovery.UnaryServerInterceptor(),
|
||||
)),
|
||||
)
|
||||
```
|
||||
|
||||
## Interceptors
|
||||
|
||||
*Please send a PR to add new interceptors or middleware to this list*
|
||||
|
||||
#### Auth
|
||||
* [`grpc_auth`](auth) - a customizable (via `AuthFunc`) piece of auth middleware
|
||||
|
||||
#### Logging
|
||||
* [`grpc_ctxtags`](tags/) - a library that adds a `Tag` map to context, with data populated from request body
|
||||
* [`grpc_zap`](logging/zap/) - integration of [zap](https://github.com/uber-go/zap) logging library into gRPC handlers.
|
||||
* [`grpc_logrus`](logging/logrus/) - integration of [logrus](https://github.com/sirupsen/logrus) logging library into gRPC handlers.
|
||||
|
||||
|
||||
#### Monitoring
|
||||
* [`grpc_prometheus`⚡](https://github.com/grpc-ecosystem/go-grpc-prometheus) - Prometheus client-side and server-side monitoring middleware
|
||||
* [`otgrpc`⚡](https://github.com/grpc-ecosystem/grpc-opentracing/tree/master/go/otgrpc) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors
|
||||
* [`grpc_opentracing`](tracing/opentracing) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors with support for streaming and handler-returned tags
|
||||
|
||||
#### Client
|
||||
* [`grpc_retry`](retry/) - a generic gRPC response code retry mechanism, client-side middleware
|
||||
|
||||
#### Server
|
||||
* [`grpc_validator`](validator/) - codegen inbound message validation from `.proto` options
|
||||
* [`grpc_recovery`](recovery/) - turn panics into gRPC errors
|
||||
|
||||
|
||||
## Status
|
||||
|
||||
This code has been running in *production* since May 2016 as the basis of the gRPC micro services stack at [Improbable](https://improbable.io).
|
||||
|
||||
Additional tooling will be added, and contributions are welcome.
|
||||
|
||||
## License
|
||||
|
||||
`go-grpc-middleware` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
|
||||
148
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md
generated
vendored
Normal file
148
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
# grpc_auth
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/auth"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_auth` a generic server-side auth middleware for gRPC.
|
||||
|
||||
### Server Side Auth Middleware
|
||||
It allows for easy assertion of `:authorization` headers in gRPC calls, be it HTTP Basic auth, or
|
||||
OAuth2 Bearer tokens.
|
||||
|
||||
The middleware takes a user-customizable `AuthFunc`, which can be customized to verify and extract
|
||||
auth information from the request. The extracted information can be put in the `context.Context` of
|
||||
handlers downstream for retrieval.
|
||||
|
||||
It also allows for per-service implementation overrides of `AuthFunc`. See `ServiceAuthFuncOverride`.
|
||||
|
||||
Please see examples for simple examples of use.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
package grpc_auth_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
cc *grpc.ClientConn
|
||||
)
|
||||
|
||||
func parseToken(token string) (struct{}, error) {
|
||||
return struct{}{}, nil
|
||||
}
|
||||
|
||||
func userClaimFromToken(struct{}) string {
|
||||
return "foobar"
|
||||
}
|
||||
|
||||
// Simple example of server initialization code.
|
||||
func Example_serverConfig() {
|
||||
exampleAuthFunc := func(ctx context.Context) (context.Context, error) {
|
||||
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenInfo, err := parseToken(token)
|
||||
if err != nil {
|
||||
return nil, grpc.Errorf(codes.Unauthenticated, "invalid auth token: %v", err)
|
||||
}
|
||||
grpc_ctxtags.Extract(ctx).Set("auth.sub", userClaimFromToken(tokenInfo))
|
||||
newCtx := context.WithValue(ctx, "tokenInfo", tokenInfo)
|
||||
return newCtx, nil
|
||||
}
|
||||
|
||||
_ = grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(exampleAuthFunc)),
|
||||
grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(exampleAuthFunc)),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware](./..)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/util/metautils](./../util/metautils)
|
||||
- [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/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func AuthFromMD(ctx context.Context, expectedScheme string) (string, error)](#AuthFromMD)
|
||||
* [func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor](#StreamServerInterceptor)
|
||||
* [func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor](#UnaryServerInterceptor)
|
||||
* [type AuthFunc](#AuthFunc)
|
||||
* [type ServiceAuthFuncOverride](#ServiceAuthFuncOverride)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Package (ServerConfig)](#example__serverConfig)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[auth.go](./auth.go) [doc.go](./doc.go) [metadata.go](./metadata.go)
|
||||
|
||||
## <a name="AuthFromMD">func</a> [AuthFromMD](./metadata.go#L24)
|
||||
``` go
|
||||
func AuthFromMD(ctx context.Context, expectedScheme string) (string, error)
|
||||
```
|
||||
AuthFromMD is a helper function for extracting the :authorization header from the gRPC metadata of the request.
|
||||
|
||||
It expects the `:authorization` header to be of a certain scheme (e.g. `basic`, `bearer`), in a
|
||||
case-insensitive format (see rfc2617, sec 1.2). If no such authorization is found, or the token
|
||||
is of wrong scheme, an error with gRPC status `Unauthenticated` is returned.
|
||||
|
||||
## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./auth.go#L51)
|
||||
``` go
|
||||
func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor
|
||||
```
|
||||
StreamServerInterceptor returns a new unary server interceptors that performs per-request auth.
|
||||
|
||||
## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./auth.go#L34)
|
||||
``` go
|
||||
func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor
|
||||
```
|
||||
UnaryServerInterceptor returns a new unary server interceptors that performs per-request auth.
|
||||
|
||||
## <a name="AuthFunc">type</a> [AuthFunc](./auth.go#L23)
|
||||
``` go
|
||||
type AuthFunc func(ctx context.Context) (context.Context, error)
|
||||
```
|
||||
AuthFunc is the pluggable function that performs authentication.
|
||||
|
||||
The passed in `Context` will contain the gRPC metadata.MD object (for header-based authentication) and
|
||||
the peer.Peer information that can contain transport-based credentials (e.g. `credentials.AuthInfo`).
|
||||
|
||||
The returned context will be propagated to handlers, allowing user changes to `Context`. However,
|
||||
please make sure that the `Context` returned is a child `Context` of the one passed in.
|
||||
|
||||
If error is returned, its `grpc.Code()` will be returned to the user as well as the verbatim message.
|
||||
Please make sure you use `codes.Unauthenticated` (lacking auth) and `codes.PermissionDenied`
|
||||
(authed, but lacking perms) appropriately.
|
||||
|
||||
## <a name="ServiceAuthFuncOverride">type</a> [ServiceAuthFuncOverride](./auth.go#L29-L31)
|
||||
``` go
|
||||
type ServiceAuthFuncOverride interface {
|
||||
AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error)
|
||||
}
|
||||
```
|
||||
ServiceAuthFuncOverride allows a given gRPC service implementation to override the global `AuthFunc`.
|
||||
|
||||
If a service implements the AuthFuncOverride method, it takes precedence over the `AuthFunc` method,
|
||||
and will be called instead of AuthFunc for all method invocations within that service.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
67
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go
generated
vendored
Normal file
67
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_auth
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// AuthFunc is the pluggable function that performs authentication.
|
||||
//
|
||||
// The passed in `Context` will contain the gRPC metadata.MD object (for header-based authentication) and
|
||||
// the peer.Peer information that can contain transport-based credentials (e.g. `credentials.AuthInfo`).
|
||||
//
|
||||
// The returned context will be propagated to handlers, allowing user changes to `Context`. However,
|
||||
// please make sure that the `Context` returned is a child `Context` of the one passed in.
|
||||
//
|
||||
// If error is returned, its `grpc.Code()` will be returned to the user as well as the verbatim message.
|
||||
// Please make sure you use `codes.Unauthenticated` (lacking auth) and `codes.PermissionDenied`
|
||||
// (authed, but lacking perms) appropriately.
|
||||
type AuthFunc func(ctx context.Context) (context.Context, error)
|
||||
|
||||
// ServiceAuthFuncOverride allows a given gRPC service implementation to override the global `AuthFunc`.
|
||||
//
|
||||
// If a service implements the AuthFuncOverride method, it takes precedence over the `AuthFunc` method,
|
||||
// and will be called instead of AuthFunc for all method invocations within that service.
|
||||
type ServiceAuthFuncOverride interface {
|
||||
AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error)
|
||||
}
|
||||
|
||||
// UnaryServerInterceptor returns a new unary server interceptors that performs per-request auth.
|
||||
func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
var newCtx context.Context
|
||||
var err error
|
||||
if overrideSrv, ok := info.Server.(ServiceAuthFuncOverride); ok {
|
||||
newCtx, err = overrideSrv.AuthFuncOverride(ctx, info.FullMethod)
|
||||
} else {
|
||||
newCtx, err = authFunc(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return handler(newCtx, req)
|
||||
}
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a new unary server interceptors that performs per-request auth.
|
||||
func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
var newCtx context.Context
|
||||
var err error
|
||||
if overrideSrv, ok := srv.(ServiceAuthFuncOverride); ok {
|
||||
newCtx, err = overrideSrv.AuthFuncOverride(stream.Context(), info.FullMethod)
|
||||
} else {
|
||||
newCtx, err = authFunc(stream.Context())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wrapped := grpc_middleware.WrapServerStream(stream)
|
||||
wrapped.WrappedContext = newCtx
|
||||
return handler(srv, wrapped)
|
||||
}
|
||||
}
|
||||
206
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth_test.go
generated
vendored
Normal file
206
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth_test.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"fmt"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/oauth"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
commonAuthToken = "some_good_token"
|
||||
overrideAuthToken = "override_token"
|
||||
|
||||
authedMarker = "some_context_marker"
|
||||
goodPing = &pb_testproto.PingRequest{Value: "something", SleepTimeMs: 9999}
|
||||
)
|
||||
|
||||
// TODO(mwitkow): Add auth from metadata client dialer, which requires TLS.
|
||||
|
||||
func buildDummyAuthFunction(expectedScheme string, expectedToken string) func(ctx context.Context) (context.Context, error) {
|
||||
return func(ctx context.Context) (context.Context, error) {
|
||||
token, err := grpc_auth.AuthFromMD(ctx, expectedScheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token != expectedToken {
|
||||
return nil, grpc.Errorf(codes.PermissionDenied, "buildDummyAuthFunction bad token")
|
||||
}
|
||||
return context.WithValue(ctx, authedMarker, "marker_exists"), nil
|
||||
}
|
||||
}
|
||||
|
||||
func assertAuthMarkerExists(t *testing.T, ctx context.Context) {
|
||||
assert.Equal(t, "marker_exists", ctx.Value(authedMarker).(string), "auth marker from buildDummyAuthFunction must be passed around")
|
||||
}
|
||||
|
||||
type assertingPingService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
T *testing.T
|
||||
}
|
||||
|
||||
func (s *assertingPingService) PingError(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.Empty, error) {
|
||||
assertAuthMarkerExists(s.T, ctx)
|
||||
return s.TestServiceServer.PingError(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *assertingPingService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
|
||||
assertAuthMarkerExists(s.T, stream.Context())
|
||||
return s.TestServiceServer.PingList(ping, stream)
|
||||
}
|
||||
|
||||
func ctxWithToken(ctx context.Context, scheme string, token string) context.Context {
|
||||
md := metadata.Pairs("authorization", fmt.Sprintf("%s %v", scheme, token))
|
||||
nCtx := metautils.NiceMD(md).ToOutgoing(ctx)
|
||||
return nCtx
|
||||
}
|
||||
|
||||
func TestAuthTestSuite(t *testing.T) {
|
||||
authFunc := buildDummyAuthFunction("bearer", commonAuthToken)
|
||||
s := &AuthTestSuite{
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &assertingPingService{&grpc_testing.TestPingService{T: t}, t},
|
||||
ServerOpts: []grpc.ServerOption{
|
||||
grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(authFunc)),
|
||||
grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(authFunc)),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type AuthTestSuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestUnary_NoAuth() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
assert.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Unauthenticated, grpc.Code(err), "must error with unauthenticated")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestUnary_BadAuth() {
|
||||
_, err := s.Client.Ping(ctxWithToken(s.SimpleCtx(), "bearer", "bad_token"), goodPing)
|
||||
assert.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.PermissionDenied, grpc.Code(err), "must error with permission denied")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestUnary_PassesAuth() {
|
||||
_, err := s.Client.Ping(ctxWithToken(s.SimpleCtx(), "bearer", commonAuthToken), goodPing)
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestUnary_PassesWithPerRpcCredentials() {
|
||||
grpcCreds := oauth.TokenSource{TokenSource: &fakeOAuth2TokenSource{accessToken: commonAuthToken}}
|
||||
client := s.NewClient(grpc.WithPerRPCCredentials(grpcCreds))
|
||||
_, err := client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestStream_NoAuth() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
_, err = stream.Recv()
|
||||
assert.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Unauthenticated, grpc.Code(err), "must error with unauthenticated")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestStream_BadAuth() {
|
||||
stream, err := s.Client.PingList(ctxWithToken(s.SimpleCtx(), "bearer", "bad_token"), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
_, err = stream.Recv()
|
||||
assert.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.PermissionDenied, grpc.Code(err), "must error with permission denied")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestStream_PassesAuth() {
|
||||
stream, err := s.Client.PingList(ctxWithToken(s.SimpleCtx(), "Bearer", commonAuthToken), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
pong, err := stream.Recv()
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
require.NotNil(s.T(), pong, "pong must not be nil")
|
||||
}
|
||||
|
||||
func (s *AuthTestSuite) TestStream_PassesWithPerRpcCredentials() {
|
||||
grpcCreds := oauth.TokenSource{TokenSource: &fakeOAuth2TokenSource{accessToken: commonAuthToken}}
|
||||
client := s.NewClient(grpc.WithPerRPCCredentials(grpcCreds))
|
||||
stream, err := client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
pong, err := stream.Recv()
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
require.NotNil(s.T(), pong, "pong must not be nil")
|
||||
}
|
||||
|
||||
type authOverrideTestService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
T *testing.T
|
||||
}
|
||||
|
||||
func (s *authOverrideTestService) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
|
||||
assert.NotEmpty(s.T, fullMethodName, "method name of caller is passed around")
|
||||
return buildDummyAuthFunction("bearer", overrideAuthToken)(ctx)
|
||||
}
|
||||
|
||||
func TestAuthOverrideTestSuite(t *testing.T) {
|
||||
authFunc := buildDummyAuthFunction("bearer", commonAuthToken)
|
||||
s := &AuthOverrideTestSuite{
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &authOverrideTestService{&assertingPingService{&grpc_testing.TestPingService{T: t}, t}, t},
|
||||
ServerOpts: []grpc.ServerOption{
|
||||
grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(authFunc)),
|
||||
grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(authFunc)),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type AuthOverrideTestSuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
}
|
||||
|
||||
func (s *AuthOverrideTestSuite) TestUnary_PassesAuth() {
|
||||
_, err := s.Client.Ping(ctxWithToken(s.SimpleCtx(), "bearer", overrideAuthToken), goodPing)
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
}
|
||||
|
||||
func (s *AuthOverrideTestSuite) TestStream_PassesAuth() {
|
||||
stream, err := s.Client.PingList(ctxWithToken(s.SimpleCtx(), "Bearer", overrideAuthToken), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
pong, err := stream.Recv()
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
require.NotNil(s.T(), pong, "pong must not be nil")
|
||||
}
|
||||
|
||||
// fakeOAuth2TokenSource implements a fake oauth2.TokenSource for the purpose of credentials test.
|
||||
type fakeOAuth2TokenSource struct {
|
||||
accessToken string
|
||||
}
|
||||
|
||||
func (ts *fakeOAuth2TokenSource) Token() (*oauth2.Token, error) {
|
||||
t := &oauth2.Token{
|
||||
AccessToken: ts.accessToken,
|
||||
Expiry: time.Now().Add(1 * time.Minute),
|
||||
TokenType: "bearer",
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go
generated
vendored
Normal file
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
/*
|
||||
`grpc_auth` a generic server-side auth middleware for gRPC.
|
||||
|
||||
Server Side Auth Middleware
|
||||
|
||||
It allows for easy assertion of `:authorization` headers in gRPC calls, be it HTTP Basic auth, or
|
||||
OAuth2 Bearer tokens.
|
||||
|
||||
The middleware takes a user-customizable `AuthFunc`, which can be customized to verify and extract
|
||||
auth information from the request. The extracted information can be put in the `context.Context` of
|
||||
handlers downstream for retrieval.
|
||||
|
||||
It also allows for per-service implementation overrides of `AuthFunc`. See `ServiceAuthFuncOverride`.
|
||||
|
||||
Please see examples for simple examples of use.
|
||||
*/
|
||||
package grpc_auth
|
||||
43
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/examples_test.go
generated
vendored
Normal file
43
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package grpc_auth_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
cc *grpc.ClientConn
|
||||
)
|
||||
|
||||
func parseToken(token string) (struct{}, error) {
|
||||
return struct{}{}, nil
|
||||
}
|
||||
|
||||
func userClaimFromToken(struct{}) string {
|
||||
return "foobar"
|
||||
}
|
||||
|
||||
// Simple example of server initialization code.
|
||||
func Example_serverConfig() {
|
||||
exampleAuthFunc := func(ctx context.Context) (context.Context, error) {
|
||||
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenInfo, err := parseToken(token)
|
||||
if err != nil {
|
||||
return nil, grpc.Errorf(codes.Unauthenticated, "invalid auth token: %v", err)
|
||||
}
|
||||
grpc_ctxtags.Extract(ctx).Set("auth.sub", userClaimFromToken(tokenInfo))
|
||||
newCtx := context.WithValue(ctx, "tokenInfo", tokenInfo)
|
||||
return newCtx, nil
|
||||
}
|
||||
|
||||
_ = grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(exampleAuthFunc)),
|
||||
grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(exampleAuthFunc)),
|
||||
)
|
||||
}
|
||||
38
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go
generated
vendored
Normal file
38
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
headerAuthorize = "authorization"
|
||||
)
|
||||
|
||||
// AuthFromMD is a helper function for extracting the :authorization header from the gRPC metadata of the request.
|
||||
//
|
||||
// It expects the `:authorization` header to be of a certain scheme (e.g. `basic`, `bearer`), in a
|
||||
// case-insensitive format (see rfc2617, sec 1.2). If no such authorization is found, or the token
|
||||
// is of wrong scheme, an error with gRPC status `Unauthenticated` is returned.
|
||||
func AuthFromMD(ctx context.Context, expectedScheme string) (string, error) {
|
||||
val := metautils.ExtractIncoming(ctx).Get(headerAuthorize)
|
||||
if val == "" {
|
||||
return "", grpc.Errorf(codes.Unauthenticated, "Request unauthenticated with "+expectedScheme)
|
||||
|
||||
}
|
||||
splits := strings.SplitN(val, " ", 2)
|
||||
if len(splits) < 2 {
|
||||
return "", grpc.Errorf(codes.Unauthenticated, "Bad authorization string")
|
||||
}
|
||||
if strings.ToLower(splits[0]) != strings.ToLower(expectedScheme) {
|
||||
return "", grpc.Errorf(codes.Unauthenticated, "Request unauthenticated with "+expectedScheme)
|
||||
}
|
||||
return splits[1], nil
|
||||
}
|
||||
74
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata_test.go
generated
vendored
Normal file
74
vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestAuthFromMD(t *testing.T) {
|
||||
for _, run := range []struct {
|
||||
md metadata.MD
|
||||
value string
|
||||
errCode codes.Code
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
md: metadata.Pairs("authorization", "bearer some_token"),
|
||||
value: "some_token",
|
||||
msg: "must extract simple bearer tokens without case checking",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", "Bearer some_token"),
|
||||
value: "some_token",
|
||||
msg: "must extract simple bearer tokens with case checking",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", "Bearer some multi string bearer"),
|
||||
value: "some multi string bearer",
|
||||
msg: "must handle string based bearers",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", "Basic login:passwd"),
|
||||
value: "",
|
||||
errCode: codes.Unauthenticated,
|
||||
msg: "must check authentication type",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", "Basic login:passwd", "authorization", "bearer some_token"),
|
||||
value: "",
|
||||
errCode: codes.Unauthenticated,
|
||||
msg: "must not allow multiple authentication methods",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", ""),
|
||||
value: "",
|
||||
errCode: codes.Unauthenticated,
|
||||
msg: "authorization string must not be empty",
|
||||
},
|
||||
{
|
||||
md: metadata.Pairs("authorization", "Bearer"),
|
||||
value: "",
|
||||
errCode: codes.Unauthenticated,
|
||||
msg: "bearer token must not be empty",
|
||||
},
|
||||
} {
|
||||
ctx := metautils.NiceMD(run.md).ToIncoming(context.TODO())
|
||||
out, err := AuthFromMD(ctx, "bearer")
|
||||
if run.errCode != codes.OK {
|
||||
assert.Equal(t, run.errCode, grpc.Code(err), run.msg)
|
||||
} else {
|
||||
assert.NoError(t, err, run.msg)
|
||||
}
|
||||
assert.Equal(t, run.value, out, run.msg)
|
||||
}
|
||||
|
||||
}
|
||||
183
vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go
generated
vendored
Normal file
183
vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
// gRPC Server Interceptor chaining middleware.
|
||||
|
||||
package grpc_middleware
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// ChainUnaryServer creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainUnaryServer(one, two, three) will execute one before two before three, and three
|
||||
// will see context changes of one and two.
|
||||
func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
|
||||
n := len(interceptors)
|
||||
|
||||
if n > 1 {
|
||||
lastI := n - 1
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
var (
|
||||
chainHandler grpc.UnaryHandler
|
||||
curI int
|
||||
)
|
||||
|
||||
chainHandler = func(currentCtx context.Context, currentReq interface{}) (interface{}, error) {
|
||||
if curI == lastI {
|
||||
return handler(currentCtx, currentReq)
|
||||
}
|
||||
curI++
|
||||
resp, err := interceptors[curI](currentCtx, currentReq, info, chainHandler)
|
||||
curI--
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return interceptors[0](ctx, req, info, chainHandler)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return interceptors[0]
|
||||
}
|
||||
|
||||
// n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
|
||||
return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
// ChainStreamServer creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainUnaryServer(one, two, three) will execute one before two before three.
|
||||
// If you want to pass context between interceptors, use WrapServerStream.
|
||||
func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor {
|
||||
n := len(interceptors)
|
||||
|
||||
if n > 1 {
|
||||
lastI := n - 1
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
var (
|
||||
chainHandler grpc.StreamHandler
|
||||
curI int
|
||||
)
|
||||
|
||||
chainHandler = func(currentSrv interface{}, currentStream grpc.ServerStream) error {
|
||||
if curI == lastI {
|
||||
return handler(currentSrv, currentStream)
|
||||
}
|
||||
curI++
|
||||
err := interceptors[curI](currentSrv, currentStream, info, chainHandler)
|
||||
curI--
|
||||
return err
|
||||
}
|
||||
|
||||
return interceptors[0](srv, stream, info, chainHandler)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return interceptors[0]
|
||||
}
|
||||
|
||||
// n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
|
||||
return func(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
return handler(srv, stream)
|
||||
}
|
||||
}
|
||||
|
||||
// ChainUnaryClient creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainUnaryClient(one, two, three) will execute one before two before three.
|
||||
func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {
|
||||
n := len(interceptors)
|
||||
|
||||
if n > 1 {
|
||||
lastI := n - 1
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
var (
|
||||
chainHandler grpc.UnaryInvoker
|
||||
curI int
|
||||
)
|
||||
|
||||
chainHandler = func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error {
|
||||
if curI == lastI {
|
||||
return invoker(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentOpts...)
|
||||
}
|
||||
curI++
|
||||
err := interceptors[curI](currentCtx, currentMethod, currentReq, currentRepl, currentConn, chainHandler, currentOpts...)
|
||||
curI--
|
||||
return err
|
||||
}
|
||||
|
||||
return interceptors[0](ctx, method, req, reply, cc, chainHandler, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return interceptors[0]
|
||||
}
|
||||
|
||||
// n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ChainStreamClient creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainStreamClient(one, two, three) will execute one before two before three.
|
||||
func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor {
|
||||
n := len(interceptors)
|
||||
|
||||
if n > 1 {
|
||||
lastI := n - 1
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
var (
|
||||
chainHandler grpc.Streamer
|
||||
curI int
|
||||
)
|
||||
|
||||
chainHandler = func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
if curI == lastI {
|
||||
return streamer(currentCtx, currentDesc, currentConn, currentMethod, currentOpts...)
|
||||
}
|
||||
curI++
|
||||
stream, err := interceptors[curI](currentCtx, currentDesc, currentConn, currentMethod, chainHandler, currentOpts...)
|
||||
curI--
|
||||
return stream, err
|
||||
}
|
||||
|
||||
return interceptors[0](ctx, desc, cc, method, chainHandler, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return interceptors[0]
|
||||
}
|
||||
|
||||
// n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Chain creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors.
|
||||
// Basically syntactic sugar.
|
||||
func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption {
|
||||
return grpc.UnaryInterceptor(ChainUnaryServer(interceptors...))
|
||||
}
|
||||
|
||||
// WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors.
|
||||
// Basically syntactic sugar.
|
||||
func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption {
|
||||
return grpc.StreamInterceptor(ChainStreamServer(interceptors...))
|
||||
}
|
||||
172
vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain_test.go
generated
vendored
Normal file
172
vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain_test.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
someServiceName = "SomeService.StreamMethod"
|
||||
parentUnaryInfo = &grpc.UnaryServerInfo{FullMethod: someServiceName}
|
||||
parentStreamInfo = &grpc.StreamServerInfo{
|
||||
FullMethod: someServiceName,
|
||||
IsServerStream: true,
|
||||
}
|
||||
someValue = 1
|
||||
parentContext = context.WithValue(context.TODO(), "parent", someValue)
|
||||
)
|
||||
|
||||
func TestChainUnaryServer(t *testing.T) {
|
||||
input := "input"
|
||||
output := "output"
|
||||
|
||||
first := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
requireContextValue(t, ctx, "parent", "first interceptor must know the parent context value")
|
||||
require.Equal(t, parentUnaryInfo, info, "first interceptor must know the someUnaryServerInfo")
|
||||
ctx = context.WithValue(ctx, "first", 1)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
second := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
requireContextValue(t, ctx, "parent", "second interceptor must know the parent context value")
|
||||
requireContextValue(t, ctx, "first", "second interceptor must know the first context value")
|
||||
require.Equal(t, parentUnaryInfo, info, "second interceptor must know the someUnaryServerInfo")
|
||||
ctx = context.WithValue(ctx, "second", 1)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
require.EqualValues(t, input, req, "handler must get the input")
|
||||
requireContextValue(t, ctx, "parent", "handler must know the parent context value")
|
||||
requireContextValue(t, ctx, "first", "handler must know the first context value")
|
||||
requireContextValue(t, ctx, "second", "handler must know the second context value")
|
||||
return output, nil
|
||||
}
|
||||
|
||||
chain := ChainUnaryServer(first, second)
|
||||
out, _ := chain(parentContext, input, parentUnaryInfo, handler)
|
||||
require.EqualValues(t, output, out, "chain must return handler's output")
|
||||
}
|
||||
|
||||
func TestChainStreamServer(t *testing.T) {
|
||||
someService := &struct{}{}
|
||||
recvMessage := "received"
|
||||
sentMessage := "sent"
|
||||
outputError := fmt.Errorf("some error")
|
||||
|
||||
first := func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
requireContextValue(t, stream.Context(), "parent", "first interceptor must know the parent context value")
|
||||
require.Equal(t, parentStreamInfo, info, "first interceptor must know the parentStreamInfo")
|
||||
require.Equal(t, someService, srv, "first interceptor must know someService")
|
||||
wrapped := WrapServerStream(stream)
|
||||
wrapped.WrappedContext = context.WithValue(stream.Context(), "first", 1)
|
||||
return handler(srv, wrapped)
|
||||
}
|
||||
second := func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
requireContextValue(t, stream.Context(), "parent", "second interceptor must know the parent context value")
|
||||
requireContextValue(t, stream.Context(), "parent", "second interceptor must know the first context value")
|
||||
require.Equal(t, parentStreamInfo, info, "second interceptor must know the parentStreamInfo")
|
||||
require.Equal(t, someService, srv, "second interceptor must know someService")
|
||||
wrapped := WrapServerStream(stream)
|
||||
wrapped.WrappedContext = context.WithValue(stream.Context(), "second", 1)
|
||||
return handler(srv, wrapped)
|
||||
}
|
||||
handler := func(srv interface{}, stream grpc.ServerStream) error {
|
||||
require.Equal(t, someService, srv, "handler must know someService")
|
||||
requireContextValue(t, stream.Context(), "parent", "handler must know the parent context value")
|
||||
requireContextValue(t, stream.Context(), "first", "handler must know the first context value")
|
||||
requireContextValue(t, stream.Context(), "second", "handler must know the second context value")
|
||||
require.NoError(t, stream.RecvMsg(recvMessage), "handler must have access to stream messages")
|
||||
require.NoError(t, stream.SendMsg(sentMessage), "handler must be able to send stream messages")
|
||||
return outputError
|
||||
}
|
||||
fakeStream := &fakeServerStream{ctx: parentContext, recvMessage: recvMessage}
|
||||
chain := ChainStreamServer(first, second)
|
||||
err := chain(someService, fakeStream, parentStreamInfo, handler)
|
||||
require.Equal(t, outputError, err, "chain must return handler's error")
|
||||
require.Equal(t, sentMessage, fakeStream.sentMessage, "handler's sent message must propagate to stream")
|
||||
}
|
||||
|
||||
func TestChainUnaryClient(t *testing.T) {
|
||||
ignoredMd := metadata.Pairs("foo", "bar")
|
||||
parentOpts := []grpc.CallOption{grpc.Header(&ignoredMd)}
|
||||
reqMessage := "request"
|
||||
replyMessage := "reply"
|
||||
outputError := fmt.Errorf("some error")
|
||||
|
||||
first := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
requireContextValue(t, ctx, "parent", "first must know the parent context value")
|
||||
require.Equal(t, someServiceName, method, "first must know someService")
|
||||
require.Len(t, opts, 1, "first should see parent CallOptions")
|
||||
wrappedCtx := context.WithValue(ctx, "first", 1)
|
||||
return invoker(wrappedCtx, method, req, reply, cc, opts...)
|
||||
}
|
||||
second := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
requireContextValue(t, ctx, "parent", "second must know the parent context value")
|
||||
require.Equal(t, someServiceName, method, "second must know someService")
|
||||
require.Len(t, opts, 1, "second should see parent CallOptions")
|
||||
wrappedOpts := append(opts, grpc.FailFast(true))
|
||||
wrappedCtx := context.WithValue(ctx, "second", 1)
|
||||
return invoker(wrappedCtx, method, req, reply, cc, wrappedOpts...)
|
||||
}
|
||||
invoker := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
|
||||
require.Equal(t, someServiceName, method, "invoker must know someService")
|
||||
requireContextValue(t, ctx, "parent", "invoker must know the parent context value")
|
||||
requireContextValue(t, ctx, "first", "invoker must know the first context value")
|
||||
requireContextValue(t, ctx, "second", "invoker must know the second context value")
|
||||
require.Len(t, opts, 2, "invoker should see both CallOpts from second and parent")
|
||||
return outputError
|
||||
}
|
||||
chain := ChainUnaryClient(first, second)
|
||||
err := chain(parentContext, someServiceName, reqMessage, replyMessage, nil, invoker, parentOpts...)
|
||||
require.Equal(t, outputError, err, "chain must return invokers's error")
|
||||
}
|
||||
|
||||
func TestChainStreamClient(t *testing.T) {
|
||||
ignoredMd := metadata.Pairs("foo", "bar")
|
||||
parentOpts := []grpc.CallOption{grpc.Header(&ignoredMd)}
|
||||
clientStream := &fakeClientStream{}
|
||||
fakeStreamDesc := &grpc.StreamDesc{ClientStreams: true, ServerStreams: true, StreamName: someServiceName}
|
||||
|
||||
first := func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
requireContextValue(t, ctx, "parent", "first must know the parent context value")
|
||||
require.Equal(t, someServiceName, method, "first must know someService")
|
||||
require.Len(t, opts, 1, "first should see parent CallOptions")
|
||||
wrappedCtx := context.WithValue(ctx, "first", 1)
|
||||
return streamer(wrappedCtx, desc, cc, method, opts...)
|
||||
}
|
||||
second := func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
requireContextValue(t, ctx, "parent", "second must know the parent context value")
|
||||
require.Equal(t, someServiceName, method, "second must know someService")
|
||||
require.Len(t, opts, 1, "second should see parent CallOptions")
|
||||
wrappedOpts := append(opts, grpc.FailFast(true))
|
||||
wrappedCtx := context.WithValue(ctx, "second", 1)
|
||||
return streamer(wrappedCtx, desc, cc, method, wrappedOpts...)
|
||||
}
|
||||
streamer := func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
require.Equal(t, someServiceName, method, "streamer must know someService")
|
||||
require.Equal(t, fakeStreamDesc, desc, "streamer must see the right StreamDesc")
|
||||
|
||||
requireContextValue(t, ctx, "parent", "streamer must know the parent context value")
|
||||
requireContextValue(t, ctx, "first", "streamer must know the first context value")
|
||||
requireContextValue(t, ctx, "second", "streamer must know the second context value")
|
||||
require.Len(t, opts, 2, "streamer should see both CallOpts from second and parent")
|
||||
return clientStream, nil
|
||||
}
|
||||
chain := ChainStreamClient(first, second)
|
||||
someStream, err := chain(parentContext, fakeStreamDesc, nil, someServiceName, streamer, parentOpts...)
|
||||
require.NoError(t, err, "chain must not return an error as nothing there reutrned it")
|
||||
require.Equal(t, clientStream, someStream, "chain must return invokers's clientstream")
|
||||
}
|
||||
|
||||
func requireContextValue(t *testing.T, ctx context.Context, key string, msg ...interface{}) {
|
||||
val := ctx.Value(key)
|
||||
require.NotNil(t, val, msg...)
|
||||
require.Equal(t, someValue, val, msg...)
|
||||
}
|
||||
69
vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go
generated
vendored
Normal file
69
vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
/*
|
||||
`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools.
|
||||
|
||||
Middleware
|
||||
|
||||
gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the
|
||||
upstream gRPC codebase is relatively bare bones.
|
||||
|
||||
This package, and most of its child packages provides commonly needed middleware for gRPC:
|
||||
client-side interceptors for retires, server-side interceptors for input validation and auth,
|
||||
functions for chaining said interceptors, metadata convenience methods and more.
|
||||
|
||||
Chaining
|
||||
|
||||
By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on
|
||||
the server side. `grpc_middleware` provides convenient chaining methods
|
||||
|
||||
Simple way of turning a multiple interceptors into a single interceptor. Here's an example for
|
||||
server chaining:
|
||||
|
||||
myServer := grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)),
|
||||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary),
|
||||
)
|
||||
|
||||
These interceptors will be executed from left to right: logging, monitoring and auth.
|
||||
|
||||
Here's an example for client side chaining:
|
||||
|
||||
clientConn, err = grpc.Dial(
|
||||
address,
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)),
|
||||
grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)),
|
||||
)
|
||||
client = pb_testproto.NewTestServiceClient(clientConn)
|
||||
resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"})
|
||||
|
||||
These interceptors will be executed from left to right: monitoring and then retry logic.
|
||||
|
||||
The retry interceptor will call every interceptor that follows it whenever when a retry happens.
|
||||
|
||||
Writing Your Own
|
||||
|
||||
Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting
|
||||
bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design.
|
||||
For example, you may want to pass the identity of the caller from the auth interceptor all the way
|
||||
to the handling function.
|
||||
|
||||
For example, a client side interceptor example for auth looks like:
|
||||
|
||||
func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
newCtx := context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(newCtx, req)
|
||||
}
|
||||
|
||||
Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within
|
||||
the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is
|
||||
needed. For example:
|
||||
|
||||
func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
newStream := grpc_middleware.WrapServerStream(stream)
|
||||
newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(srv, stream)
|
||||
}
|
||||
*/
|
||||
package grpc_middleware
|
||||
91
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md
generated
vendored
Normal file
91
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
# grpc_logging
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/logging"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
grpc_logging is a "parent" package for gRPC logging middlewares.
|
||||
|
||||
### General functionality of all middleware
|
||||
The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call
|
||||
(e.g. service and method names).
|
||||
|
||||
Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs
|
||||
that are made using the `ctx_logrus` or `ctx_zap` loggers.
|
||||
|
||||
All logging middleware will emit a final log statement. It is based on the error returned by the handler function,
|
||||
the gRPC status code, an error (if any) and it will emit at a level controlled via `WithLevels`.
|
||||
|
||||
### This parent package
|
||||
This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other
|
||||
logging middlewares *could* share . This allows code to be shared between different implementations.
|
||||
|
||||
### Field names
|
||||
All field names of loggers follow the OpenTracing semantics definitions, with `grpc.` prefix if needed:
|
||||
<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a>
|
||||
|
||||
### Implementations
|
||||
There are two implementations at the moment: logrus and zap
|
||||
|
||||
See relevant packages below.
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [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/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func DefaultDeciderMethod(fullMethodName string, err error) bool](#DefaultDeciderMethod)
|
||||
* [func DefaultErrorToCode(err error) codes.Code](#DefaultErrorToCode)
|
||||
* [type ClientPayloadLoggingDecider](#ClientPayloadLoggingDecider)
|
||||
* [type Decider](#Decider)
|
||||
* [type ErrorToCode](#ErrorToCode)
|
||||
* [type ServerPayloadLoggingDecider](#ServerPayloadLoggingDecider)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[common.go](./common.go) [doc.go](./doc.go)
|
||||
|
||||
## <a name="DefaultDeciderMethod">func</a> [DefaultDeciderMethod](./common.go#L25)
|
||||
``` go
|
||||
func DefaultDeciderMethod(fullMethodName string, err error) bool
|
||||
```
|
||||
DefaultDeciderMethod is the default implementation of decider to see if you should log the call
|
||||
by default this if always true so all calls are logged
|
||||
|
||||
## <a name="DefaultErrorToCode">func</a> [DefaultErrorToCode](./common.go#L16)
|
||||
``` go
|
||||
func DefaultErrorToCode(err error) codes.Code
|
||||
```
|
||||
|
||||
## <a name="ClientPayloadLoggingDecider">type</a> [ClientPayloadLoggingDecider](./common.go#L35)
|
||||
``` go
|
||||
type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool
|
||||
```
|
||||
ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side
|
||||
request/response payloads
|
||||
|
||||
## <a name="Decider">type</a> [Decider](./common.go#L21)
|
||||
``` go
|
||||
type Decider func(fullMethodName string, err error) bool
|
||||
```
|
||||
Decider function defines rules for suppressing any interceptor logs
|
||||
|
||||
## <a name="ErrorToCode">type</a> [ErrorToCode](./common.go#L14)
|
||||
``` go
|
||||
type ErrorToCode func(err error) codes.Code
|
||||
```
|
||||
ErrorToCode function determines the error code of an error
|
||||
This makes using custom errors with grpc middleware easier
|
||||
|
||||
## <a name="ServerPayloadLoggingDecider">type</a> [ServerPayloadLoggingDecider](./common.go#L31)
|
||||
``` go
|
||||
type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool
|
||||
```
|
||||
ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side
|
||||
request/response payloads
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go
generated
vendored
Normal file
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_logging
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// ErrorToCode function determines the error code of an error
|
||||
// This makes using custom errors with grpc middleware easier
|
||||
type ErrorToCode func(err error) codes.Code
|
||||
|
||||
func DefaultErrorToCode(err error) codes.Code {
|
||||
return grpc.Code(err)
|
||||
}
|
||||
|
||||
// Decider function defines rules for suppressing any interceptor logs
|
||||
type Decider func(fullMethodName string, err error) bool
|
||||
|
||||
// DefaultDeciderMethod is the default implementation of decider to see if you should log the call
|
||||
// by default this if always true so all calls are logged
|
||||
func DefaultDeciderMethod(fullMethodName string, err error) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side
|
||||
// request/response payloads
|
||||
type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool
|
||||
|
||||
// ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side
|
||||
// request/response payloads
|
||||
type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool
|
||||
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go
generated
vendored
Normal file
35
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
//
|
||||
/*
|
||||
grpc_logging is a "parent" package for gRPC logging middlewares.
|
||||
|
||||
General functionality of all middleware
|
||||
|
||||
The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call
|
||||
(e.g. service and method names).
|
||||
|
||||
Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs
|
||||
that are made using the `ctx_logrus` or `ctx_zap` loggers.
|
||||
|
||||
All logging middleware will emit a final log statement. It is based on the error returned by the handler function,
|
||||
the gRPC status code, an error (if any) and it will emit at a level controlled via `WithLevels`.
|
||||
|
||||
This parent package
|
||||
|
||||
This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other
|
||||
logging middlewares *could* share . This allows code to be shared between different implementations.
|
||||
|
||||
Field names
|
||||
|
||||
All field names of loggers follow the OpenTracing semantics definitions, with `grpc.` prefix if needed:
|
||||
https://github.com/opentracing/specification/blob/master/semantic_conventions.md
|
||||
|
||||
Implementations
|
||||
|
||||
There are two implementations at the moment: logrus and zap
|
||||
|
||||
See relevant packages below.
|
||||
*/
|
||||
package grpc_logging
|
||||
391
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md
generated
vendored
Normal file
391
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
# grpc_logrus
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_logrus` is a gRPC logging middleware backed by Logrus loggers
|
||||
|
||||
It accepts a user-configured `logrus.Entry` that will be used for logging completed gRPC calls. The same
|
||||
`logrus.Entry` will be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information
|
||||
to the ctx so that it will be present on subsequent use of the `ctxlogrus` logger.
|
||||
|
||||
This package also implements request and response *payload* logging, both for server-side and client-side. These will be
|
||||
logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use
|
||||
`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log
|
||||
the full request/response payload needs to be written with care, this can significantly slow down gRPC.
|
||||
|
||||
If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline
|
||||
is a string representing the time (RFC3339) when the current call will expire.
|
||||
|
||||
Logrus can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`.
|
||||
|
||||
*Server Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the server interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string logrus log levels
|
||||
"msg": "finished unary call", // string log message
|
||||
"grpc.code": "OK", // string grpc status code
|
||||
"grpc.method": "Ping", // string method name
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
|
||||
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
|
||||
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
|
||||
"grpc.request.value": "something", // string value on the request
|
||||
"grpc.time_ms": 1.234, // float32 run time of the call in ms
|
||||
"peer.address": {
|
||||
"IP": "127.0.0.1", // string IP address of calling party
|
||||
"Port": 60216, // int port call is coming in on
|
||||
"Zone": "" // string peer zone for caller
|
||||
},
|
||||
"span.kind": "server", // string client | server
|
||||
"system": "grpc" // string
|
||||
|
||||
"custom_field": "custom_value", // string user defined field
|
||||
"custom_tags.int": 1337, // int user defined tag on the ctx
|
||||
"custom_tags.string": "something", // string user defined tag on the ctx
|
||||
}
|
||||
|
||||
*Payload Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the payload interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string logrus log levels
|
||||
"msg": "client request payload logged as grpc.request.content", // string log message
|
||||
|
||||
"grpc.request.content": { // object content of RPC request
|
||||
"value": "something", // string defined by caller
|
||||
"sleepTimeMs": 9999 // int defined by caller
|
||||
},
|
||||
"grpc.method": "Ping", // string method being called
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string service being called
|
||||
"span.kind": "client", // string client | server
|
||||
"system": "grpc" // string
|
||||
}
|
||||
|
||||
Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object.
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
// Logrus entry is used, allowing pre-definition of certain fields by the user.
|
||||
logrusEntry := logrus.NewEntry(logrusLogger)
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithLevels(customFunc),
|
||||
}
|
||||
// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well.
|
||||
grpc_logrus.ReplaceGrpcLogger(logrusEntry)
|
||||
// Create a server, make sure we put the grpc_ctxtags context before everything else.
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
// Logrus entry is used, allowing pre-definition of certain fields by the user.
|
||||
logrusEntry := logrus.NewEntry(logrusLogger)
|
||||
// Shared options for the logger, with a custom duration to log field function.
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) {
|
||||
return "grpc.time_ns", duration.Nanoseconds()
|
||||
}),
|
||||
}
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/golang/protobuf/jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb)
|
||||
- [github.com/golang/protobuf/proto](https://godoc.org/github.com/golang/protobuf/proto)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware](./../..)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/logging](./..)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus](./ctxlogrus)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus](./../../tags/logrus)
|
||||
- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus)
|
||||
- [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/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
- [google.golang.org/grpc/grpclog](https://godoc.org/google.golang.org/grpc/grpclog)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [Variables](#pkg-variables)
|
||||
* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields)
|
||||
* [func DefaultClientCodeToLevel(code codes.Code) logrus.Level](#DefaultClientCodeToLevel)
|
||||
* [func DefaultCodeToLevel(code codes.Code) logrus.Level](#DefaultCodeToLevel)
|
||||
* [func DurationToDurationField(duration time.Duration) (key string, value interface{})](#DurationToDurationField)
|
||||
* [func DurationToTimeMillisField(duration time.Duration) (key string, value interface{})](#DurationToTimeMillisField)
|
||||
* [func Extract(ctx context.Context) \*logrus.Entry](#Extract)
|
||||
* [func PayloadStreamClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor](#PayloadStreamClientInterceptor)
|
||||
* [func PayloadStreamServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor](#PayloadStreamServerInterceptor)
|
||||
* [func PayloadUnaryClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor](#PayloadUnaryClientInterceptor)
|
||||
* [func PayloadUnaryServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor](#PayloadUnaryServerInterceptor)
|
||||
* [func ReplaceGrpcLogger(logger \*logrus.Entry)](#ReplaceGrpcLogger)
|
||||
* [func StreamClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamClientInterceptor](#StreamClientInterceptor)
|
||||
* [func StreamServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor)
|
||||
* [func UnaryClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor](#UnaryClientInterceptor)
|
||||
* [func UnaryServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor)
|
||||
* [type CodeToLevel](#CodeToLevel)
|
||||
* [type DurationToField](#DurationToField)
|
||||
* [type Option](#Option)
|
||||
* [func WithCodes(f grpc\_logging.ErrorToCode) Option](#WithCodes)
|
||||
* [func WithDecider(f grpc\_logging.Decider) Option](#WithDecider)
|
||||
* [func WithDurationField(f DurationToField) Option](#WithDurationField)
|
||||
* [func WithLevels(f CodeToLevel) Option](#WithLevels)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Extract (Unary)](#example_Extract_unary)
|
||||
* [WithDecider](#example_WithDecider)
|
||||
* [Package (Initialization)](#example__initialization)
|
||||
* [Package (InitializationWithDurationFieldOverride)](#example__initializationWithDurationFieldOverride)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[client_interceptors.go](./client_interceptors.go) [context.go](./context.go) [doc.go](./doc.go) [grpclogger.go](./grpclogger.go) [options.go](./options.go) [payload_interceptors.go](./payload_interceptors.go) [server_interceptors.go](./server_interceptors.go)
|
||||
|
||||
## <a name="pkg-variables">Variables</a>
|
||||
``` go
|
||||
var (
|
||||
// SystemField is used in every log statement made through grpc_logrus. Can be overwritten before any initialization code.
|
||||
SystemField = "system"
|
||||
|
||||
// KindField describes the log gield used to incicate whether this is a server or a client log statment.
|
||||
KindField = "span.kind"
|
||||
)
|
||||
```
|
||||
``` go
|
||||
var DefaultDurationToField = DurationToTimeMillisField
|
||||
```
|
||||
DefaultDurationToField is the default implementation of converting request duration to a log field (key and value).
|
||||
|
||||
``` go
|
||||
var (
|
||||
// JsonPbMarshaller is the marshaller used for serializing protobuf messages.
|
||||
JsonPbMarshaller = &jsonpb.Marshaler{}
|
||||
)
|
||||
```
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L11)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields logrus.Fields)
|
||||
```
|
||||
AddFields adds logrus fields to the logger.
|
||||
Deprecated: should use the ctxlogrus.Extract instead
|
||||
|
||||
## <a name="DefaultClientCodeToLevel">func</a> [DefaultClientCodeToLevel](./options.go#L129)
|
||||
``` go
|
||||
func DefaultClientCodeToLevel(code codes.Code) logrus.Level
|
||||
```
|
||||
DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
|
||||
|
||||
## <a name="DefaultCodeToLevel">func</a> [DefaultCodeToLevel](./options.go#L87)
|
||||
``` go
|
||||
func DefaultCodeToLevel(code codes.Code) logrus.Level
|
||||
```
|
||||
DefaultCodeToLevel is the default implementation of gRPC return codes to log levels for server side.
|
||||
|
||||
## <a name="DurationToDurationField">func</a> [DurationToDurationField](./options.go#L179)
|
||||
``` go
|
||||
func DurationToDurationField(duration time.Duration) (key string, value interface{})
|
||||
```
|
||||
DurationToDurationField uses the duration value to log the request duration.
|
||||
|
||||
## <a name="DurationToTimeMillisField">func</a> [DurationToTimeMillisField](./options.go#L174)
|
||||
``` go
|
||||
func DurationToTimeMillisField(duration time.Duration) (key string, value interface{})
|
||||
```
|
||||
DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L17)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *logrus.Entry
|
||||
```
|
||||
Extract takes the call-scoped logrus.Entry from grpc_logrus middleware.
|
||||
Deprecated: should use the ctxlogrus.Extract instead
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
// Extract a single request-scoped logrus.Logger and log messages.
|
||||
l := ctx_logrus.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="PayloadStreamClientInterceptor">func</a> [PayloadStreamClientInterceptor](./payload_interceptors.go#L74)
|
||||
``` go
|
||||
func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor
|
||||
```
|
||||
PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses.
|
||||
|
||||
## <a name="PayloadStreamServerInterceptor">func</a> [PayloadStreamServerInterceptor](./payload_interceptors.go#L45)
|
||||
``` go
|
||||
func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor
|
||||
```
|
||||
PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
|
||||
|
||||
This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a
|
||||
separate instance of the logger.
|
||||
|
||||
## <a name="PayloadUnaryClientInterceptor">func</a> [PayloadUnaryClientInterceptor](./payload_interceptors.go#L58)
|
||||
``` go
|
||||
func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor
|
||||
```
|
||||
PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses.
|
||||
|
||||
## <a name="PayloadUnaryServerInterceptor">func</a> [PayloadUnaryServerInterceptor](./payload_interceptors.go#L25)
|
||||
``` go
|
||||
func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor
|
||||
```
|
||||
PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
|
||||
|
||||
This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a
|
||||
separate instance of the logger.
|
||||
|
||||
## <a name="ReplaceGrpcLogger">func</a> [ReplaceGrpcLogger](./grpclogger.go#L13)
|
||||
``` go
|
||||
func ReplaceGrpcLogger(logger *logrus.Entry)
|
||||
```
|
||||
ReplaceGrpcLogger sets the given logrus.Logger as a gRPC-level logger.
|
||||
This should be called *before* any other initialization, preferably from init() functions.
|
||||
|
||||
## <a name="StreamClientInterceptor">func</a> [StreamClientInterceptor](./client_interceptors.go#L28)
|
||||
``` go
|
||||
func StreamClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamClientInterceptor
|
||||
```
|
||||
StreamServerInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
|
||||
|
||||
## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./server_interceptors.go#L58)
|
||||
``` go
|
||||
func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor
|
||||
```
|
||||
StreamServerInterceptor returns a new streaming server interceptor that adds logrus.Entry to the context.
|
||||
|
||||
## <a name="UnaryClientInterceptor">func</a> [UnaryClientInterceptor](./client_interceptors.go#L16)
|
||||
``` go
|
||||
func UnaryClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor
|
||||
```
|
||||
UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
|
||||
|
||||
## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./server_interceptors.go#L26)
|
||||
``` go
|
||||
func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor
|
||||
```
|
||||
UnaryServerInterceptor returns a new unary server interceptors that adds logrus.Entry to the context.
|
||||
|
||||
## <a name="CodeToLevel">type</a> [CodeToLevel](./options.go#L53)
|
||||
``` go
|
||||
type CodeToLevel func(code codes.Code) logrus.Level
|
||||
```
|
||||
CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
|
||||
|
||||
## <a name="DurationToField">type</a> [DurationToField](./options.go#L56)
|
||||
``` go
|
||||
type DurationToField func(duration time.Duration) (key string, value interface{})
|
||||
```
|
||||
DurationToField function defines how to produce duration fields for logging
|
||||
|
||||
## <a name="Option">type</a> [Option](./options.go#L50)
|
||||
``` go
|
||||
type Option func(*options)
|
||||
```
|
||||
|
||||
### <a name="WithCodes">func</a> [WithCodes](./options.go#L73)
|
||||
``` go
|
||||
func WithCodes(f grpc_logging.ErrorToCode) Option
|
||||
```
|
||||
WithCodes customizes the function for mapping errors to error codes.
|
||||
|
||||
### <a name="WithDecider">func</a> [WithDecider](./options.go#L59)
|
||||
``` go
|
||||
func WithDecider(f grpc_logging.Decider) Option
|
||||
```
|
||||
WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDecider(func(methodFullName string, err error) bool {
|
||||
// will not log gRPC calls if it was a call to healthcheck and no error was raised
|
||||
if err == nil && methodFullName == "blah.foo.healthcheck" {
|
||||
return false
|
||||
}
|
||||
|
||||
// by default you will log all calls
|
||||
return true
|
||||
}),
|
||||
}
|
||||
|
||||
_ = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
### <a name="WithDurationField">func</a> [WithDurationField](./options.go#L80)
|
||||
``` go
|
||||
func WithDurationField(f DurationToField) Option
|
||||
```
|
||||
WithDurationField customizes the function for mapping request durations to log fields.
|
||||
|
||||
### <a name="WithLevels">func</a> [WithLevels](./options.go#L66)
|
||||
``` go
|
||||
func WithLevels(f CodeToLevel) Option
|
||||
```
|
||||
WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go
generated
vendored
Normal file
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
|
||||
func UnaryClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor {
|
||||
o := evaluateClientOpt(opts)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
fields := newClientLoggerFields(ctx, method)
|
||||
startTime := time.Now()
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
logFinalClientLine(o, entry.WithFields(fields), startTime, err, "finished client unary call")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
|
||||
func StreamClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamClientInterceptor {
|
||||
o := evaluateClientOpt(opts)
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
fields := newClientLoggerFields(ctx, method)
|
||||
startTime := time.Now()
|
||||
clientStream, err := streamer(ctx, desc, cc, method, opts...)
|
||||
logFinalClientLine(o, entry.WithFields(fields), startTime, err, "finished client streaming call")
|
||||
return clientStream, err
|
||||
}
|
||||
}
|
||||
|
||||
func logFinalClientLine(o *options, entry *logrus.Entry, startTime time.Time, err error, msg string) {
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
durField, durVal := o.durationFunc(time.Now().Sub(startTime))
|
||||
fields := logrus.Fields{
|
||||
"grpc.code": code.String(),
|
||||
durField: durVal,
|
||||
}
|
||||
if err != nil {
|
||||
fields[logrus.ErrorKey] = err
|
||||
}
|
||||
levelLogf(
|
||||
entry.WithFields(fields),
|
||||
level,
|
||||
msg)
|
||||
}
|
||||
|
||||
func newClientLoggerFields(ctx context.Context, fullMethodString string) logrus.Fields {
|
||||
service := path.Dir(fullMethodString)[1:]
|
||||
method := path.Base(fullMethodString)
|
||||
return logrus.Fields{
|
||||
SystemField: "grpc",
|
||||
KindField: "client",
|
||||
"grpc.service": service,
|
||||
"grpc.method": method,
|
||||
}
|
||||
}
|
||||
189
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors_test.go
generated
vendored
Normal file
189
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package grpc_logrus_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
)
|
||||
|
||||
func customClientCodeToLevel(c codes.Code) logrus.Level {
|
||||
if c == codes.Unauthenticated {
|
||||
// Make this a special case for tests, and an error.
|
||||
return logrus.ErrorLevel
|
||||
}
|
||||
level := grpc_logrus.DefaultClientCodeToLevel(c)
|
||||
return level
|
||||
}
|
||||
|
||||
func TestLogrusClientSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithLevels(customClientCodeToLevel),
|
||||
}
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.logger.Level = logrus.DebugLevel // a lot of our stuff is on debug level by default
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_logrus.UnaryClientInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
grpc.WithStreamInterceptor(grpc_logrus.StreamClientInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
}
|
||||
suite.Run(t, &logrusClientSuite{b})
|
||||
}
|
||||
|
||||
type logrusClientSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusClientSuite) TestPing() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
assert.NoError(s.T(), err, "there must be not be an on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "handler's message must contain the correct message")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "OK codes must be logged on debug level.")
|
||||
|
||||
assert.Contains(s.T(), msgs[0], "grpc.time_ms", "interceptor log statement should contain execution time (duration in ms)")
|
||||
}
|
||||
|
||||
func (s *logrusClientSuite) TestPingList() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "handler's message must contain the correct message")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "OK codes must be logged on debug level.")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.time_ms", "interceptor log statement should contain execution time (duration in ms)")
|
||||
}
|
||||
|
||||
func (s *logrusClientSuite) TestPingError_WithCustomLevels() {
|
||||
for _, tcase := range []struct {
|
||||
code codes.Code
|
||||
level logrus.Level
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
code: codes.Internal,
|
||||
level: logrus.WarnLevel,
|
||||
msg: "Internal must remap to ErrorLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
level: logrus.DebugLevel,
|
||||
msg: "NotFound must remap to InfoLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.FailedPrecondition,
|
||||
level: logrus.DebugLevel,
|
||||
msg: "FailedPrecondition must remap to WarnLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.Unauthenticated,
|
||||
level: logrus.ErrorLevel,
|
||||
msg: "Unauthenticated is overwritten to ErrorLevel with customClientCodeToLevel override, which probably didn't work",
|
||||
},
|
||||
} {
|
||||
s.SetupTest()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)})
|
||||
|
||||
assert.Error(s.T(), err, "each call here must return an error")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only a single log message is printed")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingError", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.code"], tcase.code.String(), "all lines must contain a grpc code")
|
||||
assert.Equal(s.T(), msgs[0]["level"], tcase.level.String(), tcase.msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogrusClientOverrideSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDurationField(grpc_logrus.DurationToDurationField),
|
||||
}
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.logger.Level = logrus.DebugLevel // a lot of our stuff is on debug level by default
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_logrus.UnaryClientInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
grpc.WithStreamInterceptor(grpc_logrus.StreamClientInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
}
|
||||
suite.Run(t, &logrusClientOverrideSuite{b})
|
||||
}
|
||||
|
||||
type logrusClientOverrideSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusClientOverrideSuite) TestPing_HasOverrides() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
assert.NoError(s.T(), err, "there must be not be an on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "handler's message must contain the correct message")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.duration", "message must contain overridden duration")
|
||||
}
|
||||
|
||||
func (s *logrusClientOverrideSuite) TestPingList_HasOverrides() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "log message must be correct")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "OK codes must be logged on debug level.")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.duration", "message must contain overridden duration")
|
||||
}
|
||||
19
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go
generated
vendored
Normal file
19
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package grpc_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)
|
||||
}
|
||||
93
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md
generated
vendored
Normal file
93
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
# ctxlogrus
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`ctxlogrus` is a ctxlogger that is backed by logrus
|
||||
|
||||
It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code.
|
||||
|
||||
As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./../../../tags)
|
||||
- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus)
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields)
|
||||
* [func Extract(ctx context.Context) \*logrus.Entry](#Extract)
|
||||
* [func ToContext(ctx context.Context, entry \*logrus.Entry) context.Context](#ToContext)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Extract (Unary)](#example_Extract_unary)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[context.go](./context.go) [doc.go](./doc.go) [noop.go](./noop.go)
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L21)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields logrus.Fields)
|
||||
```
|
||||
AddFields adds logrus fields to the logger.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L35)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *logrus.Entry
|
||||
```
|
||||
Extract takes the call-scoped logrus.Entry from ctx_logrus middleware.
|
||||
|
||||
If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to
|
||||
use regardless.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
package ctxlogrus_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var logrusLogger *logrus.Logger
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
ctx := context.Background()
|
||||
// setting tags will be added to the logger as log fields
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
// Extract a single request-scoped logrus.Logger and log messages.
|
||||
l := ctxlogrus.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="ToContext">func</a> [ToContext](./context.go#L59)
|
||||
``` go
|
||||
func ToContext(ctx context.Context, entry *logrus.Entry) context.Context
|
||||
```
|
||||
ToContext adds the logrus.Entry to the context for extraction later.
|
||||
Returning the new context that has been created.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
58
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md
generated
vendored
Normal file
58
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# ctx_logrus
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`ctx_logrus` is a ctxlogger that is backed by logrus
|
||||
|
||||
It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code.
|
||||
|
||||
As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./..)
|
||||
- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus)
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields)
|
||||
* [func Extract(ctx context.Context) \*logrus.Entry](#Extract)
|
||||
* [func ToContext(ctx context.Context, entry \*logrus.Entry) context.Context](#ToContext)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[context.go](./context.go) [doc.go](./doc.go) [noop.go](./noop.go)
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L21)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields logrus.Fields)
|
||||
```
|
||||
AddFields adds logrus fields to the logger.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L35)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *logrus.Entry
|
||||
```
|
||||
Extract takes the call-scoped logrus.Entry from ctx_logrus middleware.
|
||||
|
||||
If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to
|
||||
use regardless.
|
||||
|
||||
## <a name="ToContext">func</a> [ToContext](./context.go#L59)
|
||||
``` go
|
||||
func ToContext(ctx context.Context, entry *logrus.Entry) context.Context
|
||||
```
|
||||
ToContext adds the logrus.Entry to the context for extraction later.
|
||||
Returning the new context that has been created.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go
generated
vendored
Normal file
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package ctxlogrus
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type ctxLoggerMarker struct{}
|
||||
|
||||
type ctxLogger struct {
|
||||
logger *logrus.Entry
|
||||
fields logrus.Fields
|
||||
}
|
||||
|
||||
var (
|
||||
ctxLoggerKey = &ctxLoggerMarker{}
|
||||
)
|
||||
|
||||
// AddFields adds logrus fields to the logger.
|
||||
func AddFields(ctx context.Context, fields logrus.Fields) {
|
||||
l, ok := ctx.Value(ctxLoggerKey).(*ctxLogger)
|
||||
if !ok || l == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range fields {
|
||||
l.fields[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Extract takes the call-scoped logrus.Entry from ctx_logrus middleware.
|
||||
//
|
||||
// If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to
|
||||
// use regardless.
|
||||
func Extract(ctx context.Context) *logrus.Entry {
|
||||
l, ok := ctx.Value(ctxLoggerKey).(*ctxLogger)
|
||||
if !ok || l == nil {
|
||||
return logrus.NewEntry(nullLogger)
|
||||
}
|
||||
|
||||
fields := logrus.Fields{}
|
||||
|
||||
// Add grpc_ctxtags tags metadata until now.
|
||||
tags := grpc_ctxtags.Extract(ctx)
|
||||
for k, v := range tags.Values() {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
// Add logrus fields added until now.
|
||||
for k, v := range l.fields {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
return l.logger.WithFields(fields)
|
||||
}
|
||||
|
||||
// ToContext adds the logrus.Entry to the context for extraction later.
|
||||
// Returning the new context that has been created.
|
||||
func ToContext(ctx context.Context, entry *logrus.Entry) context.Context {
|
||||
l := &ctxLogger{
|
||||
logger: entry,
|
||||
fields: logrus.Fields{},
|
||||
}
|
||||
return context.WithValue(ctx, ctxLoggerKey, l)
|
||||
}
|
||||
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go
generated
vendored
Normal file
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
`ctxlogrus` is a ctxlogger that is backed by logrus
|
||||
|
||||
It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code.
|
||||
|
||||
As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
*/
|
||||
package ctxlogrus
|
||||
21
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/examples_test.go
generated
vendored
Normal file
21
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package ctxlogrus_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var logrusLogger *logrus.Logger
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
ctx := context.Background()
|
||||
// setting tags will be added to the logger as log fields
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
// Extract a single request-scoped logrus.Logger and log messages.
|
||||
l := ctxlogrus.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
}
|
||||
16
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go
generated
vendored
Normal file
16
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package ctxlogrus
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
nullLogger = &logrus.Logger{
|
||||
Out: ioutil.Discard,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.PanicLevel,
|
||||
}
|
||||
)
|
||||
67
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go
generated
vendored
Normal file
67
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
`grpc_logrus` is a gRPC logging middleware backed by Logrus loggers
|
||||
|
||||
It accepts a user-configured `logrus.Entry` that will be used for logging completed gRPC calls. The same
|
||||
`logrus.Entry` will be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information
|
||||
to the ctx so that it will be present on subsequent use of the `ctxlogrus` logger.
|
||||
|
||||
This package also implements request and response *payload* logging, both for server-side and client-side. These will be
|
||||
logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use
|
||||
`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log
|
||||
the full request/response payload needs to be written with care, this can significantly slow down gRPC.
|
||||
|
||||
If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline
|
||||
is a string representing the time (RFC3339) when the current call will expire.
|
||||
|
||||
Logrus can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`.
|
||||
|
||||
*Server Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the server interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string logrus log levels
|
||||
"msg": "finished unary call", // string log message
|
||||
"grpc.code": "OK", // string grpc status code
|
||||
"grpc.method": "Ping", // string method name
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
|
||||
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
|
||||
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
|
||||
"grpc.request.value": "something", // string value on the request
|
||||
"grpc.time_ms": 1.234, // float32 run time of the call in ms
|
||||
"peer.address": {
|
||||
"IP": "127.0.0.1", // string IP address of calling party
|
||||
"Port": 60216, // int port call is coming in on
|
||||
"Zone": "" // string peer zone for caller
|
||||
},
|
||||
"span.kind": "server", // string client | server
|
||||
"system": "grpc" // string
|
||||
|
||||
"custom_field": "custom_value", // string user defined field
|
||||
"custom_tags.int": 1337, // int user defined tag on the ctx
|
||||
"custom_tags.string": "something", // string user defined tag on the ctx
|
||||
}
|
||||
|
||||
*Payload Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the payload interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string logrus log levels
|
||||
"msg": "client request payload logged as grpc.request.content", // string log message
|
||||
|
||||
"grpc.request.content": { // object content of RPC request
|
||||
"value": "something", // string defined by caller
|
||||
"sleepTimeMs": 9999 // int defined by caller
|
||||
},
|
||||
"grpc.method": "Ping", // string method being called
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string service being called
|
||||
"span.kind": "client", // string client | server
|
||||
"system": "grpc" // string
|
||||
}
|
||||
|
||||
Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object.
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
*/
|
||||
package grpc_logrus
|
||||
99
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/examples_test.go
generated
vendored
Normal file
99
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package grpc_logrus_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
logrusLogger *logrus.Logger
|
||||
customFunc grpc_logrus.CodeToLevel
|
||||
)
|
||||
|
||||
// Initialization shows a relatively complex initialization sequence.
|
||||
func Example_initialization() {
|
||||
// Logrus entry is used, allowing pre-definition of certain fields by the user.
|
||||
logrusEntry := logrus.NewEntry(logrusLogger)
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithLevels(customFunc),
|
||||
}
|
||||
// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well.
|
||||
grpc_logrus.ReplaceGrpcLogger(logrusEntry)
|
||||
// Create a server, make sure we put the grpc_ctxtags context before everything else.
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func Example_initializationWithDurationFieldOverride() {
|
||||
// Logrus entry is used, allowing pre-definition of certain fields by the user.
|
||||
logrusEntry := logrus.NewEntry(logrusLogger)
|
||||
// Shared options for the logger, with a custom duration to log field function.
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) {
|
||||
return "grpc.time_ns", duration.Nanoseconds()
|
||||
}),
|
||||
}
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
// Extract a single request-scoped logrus.Logger and log messages.
|
||||
l := ctx_logrus.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleWithDecider() {
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDecider(func(methodFullName string, err error) bool {
|
||||
// will not log gRPC calls if it was a call to healthcheck and no error was raised
|
||||
if err == nil && methodFullName == "blah.foo.healthcheck" {
|
||||
return false
|
||||
}
|
||||
|
||||
// by default you will log all calls
|
||||
return true
|
||||
}),
|
||||
}
|
||||
|
||||
_ = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go
generated
vendored
Normal file
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
// ReplaceGrpcLogger sets the given logrus.Logger as a gRPC-level logger.
|
||||
// This should be called *before* any other initialization, preferably from init() functions.
|
||||
func ReplaceGrpcLogger(logger *logrus.Entry) {
|
||||
grpclog.SetLogger(logger.WithField("system", SystemField))
|
||||
}
|
||||
185
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go
generated
vendored
Normal file
185
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOptions = &options{
|
||||
levelFunc: nil,
|
||||
shouldLog: grpc_logging.DefaultDeciderMethod,
|
||||
codeFunc: grpc_logging.DefaultErrorToCode,
|
||||
durationFunc: DefaultDurationToField,
|
||||
}
|
||||
)
|
||||
|
||||
type options struct {
|
||||
levelFunc CodeToLevel
|
||||
shouldLog grpc_logging.Decider
|
||||
codeFunc grpc_logging.ErrorToCode
|
||||
durationFunc DurationToField
|
||||
}
|
||||
|
||||
func evaluateServerOpt(opts []Option) *options {
|
||||
optCopy := &options{}
|
||||
*optCopy = *defaultOptions
|
||||
optCopy.levelFunc = DefaultCodeToLevel
|
||||
for _, o := range opts {
|
||||
o(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
func evaluateClientOpt(opts []Option) *options {
|
||||
optCopy := &options{}
|
||||
*optCopy = *defaultOptions
|
||||
optCopy.levelFunc = DefaultClientCodeToLevel
|
||||
for _, o := range opts {
|
||||
o(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
// CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
|
||||
type CodeToLevel func(code codes.Code) logrus.Level
|
||||
|
||||
// DurationToField function defines how to produce duration fields for logging
|
||||
type DurationToField func(duration time.Duration) (key string, value interface{})
|
||||
|
||||
// WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
|
||||
func WithDecider(f grpc_logging.Decider) Option {
|
||||
return func(o *options) {
|
||||
o.shouldLog = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
|
||||
func WithLevels(f CodeToLevel) Option {
|
||||
return func(o *options) {
|
||||
o.levelFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithCodes customizes the function for mapping errors to error codes.
|
||||
func WithCodes(f grpc_logging.ErrorToCode) Option {
|
||||
return func(o *options) {
|
||||
o.codeFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithDurationField customizes the function for mapping request durations to log fields.
|
||||
func WithDurationField(f DurationToField) Option {
|
||||
return func(o *options) {
|
||||
o.durationFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultCodeToLevel is the default implementation of gRPC return codes to log levels for server side.
|
||||
func DefaultCodeToLevel(code codes.Code) logrus.Level {
|
||||
switch code {
|
||||
case codes.OK:
|
||||
return logrus.InfoLevel
|
||||
case codes.Canceled:
|
||||
return logrus.InfoLevel
|
||||
case codes.Unknown:
|
||||
return logrus.ErrorLevel
|
||||
case codes.InvalidArgument:
|
||||
return logrus.InfoLevel
|
||||
case codes.DeadlineExceeded:
|
||||
return logrus.WarnLevel
|
||||
case codes.NotFound:
|
||||
return logrus.InfoLevel
|
||||
case codes.AlreadyExists:
|
||||
return logrus.InfoLevel
|
||||
case codes.PermissionDenied:
|
||||
return logrus.WarnLevel
|
||||
case codes.Unauthenticated:
|
||||
return logrus.InfoLevel // unauthenticated requests can happen
|
||||
case codes.ResourceExhausted:
|
||||
return logrus.WarnLevel
|
||||
case codes.FailedPrecondition:
|
||||
return logrus.WarnLevel
|
||||
case codes.Aborted:
|
||||
return logrus.WarnLevel
|
||||
case codes.OutOfRange:
|
||||
return logrus.WarnLevel
|
||||
case codes.Unimplemented:
|
||||
return logrus.ErrorLevel
|
||||
case codes.Internal:
|
||||
return logrus.ErrorLevel
|
||||
case codes.Unavailable:
|
||||
return logrus.WarnLevel
|
||||
case codes.DataLoss:
|
||||
return logrus.ErrorLevel
|
||||
default:
|
||||
return logrus.ErrorLevel
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
|
||||
func DefaultClientCodeToLevel(code codes.Code) logrus.Level {
|
||||
switch code {
|
||||
case codes.OK:
|
||||
return logrus.DebugLevel
|
||||
case codes.Canceled:
|
||||
return logrus.DebugLevel
|
||||
case codes.Unknown:
|
||||
return logrus.InfoLevel
|
||||
case codes.InvalidArgument:
|
||||
return logrus.DebugLevel
|
||||
case codes.DeadlineExceeded:
|
||||
return logrus.InfoLevel
|
||||
case codes.NotFound:
|
||||
return logrus.DebugLevel
|
||||
case codes.AlreadyExists:
|
||||
return logrus.DebugLevel
|
||||
case codes.PermissionDenied:
|
||||
return logrus.InfoLevel
|
||||
case codes.Unauthenticated:
|
||||
return logrus.InfoLevel // unauthenticated requests can happen
|
||||
case codes.ResourceExhausted:
|
||||
return logrus.DebugLevel
|
||||
case codes.FailedPrecondition:
|
||||
return logrus.DebugLevel
|
||||
case codes.Aborted:
|
||||
return logrus.DebugLevel
|
||||
case codes.OutOfRange:
|
||||
return logrus.DebugLevel
|
||||
case codes.Unimplemented:
|
||||
return logrus.WarnLevel
|
||||
case codes.Internal:
|
||||
return logrus.WarnLevel
|
||||
case codes.Unavailable:
|
||||
return logrus.WarnLevel
|
||||
case codes.DataLoss:
|
||||
return logrus.WarnLevel
|
||||
default:
|
||||
return logrus.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultDurationToField is the default implementation of converting request duration to a log field (key and value).
|
||||
var DefaultDurationToField = DurationToTimeMillisField
|
||||
|
||||
// DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
|
||||
func DurationToTimeMillisField(duration time.Duration) (key string, value interface{}) {
|
||||
return "grpc.time_ms", durationToMilliseconds(duration)
|
||||
}
|
||||
|
||||
// DurationToDurationField uses the duration value to log the request duration.
|
||||
func DurationToDurationField(duration time.Duration) (key string, value interface{}) {
|
||||
return "grpc.duration", duration
|
||||
}
|
||||
|
||||
func durationToMilliseconds(duration time.Duration) float32 {
|
||||
return float32(duration.Nanoseconds()/1000) / 1000
|
||||
}
|
||||
12
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options_test.go
generated
vendored
Normal file
12
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options_test.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDurationToTimeMillisField(t *testing.T) {
|
||||
_, val := DurationToTimeMillisField(time.Microsecond * 100)
|
||||
assert.Equal(t, val.(float32), float32(0.1), "sub millisecond values should be correct")
|
||||
}
|
||||
144
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go
generated
vendored
Normal file
144
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// JsonPbMarshaller is the marshaller used for serializing protobuf messages.
|
||||
JsonPbMarshaller = &jsonpb.Marshaler{}
|
||||
)
|
||||
|
||||
// PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
|
||||
//
|
||||
// This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a
|
||||
// separate instance of the logger.
|
||||
func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
if !decider(ctx, info.FullMethod, info.Server) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
// Use the provided logrus.Entry for logging but use the fields from context.
|
||||
logEntry := entry.WithFields(ctx_logrus.Extract(ctx).Data)
|
||||
logProtoMessageAsJson(logEntry, req, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
resp, err := handler(ctx, req)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(logEntry, resp, "grpc.response.content", "server response payload logged as grpc.request.content field")
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
|
||||
//
|
||||
// This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a
|
||||
// separate instance of the logger.
|
||||
func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
if !decider(stream.Context(), info.FullMethod, srv) {
|
||||
return handler(srv, stream)
|
||||
}
|
||||
// Use the provided logrus.Entry for logging but use the fields from context.
|
||||
logEntry := entry.WithFields(Extract(stream.Context()).Data)
|
||||
newStream := &loggingServerStream{ServerStream: stream, entry: logEntry}
|
||||
return handler(srv, newStream)
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses.
|
||||
func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor {
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
if !decider(ctx, method) {
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
logEntry := entry.WithFields(newClientLoggerFields(ctx, method))
|
||||
logProtoMessageAsJson(logEntry, req, "grpc.request.content", "client request payload logged as grpc.request.content")
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(logEntry, reply, "grpc.response.content", "client response payload logged as grpc.response.content")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses.
|
||||
func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor {
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
if !decider(ctx, method) {
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
logEntry := entry.WithFields(newClientLoggerFields(ctx, method))
|
||||
clientStream, err := streamer(ctx, desc, cc, method, opts...)
|
||||
newStream := &loggingClientStream{ClientStream: clientStream, entry: logEntry}
|
||||
return newStream, err
|
||||
}
|
||||
}
|
||||
|
||||
type loggingClientStream struct {
|
||||
grpc.ClientStream
|
||||
entry *logrus.Entry
|
||||
}
|
||||
|
||||
func (l *loggingClientStream) SendMsg(m interface{}) error {
|
||||
err := l.ClientStream.SendMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *loggingClientStream) RecvMsg(m interface{}) error {
|
||||
err := l.ClientStream.RecvMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type loggingServerStream struct {
|
||||
grpc.ServerStream
|
||||
entry *logrus.Entry
|
||||
}
|
||||
|
||||
func (l *loggingServerStream) SendMsg(m interface{}) error {
|
||||
err := l.ServerStream.SendMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *loggingServerStream) RecvMsg(m interface{}) error {
|
||||
err := l.ServerStream.RecvMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func logProtoMessageAsJson(entry *logrus.Entry, pbMsg interface{}, key string, msg string) {
|
||||
if p, ok := pbMsg.(proto.Message); ok {
|
||||
entry.WithField(key, &jsonpbMarshalleble{p}).Info(msg)
|
||||
}
|
||||
}
|
||||
|
||||
type jsonpbMarshalleble struct {
|
||||
proto.Message
|
||||
}
|
||||
|
||||
func (j *jsonpbMarshalleble) MarshalJSON() ([]byte, error) {
|
||||
b := &bytes.Buffer{}
|
||||
if err := JsonPbMarshaller.Marshal(b, j.Message); err != nil {
|
||||
return nil, fmt.Errorf("jsonpb serializer failed: %v", err)
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
135
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors_test.go
generated
vendored
Normal file
135
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package grpc_logrus_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"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 (
|
||||
nullLogger = &logrus.Logger{
|
||||
Out: ioutil.Discard,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.PanicLevel,
|
||||
}
|
||||
)
|
||||
|
||||
func TestLogrusPayloadSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
alwaysLoggingDeciderServer := func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { return true }
|
||||
alwaysLoggingDeciderClient := func(ctx context.Context, fullMethodName string) bool { return true }
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_logrus.PayloadUnaryClientInterceptor(logrus.NewEntry(b.logger), alwaysLoggingDeciderClient)),
|
||||
grpc.WithStreamInterceptor(grpc_logrus.PayloadStreamClientInterceptor(logrus.NewEntry(b.logger), alwaysLoggingDeciderClient)),
|
||||
}
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(nullLogger)),
|
||||
grpc_logrus.PayloadStreamServerInterceptor(logrus.NewEntry(b.logger), alwaysLoggingDeciderServer)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(nullLogger)),
|
||||
grpc_logrus.PayloadUnaryServerInterceptor(logrus.NewEntry(b.logger), alwaysLoggingDeciderServer)),
|
||||
}
|
||||
suite.Run(t, &logrusPayloadSuite{b})
|
||||
}
|
||||
|
||||
type logrusPayloadSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusPayloadSuite) getServerAndClientMessages(expectedServer int, expectedClient int) (serverMsgs []map[string]interface{}, clientMsgs []map[string]interface{}) {
|
||||
msgs := s.getOutputJSONs()
|
||||
for _, m := range msgs {
|
||||
if m["span.kind"] == "server" {
|
||||
serverMsgs = append(serverMsgs, m)
|
||||
} else if m["span.kind"] == "client" {
|
||||
clientMsgs = append(clientMsgs, m)
|
||||
}
|
||||
}
|
||||
|
||||
require.Len(s.T(), serverMsgs, expectedServer, "must match expected number of server log messages")
|
||||
require.Len(s.T(), clientMsgs, expectedClient, "must match expected number of client log messages")
|
||||
return serverMsgs, clientMsgs
|
||||
}
|
||||
|
||||
func (s *logrusPayloadSuite) TestPing_LogsBothRequestAndResponse() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an on a successful call")
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(2, 2)
|
||||
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "all lines must contain method name")
|
||||
}
|
||||
|
||||
serverReq, serverResp := serverMsgs[0], serverMsgs[1]
|
||||
clientReq, clientResp := clientMsgs[0], clientMsgs[1]
|
||||
assert.Contains(s.T(), clientReq, "grpc.request.content", "request payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), serverReq, "grpc.request.content", "request payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), clientResp, "grpc.response.content", "response payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), serverResp, "grpc.response.content", "response payload must be logged in a structured way")
|
||||
}
|
||||
|
||||
func (s *logrusPayloadSuite) TestPingError_LogsOnlyRequestsOnError() {
|
||||
_, err := s.Client.PingError(s.SimpleCtx(), &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(4)})
|
||||
require.Error(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(1, 1)
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "all lines must be logged at info level")
|
||||
}
|
||||
|
||||
assert.Contains(s.T(), clientMsgs[0], "grpc.request.content", "request payload must be logged by the client")
|
||||
assert.Contains(s.T(), serverMsgs[0], "grpc.request.content", "request payload must be logged by the server")
|
||||
}
|
||||
|
||||
func (s *logrusPayloadSuite) TestPingStream_LogsAllRequestsAndResponses() {
|
||||
messagesExpected := 20
|
||||
stream, err := s.Client.PingStream(s.SimpleCtx())
|
||||
require.NoError(s.T(), err, "no error on stream creation")
|
||||
for i := 0; i < messagesExpected; i++ {
|
||||
require.NoError(s.T(), stream.Send(goodPing), "sending must succeed")
|
||||
}
|
||||
require.NoError(s.T(), stream.CloseSend(), "no error on close of stream")
|
||||
|
||||
for {
|
||||
pong := &pb_testproto.PingResponse{}
|
||||
err := stream.RecvMsg(pong)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "no error on receive")
|
||||
}
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(2*messagesExpected, 2*messagesExpected)
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingStream", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "all lines must be at info log level")
|
||||
|
||||
content := m["grpc.request.content"] != nil || m["grpc.response.content"] != nil
|
||||
assert.True(s.T(), content, "all messages must contain a payload")
|
||||
}
|
||||
}
|
||||
129
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go
generated
vendored
Normal file
129
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
|
||||
|
||||
package grpc_logrus
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// SystemField is used in every log statement made through grpc_logrus. Can be overwritten before any initialization code.
|
||||
SystemField = "system"
|
||||
|
||||
// KindField describes the log gield used to incicate whether this is a server or a client log statment.
|
||||
KindField = "span.kind"
|
||||
)
|
||||
|
||||
// UnaryServerInterceptor returns a new unary server interceptors that adds logrus.Entry to the context.
|
||||
func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor {
|
||||
o := evaluateServerOpt(opts)
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
startTime := time.Now()
|
||||
newCtx := newLoggerForCall(ctx, entry, info.FullMethod, startTime)
|
||||
|
||||
resp, err := handler(newCtx, req)
|
||||
|
||||
if !o.shouldLog(info.FullMethod, err) {
|
||||
return resp, err
|
||||
}
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
durField, durVal := o.durationFunc(time.Since(startTime))
|
||||
fields := logrus.Fields{
|
||||
"grpc.code": code.String(),
|
||||
durField: durVal,
|
||||
}
|
||||
if err != nil {
|
||||
fields[logrus.ErrorKey] = err
|
||||
}
|
||||
|
||||
levelLogf(
|
||||
ctx_logrus.Extract(newCtx).WithFields(fields), // re-extract logger from newCtx, as it may have extra fields that changed in the holder.
|
||||
level,
|
||||
"finished unary call with code "+code.String())
|
||||
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a new streaming server interceptor that adds logrus.Entry to the context.
|
||||
func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor {
|
||||
o := evaluateServerOpt(opts)
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
startTime := time.Now()
|
||||
newCtx := newLoggerForCall(stream.Context(), entry, info.FullMethod, startTime)
|
||||
wrapped := grpc_middleware.WrapServerStream(stream)
|
||||
wrapped.WrappedContext = newCtx
|
||||
|
||||
err := handler(srv, wrapped)
|
||||
|
||||
if !o.shouldLog(info.FullMethod, err) {
|
||||
return err
|
||||
}
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
durField, durVal := o.durationFunc(time.Since(startTime))
|
||||
fields := logrus.Fields{
|
||||
"grpc.code": code.String(),
|
||||
durField: durVal,
|
||||
}
|
||||
if err != nil {
|
||||
fields[logrus.ErrorKey] = err
|
||||
}
|
||||
|
||||
levelLogf(
|
||||
ctx_logrus.Extract(newCtx).WithFields(fields), // re-extract logger from newCtx, as it may have extra fields that changed in the holder.
|
||||
level,
|
||||
"finished streaming call with code "+code.String())
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func levelLogf(entry *logrus.Entry, level logrus.Level, format string, args ...interface{}) {
|
||||
switch level {
|
||||
case logrus.DebugLevel:
|
||||
entry.Debugf(format, args...)
|
||||
case logrus.InfoLevel:
|
||||
entry.Infof(format, args...)
|
||||
case logrus.WarnLevel:
|
||||
entry.Warningf(format, args...)
|
||||
case logrus.ErrorLevel:
|
||||
entry.Errorf(format, args...)
|
||||
case logrus.FatalLevel:
|
||||
entry.Fatalf(format, args...)
|
||||
case logrus.PanicLevel:
|
||||
entry.Panicf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func newLoggerForCall(ctx context.Context, entry *logrus.Entry, fullMethodString string, start time.Time) context.Context {
|
||||
service := path.Dir(fullMethodString)[1:]
|
||||
method := path.Base(fullMethodString)
|
||||
callLog := entry.WithFields(
|
||||
logrus.Fields{
|
||||
SystemField: "grpc",
|
||||
KindField: "server",
|
||||
"grpc.service": service,
|
||||
"grpc.method": method,
|
||||
"grpc.start_time": start.Format(time.RFC3339),
|
||||
})
|
||||
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
callLog = callLog.WithFields(
|
||||
logrus.Fields{
|
||||
"grpc.request.deadline": d.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
callLog = callLog.WithFields(ctx_logrus.Extract(ctx).Data)
|
||||
return ctxlogrus.ToContext(ctx, callLog)
|
||||
}
|
||||
322
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors_test.go
generated
vendored
Normal file
322
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
package grpc_logrus_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
func TestLogrusServerSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithLevels(customCodeToLevel),
|
||||
}
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
}
|
||||
suite.Run(t, &logrusServerSuite{b})
|
||||
}
|
||||
|
||||
type logrusServerSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusServerSuite) TestPing_WithCustomTags() {
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
_, err := s.Client.Ping(s.DeadlineCtx(deadline), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)")
|
||||
assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain `custom_tags.string` with expected value")
|
||||
assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain the correct request value")
|
||||
assert.Equal(s.T(), m["custom_field"], "custom_value", "all lines must contain `custom_field` with the correct value")
|
||||
|
||||
assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int`")
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time of the call")
|
||||
_, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse start time as RFC3339")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.request.deadline", "all lines must contain the deadline of the call")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.request.deadline"].(string))
|
||||
require.NoError(s.T(), err, "should be able to parse deadline as RFC3339")
|
||||
assert.Equal(s.T(), m["grpc.request.deadline"], deadline.Format(time.RFC3339), "should have the same deadline that was set by the caller")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "first message must contain the correct user message")
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "second message must contain the correct user message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "OK codes must be logged on info level.")
|
||||
|
||||
assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time")
|
||||
}
|
||||
|
||||
func (s *logrusServerSuite) TestPingError_WithCustomLevels() {
|
||||
for _, tcase := range []struct {
|
||||
code codes.Code
|
||||
level logrus.Level
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
code: codes.Internal,
|
||||
level: logrus.ErrorLevel,
|
||||
msg: "Internal must remap to ErrorLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
level: logrus.InfoLevel,
|
||||
msg: "NotFound must remap to InfoLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.FailedPrecondition,
|
||||
level: logrus.WarnLevel,
|
||||
msg: "FailedPrecondition must remap to WarnLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.Unauthenticated,
|
||||
level: logrus.ErrorLevel,
|
||||
msg: "Unauthenticated is overwritten to ErrorLevel with customCodeToLevel override, which probably didn't work",
|
||||
},
|
||||
} {
|
||||
s.buffer.Reset()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)})
|
||||
require.Error(s.T(), err, "each call here must return an error")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
|
||||
m := msgs[0]
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["grpc.code"], tcase.code.String(), "a gRPC code must be present")
|
||||
assert.Equal(s.T(), m["level"], tcase.level.String(), tcase.msg)
|
||||
assert.Equal(s.T(), m["msg"], "finished unary call with code "+tcase.code.String(), "must have the correct finish message")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain a start time for the call")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse the start time as RFC3339")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.request.deadline", "all lines must contain the deadline of the call")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.request.deadline"].(string))
|
||||
require.NoError(s.T(), err, "should be able to parse deadline as RFC3339")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *logrusServerSuite) TestPingList_WithCustomTags() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain the correct service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain the correct method name")
|
||||
assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)")
|
||||
assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain the correct `custom_tags.string`")
|
||||
assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain the correct request value")
|
||||
|
||||
assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int`")
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time for the call")
|
||||
_, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse start time as RFC3339")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.request.deadline", "all lines must contain the deadline of the call")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.request.deadline"].(string))
|
||||
require.NoError(s.T(), err, "should be able to parse deadline as RFC3339")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "msg must be the correct message")
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "msg must be the correct message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "OK codes must be logged on info level.")
|
||||
|
||||
assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time")
|
||||
}
|
||||
|
||||
func TestLogrusServerOverrideSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDurationField(grpc_logrus.DurationToDurationField),
|
||||
}
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
}
|
||||
suite.Run(t, &logrusServerOverrideSuite{b})
|
||||
}
|
||||
|
||||
type logrusServerOverrideSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusServerOverrideSuite) TestPing_HasOverriddenDuration() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "first message must be correct")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "first message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "first message must not contain overridden duration")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "second message must be correct")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "second must be logged on info level.")
|
||||
assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "second message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.duration", "second message must contain overridden duration")
|
||||
}
|
||||
|
||||
func (s *logrusServerOverrideSuite) TestPingList_HasOverriddenDuration() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain method name")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "first message must contain user message")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "first message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "first message must not contain overridden duration")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "second message must contain correct message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "second message must be logged on info level.")
|
||||
assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "second message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.duration", "second message must contain overridden duration")
|
||||
}
|
||||
|
||||
func TestLogrusServerOverrideDeciderSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_logrus.Option{
|
||||
grpc_logrus.WithDecider(func(method string, err error) bool {
|
||||
if err != nil && method == "/mwitkow.testproto.TestService/PingError" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}),
|
||||
}
|
||||
b := newLogrusBaseSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(b.logger), opts...)),
|
||||
}
|
||||
suite.Run(t, &logrusServerOverrideDeciderSuite{b})
|
||||
}
|
||||
|
||||
type logrusServerOverrideDeciderSuite struct {
|
||||
*logrusBaseSuite
|
||||
}
|
||||
|
||||
func (s *logrusServerOverrideDeciderSuite) TestPing_HasOverriddenDecider() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "single log statements should be logged")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message")
|
||||
}
|
||||
|
||||
func (s *logrusServerOverrideDeciderSuite) TestPingError_HasOverriddenDecider() {
|
||||
code := codes.NotFound
|
||||
level := logrus.InfoLevel
|
||||
msg := "NotFound must remap to InfoLevel in DefaultCodeToLevel"
|
||||
|
||||
s.buffer.Reset()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(code)})
|
||||
require.Error(s.T(), err, "each call here must return an error")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
|
||||
m := msgs[0]
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["grpc.code"], code.String(), "all lines must correct gRPC code")
|
||||
assert.Equal(s.T(), m["level"], level.String(), msg)
|
||||
}
|
||||
|
||||
func (s *logrusServerOverrideDeciderSuite) TestPingList_HasOverriddenDecider() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "single log statements should be logged")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration")
|
||||
}
|
||||
105
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/shared_test.go
generated
vendored
Normal file
105
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/shared_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package grpc_logrus_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
goodPing = &pb_testproto.PingRequest{Value: "something", SleepTimeMs: 9999}
|
||||
)
|
||||
|
||||
type loggingPingService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
}
|
||||
|
||||
func customCodeToLevel(c codes.Code) logrus.Level {
|
||||
if c == codes.Unauthenticated {
|
||||
// Make this a special case for tests, and an error.
|
||||
return logrus.ErrorLevel
|
||||
}
|
||||
level := grpc_logrus.DefaultCodeToLevel(c)
|
||||
return level
|
||||
}
|
||||
|
||||
func (s *loggingPingService) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
ctx_logrus.AddFields(ctx, logrus.Fields{"custom_field": "custom_value"})
|
||||
ctx_logrus.Extract(ctx).Info("some ping")
|
||||
return s.TestServiceServer.Ping(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingError(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.Empty, error) {
|
||||
return s.TestServiceServer.PingError(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
|
||||
grpc_ctxtags.Extract(stream.Context()).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
ctx_logrus.AddFields(stream.Context(), logrus.Fields{"custom_field": "custom_value"})
|
||||
ctx_logrus.Extract(stream.Context()).Info("some pinglist")
|
||||
return s.TestServiceServer.PingList(ping, stream)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingEmpty(ctx context.Context, empty *pb_testproto.Empty) (*pb_testproto.PingResponse, error) {
|
||||
return s.TestServiceServer.PingEmpty(ctx, empty)
|
||||
}
|
||||
|
||||
type logrusBaseSuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
mutexBuffer *grpc_testing.MutexReadWriter
|
||||
buffer *bytes.Buffer
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func newLogrusBaseSuite(t *testing.T) *logrusBaseSuite {
|
||||
b := &bytes.Buffer{}
|
||||
muB := grpc_testing.NewMutexReadWriter(b)
|
||||
logger := logrus.New()
|
||||
logger.Out = muB
|
||||
logger.Formatter = &logrus.JSONFormatter{DisableTimestamp: true}
|
||||
return &logrusBaseSuite{
|
||||
logger: logger,
|
||||
buffer: b,
|
||||
mutexBuffer: muB,
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &loggingPingService{&grpc_testing.TestPingService{T: t}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *logrusBaseSuite) SetupTest() {
|
||||
s.mutexBuffer.Lock()
|
||||
s.buffer.Reset()
|
||||
s.mutexBuffer.Unlock()
|
||||
}
|
||||
|
||||
func (s *logrusBaseSuite) getOutputJSONs() []map[string]interface{} {
|
||||
ret := make([]map[string]interface{}, 0)
|
||||
dec := json.NewDecoder(s.mutexBuffer)
|
||||
|
||||
for {
|
||||
var val map[string]interface{}
|
||||
err := dec.Decode(&val)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
s.T().Fatalf("failed decoding output from Logrus JSON: %v", err)
|
||||
}
|
||||
|
||||
ret = append(ret, val)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
402
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/DOC.md
generated
vendored
Normal file
402
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,402 @@
|
||||
# grpc_zap
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_zap` is a gRPC logging middleware backed by ZAP loggers
|
||||
|
||||
It accepts a user-configured `zap.Logger` that will be used for logging completed gRPC calls. The same `zap.Logger` will
|
||||
be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information
|
||||
to the ctx so that it will be present on subsequent use of the `ctx_zap` logger.
|
||||
|
||||
If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline
|
||||
is a string representing the time (RFC3339) when the current call will expire.
|
||||
|
||||
This package also implements request and response *payload* logging, both for server-side and client-side. These will be
|
||||
logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use
|
||||
`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log
|
||||
the full request/response payload needs to be written with care, this can significantly slow down gRPC.
|
||||
|
||||
ZAP can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`.
|
||||
|
||||
*Server Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the server interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string zap log levels
|
||||
"msg": "finished unary call", // string log message
|
||||
|
||||
"grpc.code": "OK", // string grpc status code
|
||||
"grpc.method": "Ping", // string method name
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
|
||||
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
|
||||
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
|
||||
"grpc.request.value": "something", // string value on the request
|
||||
"grpc.time_ms": 1.345, // float32 run time of the call in ms
|
||||
|
||||
"peer.address": {
|
||||
"IP": "127.0.0.1", // string IP address of calling party
|
||||
"Port": 60216, // int port call is coming in on
|
||||
"Zone": "" // string peer zone for caller
|
||||
},
|
||||
"span.kind": "server", // string client | server
|
||||
"system": "grpc" // string
|
||||
|
||||
"custom_field": "custom_value", // string user defined field
|
||||
"custom_tags.int": 1337, // int user defined tag on the ctx
|
||||
"custom_tags.string": "something", // string user defined tag on the ctx
|
||||
}
|
||||
|
||||
*Payload Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the payload interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string zap log levels
|
||||
"msg": "client request payload logged as grpc.request.content", // string log message
|
||||
|
||||
"grpc.request.content": { // object content of RPC request
|
||||
"msg" : { // object ZAP specific inner object
|
||||
"value": "something", // string defined by caller
|
||||
"sleepTimeMs": 9999 // int defined by caller
|
||||
}
|
||||
},
|
||||
"grpc.method": "Ping", // string method being called
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string service being called
|
||||
|
||||
"span.kind": "client", // string client | server
|
||||
"system": "grpc" // string
|
||||
}
|
||||
|
||||
Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object.
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithLevels(customFunc),
|
||||
}
|
||||
// Make sure that log statements internal to gRPC library are logged using the zapLogger as well.
|
||||
grpc_zap.ReplaceGrpcLogger(zapLogger)
|
||||
// Create a server, make sure we put the grpc_ctxtags context before everything else.
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDecider(func(fullMethodName string, err error) bool {
|
||||
// will not log gRPC calls if it was a call to healthcheck and no error was raised
|
||||
if err == nil && fullMethodName == "foo.bar.healthcheck" {
|
||||
return false
|
||||
}
|
||||
|
||||
// by default everything will be logged
|
||||
return true
|
||||
}),
|
||||
}
|
||||
|
||||
_ = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(zap.NewNop(), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(zap.NewNop(), opts...)),
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDurationField(func(duration time.Duration) zapcore.Field {
|
||||
return zap.Int64("grpc.time_ns", duration.Nanoseconds())
|
||||
}),
|
||||
}
|
||||
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/golang/protobuf/jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb)
|
||||
- [github.com/golang/protobuf/proto](https://godoc.org/github.com/golang/protobuf/proto)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware](./../..)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/logging](./..)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap](./ctxzap)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags/zap](./../../tags/zap)
|
||||
- [go.uber.org/zap](https://godoc.org/go.uber.org/zap)
|
||||
- [go.uber.org/zap/zapcore](https://godoc.org/go.uber.org/zap/zapcore)
|
||||
- [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/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
- [google.golang.org/grpc/grpclog](https://godoc.org/google.golang.org/grpc/grpclog)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [Variables](#pkg-variables)
|
||||
* [func AddFields(ctx context.Context, fields ...zapcore.Field)](#AddFields)
|
||||
* [func DefaultClientCodeToLevel(code codes.Code) zapcore.Level](#DefaultClientCodeToLevel)
|
||||
* [func DefaultCodeToLevel(code codes.Code) zapcore.Level](#DefaultCodeToLevel)
|
||||
* [func DurationToDurationField(duration time.Duration) zapcore.Field](#DurationToDurationField)
|
||||
* [func DurationToTimeMillisField(duration time.Duration) zapcore.Field](#DurationToTimeMillisField)
|
||||
* [func Extract(ctx context.Context) \*zap.Logger](#Extract)
|
||||
* [func PayloadStreamClientInterceptor(logger \*zap.Logger, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor](#PayloadStreamClientInterceptor)
|
||||
* [func PayloadStreamServerInterceptor(logger \*zap.Logger, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor](#PayloadStreamServerInterceptor)
|
||||
* [func PayloadUnaryClientInterceptor(logger \*zap.Logger, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor](#PayloadUnaryClientInterceptor)
|
||||
* [func PayloadUnaryServerInterceptor(logger \*zap.Logger, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor](#PayloadUnaryServerInterceptor)
|
||||
* [func ReplaceGrpcLogger(logger \*zap.Logger)](#ReplaceGrpcLogger)
|
||||
* [func StreamClientInterceptor(logger \*zap.Logger, opts ...Option) grpc.StreamClientInterceptor](#StreamClientInterceptor)
|
||||
* [func StreamServerInterceptor(logger \*zap.Logger, opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor)
|
||||
* [func UnaryClientInterceptor(logger \*zap.Logger, opts ...Option) grpc.UnaryClientInterceptor](#UnaryClientInterceptor)
|
||||
* [func UnaryServerInterceptor(logger \*zap.Logger, opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor)
|
||||
* [type CodeToLevel](#CodeToLevel)
|
||||
* [type DurationToField](#DurationToField)
|
||||
* [type Option](#Option)
|
||||
* [func WithCodes(f grpc\_logging.ErrorToCode) Option](#WithCodes)
|
||||
* [func WithDecider(f grpc\_logging.Decider) Option](#WithDecider)
|
||||
* [func WithDurationField(f DurationToField) Option](#WithDurationField)
|
||||
* [func WithLevels(f CodeToLevel) Option](#WithLevels)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Extract (Unary)](#example_Extract_unary)
|
||||
* [Package (Initialization)](#example__initialization)
|
||||
* [Package (InitializationWithDecider)](#example__initializationWithDecider)
|
||||
* [Package (InitializationWithDurationFieldOverride)](#example__initializationWithDurationFieldOverride)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[client_interceptors.go](./client_interceptors.go) [context.go](./context.go) [doc.go](./doc.go) [grpclogger.go](./grpclogger.go) [options.go](./options.go) [payload_interceptors.go](./payload_interceptors.go) [server_interceptors.go](./server_interceptors.go)
|
||||
|
||||
## <a name="pkg-variables">Variables</a>
|
||||
``` go
|
||||
var (
|
||||
// SystemField is used in every log statement made through grpc_zap. Can be overwritten before any initialization code.
|
||||
SystemField = zap.String("system", "grpc")
|
||||
|
||||
// ServerField is used in every server-side log statement made through grpc_zap.Can be overwritten before initialization.
|
||||
ServerField = zap.String("span.kind", "server")
|
||||
)
|
||||
```
|
||||
``` go
|
||||
var (
|
||||
// ClientField is used in every client-side log statement made through grpc_zap. Can be overwritten before initialization.
|
||||
ClientField = zap.String("span.kind", "client")
|
||||
)
|
||||
```
|
||||
``` go
|
||||
var DefaultDurationToField = DurationToTimeMillisField
|
||||
```
|
||||
DefaultDurationToField is the default implementation of converting request duration to a Zap field.
|
||||
|
||||
``` go
|
||||
var (
|
||||
// JsonPbMarshaller is the marshaller used for serializing protobuf messages.
|
||||
JsonPbMarshaller = &jsonpb.Marshaler{}
|
||||
)
|
||||
```
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L12)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields ...zapcore.Field)
|
||||
```
|
||||
AddFields adds zap fields to the logger.
|
||||
Deprecated: should use the ctxzap.AddFields instead
|
||||
|
||||
## <a name="DefaultClientCodeToLevel">func</a> [DefaultClientCodeToLevel](./options.go#L127)
|
||||
``` go
|
||||
func DefaultClientCodeToLevel(code codes.Code) zapcore.Level
|
||||
```
|
||||
DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
|
||||
|
||||
## <a name="DefaultCodeToLevel">func</a> [DefaultCodeToLevel](./options.go#L85)
|
||||
``` go
|
||||
func DefaultCodeToLevel(code codes.Code) zapcore.Level
|
||||
```
|
||||
DefaultCodeToLevel is the default implementation of gRPC return codes and interceptor log level for server side.
|
||||
|
||||
## <a name="DurationToDurationField">func</a> [DurationToDurationField](./options.go#L178)
|
||||
``` go
|
||||
func DurationToDurationField(duration time.Duration) zapcore.Field
|
||||
```
|
||||
DurationToDurationField uses a Duration field to log the request duration
|
||||
and leaves it up to Zap's encoder settings to determine how that is output.
|
||||
|
||||
## <a name="DurationToTimeMillisField">func</a> [DurationToTimeMillisField](./options.go#L172)
|
||||
``` go
|
||||
func DurationToTimeMillisField(duration time.Duration) zapcore.Field
|
||||
```
|
||||
DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L18)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *zap.Logger
|
||||
```
|
||||
Extract takes the call-scoped Logger from grpc_zap middleware.
|
||||
Deprecated: should use the ctxzap.Extract instead
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
|
||||
// Extract a single request-scoped zap.Logger and log messages. (containing the grpc.xxx tags)
|
||||
l := ctx_zap.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="PayloadStreamClientInterceptor">func</a> [PayloadStreamClientInterceptor](./payload_interceptors.go#L74)
|
||||
``` go
|
||||
func PayloadStreamClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor
|
||||
```
|
||||
PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses.
|
||||
|
||||
## <a name="PayloadStreamServerInterceptor">func</a> [PayloadStreamServerInterceptor](./payload_interceptors.go#L46)
|
||||
``` go
|
||||
func PayloadStreamServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor
|
||||
```
|
||||
PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
|
||||
|
||||
This *only* works when placed *after* the `grpc_zap.StreamServerInterceptor`. However, the logging can be done to a
|
||||
separate instance of the logger.
|
||||
|
||||
## <a name="PayloadUnaryClientInterceptor">func</a> [PayloadUnaryClientInterceptor](./payload_interceptors.go#L58)
|
||||
``` go
|
||||
func PayloadUnaryClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor
|
||||
```
|
||||
PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses.
|
||||
|
||||
## <a name="PayloadUnaryServerInterceptor">func</a> [PayloadUnaryServerInterceptor](./payload_interceptors.go#L26)
|
||||
``` go
|
||||
func PayloadUnaryServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor
|
||||
```
|
||||
PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
|
||||
|
||||
This *only* works when placed *after* the `grpc_zap.UnaryServerInterceptor`. However, the logging can be done to a
|
||||
separate instance of the logger.
|
||||
|
||||
## <a name="ReplaceGrpcLogger">func</a> [ReplaceGrpcLogger](./grpclogger.go#L15)
|
||||
``` go
|
||||
func ReplaceGrpcLogger(logger *zap.Logger)
|
||||
```
|
||||
ReplaceGrpcLogger sets the given zap.Logger as a gRPC-level logger.
|
||||
This should be called *before* any other initialization, preferably from init() functions.
|
||||
|
||||
## <a name="StreamClientInterceptor">func</a> [StreamClientInterceptor](./client_interceptors.go#L34)
|
||||
``` go
|
||||
func StreamClientInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamClientInterceptor
|
||||
```
|
||||
StreamClientInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
|
||||
|
||||
## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./server_interceptors.go#L51)
|
||||
``` go
|
||||
func StreamServerInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamServerInterceptor
|
||||
```
|
||||
StreamServerInterceptor returns a new streaming server interceptor that adds zap.Logger to the context.
|
||||
|
||||
## <a name="UnaryClientInterceptor">func</a> [UnaryClientInterceptor](./client_interceptors.go#L22)
|
||||
``` go
|
||||
func UnaryClientInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryClientInterceptor
|
||||
```
|
||||
UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
|
||||
|
||||
## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./server_interceptors.go#L25)
|
||||
``` go
|
||||
func UnaryServerInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryServerInterceptor
|
||||
```
|
||||
UnaryServerInterceptor returns a new unary server interceptors that adds zap.Logger to the context.
|
||||
|
||||
## <a name="CodeToLevel">type</a> [CodeToLevel](./options.go#L51)
|
||||
``` go
|
||||
type CodeToLevel func(code codes.Code) zapcore.Level
|
||||
```
|
||||
CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
|
||||
|
||||
## <a name="DurationToField">type</a> [DurationToField](./options.go#L54)
|
||||
``` go
|
||||
type DurationToField func(duration time.Duration) zapcore.Field
|
||||
```
|
||||
DurationToField function defines how to produce duration fields for logging
|
||||
|
||||
## <a name="Option">type</a> [Option](./options.go#L48)
|
||||
``` go
|
||||
type Option func(*options)
|
||||
```
|
||||
|
||||
### <a name="WithCodes">func</a> [WithCodes](./options.go#L71)
|
||||
``` go
|
||||
func WithCodes(f grpc_logging.ErrorToCode) Option
|
||||
```
|
||||
WithCodes customizes the function for mapping errors to error codes.
|
||||
|
||||
### <a name="WithDecider">func</a> [WithDecider](./options.go#L57)
|
||||
``` go
|
||||
func WithDecider(f grpc_logging.Decider) Option
|
||||
```
|
||||
WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
|
||||
|
||||
### <a name="WithDurationField">func</a> [WithDurationField](./options.go#L78)
|
||||
``` go
|
||||
func WithDurationField(f DurationToField) Option
|
||||
```
|
||||
WithDurationField customizes the function for mapping request durations to Zap fields.
|
||||
|
||||
### <a name="WithLevels">func</a> [WithLevels](./options.go#L64)
|
||||
``` go
|
||||
func WithLevels(f CodeToLevel) Option
|
||||
```
|
||||
WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
64
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/client_interceptors.go
generated
vendored
Normal file
64
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/client_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// ClientField is used in every client-side log statement made through grpc_zap. Can be overwritten before initialization.
|
||||
ClientField = zap.String("span.kind", "client")
|
||||
)
|
||||
|
||||
// UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
|
||||
func UnaryClientInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryClientInterceptor {
|
||||
o := evaluateClientOpt(opts)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
fields := newClientLoggerFields(ctx, method)
|
||||
startTime := time.Now()
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
logFinalClientLine(o, logger.With(fields...), startTime, err, "finished client unary call")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// StreamClientInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
|
||||
func StreamClientInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamClientInterceptor {
|
||||
o := evaluateClientOpt(opts)
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
fields := newClientLoggerFields(ctx, method)
|
||||
startTime := time.Now()
|
||||
clientStream, err := streamer(ctx, desc, cc, method, opts...)
|
||||
logFinalClientLine(o, logger.With(fields...), startTime, err, "finished client streaming call")
|
||||
return clientStream, err
|
||||
}
|
||||
}
|
||||
|
||||
func logFinalClientLine(o *options, logger *zap.Logger, startTime time.Time, err error, msg string) {
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
logger.Check(level, msg).Write(
|
||||
zap.Error(err),
|
||||
zap.String("grpc.code", code.String()),
|
||||
o.durationFunc(time.Now().Sub(startTime)),
|
||||
)
|
||||
}
|
||||
|
||||
func newClientLoggerFields(ctx context.Context, fullMethodString string) []zapcore.Field {
|
||||
service := path.Dir(fullMethodString)[1:]
|
||||
method := path.Base(fullMethodString)
|
||||
return []zapcore.Field{
|
||||
SystemField,
|
||||
ClientField,
|
||||
zap.String("grpc.service", service),
|
||||
zap.String("grpc.method", method),
|
||||
}
|
||||
}
|
||||
184
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/client_interceptors_test.go
generated
vendored
Normal file
184
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/client_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package grpc_zap_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func customClientCodeToLevel(c codes.Code) zapcore.Level {
|
||||
if c == codes.Unauthenticated {
|
||||
// Make this a special case for tests, and an error.
|
||||
return zapcore.ErrorLevel
|
||||
}
|
||||
level := grpc_zap.DefaultClientCodeToLevel(c)
|
||||
return level
|
||||
}
|
||||
|
||||
func TestZapClientSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithLevels(customClientCodeToLevel),
|
||||
}
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_zap.UnaryClientInterceptor(b.log, opts...)),
|
||||
grpc.WithStreamInterceptor(grpc_zap.StreamClientInterceptor(b.log, opts...)),
|
||||
}
|
||||
suite.Run(t, &zapClientSuite{b})
|
||||
}
|
||||
|
||||
type zapClientSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapClientSuite) TestPing() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "must contain correct message")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "must be logged on debug level.")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.time_ms", "interceptor log statement should contain execution time")
|
||||
}
|
||||
|
||||
func (s *zapClientSuite) TestPingList() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "OK codes must be logged on debug level.")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.time_ms", "handler's message must contain time in ms")
|
||||
}
|
||||
|
||||
func (s *zapClientSuite) TestPingError_WithCustomLevels() {
|
||||
for _, tcase := range []struct {
|
||||
code codes.Code
|
||||
level zapcore.Level
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
code: codes.Internal,
|
||||
level: zapcore.WarnLevel,
|
||||
msg: "Internal must remap to ErrorLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
level: zapcore.DebugLevel,
|
||||
msg: "NotFound must remap to InfoLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.FailedPrecondition,
|
||||
level: zapcore.DebugLevel,
|
||||
msg: "FailedPrecondition must remap to WarnLevel in DefaultClientCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.Unauthenticated,
|
||||
level: zapcore.ErrorLevel,
|
||||
msg: "Unauthenticated is overwritten to ErrorLevel with customClientCodeToLevel override, which probably didn't work",
|
||||
},
|
||||
} {
|
||||
s.SetupTest()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)})
|
||||
require.Error(s.T(), err, "each call here must return an error")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingError", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.code"], tcase.code.String(), "all lines must contain the correct gRPC code")
|
||||
assert.Equal(s.T(), msgs[0]["level"], tcase.level.String(), tcase.msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZapClientOverrideSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDurationField(grpc_zap.DurationToDurationField),
|
||||
}
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_zap.UnaryClientInterceptor(b.log, opts...)),
|
||||
grpc.WithStreamInterceptor(grpc_zap.StreamClientInterceptor(b.log, opts...)),
|
||||
}
|
||||
suite.Run(t, &zapClientOverrideSuite{b})
|
||||
}
|
||||
|
||||
type zapClientOverrideSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapClientOverrideSuite) TestPing_HasOverrides() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "handler's message must contain user message")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.duration", "handler's message must contain overridden duration")
|
||||
}
|
||||
|
||||
func (s *zapClientOverrideSuite) TestPingList_HasOverrides() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "one log statement should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
|
||||
assert.Equal(s.T(), msgs[0]["level"], "debug", "must be logged on debug level.")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[0], "grpc.duration", "handler's message must contain overridden duration")
|
||||
}
|
||||
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/context.go
generated
vendored
Normal file
20
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/context.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package grpc_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)
|
||||
}
|
||||
105
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/DOC.md
generated
vendored
Normal file
105
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
# ctxzap
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`ctxzap` is a ctxlogger that is backed by Zap
|
||||
|
||||
It accepts a user-configured `zap.Logger` that will be used for logging. The same `zap.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctxzap.Extract` to log into a request-scoped `zap.Logger` instance in your handler code.
|
||||
|
||||
As `ctxzap.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./../../../tags)
|
||||
- [go.uber.org/zap](https://godoc.org/go.uber.org/zap)
|
||||
- [go.uber.org/zap/zapcore](https://godoc.org/go.uber.org/zap/zapcore)
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func AddFields(ctx context.Context, fields ...zapcore.Field)](#AddFields)
|
||||
* [func Extract(ctx context.Context) \*zap.Logger](#Extract)
|
||||
* [func TagsToFields(ctx context.Context) []zapcore.Field](#TagsToFields)
|
||||
* [func ToContext(ctx context.Context, logger \*zap.Logger) context.Context](#ToContext)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Extract (Unary)](#example_Extract_unary)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[context.go](./context.go) [doc.go](./doc.go)
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L23)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields ...zapcore.Field)
|
||||
```
|
||||
AddFields adds zap fields to the logger.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L35)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *zap.Logger
|
||||
```
|
||||
Extract takes the call-scoped Logger from grpc_zap middleware.
|
||||
|
||||
It always returns a Logger that has all the grpc_ctxtags updated.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
package ctxzap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var zapLogger *zap.Logger
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
|
||||
// Extract a single request-scoped zap.Logger and log messages.
|
||||
l := ctxzap.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="TagsToFields">func</a> [TagsToFields](./context.go#L48)
|
||||
``` go
|
||||
func TagsToFields(ctx context.Context) []zapcore.Field
|
||||
```
|
||||
TagsToFields transforms the Tags on the supplied context into zap fields.
|
||||
|
||||
## <a name="ToContext">func</a> [ToContext](./context.go#L59)
|
||||
``` go
|
||||
func ToContext(ctx context.Context, logger *zap.Logger) context.Context
|
||||
```
|
||||
ToContext adds the zap.Logger to the context for extraction later.
|
||||
Returning the new context that has been created.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/README.md
generated
vendored
Normal file
65
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/README.md
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# ctx_zap
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/tags/zap"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`ctx_zap` is a ctxlogger that is backed by Zap
|
||||
|
||||
It accepts a user-configured `zap.Logger` that will be used for logging. The same `zap.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctx_zap.Extract` to log into a request-scoped `zap.Logger` instance in your handler code.
|
||||
|
||||
As `ctx_zap.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./..)
|
||||
- [go.uber.org/zap](https://godoc.org/go.uber.org/zap)
|
||||
- [go.uber.org/zap/zapcore](https://godoc.org/go.uber.org/zap/zapcore)
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func AddFields(ctx context.Context, fields ...zapcore.Field)](#AddFields)
|
||||
* [func Extract(ctx context.Context) \*zap.Logger](#Extract)
|
||||
* [func TagsToFields(ctx context.Context) []zapcore.Field](#TagsToFields)
|
||||
* [func ToContext(ctx context.Context, logger \*zap.Logger) context.Context](#ToContext)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[context.go](./context.go) [doc.go](./doc.go)
|
||||
|
||||
## <a name="AddFields">func</a> [AddFields](./context.go#L23)
|
||||
``` go
|
||||
func AddFields(ctx context.Context, fields ...zapcore.Field)
|
||||
```
|
||||
AddFields adds zap fields to the logger.
|
||||
|
||||
## <a name="Extract">func</a> [Extract](./context.go#L35)
|
||||
``` go
|
||||
func Extract(ctx context.Context) *zap.Logger
|
||||
```
|
||||
Extract takes the call-scoped Logger from grpc_zap middleware.
|
||||
|
||||
It always returns a Logger that has all the grpc_ctxtags updated.
|
||||
|
||||
## <a name="TagsToFields">func</a> [TagsToFields](./context.go#L48)
|
||||
``` go
|
||||
func TagsToFields(ctx context.Context) []zapcore.Field
|
||||
```
|
||||
TagsToFields transforms the Tags on the supplied context into zap fields.
|
||||
|
||||
## <a name="ToContext">func</a> [ToContext](./context.go#L59)
|
||||
``` go
|
||||
func ToContext(ctx context.Context, logger *zap.Logger) context.Context
|
||||
```
|
||||
ToContext adds the zap.Logger to the context for extraction later.
|
||||
Returning the new context that has been created.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
64
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/context.go
generated
vendored
Normal file
64
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/context.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package ctxzap
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type ctxMarker struct{}
|
||||
|
||||
type ctxLogger struct {
|
||||
logger *zap.Logger
|
||||
fields []zapcore.Field
|
||||
}
|
||||
|
||||
var (
|
||||
ctxMarkerKey = &ctxMarker{}
|
||||
nullLogger = zap.NewNop()
|
||||
)
|
||||
|
||||
// AddFields adds zap fields to the logger.
|
||||
func AddFields(ctx context.Context, fields ...zapcore.Field) {
|
||||
l, ok := ctx.Value(ctxMarkerKey).(*ctxLogger)
|
||||
if !ok || l == nil {
|
||||
return
|
||||
}
|
||||
l.fields = append(l.fields, fields...)
|
||||
|
||||
}
|
||||
|
||||
// Extract takes the call-scoped Logger from grpc_zap middleware.
|
||||
//
|
||||
// It always returns a Logger that has all the grpc_ctxtags updated.
|
||||
func Extract(ctx context.Context) *zap.Logger {
|
||||
l, ok := ctx.Value(ctxMarkerKey).(*ctxLogger)
|
||||
if !ok || l == nil {
|
||||
return nullLogger
|
||||
}
|
||||
// Add grpc_ctxtags tags metadata until now.
|
||||
fields := TagsToFields(ctx)
|
||||
// Add zap fields added until now.
|
||||
fields = append(fields, l.fields...)
|
||||
return l.logger.With(fields...)
|
||||
}
|
||||
|
||||
// TagsToFields transforms the Tags on the supplied context into zap fields.
|
||||
func TagsToFields(ctx context.Context) []zapcore.Field {
|
||||
fields := []zapcore.Field{}
|
||||
tags := grpc_ctxtags.Extract(ctx)
|
||||
for k, v := range tags.Values() {
|
||||
fields = append(fields, zap.Any(k, v))
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// ToContext adds the zap.Logger to the context for extraction later.
|
||||
// Returning the new context that has been created.
|
||||
func ToContext(ctx context.Context, logger *zap.Logger) context.Context {
|
||||
l := &ctxLogger{
|
||||
logger: logger,
|
||||
}
|
||||
return context.WithValue(ctx, ctxMarkerKey, l)
|
||||
}
|
||||
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/doc.go
generated
vendored
Normal file
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/doc.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
`ctxzap` is a ctxlogger that is backed by Zap
|
||||
|
||||
It accepts a user-configured `zap.Logger` that will be used for logging. The same `zap.Logger` will
|
||||
be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
You can use `ctxzap.Extract` to log into a request-scoped `zap.Logger` instance in your handler code.
|
||||
|
||||
As `ctxzap.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you
|
||||
extract once at the start of the function from the context and reuse it for the remainder of the function (see examples).
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
*/
|
||||
package ctxzap
|
||||
26
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/examples_test.go
generated
vendored
Normal file
26
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package ctxzap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var zapLogger *zap.Logger
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
|
||||
// Extract a single request-scoped zap.Logger and log messages.
|
||||
l := ctxzap.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/doc.go
generated
vendored
Normal file
74
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/doc.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
`grpc_zap` is a gRPC logging middleware backed by ZAP loggers
|
||||
|
||||
It accepts a user-configured `zap.Logger` that will be used for logging completed gRPC calls. The same `zap.Logger` will
|
||||
be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code.
|
||||
|
||||
On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information
|
||||
to the ctx so that it will be present on subsequent use of the `ctx_zap` logger.
|
||||
|
||||
If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline
|
||||
is a string representing the time (RFC3339) when the current call will expire.
|
||||
|
||||
This package also implements request and response *payload* logging, both for server-side and client-side. These will be
|
||||
logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use
|
||||
`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log
|
||||
the full request/response payload needs to be written with care, this can significantly slow down gRPC.
|
||||
|
||||
ZAP can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`.
|
||||
|
||||
|
||||
*Server Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the server interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string zap log levels
|
||||
"msg": "finished unary call", // string log message
|
||||
|
||||
"grpc.code": "OK", // string grpc status code
|
||||
"grpc.method": "Ping", // string method name
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
|
||||
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
|
||||
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
|
||||
"grpc.request.value": "something", // string value on the request
|
||||
"grpc.time_ms": 1.345, // float32 run time of the call in ms
|
||||
|
||||
"peer.address": {
|
||||
"IP": "127.0.0.1", // string IP address of calling party
|
||||
"Port": 60216, // int port call is coming in on
|
||||
"Zone": "" // string peer zone for caller
|
||||
},
|
||||
"span.kind": "server", // string client | server
|
||||
"system": "grpc" // string
|
||||
|
||||
"custom_field": "custom_value", // string user defined field
|
||||
"custom_tags.int": 1337, // int user defined tag on the ctx
|
||||
"custom_tags.string": "something", // string user defined tag on the ctx
|
||||
}
|
||||
|
||||
*Payload Interceptor*
|
||||
Below is a JSON formatted example of a log that would be logged by the payload interceptor:
|
||||
|
||||
{
|
||||
"level": "info", // string zap log levels
|
||||
"msg": "client request payload logged as grpc.request.content", // string log message
|
||||
|
||||
"grpc.request.content": { // object content of RPC request
|
||||
"msg" : { // object ZAP specific inner object
|
||||
"value": "something", // string defined by caller
|
||||
"sleepTimeMs": 9999 // int defined by caller
|
||||
}
|
||||
},
|
||||
"grpc.method": "Ping", // string method being called
|
||||
"grpc.service": "mwitkow.testproto.TestService", // string service being called
|
||||
|
||||
"span.kind": "client", // string client | server
|
||||
"system": "grpc" // string
|
||||
}
|
||||
|
||||
Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object.
|
||||
|
||||
|
||||
Please see examples and tests for examples of use.
|
||||
*/
|
||||
package grpc_zap
|
||||
98
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/examples_test.go
generated
vendored
Normal file
98
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package grpc_zap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/zap"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
zapLogger *zap.Logger
|
||||
customFunc grpc_zap.CodeToLevel
|
||||
)
|
||||
|
||||
// Initialization shows a relatively complex initialization sequence.
|
||||
func Example_initialization() {
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithLevels(customFunc),
|
||||
}
|
||||
// Make sure that log statements internal to gRPC library are logged using the zapLogger as well.
|
||||
grpc_zap.ReplaceGrpcLogger(zapLogger)
|
||||
// Create a server, make sure we put the grpc_ctxtags context before everything else.
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Initialization shows an initialization sequence with the duration field generation overridden.
|
||||
func Example_initializationWithDurationFieldOverride() {
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDurationField(func(duration time.Duration) zapcore.Field {
|
||||
return zap.Int64("grpc.time_ns", duration.Nanoseconds())
|
||||
}),
|
||||
}
|
||||
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements.
|
||||
func ExampleExtract_unary() {
|
||||
_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
// Add fields the ctxtags of the request which will be added to all extracted loggers.
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
|
||||
// Extract a single request-scoped zap.Logger and log messages. (containing the grpc.xxx tags)
|
||||
l := ctx_zap.Extract(ctx)
|
||||
l.Info("some ping")
|
||||
l.Info("another ping")
|
||||
return &pb_testproto.PingResponse{Value: ping.Value}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Example_initializationWithDecider() {
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDecider(func(fullMethodName string, err error) bool {
|
||||
// will not log gRPC calls if it was a call to healthcheck and no error was raised
|
||||
if err == nil && fullMethodName == "foo.bar.healthcheck" {
|
||||
return false
|
||||
}
|
||||
|
||||
// by default everything will be logged
|
||||
return true
|
||||
}),
|
||||
}
|
||||
|
||||
_ = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(zap.NewNop(), opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(zap.NewNop(), opts...)),
|
||||
}
|
||||
}
|
||||
46
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/grpclogger.go
generated
vendored
Normal file
46
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/grpclogger.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
// ReplaceGrpcLogger sets the given zap.Logger as a gRPC-level logger.
|
||||
// This should be called *before* any other initialization, preferably from init() functions.
|
||||
func ReplaceGrpcLogger(logger *zap.Logger) {
|
||||
zgl := &zapGrpcLogger{logger.With(SystemField, zap.Bool("grpc_log", true))}
|
||||
grpclog.SetLogger(zgl)
|
||||
}
|
||||
|
||||
type zapGrpcLogger struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Fatal(args ...interface{}) {
|
||||
l.logger.Fatal(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Fatalf(format string, args ...interface{}) {
|
||||
l.logger.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Fatalln(args ...interface{}) {
|
||||
l.logger.Fatal(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Print(args ...interface{}) {
|
||||
l.logger.Info(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Printf(format string, args ...interface{}) {
|
||||
l.logger.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (l *zapGrpcLogger) Println(args ...interface{}) {
|
||||
l.logger.Info(fmt.Sprint(args...))
|
||||
}
|
||||
184
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/options.go
generated
vendored
Normal file
184
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/options.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOptions = &options{
|
||||
levelFunc: DefaultCodeToLevel,
|
||||
shouldLog: grpc_logging.DefaultDeciderMethod,
|
||||
codeFunc: grpc_logging.DefaultErrorToCode,
|
||||
durationFunc: DefaultDurationToField,
|
||||
}
|
||||
)
|
||||
|
||||
type options struct {
|
||||
levelFunc CodeToLevel
|
||||
shouldLog grpc_logging.Decider
|
||||
codeFunc grpc_logging.ErrorToCode
|
||||
durationFunc DurationToField
|
||||
}
|
||||
|
||||
func evaluateServerOpt(opts []Option) *options {
|
||||
optCopy := &options{}
|
||||
*optCopy = *defaultOptions
|
||||
optCopy.levelFunc = DefaultCodeToLevel
|
||||
for _, o := range opts {
|
||||
o(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
func evaluateClientOpt(opts []Option) *options {
|
||||
optCopy := &options{}
|
||||
*optCopy = *defaultOptions
|
||||
optCopy.levelFunc = DefaultClientCodeToLevel
|
||||
for _, o := range opts {
|
||||
o(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
// CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
|
||||
type CodeToLevel func(code codes.Code) zapcore.Level
|
||||
|
||||
// DurationToField function defines how to produce duration fields for logging
|
||||
type DurationToField func(duration time.Duration) zapcore.Field
|
||||
|
||||
// WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
|
||||
func WithDecider(f grpc_logging.Decider) Option {
|
||||
return func(o *options) {
|
||||
o.shouldLog = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
|
||||
func WithLevels(f CodeToLevel) Option {
|
||||
return func(o *options) {
|
||||
o.levelFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithCodes customizes the function for mapping errors to error codes.
|
||||
func WithCodes(f grpc_logging.ErrorToCode) Option {
|
||||
return func(o *options) {
|
||||
o.codeFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithDurationField customizes the function for mapping request durations to Zap fields.
|
||||
func WithDurationField(f DurationToField) Option {
|
||||
return func(o *options) {
|
||||
o.durationFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultCodeToLevel is the default implementation of gRPC return codes and interceptor log level for server side.
|
||||
func DefaultCodeToLevel(code codes.Code) zapcore.Level {
|
||||
switch code {
|
||||
case codes.OK:
|
||||
return zap.InfoLevel
|
||||
case codes.Canceled:
|
||||
return zap.InfoLevel
|
||||
case codes.Unknown:
|
||||
return zap.ErrorLevel
|
||||
case codes.InvalidArgument:
|
||||
return zap.InfoLevel
|
||||
case codes.DeadlineExceeded:
|
||||
return zap.WarnLevel
|
||||
case codes.NotFound:
|
||||
return zap.InfoLevel
|
||||
case codes.AlreadyExists:
|
||||
return zap.InfoLevel
|
||||
case codes.PermissionDenied:
|
||||
return zap.WarnLevel
|
||||
case codes.Unauthenticated:
|
||||
return zap.InfoLevel // unauthenticated requests can happen
|
||||
case codes.ResourceExhausted:
|
||||
return zap.WarnLevel
|
||||
case codes.FailedPrecondition:
|
||||
return zap.WarnLevel
|
||||
case codes.Aborted:
|
||||
return zap.WarnLevel
|
||||
case codes.OutOfRange:
|
||||
return zap.WarnLevel
|
||||
case codes.Unimplemented:
|
||||
return zap.ErrorLevel
|
||||
case codes.Internal:
|
||||
return zap.ErrorLevel
|
||||
case codes.Unavailable:
|
||||
return zap.WarnLevel
|
||||
case codes.DataLoss:
|
||||
return zap.ErrorLevel
|
||||
default:
|
||||
return zap.ErrorLevel
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
|
||||
func DefaultClientCodeToLevel(code codes.Code) zapcore.Level {
|
||||
switch code {
|
||||
case codes.OK:
|
||||
return zap.DebugLevel
|
||||
case codes.Canceled:
|
||||
return zap.DebugLevel
|
||||
case codes.Unknown:
|
||||
return zap.InfoLevel
|
||||
case codes.InvalidArgument:
|
||||
return zap.DebugLevel
|
||||
case codes.DeadlineExceeded:
|
||||
return zap.InfoLevel
|
||||
case codes.NotFound:
|
||||
return zap.DebugLevel
|
||||
case codes.AlreadyExists:
|
||||
return zap.DebugLevel
|
||||
case codes.PermissionDenied:
|
||||
return zap.InfoLevel
|
||||
case codes.Unauthenticated:
|
||||
return zap.InfoLevel // unauthenticated requests can happen
|
||||
case codes.ResourceExhausted:
|
||||
return zap.DebugLevel
|
||||
case codes.FailedPrecondition:
|
||||
return zap.DebugLevel
|
||||
case codes.Aborted:
|
||||
return zap.DebugLevel
|
||||
case codes.OutOfRange:
|
||||
return zap.DebugLevel
|
||||
case codes.Unimplemented:
|
||||
return zap.WarnLevel
|
||||
case codes.Internal:
|
||||
return zap.WarnLevel
|
||||
case codes.Unavailable:
|
||||
return zap.WarnLevel
|
||||
case codes.DataLoss:
|
||||
return zap.WarnLevel
|
||||
default:
|
||||
return zap.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultDurationToField is the default implementation of converting request duration to a Zap field.
|
||||
var DefaultDurationToField = DurationToTimeMillisField
|
||||
|
||||
// DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
|
||||
func DurationToTimeMillisField(duration time.Duration) zapcore.Field {
|
||||
return zap.Float32("grpc.time_ms", durationToMilliseconds(duration))
|
||||
}
|
||||
|
||||
// DurationToDurationField uses a Duration field to log the request duration
|
||||
// and leaves it up to Zap's encoder settings to determine how that is output.
|
||||
func DurationToDurationField(duration time.Duration) zapcore.Field {
|
||||
return zap.Duration("grpc.duration", duration)
|
||||
}
|
||||
|
||||
func durationToMilliseconds(duration time.Duration) float32 {
|
||||
return float32(duration.Nanoseconds()/1000) / 1000
|
||||
}
|
||||
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/options_test.go
generated
vendored
Normal file
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/options_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDurationToTimeMillisField(t *testing.T) {
|
||||
val := DurationToTimeMillisField(time.Microsecond * 100)
|
||||
assert.Equal(t, val.Type, zapcore.Float32Type, "should be a float type")
|
||||
assert.Equal(t, math.Float32frombits(uint32(val.Integer)), float32(0.1), "sub millisecond values should be correct")
|
||||
}
|
||||
149
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/payload_interceptors.go
generated
vendored
Normal file
149
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/payload_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// JsonPbMarshaller is the marshaller used for serializing protobuf messages.
|
||||
JsonPbMarshaller = &jsonpb.Marshaler{}
|
||||
)
|
||||
|
||||
// PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
|
||||
//
|
||||
// This *only* works when placed *after* the `grpc_zap.UnaryServerInterceptor`. However, the logging can be done to a
|
||||
// separate instance of the logger.
|
||||
func PayloadUnaryServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
if !decider(ctx, info.FullMethod, info.Server) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
// Use the provided zap.Logger for logging but use the fields from context.
|
||||
logEntry := logger.With(append(serverCallFields(info.FullMethod), ctxzap.TagsToFields(ctx)...)...)
|
||||
logProtoMessageAsJson(logEntry, req, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
resp, err := handler(ctx, req)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(logEntry, resp, "grpc.response.content", "server response payload logged as grpc.request.content field")
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
|
||||
//
|
||||
// This *only* works when placed *after* the `grpc_zap.StreamServerInterceptor`. However, the logging can be done to a
|
||||
// separate instance of the logger.
|
||||
func PayloadStreamServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
if !decider(stream.Context(), info.FullMethod, srv) {
|
||||
return handler(srv, stream)
|
||||
}
|
||||
logEntry := logger.With(append(serverCallFields(info.FullMethod), ctxzap.TagsToFields(stream.Context())...)...)
|
||||
newStream := &loggingServerStream{ServerStream: stream, logger: logEntry}
|
||||
return handler(srv, newStream)
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses.
|
||||
func PayloadUnaryClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor {
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
if !decider(ctx, method) {
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
logEntry := logger.With(newClientLoggerFields(ctx, method)...)
|
||||
logProtoMessageAsJson(logEntry, req, "grpc.request.content", "client request payload logged as grpc.request.content")
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(logEntry, reply, "grpc.response.content", "client response payload logged as grpc.response.content")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses.
|
||||
func PayloadStreamClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor {
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
if !decider(ctx, method) {
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
logEntry := logger.With(newClientLoggerFields(ctx, method)...)
|
||||
clientStream, err := streamer(ctx, desc, cc, method, opts...)
|
||||
newStream := &loggingClientStream{ClientStream: clientStream, logger: logEntry}
|
||||
return newStream, err
|
||||
}
|
||||
}
|
||||
|
||||
type loggingClientStream struct {
|
||||
grpc.ClientStream
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *loggingClientStream) SendMsg(m interface{}) error {
|
||||
err := l.ClientStream.SendMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.logger, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *loggingClientStream) RecvMsg(m interface{}) error {
|
||||
err := l.ClientStream.RecvMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.logger, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type loggingServerStream struct {
|
||||
grpc.ServerStream
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *loggingServerStream) SendMsg(m interface{}) error {
|
||||
err := l.ServerStream.SendMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.logger, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *loggingServerStream) RecvMsg(m interface{}) error {
|
||||
err := l.ServerStream.RecvMsg(m)
|
||||
if err == nil {
|
||||
logProtoMessageAsJson(l.logger, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func logProtoMessageAsJson(logger *zap.Logger, pbMsg interface{}, key string, msg string) {
|
||||
if p, ok := pbMsg.(proto.Message); ok {
|
||||
logger.Check(zapcore.InfoLevel, msg).Write(zap.Object(key, &jsonpbObjectMarshaler{pb: p}))
|
||||
}
|
||||
}
|
||||
|
||||
type jsonpbObjectMarshaler struct {
|
||||
pb proto.Message
|
||||
}
|
||||
|
||||
func (j *jsonpbObjectMarshaler) MarshalLogObject(e zapcore.ObjectEncoder) error {
|
||||
// ZAP jsonEncoder deals with AddReflect by using json.MarshalObject. The same thing applies for consoleEncoder.
|
||||
return e.AddReflected("msg", j)
|
||||
}
|
||||
|
||||
func (j *jsonpbObjectMarshaler) MarshalJSON() ([]byte, error) {
|
||||
b := &bytes.Buffer{}
|
||||
if err := JsonPbMarshaller.Marshal(b, j.pb); err != nil {
|
||||
return nil, fmt.Errorf("jsonpb serializer failed: %v", err)
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
135
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/payload_interceptors_test.go
generated
vendored
Normal file
135
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/payload_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package grpc_zap_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func TestZapPayloadSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
|
||||
alwaysLoggingDeciderServer := func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { return true }
|
||||
alwaysLoggingDeciderClient := func(ctx context.Context, fullMethodName string) bool { return true }
|
||||
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_zap.PayloadUnaryClientInterceptor(b.log, alwaysLoggingDeciderClient)),
|
||||
grpc.WithStreamInterceptor(grpc_zap.PayloadStreamClientInterceptor(b.log, alwaysLoggingDeciderClient)),
|
||||
}
|
||||
noOpZap := zap.New(zapcore.NewNopCore())
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.StreamServerInterceptor(noOpZap),
|
||||
grpc_zap.PayloadStreamServerInterceptor(b.log, alwaysLoggingDeciderServer)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.UnaryServerInterceptor(noOpZap),
|
||||
grpc_zap.PayloadUnaryServerInterceptor(b.log, alwaysLoggingDeciderServer)),
|
||||
}
|
||||
suite.Run(t, &zapPayloadSuite{b})
|
||||
}
|
||||
|
||||
type zapPayloadSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapPayloadSuite) getServerAndClientMessages(expectedServer int, expectedClient int) (serverMsgs []map[string]interface{}, clientMsgs []map[string]interface{}) {
|
||||
msgs := s.getOutputJSONs()
|
||||
for _, m := range msgs {
|
||||
if m["span.kind"] == "server" {
|
||||
serverMsgs = append(serverMsgs, m)
|
||||
} else if m["span.kind"] == "client" {
|
||||
clientMsgs = append(clientMsgs, m)
|
||||
}
|
||||
}
|
||||
require.Len(s.T(), serverMsgs, expectedServer, "must match expected number of server log messages")
|
||||
require.Len(s.T(), clientMsgs, expectedClient, "must match expected number of client log messages")
|
||||
return serverMsgs, clientMsgs
|
||||
}
|
||||
|
||||
func (s *zapPayloadSuite) TestPing_LogsBothRequestAndResponse() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(2, 2)
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "all payloads must be logged on info level")
|
||||
}
|
||||
|
||||
serverReq, serverResp := serverMsgs[0], serverMsgs[1]
|
||||
clientReq, clientResp := clientMsgs[0], clientMsgs[1]
|
||||
s.T().Log(clientReq)
|
||||
assert.Contains(s.T(), clientReq, "grpc.request.content", "request payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), serverReq, "grpc.request.content", "request payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), clientResp, "grpc.response.content", "response payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), serverResp, "grpc.response.content", "response payload must be logged in a structured way")
|
||||
}
|
||||
|
||||
func (s *zapPayloadSuite) TestPingError_LogsOnlyRequestsOnError() {
|
||||
_, err := s.Client.PingError(s.SimpleCtx(), &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(4)})
|
||||
|
||||
require.Error(s.T(), err, "there must be an error on an unsuccessful call")
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(1, 1)
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "must be logged at the info level")
|
||||
}
|
||||
|
||||
assert.Contains(s.T(), clientMsgs[0], "grpc.request.content", "request payload must be logged in a structured way")
|
||||
assert.Contains(s.T(), serverMsgs[0], "grpc.request.content", "request payload must be logged in a structured way")
|
||||
}
|
||||
|
||||
func (s *zapPayloadSuite) TestPingStream_LogsAllRequestsAndResponses() {
|
||||
messagesExpected := 20
|
||||
stream, err := s.Client.PingStream(s.SimpleCtx())
|
||||
|
||||
require.NoError(s.T(), err, "no error on stream creation")
|
||||
for i := 0; i < messagesExpected; i++ {
|
||||
require.NoError(s.T(), stream.Send(goodPing), "sending must succeed")
|
||||
}
|
||||
require.NoError(s.T(), stream.CloseSend(), "no error on send stream")
|
||||
|
||||
for {
|
||||
pong := &pb_testproto.PingResponse{}
|
||||
err := stream.RecvMsg(pong)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "no error on receive")
|
||||
}
|
||||
|
||||
serverMsgs, clientMsgs := s.getServerAndClientMessages(2*messagesExpected, 2*messagesExpected)
|
||||
for _, m := range append(serverMsgs, clientMsgs...) {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingStream", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["level"], "info", "all lines must logged at info level")
|
||||
|
||||
content := m["grpc.request.content"] != nil || m["grpc.response.content"] != nil
|
||||
assert.True(s.T(), content, "all messages must contain payloads")
|
||||
}
|
||||
}
|
||||
96
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/server_interceptors.go
generated
vendored
Normal file
96
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/server_interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package grpc_zap
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/zap"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// SystemField is used in every log statement made through grpc_zap. Can be overwritten before any initialization code.
|
||||
SystemField = zap.String("system", "grpc")
|
||||
|
||||
// ServerField is used in every server-side log statement made through grpc_zap.Can be overwritten before initialization.
|
||||
ServerField = zap.String("span.kind", "server")
|
||||
)
|
||||
|
||||
// UnaryServerInterceptor returns a new unary server interceptors that adds zap.Logger to the context.
|
||||
func UnaryServerInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryServerInterceptor {
|
||||
o := evaluateServerOpt(opts)
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
newCtx := newLoggerForCall(ctx, logger, info.FullMethod, startTime)
|
||||
|
||||
resp, err := handler(newCtx, req)
|
||||
if !o.shouldLog(info.FullMethod, err) {
|
||||
return resp, err
|
||||
}
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
|
||||
// re-extract logger from newCtx, as it may have extra fields that changed in the holder.
|
||||
ctx_zap.Extract(newCtx).Check(level, "finished unary call with code "+code.String()).Write(
|
||||
zap.Error(err),
|
||||
zap.String("grpc.code", code.String()),
|
||||
o.durationFunc(time.Since(startTime)),
|
||||
)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a new streaming server interceptor that adds zap.Logger to the context.
|
||||
func StreamServerInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamServerInterceptor {
|
||||
o := evaluateServerOpt(opts)
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
startTime := time.Now()
|
||||
newCtx := newLoggerForCall(stream.Context(), logger, info.FullMethod, startTime)
|
||||
wrapped := grpc_middleware.WrapServerStream(stream)
|
||||
wrapped.WrappedContext = newCtx
|
||||
|
||||
err := handler(srv, wrapped)
|
||||
if !o.shouldLog(info.FullMethod, err) {
|
||||
return err
|
||||
}
|
||||
code := o.codeFunc(err)
|
||||
level := o.levelFunc(code)
|
||||
|
||||
// re-extract logger from newCtx, as it may have extra fields that changed in the holder.
|
||||
ctx_zap.Extract(newCtx).Check(level, "finished streaming call with code "+code.String()).Write(
|
||||
zap.Error(err),
|
||||
zap.String("grpc.code", code.String()),
|
||||
o.durationFunc(time.Since(startTime)),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func serverCallFields(fullMethodString string) []zapcore.Field {
|
||||
service := path.Dir(fullMethodString)[1:]
|
||||
method := path.Base(fullMethodString)
|
||||
return []zapcore.Field{
|
||||
SystemField,
|
||||
ServerField,
|
||||
zap.String("grpc.service", service),
|
||||
zap.String("grpc.method", method),
|
||||
}
|
||||
}
|
||||
|
||||
func newLoggerForCall(ctx context.Context, logger *zap.Logger, fullMethodString string, start time.Time) context.Context {
|
||||
f := ctxzap.TagsToFields(ctx)
|
||||
f = append(f, zap.String("grpc.start_time", start.Format(time.RFC3339)))
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
f = append(f, zap.String("grpc.request.deadline", d.Format(time.RFC3339)))
|
||||
}
|
||||
callLog := logger.With(append(f, serverCallFields(fullMethodString)...)...)
|
||||
return ctxzap.ToContext(ctx, callLog)
|
||||
}
|
||||
322
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/server_interceptors_test.go
generated
vendored
Normal file
322
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/server_interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
package grpc_zap_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
func customCodeToLevel(c codes.Code) zapcore.Level {
|
||||
if c == codes.Unauthenticated {
|
||||
// Make this a special case for tests, and an error.
|
||||
return zap.DPanicLevel
|
||||
}
|
||||
level := grpc_zap.DefaultCodeToLevel(c)
|
||||
return level
|
||||
}
|
||||
|
||||
func TestZapLoggingSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithLevels(customCodeToLevel),
|
||||
}
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.StreamServerInterceptor(b.log, opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||
grpc_zap.UnaryServerInterceptor(b.log, opts...)),
|
||||
}
|
||||
suite.Run(t, &zapServerSuite{b})
|
||||
}
|
||||
|
||||
type zapServerSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapServerSuite) TestPing_WithCustomTags() {
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
_, err := s.Client.Ping(s.DeadlineCtx(deadline), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)")
|
||||
assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain `custom_tags.string`")
|
||||
assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain fields extracted")
|
||||
assert.Equal(s.T(), m["custom_field"], "custom_value", "all lines must contain `custom_field`")
|
||||
|
||||
assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int`")
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time")
|
||||
_, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse start time as RFC3339")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.request.deadline", "all lines must contain the deadline of the call")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.request.deadline"].(string))
|
||||
require.NoError(s.T(), err, "should be able to parse deadline as RFC3339")
|
||||
assert.Equal(s.T(), m["grpc.request.deadline"], deadline.Format(time.RFC3339), "should have the same deadline that was set by the caller")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "must be logged at info level")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time")
|
||||
}
|
||||
|
||||
func (s *zapServerSuite) TestPingError_WithCustomLevels() {
|
||||
for _, tcase := range []struct {
|
||||
code codes.Code
|
||||
level zapcore.Level
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
code: codes.Internal,
|
||||
level: zap.ErrorLevel,
|
||||
msg: "Internal must remap to ErrorLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
level: zap.InfoLevel,
|
||||
msg: "NotFound must remap to InfoLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.FailedPrecondition,
|
||||
level: zap.WarnLevel,
|
||||
msg: "FailedPrecondition must remap to WarnLevel in DefaultCodeToLevel",
|
||||
},
|
||||
{
|
||||
code: codes.Unauthenticated,
|
||||
level: zap.DPanicLevel,
|
||||
msg: "Unauthenticated is overwritten to DPanicLevel with customCodeToLevel override, which probably didn't work",
|
||||
},
|
||||
} {
|
||||
s.buffer.Reset()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)})
|
||||
require.Error(s.T(), err, "each call here must return an error")
|
||||
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
|
||||
|
||||
m := msgs[0]
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["grpc.code"], tcase.code.String(), "all lines have the correct gRPC code")
|
||||
assert.Equal(s.T(), m["level"], tcase.level.String(), tcase.msg)
|
||||
assert.Equal(s.T(), m["msg"], "finished unary call with code "+tcase.code.String(), "needs the correct end message")
|
||||
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time")
|
||||
_, err = time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse start time as RFC3339")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *zapServerSuite) TestPingList_WithCustomTags() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)")
|
||||
assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain `custom_tags.string` set by AddFields")
|
||||
assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain fields extracted from goodPing because of test.manual_extractfields.pb")
|
||||
|
||||
assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int` set by AddFields")
|
||||
require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time")
|
||||
_, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string))
|
||||
assert.NoError(s.T(), err, "should be able to parse start time as RFC3339")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "OK codes must be logged on info level.")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time")
|
||||
}
|
||||
|
||||
func TestZapLoggingOverrideSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDurationField(grpc_zap.DurationToDurationField),
|
||||
}
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(b.log, opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(b.log, opts...)),
|
||||
}
|
||||
suite.Run(t, &zapServerOverrideSuite{b})
|
||||
}
|
||||
|
||||
type zapServerOverrideSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapServerOverrideSuite) TestPing_HasOverriddenDuration() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
|
||||
for _, m := range msgs {
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name")
|
||||
}
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "OK error codes must be logged on info level.")
|
||||
assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.duration", "handler's message must contain overridden duration")
|
||||
}
|
||||
|
||||
func (s *zapServerOverrideSuite) TestPingList_HasOverriddenDuration() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 2, "two log statements should be logged")
|
||||
for _, m := range msgs {
|
||||
s.T()
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain method name")
|
||||
}
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration")
|
||||
|
||||
assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "handler's message must contain user message")
|
||||
assert.Equal(s.T(), msgs[1]["level"], "info", "OK error codes must be logged on info level.")
|
||||
assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.Contains(s.T(), msgs[1], "grpc.duration", "handler's message must contain overridden duration")
|
||||
}
|
||||
|
||||
func TestZapServerOverrideSuppressedSuite(t *testing.T) {
|
||||
if strings.HasPrefix(runtime.Version(), "go1.7") {
|
||||
t.Skip("Skipping due to json.RawMessage incompatibility with go1.7")
|
||||
return
|
||||
}
|
||||
opts := []grpc_zap.Option{
|
||||
grpc_zap.WithDecider(func(method string, err error) bool {
|
||||
if err != nil && method == "/mwitkow.testproto.TestService/PingError" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}),
|
||||
}
|
||||
b := newBaseZapSuite(t)
|
||||
b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_ctxtags.StreamServerInterceptor(),
|
||||
grpc_zap.StreamServerInterceptor(b.log, opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_ctxtags.UnaryServerInterceptor(),
|
||||
grpc_zap.UnaryServerInterceptor(b.log, opts...)),
|
||||
}
|
||||
suite.Run(t, &zapServerOverridenDeciderSuite{b})
|
||||
}
|
||||
|
||||
type zapServerOverridenDeciderSuite struct {
|
||||
*zapBaseSuite
|
||||
}
|
||||
|
||||
func (s *zapServerOverridenDeciderSuite) TestPing_HasOverriddenDecider() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "there must be not be an error on a successful call")
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "single log statements should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message")
|
||||
}
|
||||
|
||||
func (s *zapServerOverridenDeciderSuite) TestPingError_HasOverriddenDecider() {
|
||||
code := codes.NotFound
|
||||
level := logrus.InfoLevel
|
||||
msg := "NotFound must remap to InfoLevel in DefaultCodeToLevel"
|
||||
|
||||
s.buffer.Reset()
|
||||
_, err := s.Client.PingError(
|
||||
s.SimpleCtx(),
|
||||
&pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(code)})
|
||||
require.Error(s.T(), err, "each call here must return an error")
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
|
||||
m := msgs[0]
|
||||
assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name")
|
||||
assert.Equal(s.T(), m["grpc.code"], code.String(), "all lines must contain the correct gRPC code")
|
||||
assert.Equal(s.T(), m["level"], level.String(), msg)
|
||||
}
|
||||
|
||||
func (s *zapServerOverridenDeciderSuite) TestPingList_HasOverriddenDecider() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(s.T(), err, "reading stream should not fail")
|
||||
}
|
||||
msgs := s.getOutputJSONs()
|
||||
require.Len(s.T(), msgs, 1, "single log statements should be logged")
|
||||
|
||||
assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
|
||||
assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
|
||||
assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message")
|
||||
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
|
||||
assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration")
|
||||
}
|
||||
106
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/shared_test.go
generated
vendored
Normal file
106
vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/shared_test.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package grpc_zap_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/tags/zap"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
goodPing = &pb_testproto.PingRequest{Value: "something", SleepTimeMs: 9999}
|
||||
)
|
||||
|
||||
type loggingPingService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
}
|
||||
|
||||
func (s *loggingPingService) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
ctx_zap.AddFields(ctx, zap.String("custom_field", "custom_value"))
|
||||
ctx_zap.Extract(ctx).Info("some ping")
|
||||
return s.TestServiceServer.Ping(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingError(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.Empty, error) {
|
||||
return s.TestServiceServer.PingError(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
|
||||
grpc_ctxtags.Extract(stream.Context()).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
|
||||
ctx_zap.Extract(stream.Context()).Info("some pinglist")
|
||||
return s.TestServiceServer.PingList(ping, stream)
|
||||
}
|
||||
|
||||
func (s *loggingPingService) PingEmpty(ctx context.Context, empty *pb_testproto.Empty) (*pb_testproto.PingResponse, error) {
|
||||
return s.TestServiceServer.PingEmpty(ctx, empty)
|
||||
}
|
||||
|
||||
func newBaseZapSuite(t *testing.T) *zapBaseSuite {
|
||||
b := &bytes.Buffer{}
|
||||
muB := grpc_testing.NewMutexReadWriter(b)
|
||||
zap.NewDevelopmentConfig()
|
||||
jsonEncoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.EpochTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
})
|
||||
core := zapcore.NewCore(jsonEncoder, zapcore.AddSync(muB), zap.LevelEnablerFunc(func(zapcore.Level) bool { return true }))
|
||||
log := zap.New(core)
|
||||
s := &zapBaseSuite{
|
||||
log: log,
|
||||
buffer: b,
|
||||
mutexBuffer: muB,
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &loggingPingService{&grpc_testing.TestPingService{T: t}},
|
||||
},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type zapBaseSuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
mutexBuffer *grpc_testing.MutexReadWriter
|
||||
buffer *bytes.Buffer
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func (s *zapBaseSuite) SetupTest() {
|
||||
s.mutexBuffer.Lock()
|
||||
s.buffer.Reset()
|
||||
s.mutexBuffer.Unlock()
|
||||
}
|
||||
|
||||
func (s *zapBaseSuite) getOutputJSONs() []map[string]interface{} {
|
||||
ret := make([]map[string]interface{}, 0)
|
||||
dec := json.NewDecoder(s.mutexBuffer)
|
||||
|
||||
for {
|
||||
var val map[string]interface{}
|
||||
err := dec.Decode(&val)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
s.T().Fatalf("failed decoding output from Logrus JSON: %v", err)
|
||||
}
|
||||
|
||||
ret = append(ret, val)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
22
vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile
generated
vendored
Normal file
22
vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
SHELL="/bin/bash"
|
||||
|
||||
GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
all: vet fmt docs test
|
||||
|
||||
docs:
|
||||
./scripts/docs.sh generate
|
||||
|
||||
checkdocs:
|
||||
./scripts/docs.sh check
|
||||
|
||||
fmt:
|
||||
go fmt $(GOFILES_NOVENDOR)
|
||||
|
||||
vet:
|
||||
go vet $(GOFILES_NOVENDOR)
|
||||
|
||||
test: vet
|
||||
./scripts/test_all.sh
|
||||
|
||||
.PHONY: all docs validate test
|
||||
107
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md
generated
vendored
Normal file
107
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
# grpc_recovery
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/recovery"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_recovery` are intereceptors that recover from gRPC handler panics.
|
||||
|
||||
### Server Side Recovery Middleware
|
||||
By default a panic will be converted into a gRPC error with `code.Internal`.
|
||||
|
||||
Handling can be customised by providing an alternate recovery function.
|
||||
|
||||
Please see examples for simple examples of use.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
package grpc_recovery_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
customFunc grpc_recovery.RecoveryHandlerFunc
|
||||
)
|
||||
|
||||
// Initialization shows an initialization sequence with a custom recovery handler func.
|
||||
func Example_initialization() {
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_recovery.Option{
|
||||
grpc_recovery.WithRecoveryHandler(customFunc),
|
||||
}
|
||||
// Create a server. Recovery handlers should typically be last in the chain so that other middleware
|
||||
// (e.g. logging) can operate on the recovered state instead of being directly affected by any panic
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_recovery.UnaryServerInterceptor(opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_recovery.StreamServerInterceptor(opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [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/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor)
|
||||
* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor)
|
||||
* [type Option](#Option)
|
||||
* [func WithRecoveryHandler(f RecoveryHandlerFunc) Option](#WithRecoveryHandler)
|
||||
* [type RecoveryHandlerFunc](#RecoveryHandlerFunc)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [Package (Initialization)](#example__initialization)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[doc.go](./doc.go) [interceptors.go](./interceptors.go) [options.go](./options.go)
|
||||
|
||||
## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L30)
|
||||
``` go
|
||||
func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor
|
||||
```
|
||||
StreamServerInterceptor returns a new streaming server interceptor for panic recovery.
|
||||
|
||||
## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L16)
|
||||
``` go
|
||||
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor
|
||||
```
|
||||
UnaryServerInterceptor returns a new unary server interceptor for panic recovery.
|
||||
|
||||
## <a name="Option">type</a> [Option](./options.go#L25)
|
||||
``` go
|
||||
type Option func(*options)
|
||||
```
|
||||
|
||||
### <a name="WithRecoveryHandler">func</a> [WithRecoveryHandler](./options.go#L28)
|
||||
``` go
|
||||
func WithRecoveryHandler(f RecoveryHandlerFunc) Option
|
||||
```
|
||||
WithRecoveryHandler customizes the function for recovering from a panic.
|
||||
|
||||
## <a name="RecoveryHandlerFunc">type</a> [RecoveryHandlerFunc](./interceptors.go#L13)
|
||||
``` go
|
||||
type RecoveryHandlerFunc func(p interface{}) (err error)
|
||||
```
|
||||
RecoveryHandlerFunc is a function that recovers from the panic `p` by returning an `error`.
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go
generated
vendored
Normal file
15
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 David Ackroyd. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
/*
|
||||
`grpc_recovery` are intereceptors that recover from gRPC handler panics.
|
||||
|
||||
Server Side Recovery Middleware
|
||||
|
||||
By default a panic will be converted into a gRPC error with `code.Internal`.
|
||||
|
||||
Handling can be customised by providing an alternate recovery function.
|
||||
|
||||
Please see examples for simple examples of use.
|
||||
*/
|
||||
package grpc_recovery
|
||||
32
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/examples_test.go
generated
vendored
Normal file
32
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2017 David Ackroyd. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_recovery_test
|
||||
|
||||
import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
customFunc grpc_recovery.RecoveryHandlerFunc
|
||||
)
|
||||
|
||||
// Initialization shows an initialization sequence with a custom recovery handler func.
|
||||
func Example_initialization() {
|
||||
// Shared options for the logger, with a custom gRPC code to log level function.
|
||||
opts := []grpc_recovery.Option{
|
||||
grpc_recovery.WithRecoveryHandler(customFunc),
|
||||
}
|
||||
// Create a server. Recovery handlers should typically be last in the chain so that other middleware
|
||||
// (e.g. logging) can operate on the recovered state instead of being directly affected by any panic
|
||||
_ = grpc.NewServer(
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_recovery.UnaryServerInterceptor(opts...),
|
||||
),
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_recovery.StreamServerInterceptor(opts...),
|
||||
),
|
||||
)
|
||||
}
|
||||
48
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go
generated
vendored
Normal file
48
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017 David Ackroyd. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_recovery
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// RecoveryHandlerFunc is a function that recovers from the panic `p` by returning an `error`.
|
||||
type RecoveryHandlerFunc func(p interface{}) (err error)
|
||||
|
||||
// UnaryServerInterceptor returns a new unary server interceptor for panic recovery.
|
||||
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {
|
||||
o := evaluateOptions(opts)
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = recoverFrom(r, o.recoveryHandlerFunc)
|
||||
}
|
||||
}()
|
||||
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a new streaming server interceptor for panic recovery.
|
||||
func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor {
|
||||
o := evaluateOptions(opts)
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = recoverFrom(r, o.recoveryHandlerFunc)
|
||||
}
|
||||
}()
|
||||
|
||||
return handler(srv, stream)
|
||||
}
|
||||
}
|
||||
|
||||
func recoverFrom(p interface{}, r RecoveryHandlerFunc) error {
|
||||
if r == nil {
|
||||
return grpc.Errorf(codes.Internal, "%s", p)
|
||||
}
|
||||
return r(p)
|
||||
}
|
||||
143
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors_test.go
generated
vendored
Normal file
143
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors_test.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright 2017 David Ackroyd. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_recovery_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
"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"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
goodPing = &pb_testproto.PingRequest{Value: "something", SleepTimeMs: 9999}
|
||||
panicPing = &pb_testproto.PingRequest{Value: "panic", SleepTimeMs: 9999}
|
||||
)
|
||||
|
||||
type recoveryAssertService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
}
|
||||
|
||||
func (s *recoveryAssertService) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
if ping.Value == "panic" {
|
||||
panic("very bad thing happened")
|
||||
}
|
||||
return s.TestServiceServer.Ping(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *recoveryAssertService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
|
||||
if ping.Value == "panic" {
|
||||
panic("very bad thing happened")
|
||||
}
|
||||
return s.TestServiceServer.PingList(ping, stream)
|
||||
}
|
||||
|
||||
func TestRecoverySuite(t *testing.T) {
|
||||
s := &RecoverySuite{
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &recoveryAssertService{TestServiceServer: &grpc_testing.TestPingService{T: t}},
|
||||
ServerOpts: []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_recovery.StreamServerInterceptor()),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_recovery.UnaryServerInterceptor()),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type RecoverySuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
}
|
||||
|
||||
func (s *RecoverySuite) TestUnary_SuccessfulRequest() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
}
|
||||
|
||||
func (s *RecoverySuite) TestUnary_PanickingRequest() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), panicPing)
|
||||
require.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Internal, grpc.Code(err), "must error with internal")
|
||||
assert.Equal(s.T(), "very bad thing happened", grpc.ErrorDesc(err), "must error with message")
|
||||
}
|
||||
|
||||
func (s *RecoverySuite) TestStream_SuccessfulReceive() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
pong, err := stream.Recv()
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
require.NotNil(s.T(), pong, "pong must not be nil")
|
||||
}
|
||||
|
||||
func (s *RecoverySuite) TestStream_PanickingReceive() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), panicPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
_, err = stream.Recv()
|
||||
require.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Internal, grpc.Code(err), "must error with internal")
|
||||
assert.Equal(s.T(), "very bad thing happened", grpc.ErrorDesc(err), "must error with message")
|
||||
}
|
||||
|
||||
func TestRecoveryOverrideSuite(t *testing.T) {
|
||||
opts := []grpc_recovery.Option{
|
||||
grpc_recovery.WithRecoveryHandler(func(p interface{}) (err error) {
|
||||
return grpc.Errorf(codes.Unknown, "panic triggered: %v", p)
|
||||
}),
|
||||
}
|
||||
s := &RecoveryOverrideSuite{
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: &recoveryAssertService{TestServiceServer: &grpc_testing.TestPingService{T: t}},
|
||||
ServerOpts: []grpc.ServerOption{
|
||||
grpc_middleware.WithStreamServerChain(
|
||||
grpc_recovery.StreamServerInterceptor(opts...)),
|
||||
grpc_middleware.WithUnaryServerChain(
|
||||
grpc_recovery.UnaryServerInterceptor(opts...)),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type RecoveryOverrideSuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
}
|
||||
|
||||
func (s *RecoveryOverrideSuite) TestUnary_SuccessfulRequest() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
}
|
||||
|
||||
func (s *RecoveryOverrideSuite) TestUnary_PanickingRequest() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), panicPing)
|
||||
require.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Unknown, grpc.Code(err), "must error with unknown")
|
||||
assert.Equal(s.T(), "panic triggered: very bad thing happened", grpc.ErrorDesc(err), "must error with message")
|
||||
}
|
||||
|
||||
func (s *RecoveryOverrideSuite) TestStream_SuccessfulReceive() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
pong, err := stream.Recv()
|
||||
require.NoError(s.T(), err, "no error must occur")
|
||||
require.NotNil(s.T(), pong, "pong must not be nil")
|
||||
}
|
||||
|
||||
func (s *RecoveryOverrideSuite) TestStream_PanickingReceive() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), panicPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
_, err = stream.Recv()
|
||||
require.Error(s.T(), err, "there must be an error")
|
||||
assert.Equal(s.T(), codes.Unknown, grpc.Code(err), "must error with unknown")
|
||||
assert.Equal(s.T(), "panic triggered: very bad thing happened", grpc.ErrorDesc(err), "must error with message")
|
||||
}
|
||||
32
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go
generated
vendored
Normal file
32
vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2017 David Ackroyd. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_recovery
|
||||
|
||||
var (
|
||||
defaultOptions = &options{
|
||||
recoveryHandlerFunc: nil,
|
||||
}
|
||||
)
|
||||
|
||||
type options struct {
|
||||
recoveryHandlerFunc RecoveryHandlerFunc
|
||||
}
|
||||
|
||||
func evaluateOptions(opts []Option) *options {
|
||||
optCopy := &options{}
|
||||
*optCopy = *defaultOptions
|
||||
for _, o := range opts {
|
||||
o(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
// WithRecoveryHandler customizes the function for recovering from a panic.
|
||||
func WithRecoveryHandler(f RecoveryHandlerFunc) Option {
|
||||
return func(o *options) {
|
||||
o.recoveryHandlerFunc = f
|
||||
}
|
||||
}
|
||||
255
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/DOC.md
generated
vendored
Normal file
255
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/DOC.md
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
# grpc_retry
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware/retry"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
* [Examples](#pkg-examples)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_retry` provides client-side request retry logic for gRPC.
|
||||
|
||||
### Client-Side Request Retry Interceptor
|
||||
It allows for automatic retry, inside the generated gRPC code of requests based on the gRPC status
|
||||
of the reply. It supports unary (1:1), and server stream (1:n) requests.
|
||||
|
||||
By default the interceptors *are disabled*, preventing accidental use of retries. You can easily
|
||||
override the number of retries (setting them to more than 0) with a `grpc.ClientOption`, e.g.:
|
||||
|
||||
myclient.Ping(ctx, goodPing, grpc_retry.WithMax(5))
|
||||
|
||||
Other default options are: retry on `ResourceExhausted` and `Unavailable` gRPC codes, use a 50ms
|
||||
linear backoff with 10% jitter.
|
||||
|
||||
For chained interceptors, the retry interceptor will call every interceptor that follows it
|
||||
whenever when a retry happens.
|
||||
|
||||
Please see examples for more advanced use.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
grpc.Dial("myservice.example.com",
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor()),
|
||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor()),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
opts := []grpc_retry.CallOption{
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(100 * time.Millisecond)),
|
||||
grpc_retry.WithCodes(codes.NotFound, codes.Aborted),
|
||||
}
|
||||
grpc.Dial("myservice.example.com",
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(opts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(opts...)),
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
client := pb_testproto.NewTestServiceClient(cc)
|
||||
stream, _ := client.PingList(newCtx(1*time.Second), &pb_testproto.PingRequest{}, grpc_retry.WithMax(3))
|
||||
|
||||
for {
|
||||
pong, err := stream.Recv() // retries happen here
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("got pong: %v", pong)
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/util/backoffutils](./../util/backoffutils)
|
||||
- [github.com/grpc-ecosystem/go-grpc-middleware/util/metautils](./../util/metautils)
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
- [golang.org/x/net/trace](https://godoc.org/golang.org/x/net/trace)
|
||||
- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc)
|
||||
- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes)
|
||||
- [google.golang.org/grpc/metadata](https://godoc.org/google.golang.org/grpc/metadata)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [Constants](#pkg-constants)
|
||||
* [Variables](#pkg-variables)
|
||||
* [func StreamClientInterceptor(optFuncs ...CallOption) grpc.StreamClientInterceptor](#StreamClientInterceptor)
|
||||
* [func UnaryClientInterceptor(optFuncs ...CallOption) grpc.UnaryClientInterceptor](#UnaryClientInterceptor)
|
||||
* [type BackoffFunc](#BackoffFunc)
|
||||
* [func BackoffLinear(waitBetween time.Duration) BackoffFunc](#BackoffLinear)
|
||||
* [func BackoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) BackoffFunc](#BackoffLinearWithJitter)
|
||||
* [type CallOption](#CallOption)
|
||||
* [func Disable() CallOption](#Disable)
|
||||
* [func WithBackoff(bf BackoffFunc) CallOption](#WithBackoff)
|
||||
* [func WithCodes(retryCodes ...codes.Code) CallOption](#WithCodes)
|
||||
* [func WithMax(maxRetries uint) CallOption](#WithMax)
|
||||
* [func WithPerRetryTimeout(timeout time.Duration) CallOption](#WithPerRetryTimeout)
|
||||
|
||||
#### <a name="pkg-examples">Examples</a>
|
||||
* [WithPerRetryTimeout](#example_WithPerRetryTimeout)
|
||||
* [Package (Initialization)](#example__initialization)
|
||||
* [Package (InitializationWithOptions)](#example__initializationWithOptions)
|
||||
* [Package (SimpleCall)](#example__simpleCall)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[backoff.go](./backoff.go) [doc.go](./doc.go) [options.go](./options.go) [retry.go](./retry.go)
|
||||
|
||||
## <a name="pkg-constants">Constants</a>
|
||||
``` go
|
||||
const (
|
||||
AttemptMetadataKey = "x-retry-attempty"
|
||||
)
|
||||
```
|
||||
|
||||
## <a name="pkg-variables">Variables</a>
|
||||
``` go
|
||||
var (
|
||||
// DefaultRetriableCodes is a set of well known types gRPC codes that should be retri-able.
|
||||
//
|
||||
// `ResourceExhausted` means that the user quota, e.g. per-RPC limits, have been reached.
|
||||
// `Unavailable` means that system is currently unavailable and the client should retry again.
|
||||
DefaultRetriableCodes = []codes.Code{codes.ResourceExhausted, codes.Unavailable}
|
||||
)
|
||||
```
|
||||
|
||||
## <a name="StreamClientInterceptor">func</a> [StreamClientInterceptor](./retry.go#L76)
|
||||
``` go
|
||||
func StreamClientInterceptor(optFuncs ...CallOption) grpc.StreamClientInterceptor
|
||||
```
|
||||
StreamClientInterceptor returns a new retrying stream client interceptor for server side streaming calls.
|
||||
|
||||
The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
|
||||
Retry logic is available *only for ServerStreams*, i.e. 1:n streams, as the internal logic needs
|
||||
to buffer the messages sent by the client. If retry is enabled on any other streams (ClientStreams,
|
||||
BidiStreams), the retry interceptor will fail the call.
|
||||
|
||||
## <a name="UnaryClientInterceptor">func</a> [UnaryClientInterceptor](./retry.go#L28)
|
||||
``` go
|
||||
func UnaryClientInterceptor(optFuncs ...CallOption) grpc.UnaryClientInterceptor
|
||||
```
|
||||
UnaryClientInterceptor returns a new retrying unary client interceptor.
|
||||
|
||||
The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
|
||||
## <a name="BackoffFunc">type</a> [BackoffFunc](./options.go#L35)
|
||||
``` go
|
||||
type BackoffFunc func(attempt uint) time.Duration
|
||||
```
|
||||
BackoffFunc denotes a family of functions that control the backoff duration between call retries.
|
||||
|
||||
They are called with an identifier of the attempt, and should return a time the system client should
|
||||
hold off for. If the time returned is longer than the `context.Context.Deadline` of the request
|
||||
the deadline of the request takes precedence and the wait will be interrupted before proceeding
|
||||
with the next iteration.
|
||||
|
||||
### <a name="BackoffLinear">func</a> [BackoffLinear](./backoff.go#L13)
|
||||
``` go
|
||||
func BackoffLinear(waitBetween time.Duration) BackoffFunc
|
||||
```
|
||||
BackoffLinear is very simple: it waits for a fixed period of time between calls.
|
||||
|
||||
### <a name="BackoffLinearWithJitter">func</a> [BackoffLinearWithJitter](./backoff.go#L22)
|
||||
``` go
|
||||
func BackoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) BackoffFunc
|
||||
```
|
||||
BackoffLinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment).
|
||||
|
||||
For example waitBetween=1s and jitter=0.10 can generate waits between 900ms and 1100ms.
|
||||
|
||||
## <a name="CallOption">type</a> [CallOption](./options.go#L94-L97)
|
||||
``` go
|
||||
type CallOption struct {
|
||||
grpc.EmptyCallOption // make sure we implement private after() and before() fields so we don't panic.
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
```
|
||||
CallOption is a grpc.CallOption that is local to grpc_retry.
|
||||
|
||||
### <a name="Disable">func</a> [Disable](./options.go#L40)
|
||||
``` go
|
||||
func Disable() CallOption
|
||||
```
|
||||
Disable disables the retry behaviour on this call, or this interceptor.
|
||||
|
||||
Its semantically the same to `WithMax`
|
||||
|
||||
### <a name="WithBackoff">func</a> [WithBackoff](./options.go#L52)
|
||||
``` go
|
||||
func WithBackoff(bf BackoffFunc) CallOption
|
||||
```
|
||||
WithBackoff sets the `BackoffFunc `used to control time between retries.
|
||||
|
||||
### <a name="WithCodes">func</a> [WithCodes](./options.go#L63)
|
||||
``` go
|
||||
func WithCodes(retryCodes ...codes.Code) CallOption
|
||||
```
|
||||
WithCodes sets which codes should be retried.
|
||||
|
||||
Please *use with care*, as you may be retrying non-idempotend calls.
|
||||
|
||||
You cannot automatically retry on Cancelled and Deadline, please use `WithPerRetryTimeout` for these.
|
||||
|
||||
### <a name="WithMax">func</a> [WithMax](./options.go#L45)
|
||||
``` go
|
||||
func WithMax(maxRetries uint) CallOption
|
||||
```
|
||||
WithMax sets the maximum number of retries on this call, or this interceptor.
|
||||
|
||||
### <a name="WithPerRetryTimeout">func</a> [WithPerRetryTimeout](./options.go#L79)
|
||||
``` go
|
||||
func WithPerRetryTimeout(timeout time.Duration) CallOption
|
||||
```
|
||||
WithPerRetryTimeout sets the RPC timeout per call (including initial call) on this call, or this interceptor.
|
||||
|
||||
The context.Deadline of the call takes precedence and sets the maximum time the whole invocation
|
||||
will take, but WithPerRetryTimeout can be used to limit the RPC time per each call.
|
||||
|
||||
For example, with context.Deadline = now + 10s, and WithPerRetryTimeout(3 * time.Seconds), each
|
||||
of the retry calls (including the initial one) will have a deadline of now + 3s.
|
||||
|
||||
A value of 0 disables the timeout overrides completely and returns to each retry call using the
|
||||
parent `context.Deadline`.
|
||||
|
||||
#### Example:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand code.</summary>
|
||||
|
||||
```go
|
||||
client := pb_testproto.NewTestServiceClient(cc)
|
||||
pong, _ := client.Ping(
|
||||
newCtx(5*time.Second),
|
||||
&pb_testproto.PingRequest{},
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithPerRetryTimeout(1*time.Second))
|
||||
|
||||
fmt.Printf("got pong: %v", pong)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
||||
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/README.md
generated
vendored
Symbolic link
1
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/README.md
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
DOC.md
|
||||
26
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/backoff.go
generated
vendored
Normal file
26
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/backoff.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_retry
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/util/backoffutils"
|
||||
)
|
||||
|
||||
// BackoffLinear is very simple: it waits for a fixed period of time between calls.
|
||||
func BackoffLinear(waitBetween time.Duration) BackoffFunc {
|
||||
return func(attempt uint) time.Duration {
|
||||
return waitBetween
|
||||
}
|
||||
}
|
||||
|
||||
// BackoffLinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment).
|
||||
//
|
||||
// For example waitBetween=1s and jitter=0.10 can generate waits between 900ms and 1100ms.
|
||||
func BackoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) BackoffFunc {
|
||||
return func(attempt uint) time.Duration {
|
||||
return backoffutils.JitterUp(waitBetween, jitterFraction)
|
||||
}
|
||||
}
|
||||
25
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/doc.go
generated
vendored
Normal file
25
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/doc.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
/*
|
||||
`grpc_retry` provides client-side request retry logic for gRPC.
|
||||
|
||||
Client-Side Request Retry Interceptor
|
||||
|
||||
It allows for automatic retry, inside the generated gRPC code of requests based on the gRPC status
|
||||
of the reply. It supports unary (1:1), and server stream (1:n) requests.
|
||||
|
||||
By default the interceptors *are disabled*, preventing accidental use of retries. You can easily
|
||||
override the number of retries (setting them to more than 0) with a `grpc.ClientOption`, e.g.:
|
||||
|
||||
myclient.Ping(ctx, goodPing, grpc_retry.WithMax(5))
|
||||
|
||||
Other default options are: retry on `ResourceExhausted` and `Unavailable` gRPC codes, use a 50ms
|
||||
linear backoff with 10% jitter.
|
||||
|
||||
For chained interceptors, the retry interceptor will call every interceptor that follows it
|
||||
whenever when a retry happens.
|
||||
|
||||
Please see examples for more advanced use.
|
||||
*/
|
||||
package grpc_retry
|
||||
79
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/examples_test.go
generated
vendored
Normal file
79
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/examples_test.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_retry_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var cc *grpc.ClientConn
|
||||
|
||||
func newCtx(timeout time.Duration) context.Context {
|
||||
ctx, _ := context.WithTimeout(context.TODO(), timeout)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Simple example of using the default interceptor configuration.
|
||||
func Example_initialization() {
|
||||
grpc.Dial("myservice.example.com",
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor()),
|
||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor()),
|
||||
)
|
||||
}
|
||||
|
||||
// Complex example with a 100ms linear backoff interval, and retry only on NotFound and Unavailable.
|
||||
func Example_initializationWithOptions() {
|
||||
opts := []grpc_retry.CallOption{
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(100 * time.Millisecond)),
|
||||
grpc_retry.WithCodes(codes.NotFound, codes.Aborted),
|
||||
}
|
||||
grpc.Dial("myservice.example.com",
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(opts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(opts...)),
|
||||
)
|
||||
}
|
||||
|
||||
// Simple example of an idempotent `ServerStream` call, that will be retried automatically 3 times.
|
||||
func Example_simpleCall() {
|
||||
client := pb_testproto.NewTestServiceClient(cc)
|
||||
stream, _ := client.PingList(newCtx(1*time.Second), &pb_testproto.PingRequest{}, grpc_retry.WithMax(3))
|
||||
|
||||
for {
|
||||
pong, err := stream.Recv() // retries happen here
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("got pong: %v", pong)
|
||||
}
|
||||
}
|
||||
|
||||
// This is an example of an `Unary` call that will also retry on deadlines.
|
||||
//
|
||||
// Because the passed in context has a `5s` timeout, the whole `Ping` invocation should finish
|
||||
// within that time. However, by defauly all retried calls will use the parent context for their
|
||||
// deadlines. This means, that unless you shorten the deadline of each call of the retry, you won't
|
||||
// be able to retry the first call at all.
|
||||
//
|
||||
// `WithPerRetryTimeout` allows you to shorten the deadline of each retry call, allowing you to fit
|
||||
// multiple retries in the single parent deadline.
|
||||
func ExampleWithPerRetryTimeout() {
|
||||
client := pb_testproto.NewTestServiceClient(cc)
|
||||
pong, _ := client.Ping(
|
||||
newCtx(5*time.Second),
|
||||
&pb_testproto.PingRequest{},
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithPerRetryTimeout(1*time.Second))
|
||||
|
||||
fmt.Printf("got pong: %v", pong)
|
||||
}
|
||||
120
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/options.go
generated
vendored
Normal file
120
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/options.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_retry
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultRetriableCodes is a set of well known types gRPC codes that should be retri-able.
|
||||
//
|
||||
// `ResourceExhausted` means that the user quota, e.g. per-RPC limits, have been reached.
|
||||
// `Unavailable` means that system is currently unavailable and the client should retry again.
|
||||
DefaultRetriableCodes = []codes.Code{codes.ResourceExhausted, codes.Unavailable}
|
||||
|
||||
defaultOptions = &options{
|
||||
max: 0, // disabed
|
||||
perCallTimeout: 0, // disabled
|
||||
includeHeader: true,
|
||||
codes: DefaultRetriableCodes,
|
||||
backoffFunc: BackoffLinearWithJitter(50*time.Millisecond /*jitter*/, 0.10),
|
||||
}
|
||||
)
|
||||
|
||||
// BackoffFunc denotes a family of functions that control the backoff duration between call retries.
|
||||
//
|
||||
// They are called with an identifier of the attempt, and should return a time the system client should
|
||||
// hold off for. If the time returned is longer than the `context.Context.Deadline` of the request
|
||||
// the deadline of the request takes precedence and the wait will be interrupted before proceeding
|
||||
// with the next iteration.
|
||||
type BackoffFunc func(attempt uint) time.Duration
|
||||
|
||||
// Disable disables the retry behaviour on this call, or this interceptor.
|
||||
//
|
||||
// Its semantically the same to `WithMax`
|
||||
func Disable() CallOption {
|
||||
return WithMax(0)
|
||||
}
|
||||
|
||||
// WithMax sets the maximum number of retries on this call, or this interceptor.
|
||||
func WithMax(maxRetries uint) CallOption {
|
||||
return CallOption{applyFunc: func(o *options) {
|
||||
o.max = maxRetries
|
||||
}}
|
||||
}
|
||||
|
||||
// WithBackoff sets the `BackoffFunc `used to control time between retries.
|
||||
func WithBackoff(bf BackoffFunc) CallOption {
|
||||
return CallOption{applyFunc: func(o *options) {
|
||||
o.backoffFunc = bf
|
||||
}}
|
||||
}
|
||||
|
||||
// WithCodes sets which codes should be retried.
|
||||
//
|
||||
// Please *use with care*, as you may be retrying non-idempotend calls.
|
||||
//
|
||||
// You cannot automatically retry on Cancelled and Deadline, please use `WithPerRetryTimeout` for these.
|
||||
func WithCodes(retryCodes ...codes.Code) CallOption {
|
||||
return CallOption{applyFunc: func(o *options) {
|
||||
o.codes = retryCodes
|
||||
}}
|
||||
}
|
||||
|
||||
// WithPerRetryTimeout sets the RPC timeout per call (including initial call) on this call, or this interceptor.
|
||||
//
|
||||
// The context.Deadline of the call takes precedence and sets the maximum time the whole invocation
|
||||
// will take, but WithPerRetryTimeout can be used to limit the RPC time per each call.
|
||||
//
|
||||
// For example, with context.Deadline = now + 10s, and WithPerRetryTimeout(3 * time.Seconds), each
|
||||
// of the retry calls (including the initial one) will have a deadline of now + 3s.
|
||||
//
|
||||
// A value of 0 disables the timeout overrides completely and returns to each retry call using the
|
||||
// parent `context.Deadline`.
|
||||
func WithPerRetryTimeout(timeout time.Duration) CallOption {
|
||||
return CallOption{applyFunc: func(o *options) {
|
||||
o.perCallTimeout = timeout
|
||||
}}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
max uint
|
||||
perCallTimeout time.Duration
|
||||
includeHeader bool
|
||||
codes []codes.Code
|
||||
backoffFunc BackoffFunc
|
||||
}
|
||||
|
||||
// CallOption is a grpc.CallOption that is local to grpc_retry.
|
||||
type CallOption struct {
|
||||
grpc.EmptyCallOption // make sure we implement private after() and before() fields so we don't panic.
|
||||
applyFunc func(opt *options)
|
||||
}
|
||||
|
||||
func reuseOrNewWithCallOptions(opt *options, callOptions []CallOption) *options {
|
||||
if len(callOptions) == 0 {
|
||||
return opt
|
||||
}
|
||||
optCopy := &options{}
|
||||
*optCopy = *opt
|
||||
for _, f := range callOptions {
|
||||
f.applyFunc(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
func filterCallOptions(callOptions []grpc.CallOption) (grpcOptions []grpc.CallOption, retryOptions []CallOption) {
|
||||
for _, opt := range callOptions {
|
||||
if co, ok := opt.(CallOption); ok {
|
||||
retryOptions = append(retryOptions, co)
|
||||
} else {
|
||||
grpcOptions = append(grpcOptions, opt)
|
||||
}
|
||||
}
|
||||
return grpcOptions, retryOptions
|
||||
}
|
||||
296
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/retry.go
generated
vendored
Normal file
296
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_retry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
AttemptMetadataKey = "x-retry-attempty"
|
||||
)
|
||||
|
||||
// UnaryClientInterceptor returns a new retrying unary client interceptor.
|
||||
//
|
||||
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
func UnaryClientInterceptor(optFuncs ...CallOption) grpc.UnaryClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(parentCtx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
if callOpts.max == 0 {
|
||||
return invoker(parentCtx, method, req, reply, cc, grpcOpts...)
|
||||
}
|
||||
var lastErr error
|
||||
for attempt := uint(0); attempt < callOpts.max; attempt++ {
|
||||
if err := waitRetryBackoff(attempt, parentCtx, callOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
callCtx := perCallContext(parentCtx, callOpts, attempt)
|
||||
lastErr = invoker(callCtx, method, req, reply, cc, grpcOpts...)
|
||||
// TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
if lastErr == nil {
|
||||
return nil
|
||||
}
|
||||
logTrace(parentCtx, "grpc_retry attempt: %d, got err: %v", attempt, lastErr)
|
||||
if isContextError(lastErr) {
|
||||
if parentCtx.Err() != nil {
|
||||
logTrace(parentCtx, "grpc_retry attempt: %d, parent context error: %v", attempt, parentCtx.Err())
|
||||
// its the parent context deadline or cancellation.
|
||||
return lastErr
|
||||
} else {
|
||||
logTrace(parentCtx, "grpc_retry attempt: %d, context error from retry call", attempt)
|
||||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !isRetriable(lastErr, callOpts) {
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
|
||||
// StreamClientInterceptor returns a new retrying stream client interceptor for server side streaming calls.
|
||||
//
|
||||
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
//
|
||||
// Retry logic is available *only for ServerStreams*, i.e. 1:n streams, as the internal logic needs
|
||||
// to buffer the messages sent by the client. If retry is enabled on any other streams (ClientStreams,
|
||||
// BidiStreams), the retry interceptor will fail the call.
|
||||
func StreamClientInterceptor(optFuncs ...CallOption) grpc.StreamClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(parentCtx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
if callOpts.max == 0 {
|
||||
return streamer(parentCtx, desc, cc, method, grpcOpts...)
|
||||
}
|
||||
if desc.ClientStreams {
|
||||
return nil, grpc.Errorf(codes.Unimplemented, "grpc_retry: cannot retry on ClientStreams, set grpc_retry.Disable()")
|
||||
}
|
||||
logTrace(parentCtx, "grpc_retry attempt: %d, no backoff for this call", 0)
|
||||
callCtx := perCallContext(parentCtx, callOpts, 0)
|
||||
newStreamer, err := streamer(callCtx, desc, cc, method, grpcOpts...)
|
||||
if err != nil {
|
||||
// TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
return nil, err
|
||||
}
|
||||
retryingStreamer := &serverStreamingRetryingStream{
|
||||
ClientStream: newStreamer,
|
||||
callOpts: callOpts,
|
||||
parentCtx: parentCtx,
|
||||
streamerCall: func(ctx context.Context) (grpc.ClientStream, error) {
|
||||
return streamer(ctx, desc, cc, method, grpcOpts...)
|
||||
},
|
||||
}
|
||||
return retryingStreamer, nil
|
||||
}
|
||||
}
|
||||
|
||||
// type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a
|
||||
// proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish
|
||||
// a new ClientStream according to the retry policy.
|
||||
type serverStreamingRetryingStream struct {
|
||||
grpc.ClientStream
|
||||
bufferedSends []interface{} // single messsage that the client can sen
|
||||
receivedGood bool // indicates whether any prior receives were successful
|
||||
wasClosedSend bool // indicates that CloseSend was closed
|
||||
parentCtx context.Context
|
||||
callOpts *options
|
||||
streamerCall func(ctx context.Context) (grpc.ClientStream, error)
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) setStream(clientStream grpc.ClientStream) {
|
||||
s.mu.Lock()
|
||||
s.ClientStream = clientStream
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) getStream() grpc.ClientStream {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.ClientStream
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) SendMsg(m interface{}) error {
|
||||
s.mu.Lock()
|
||||
s.bufferedSends = append(s.bufferedSends, m)
|
||||
s.mu.Unlock()
|
||||
return s.getStream().SendMsg(m)
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) CloseSend() error {
|
||||
s.mu.Lock()
|
||||
s.wasClosedSend = true
|
||||
s.mu.Unlock()
|
||||
return s.getStream().CloseSend()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) Header() (metadata.MD, error) {
|
||||
return s.getStream().Header()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) Trailer() metadata.MD {
|
||||
return s.getStream().Trailer()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) RecvMsg(m interface{}) error {
|
||||
attemptRetry, lastErr := s.receiveMsgAndIndicateRetry(m)
|
||||
if !attemptRetry {
|
||||
return lastErr // success or hard failure
|
||||
}
|
||||
// We start off from attempt 1, because zeroth was already made on normal SendMsg().
|
||||
for attempt := uint(1); attempt < s.callOpts.max; attempt++ {
|
||||
if err := waitRetryBackoff(attempt, s.parentCtx, s.callOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
callCtx := perCallContext(s.parentCtx, s.callOpts, attempt)
|
||||
newStream, err := s.reestablishStreamAndResendBuffer(callCtx)
|
||||
if err != nil {
|
||||
// TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
return err
|
||||
}
|
||||
s.setStream(newStream)
|
||||
attemptRetry, lastErr = s.receiveMsgAndIndicateRetry(m)
|
||||
//fmt.Printf("Received message and indicate: %v %v\n", attemptRetry, lastErr)
|
||||
if !attemptRetry {
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{}) (bool, error) {
|
||||
s.mu.RLock()
|
||||
wasGood := s.receivedGood
|
||||
s.mu.RUnlock()
|
||||
err := s.getStream().RecvMsg(m)
|
||||
if err == nil || err == io.EOF {
|
||||
s.mu.Lock()
|
||||
s.receivedGood = true
|
||||
s.mu.Unlock()
|
||||
return false, err
|
||||
} else if wasGood {
|
||||
// previous RecvMsg in the stream succeeded, no retry logic should interfere
|
||||
return false, err
|
||||
}
|
||||
if isContextError(err) {
|
||||
if s.parentCtx.Err() != nil {
|
||||
logTrace(s.parentCtx, "grpc_retry parent context error: %v", s.parentCtx.Err())
|
||||
return false, err
|
||||
} else {
|
||||
logTrace(s.parentCtx, "grpc_retry context error from retry call")
|
||||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
return isRetriable(err, s.callOpts), err
|
||||
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) reestablishStreamAndResendBuffer(callCtx context.Context) (grpc.ClientStream, error) {
|
||||
s.mu.RLock()
|
||||
bufferedSends := s.bufferedSends
|
||||
s.mu.RUnlock()
|
||||
newStream, err := s.streamerCall(callCtx)
|
||||
if err != nil {
|
||||
logTrace(callCtx, "grpc_retry failed redialing new stream: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, msg := range bufferedSends {
|
||||
if err := newStream.SendMsg(msg); err != nil {
|
||||
logTrace(callCtx, "grpc_retry failed resending message: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := newStream.CloseSend(); err != nil {
|
||||
logTrace(callCtx, "grpc_retry failed CloseSend on new stream %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return newStream, nil
|
||||
}
|
||||
|
||||
func waitRetryBackoff(attempt uint, parentCtx context.Context, callOpts *options) error {
|
||||
var waitTime time.Duration = 0
|
||||
if attempt > 0 {
|
||||
waitTime = callOpts.backoffFunc(attempt)
|
||||
}
|
||||
if waitTime > 0 {
|
||||
logTrace(parentCtx, "grpc_retry attempt: %d, backoff for %v", attempt, waitTime)
|
||||
timer := time.NewTimer(waitTime)
|
||||
select {
|
||||
case <-parentCtx.Done():
|
||||
timer.Stop()
|
||||
return contextErrToGrpcErr(parentCtx.Err())
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isRetriable(err error, callOpts *options) bool {
|
||||
errCode := grpc.Code(err)
|
||||
if isContextError(err) {
|
||||
// context errors are not retriable based on user settings.
|
||||
return false
|
||||
}
|
||||
for _, code := range callOpts.codes {
|
||||
if code == errCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isContextError(err error) bool {
|
||||
return grpc.Code(err) == codes.DeadlineExceeded || grpc.Code(err) == codes.Canceled
|
||||
}
|
||||
|
||||
func perCallContext(parentCtx context.Context, callOpts *options, attempt uint) context.Context {
|
||||
ctx := parentCtx
|
||||
if callOpts.perCallTimeout != 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, callOpts.perCallTimeout)
|
||||
}
|
||||
if attempt > 0 && callOpts.includeHeader {
|
||||
mdClone := metautils.ExtractOutgoing(ctx).Clone().Set(AttemptMetadataKey, fmt.Sprintf("%d", attempt))
|
||||
ctx = mdClone.ToOutgoing(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func contextErrToGrpcErr(err error) error {
|
||||
switch err {
|
||||
case context.DeadlineExceeded:
|
||||
return grpc.Errorf(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled:
|
||||
return grpc.Errorf(codes.Canceled, err.Error())
|
||||
default:
|
||||
return grpc.Errorf(codes.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func logTrace(ctx context.Context, format string, a ...interface{}) {
|
||||
tr, ok := trace.FromContext(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
tr.LazyPrintf(format, a...)
|
||||
}
|
||||
344
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/retry_test.go
generated
vendored
Normal file
344
vendor/github.com/grpc-ecosystem/go-grpc-middleware/retry/retry_test.go
generated
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
|
||||
package grpc_retry_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
retriableErrors = []codes.Code{codes.Unavailable, codes.DataLoss}
|
||||
goodPing = &pb_testproto.PingRequest{Value: "something"}
|
||||
noSleep = 0 * time.Second
|
||||
retryTimeout = 50 * time.Millisecond
|
||||
)
|
||||
|
||||
type failingService struct {
|
||||
pb_testproto.TestServiceServer
|
||||
reqCounter uint
|
||||
reqModulo uint
|
||||
reqSleep time.Duration
|
||||
reqError codes.Code
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *failingService) resetFailingConfiguration(modulo uint, errorCode codes.Code, sleepTime time.Duration) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.reqCounter = 0
|
||||
s.reqModulo = modulo
|
||||
s.reqError = errorCode
|
||||
s.reqSleep = sleepTime
|
||||
}
|
||||
|
||||
func (s *failingService) requestCount() uint {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.reqCounter
|
||||
}
|
||||
|
||||
func (s *failingService) maybeFailRequest() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.reqCounter += 1
|
||||
if (s.reqModulo > 0) && (s.reqCounter%s.reqModulo == 0) {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(s.reqSleep)
|
||||
return grpc.Errorf(s.reqError, "maybeFailRequest: failing it")
|
||||
}
|
||||
|
||||
func (s *failingService) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
|
||||
if err := s.maybeFailRequest(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.TestServiceServer.Ping(ctx, ping)
|
||||
}
|
||||
|
||||
func (s *failingService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
|
||||
if err := s.maybeFailRequest(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.TestServiceServer.PingList(ping, stream)
|
||||
}
|
||||
|
||||
func (s *failingService) PingStream(stream pb_testproto.TestService_PingStreamServer) error {
|
||||
if err := s.maybeFailRequest(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.TestServiceServer.PingStream(stream)
|
||||
}
|
||||
|
||||
func TestRetrySuite(t *testing.T) {
|
||||
service := &failingService{
|
||||
TestServiceServer: &grpc_testing.TestPingService{T: t},
|
||||
}
|
||||
unaryInterceptor := grpc_retry.UnaryClientInterceptor(
|
||||
grpc_retry.WithCodes(retriableErrors...),
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(retryTimeout)),
|
||||
)
|
||||
streamInterceptor := grpc_retry.StreamClientInterceptor(
|
||||
grpc_retry.WithCodes(retriableErrors...),
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(retryTimeout)),
|
||||
)
|
||||
s := &RetrySuite{
|
||||
srv: service,
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: service,
|
||||
ClientOpts: []grpc.DialOption{
|
||||
grpc.WithStreamInterceptor(streamInterceptor),
|
||||
grpc.WithUnaryInterceptor(unaryInterceptor),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type RetrySuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
srv *failingService
|
||||
}
|
||||
|
||||
func (s *RetrySuite) SetupTest() {
|
||||
s.srv.resetFailingConfiguration( /* don't fail */ 0, codes.OK, noSleep)
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestUnary_FailsOnNonRetriableError() {
|
||||
s.srv.resetFailingConfiguration(5, codes.Internal, noSleep)
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.Error(s.T(), err, "error must occur from the failing service")
|
||||
require.Equal(s.T(), codes.Internal, grpc.Code(err), "failure code must come from retrier")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestCallOptionsDontPanicWithoutInterceptor() {
|
||||
// Fix for https://github.com/grpc-ecosystem/go-grpc-middleware/issues/37
|
||||
// If this code doesn't panic, that's good.
|
||||
s.srv.resetFailingConfiguration(100, codes.DataLoss, noSleep) // doesn't matter all requests should fail
|
||||
nonMiddlewareClient := s.NewClient()
|
||||
_, err := nonMiddlewareClient.Ping(s.SimpleCtx(), goodPing,
|
||||
grpc_retry.WithMax(5),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1*time.Millisecond)),
|
||||
grpc_retry.WithCodes(codes.DataLoss),
|
||||
grpc_retry.WithPerRetryTimeout(1*time.Millisecond),
|
||||
)
|
||||
require.Error(s.T(), err)
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestServerStream_FailsOnNonRetriableError() {
|
||||
s.srv.resetFailingConfiguration(5, codes.Internal, noSleep)
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "should not fail on establishing the stream")
|
||||
_, err = stream.Recv()
|
||||
require.Error(s.T(), err, "error must occur from the failing service")
|
||||
require.Equal(s.T(), codes.Internal, grpc.Code(err), "failure code must come from retrier")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestUnary_SucceedsOnRetriableError() {
|
||||
s.srv.resetFailingConfiguration(3, codes.DataLoss, noSleep) // see retriable_errors
|
||||
out, err := s.Client.Ping(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "the third invocation should succeed")
|
||||
require.NotNil(s.T(), out, "Pong must be not nill")
|
||||
require.EqualValues(s.T(), 3, s.srv.requestCount(), "three requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestUnary_OverrideFromDialOpts() {
|
||||
s.srv.resetFailingConfiguration(5, codes.ResourceExhausted, noSleep) // default is 3 and retriable_errors
|
||||
out, err := s.Client.Ping(s.SimpleCtx(), goodPing, grpc_retry.WithCodes(codes.ResourceExhausted), grpc_retry.WithMax(5))
|
||||
require.NoError(s.T(), err, "the fifth invocation should succeed")
|
||||
require.NotNil(s.T(), out, "Pong must be not nill")
|
||||
require.EqualValues(s.T(), 5, s.srv.requestCount(), "five requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestUnary_PerCallDeadline_Succeeds() {
|
||||
// This tests 5 requests, with first 4 sleeping for 10 millisecond, and the retry logic firing
|
||||
// a retry call with a 5 millisecond deadline. The 5th one doesn't sleep and succeeds.
|
||||
deadlinePerCall := 5 * time.Millisecond
|
||||
s.srv.resetFailingConfiguration(5, codes.NotFound, 2*deadlinePerCall)
|
||||
out, err := s.Client.Ping(s.SimpleCtx(), goodPing, grpc_retry.WithPerRetryTimeout(deadlinePerCall),
|
||||
grpc_retry.WithMax(5))
|
||||
require.NoError(s.T(), err, "the fifth invocation should succeed")
|
||||
require.NotNil(s.T(), out, "Pong must be not nill")
|
||||
require.EqualValues(s.T(), 5, s.srv.requestCount(), "five requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestUnary_PerCallDeadline_FailsOnParent() {
|
||||
// This tests that the parent context (passed to the invocation) takes precedence over retries.
|
||||
// The parent context has 150 milliseconds of deadline.
|
||||
// Each failed call sleeps for 100milliseconds, and there is 5 milliseconds between each one.
|
||||
// This means that unlike in TestUnary_PerCallDeadline_Succeeds, the fifth successful call won't
|
||||
// be made.
|
||||
parentDeadline := 150 * time.Millisecond
|
||||
deadlinePerCall := 50 * time.Millisecond
|
||||
// All 0-4 requests should have 10 millisecond sleeps and deadline, while the last one works.
|
||||
s.srv.resetFailingConfiguration(5, codes.NotFound, 2*deadlinePerCall)
|
||||
ctx, _ := context.WithTimeout(context.TODO(), parentDeadline)
|
||||
_, err := s.Client.Ping(ctx, goodPing, grpc_retry.WithPerRetryTimeout(deadlinePerCall),
|
||||
grpc_retry.WithMax(5))
|
||||
require.Error(s.T(), err, "the retries must fail due to context deadline exceeded")
|
||||
require.Equal(s.T(), codes.DeadlineExceeded, grpc.Code(err), "failre code must be a gRPC error of Deadline class")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestServerStream_SucceedsOnRetriableError() {
|
||||
s.srv.resetFailingConfiguration(3, codes.DataLoss, noSleep) // see retriable_errors
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
|
||||
require.NoError(s.T(), err, "establishing the connection must always succeed")
|
||||
s.assertPingListWasCorrect(stream)
|
||||
require.EqualValues(s.T(), 3, s.srv.requestCount(), "three requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestServerStream_OverrideFromContext() {
|
||||
s.srv.resetFailingConfiguration(5, codes.ResourceExhausted, noSleep) // default is 3 and retriable_errors
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing, grpc_retry.WithCodes(codes.ResourceExhausted), grpc_retry.WithMax(5))
|
||||
require.NoError(s.T(), err, "establishing the connection must always succeed")
|
||||
s.assertPingListWasCorrect(stream)
|
||||
require.EqualValues(s.T(), 5, s.srv.requestCount(), "three requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestServerStream_PerCallDeadline_Succeeds() {
|
||||
// This tests 5 requests, with first 4 sleeping for 100 millisecond, and the retry logic firing
|
||||
// a retry call with a 50 millisecond deadline. The 5th one doesn't sleep and succeeds.
|
||||
deadlinePerCall := 50 * time.Millisecond
|
||||
s.srv.resetFailingConfiguration(5, codes.NotFound, 2*deadlinePerCall)
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing, grpc_retry.WithPerRetryTimeout(deadlinePerCall),
|
||||
grpc_retry.WithMax(5))
|
||||
require.NoError(s.T(), err, "establishing the connection must always succeed")
|
||||
s.assertPingListWasCorrect(stream)
|
||||
require.EqualValues(s.T(), 5, s.srv.requestCount(), "three requests should have been made")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestServerStream_PerCallDeadline_FailsOnParent() {
|
||||
// This tests that the parent context (passed to the invocation) takes precedence over retries.
|
||||
// The parent context has 150 milliseconds of deadline.
|
||||
// Each failed call sleeps for 50milliseconds, and there is 25 milliseconds between each one.
|
||||
// This means that unlike in TestServerStream_PerCallDeadline_Succeeds, the fifth successful call won't
|
||||
// be made.
|
||||
parentDeadline := 150 * time.Millisecond
|
||||
deadlinePerCall := 50 * time.Millisecond
|
||||
// All 0-4 requests should have 10 millisecond sleeps and deadline, while the last one works.
|
||||
s.srv.resetFailingConfiguration(5, codes.NotFound, 2*deadlinePerCall)
|
||||
parentCtx, _ := context.WithTimeout(context.TODO(), parentDeadline)
|
||||
stream, err := s.Client.PingList(parentCtx, goodPing, grpc_retry.WithPerRetryTimeout(deadlinePerCall),
|
||||
grpc_retry.WithMax(5))
|
||||
require.NoError(s.T(), err, "establishing the connection must always succeed")
|
||||
_, err = stream.Recv()
|
||||
require.Equal(s.T(), codes.DeadlineExceeded, grpc.Code(err), "failre code must be a gRPC error of Deadline class")
|
||||
}
|
||||
|
||||
func (s *RetrySuite) assertPingListWasCorrect(stream pb_testproto.TestService_PingListClient) {
|
||||
count := 0
|
||||
for {
|
||||
pong, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NotNil(s.T(), pong, "received values must not be nill")
|
||||
require.NoError(s.T(), err, "no errors during receive on client side")
|
||||
require.Equal(s.T(), goodPing.Value, pong.Value, "the returned pong contained the outgoing ping")
|
||||
count += 1
|
||||
}
|
||||
require.EqualValues(s.T(), grpc_testing.ListResponseCount, count, "should have received all ping items")
|
||||
}
|
||||
|
||||
type trackedInterceptor struct {
|
||||
called int
|
||||
}
|
||||
|
||||
func (ti *trackedInterceptor) UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ti.called++
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
|
||||
func (ti *trackedInterceptor) StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ti.called++
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
|
||||
func TestChainedRetrySuite(t *testing.T) {
|
||||
service := &failingService{
|
||||
TestServiceServer: &grpc_testing.TestPingService{T: t},
|
||||
}
|
||||
preRetryInterceptor := &trackedInterceptor{}
|
||||
postRetryInterceptor := &trackedInterceptor{}
|
||||
s := &ChainedRetrySuite{
|
||||
srv: service,
|
||||
preRetryInterceptor: preRetryInterceptor,
|
||||
postRetryInterceptor: postRetryInterceptor,
|
||||
InterceptorTestSuite: &grpc_testing.InterceptorTestSuite{
|
||||
TestService: service,
|
||||
ClientOpts: []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(preRetryInterceptor.UnaryClientInterceptor, grpc_retry.UnaryClientInterceptor(), postRetryInterceptor.UnaryClientInterceptor)),
|
||||
grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(preRetryInterceptor.StreamClientInterceptor, grpc_retry.StreamClientInterceptor(), postRetryInterceptor.StreamClientInterceptor)),
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type ChainedRetrySuite struct {
|
||||
*grpc_testing.InterceptorTestSuite
|
||||
srv *failingService
|
||||
preRetryInterceptor *trackedInterceptor
|
||||
postRetryInterceptor *trackedInterceptor
|
||||
}
|
||||
|
||||
func (s *ChainedRetrySuite) SetupTest() {
|
||||
s.srv.resetFailingConfiguration( /* don't fail */ 0, codes.OK, noSleep)
|
||||
s.preRetryInterceptor.called = 0
|
||||
s.postRetryInterceptor.called = 0
|
||||
}
|
||||
|
||||
func (s *ChainedRetrySuite) TestUnaryWithChainedInterceptors_NoFailure() {
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing, grpc_retry.WithMax(2))
|
||||
require.NoError(s.T(), err, "the invocation should succeed")
|
||||
require.EqualValues(s.T(), 1, s.srv.requestCount(), "one request should have been made")
|
||||
require.EqualValues(s.T(), 1, s.preRetryInterceptor.called, "pre-retry interceptor should be called once")
|
||||
require.EqualValues(s.T(), 1, s.postRetryInterceptor.called, "post-retry interceptor should be called once")
|
||||
}
|
||||
|
||||
func (s *ChainedRetrySuite) TestUnaryWithChainedInterceptors_WithRetry() {
|
||||
s.srv.resetFailingConfiguration(2, codes.Unavailable, noSleep)
|
||||
_, err := s.Client.Ping(s.SimpleCtx(), goodPing, grpc_retry.WithMax(2))
|
||||
require.NoError(s.T(), err, "the second invocation should succeed")
|
||||
require.EqualValues(s.T(), 2, s.srv.requestCount(), "two requests should have been made")
|
||||
require.EqualValues(s.T(), 1, s.preRetryInterceptor.called, "pre-retry interceptor should be called once")
|
||||
require.EqualValues(s.T(), 2, s.postRetryInterceptor.called, "post-retry interceptor should be called twice")
|
||||
}
|
||||
|
||||
func (s *ChainedRetrySuite) TestStreamWithChainedInterceptors_NoFailure() {
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing, grpc_retry.WithMax(2))
|
||||
require.NoError(s.T(), err, "the invocation should succeed")
|
||||
_, err = stream.Recv()
|
||||
require.NoError(s.T(), err, "the Recv should succeed")
|
||||
require.EqualValues(s.T(), 1, s.srv.requestCount(), "one request should have been made")
|
||||
require.EqualValues(s.T(), 1, s.preRetryInterceptor.called, "pre-retry interceptor should be called once")
|
||||
require.EqualValues(s.T(), 1, s.postRetryInterceptor.called, "post-retry interceptor should be called once")
|
||||
}
|
||||
|
||||
func (s *ChainedRetrySuite) TestStreamWithChainedInterceptors_WithRetry() {
|
||||
s.srv.resetFailingConfiguration(2, codes.Unavailable, noSleep)
|
||||
stream, err := s.Client.PingList(s.SimpleCtx(), goodPing, grpc_retry.WithMax(2))
|
||||
require.NoError(s.T(), err, "the second invocation should succeed")
|
||||
_, err = stream.Recv()
|
||||
require.NoError(s.T(), err, "the Recv should succeed")
|
||||
require.EqualValues(s.T(), 2, s.srv.requestCount(), "two requests should have been made")
|
||||
require.EqualValues(s.T(), 1, s.preRetryInterceptor.called, "pre-retry interceptor should be called once")
|
||||
require.EqualValues(s.T(), 2, s.postRetryInterceptor.called, "post-retry interceptor should be called twice")
|
||||
}
|
||||
42
vendor/github.com/grpc-ecosystem/go-grpc-middleware/scripts/docs.sh
generated
vendored
Executable file
42
vendor/github.com/grpc-ecosystem/go-grpc-middleware/scripts/docs.sh
generated
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# Script that checks the code for errors.
|
||||
|
||||
GOBIN=${GOBIN:="$GOPATH/bin"}
|
||||
|
||||
function print_real_go_files {
|
||||
grep --files-without-match 'DO NOT EDIT!' $(find . -iname '*.go') --exclude=./vendor/*
|
||||
}
|
||||
|
||||
function generate_markdown {
|
||||
echo "Generating Github markdown"
|
||||
oldpwd=$(pwd)
|
||||
for i in $(find . -iname 'doc.go' -not -path "*vendor/*"); do
|
||||
realdir=$(cd $(dirname ${i}) && pwd -P)
|
||||
package=${realdir##${GOPATH}/src/}
|
||||
echo "$package"
|
||||
cd ${dir}
|
||||
${GOBIN}/godoc2ghmd -ex -file DOC.md ${package}
|
||||
ln -s DOC.md README.md 2> /dev/null # can fail
|
||||
cd ${oldpwd}
|
||||
done;
|
||||
}
|
||||
|
||||
function generate {
|
||||
go get github.com/devnev/godoc2ghmd
|
||||
generate_markdown
|
||||
echo "returning $?"
|
||||
}
|
||||
|
||||
function check {
|
||||
generate
|
||||
count=$(git diff --numstat | wc -l | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
|
||||
echo $count
|
||||
if [ "$count" = "0" ]; then
|
||||
return 0
|
||||
else
|
||||
echo "Your markdown docs seem to be out of sync with the package docs. Please run make and consult CONTRIBUTING.MD"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
"$@"
|
||||
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/scripts/test_all.sh
generated
vendored
Executable file
14
vendor/github.com/grpc-ecosystem/go-grpc-middleware/scripts/test_all.sh
generated
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
for d in $(go list ./... | grep -v vendor); do
|
||||
echo -e "TESTS FOR: for \033[0;35m${d}\033[0m"
|
||||
go test -race -v -coverprofile=profile.coverage.out -covermode=atomic $d
|
||||
if [ -f profile.coverage.out ]; then
|
||||
cat profile.coverage.out >> coverage.txt
|
||||
rm profile.coverage.out
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
BIN
vendor/github.com/grpc-ecosystem/go-grpc-middleware/slack.png
generated
vendored
Normal file
BIN
vendor/github.com/grpc-ecosystem/go-grpc-middleware/slack.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
5
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/gen_cert.sh
generated
vendored
Normal file
5
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/gen_cert.sh
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Regenerate the self-signed certificate for local host.
|
||||
|
||||
openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 3650 -keyout localhost.key -out localhost.crt
|
||||
|
||||
24
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/localhost.crt
generated
vendored
Normal file
24
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/localhost.crt
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIECTCCAvGgAwIBAgIJAOizA5bposrzMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
|
||||
BAYTAlVLMRMwEQYDVQQIEwpTb21lLVN0YXRlMQ8wDQYDVQQHEwZMb25kb24xFzAV
|
||||
BgNVBAoTDkdycGMgRWNvc3lzdGVtMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTcw
|
||||
ODI1MDc0NTMyWhcNMjcwODIzMDc0NTMyWjBgMQswCQYDVQQGEwJVSzETMBEGA1UE
|
||||
CBMKU29tZS1TdGF0ZTEPMA0GA1UEBxMGTG9uZG9uMRcwFQYDVQQKEw5HcnBjIEVj
|
||||
b3N5c3RlbTESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEA2uDY9N8eGScSGAr/DIeFxyCrytqCkYvqVPYqrZv4VOZvyfak
|
||||
Un7bW4q8zeNWwj64sU0al7Bz8KBmAM7raLnkvkZxmXUCls+Yw8BgXQr+1aDvYYd0
|
||||
JIVJvQqQd1oNXQeZs6GXfalCXh9mhHkJIHOm2ynk2GtS81NQik7ZMg+W3qygeK8O
|
||||
40vbnYxqq2zkvub1dztxuApUzLmuXxLZRo+3faZZetNZ1eMCuqyqzibDRwcYhr3a
|
||||
soC8d+6wWFobwV5AX7h7+qnS6mclESkN7FHMZT1r3OEvix012ry74xl79ploae8d
|
||||
1dkR45C1DEaxwHYz7zJgP5IOXfKcv6KJhgEiYwIDAQABo4HFMIHCMB0GA1UdDgQW
|
||||
BBRXiz6enaVw2tDnZ6/fEuW2USDSKjCBkgYDVR0jBIGKMIGHgBRXiz6enaVw2tDn
|
||||
Z6/fEuW2USDSKqFkpGIwYDELMAkGA1UEBhMCVUsxEzARBgNVBAgTClNvbWUtU3Rh
|
||||
dGUxDzANBgNVBAcTBkxvbmRvbjEXMBUGA1UEChMOR3JwYyBFY29zeXN0ZW0xEjAQ
|
||||
BgNVBAMTCWxvY2FsaG9zdIIJAOizA5bposrzMAwGA1UdEwQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggEBADFmrqug4HYCtcy2F61o53vXIJQ6SyGwPCE5gOq07tkEz2id
|
||||
oKcb2qNF1Zh+lOQ9GkvMkfzop0a4nhW8ra28gEBwhESS/aGKc7/isZN4ZKyy9JvJ
|
||||
taTmVgXGp8s0w5dCXqDf3YU3XpdF53J1R7dyEfey3nEjMPRj/Ebs7rmpfRzOI4cX
|
||||
6SJWJ71ShXK/IZbSVfobTzRdmAlz7uiwp2OVveGqcpQk46wgVoduwrC2hCe4wI6y
|
||||
Dlo/GLmyCVlIiPaNA32sw+lca02Z5EXcQutRov2R/lUfoUCS/gOjCD7hVZXSRo+9
|
||||
tFmGR8IuQDFseOW7CWTWqkiwu9F80HXmovboMKQ=
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/localhost.key
generated
vendored
Normal file
27
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/certs/localhost.key
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA2uDY9N8eGScSGAr/DIeFxyCrytqCkYvqVPYqrZv4VOZvyfak
|
||||
Un7bW4q8zeNWwj64sU0al7Bz8KBmAM7raLnkvkZxmXUCls+Yw8BgXQr+1aDvYYd0
|
||||
JIVJvQqQd1oNXQeZs6GXfalCXh9mhHkJIHOm2ynk2GtS81NQik7ZMg+W3qygeK8O
|
||||
40vbnYxqq2zkvub1dztxuApUzLmuXxLZRo+3faZZetNZ1eMCuqyqzibDRwcYhr3a
|
||||
soC8d+6wWFobwV5AX7h7+qnS6mclESkN7FHMZT1r3OEvix012ry74xl79ploae8d
|
||||
1dkR45C1DEaxwHYz7zJgP5IOXfKcv6KJhgEiYwIDAQABAoIBAEC/gQ63LhUsvKqG
|
||||
HjrboFRussM4bqWVqUVm6Osgl46t+GXbY3LP+h4TZZTWaqTdnw88CuDSnwmQuXwF
|
||||
uhfp3eP3vMUzmwd38+38a8U6anZRvF6yjkX7c/vLMz9LUWayx+IerEzNFEf5q/8G
|
||||
Ow/xhmuQ8hSfcxNuEwZHGvM3L19BT852iXCh4uEJyR5G0e/5RI7f/bBieLo4exO9
|
||||
EO/vcxK+fcZk4XAwIa0UWdWfzk+lgTDfw0kG1fNm5QJgykGSpXw51GNrakcGsBX1
|
||||
ocu2DM6wIU7cb+orTmxRM4ra+NV4oiH63GVB79mnQtQ1PSwZv+v+tQdx/APu12MX
|
||||
P23qfZECgYEA7T9NFbKQxMQxWc4YoMhtUNrURhRsgLtbkRLzzScfDhCAf576HQBe
|
||||
vO3oPIF4b1i/SHQijLTjHuP32az1n9FJYakRTNUIYlKOPpRC8snULtvbhVUVoRXZ
|
||||
iVnX6Nk5ISK5CpVQP9MDscbkmXejTSld4IK8y8f5eUV899rbfHq8prkCgYEA7C3Z
|
||||
txJytRhwanUykG8fwF2KkJxWrgrUG6FyxQPt9hqIGbtG7naN0kucjcDmhywe5Luq
|
||||
2DGxirEs08YpQKgyP+2o5+d6hCuAweA3HsZS4MdQO45IMubV1EEy/4mqODLxvGhr
|
||||
0BQZ08px692iEUOQHmxbynIx8v7UwObbAncBg/sCgYANr/o9hzjM8Vi7iqVnGUkd
|
||||
QhK/HEyKPMTyoLpKGriy8gbH1h8t7yiIE92zJ/EP03ntry2TGpiD3rkc6Nqe5s8q
|
||||
ADwMR7odGhA00g7uvPAt5+dJUPhXe9A6GWHYgDfIjp1IXAbuN7ld11C2/e0A9aMB
|
||||
4ekMhLQC6BxmnEzPaLFbmQKBgQCR1i4d0ZJrKNAhrISwky20Bw7NBPyxM5aOCT9p
|
||||
UGfcE73+xZmjHimstWvSTE8NIL1i2E9CeEz9KzJXAoHunu2IdCRcLp+EhahYDy96
|
||||
HVSH4As2+0CxzcAz9J6dFFC7qxmCEaihMAhSsyttTcoJhwKzBo4XkDQvrBOLmgGa
|
||||
oAmbiQKBgGacQ3nzSjlAfTqUKZc3wHKO2BNxSQfq7BejvGAR9bH/qUvB0Q7r7nb+
|
||||
98Epzx8K+om7BRh5cQc6QQIP4gUIi+K7IIaPLgoUwo3Uj+nq0m3cT+iWQKEwo6c3
|
||||
wtMi5c6K0MfzcMk0RVxcraL3isAV1zZMPsuAD5EzCsYF5a+opQNq
|
||||
-----END RSA PRIVATE KEY-----
|
||||
10
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/gogotestproto/Makefile
generated
vendored
Normal file
10
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/gogotestproto/Makefile
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
all: test_go
|
||||
|
||||
fields_go: fields.proto
|
||||
PATH="${GOPATH}/bin:${PATH}" protoc \
|
||||
-I. \
|
||||
-I${GOPATH}/src \
|
||||
--gogo_out=plugins=grpc:. \
|
||||
fields.proto
|
||||
|
||||
|
||||
163
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/gogotestproto/fields.pb.go
generated
vendored
Normal file
163
vendor/github.com/grpc-ecosystem/go-grpc-middleware/testing/gogotestproto/fields.pb.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
// Code generated by protoc-gen-gogo.
|
||||
// source: fields.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package mwitkow_testproto is a generated protocol buffer package.
|
||||
|
||||
This file is used for testing discovery of log fields from requests using reflection and gogo proto more tags.
|
||||
|
||||
It is generated from these files:
|
||||
fields.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Metadata
|
||||
PingId
|
||||
Ping
|
||||
PingRequest
|
||||
Pong
|
||||
PongRequest
|
||||
*/
|
||||
package mwitkow_testproto
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Metadata struct {
|
||||
Tags []string `protobuf:"bytes,1,rep,name=tags" json:"tags,omitempty" log_field:"meta_tags"`
|
||||
}
|
||||
|
||||
func (m *Metadata) Reset() { *m = Metadata{} }
|
||||
func (m *Metadata) String() string { return proto.CompactTextString(m) }
|
||||
func (*Metadata) ProtoMessage() {}
|
||||
func (*Metadata) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{0} }
|
||||
|
||||
type PingId struct {
|
||||
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty" log_field:"ping_id"`
|
||||
}
|
||||
|
||||
func (m *PingId) Reset() { *m = PingId{} }
|
||||
func (m *PingId) String() string { return proto.CompactTextString(m) }
|
||||
func (*PingId) ProtoMessage() {}
|
||||
func (*PingId) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{1} }
|
||||
|
||||
type Ping struct {
|
||||
Id *PingId `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Ping) Reset() { *m = Ping{} }
|
||||
func (m *Ping) String() string { return proto.CompactTextString(m) }
|
||||
func (*Ping) ProtoMessage() {}
|
||||
func (*Ping) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{2} }
|
||||
|
||||
func (m *Ping) GetId() *PingId {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PingRequest struct {
|
||||
Ping *Ping `protobuf:"bytes,1,opt,name=ping" json:"ping,omitempty"`
|
||||
Meta *Metadata `protobuf:"bytes,2,opt,name=meta" json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PingRequest) Reset() { *m = PingRequest{} }
|
||||
func (m *PingRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PingRequest) ProtoMessage() {}
|
||||
func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{3} }
|
||||
|
||||
func (m *PingRequest) GetPing() *Ping {
|
||||
if m != nil {
|
||||
return m.Ping
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PingRequest) GetMeta() *Metadata {
|
||||
if m != nil {
|
||||
return m.Meta
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Pong struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" log_field:"pong_id"`
|
||||
}
|
||||
|
||||
func (m *Pong) Reset() { *m = Pong{} }
|
||||
func (m *Pong) String() string { return proto.CompactTextString(m) }
|
||||
func (*Pong) ProtoMessage() {}
|
||||
func (*Pong) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{4} }
|
||||
|
||||
type PongRequest struct {
|
||||
Pong *Pong `protobuf:"bytes,1,opt,name=pong" json:"pong,omitempty"`
|
||||
Meta *Metadata `protobuf:"bytes,2,opt,name=meta" json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PongRequest) Reset() { *m = PongRequest{} }
|
||||
func (m *PongRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PongRequest) ProtoMessage() {}
|
||||
func (*PongRequest) Descriptor() ([]byte, []int) { return fileDescriptorFields, []int{5} }
|
||||
|
||||
func (m *PongRequest) GetPong() *Pong {
|
||||
if m != nil {
|
||||
return m.Pong
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PongRequest) GetMeta() *Metadata {
|
||||
if m != nil {
|
||||
return m.Meta
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Metadata)(nil), "mwitkow.testproto.Metadata")
|
||||
proto.RegisterType((*PingId)(nil), "mwitkow.testproto.PingId")
|
||||
proto.RegisterType((*Ping)(nil), "mwitkow.testproto.Ping")
|
||||
proto.RegisterType((*PingRequest)(nil), "mwitkow.testproto.PingRequest")
|
||||
proto.RegisterType((*Pong)(nil), "mwitkow.testproto.Pong")
|
||||
proto.RegisterType((*PongRequest)(nil), "mwitkow.testproto.PongRequest")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("fields.proto", fileDescriptorFields) }
|
||||
|
||||
var fileDescriptorFields = []byte{
|
||||
// 304 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x50, 0x41, 0x4e, 0xeb, 0x30,
|
||||
0x14, 0xfc, 0xe9, 0x77, 0x2b, 0xea, 0xb2, 0xc1, 0x80, 0xda, 0x82, 0x44, 0x2b, 0x6f, 0x28, 0x42,
|
||||
0x4d, 0x44, 0x59, 0xc1, 0xb2, 0x1b, 0xc4, 0x02, 0xa9, 0xf2, 0x05, 0x22, 0x07, 0xbb, 0xc6, 0x6a,
|
||||
0x92, 0x57, 0x88, 0x43, 0xaf, 0xc2, 0xe9, 0xba, 0xe1, 0x06, 0x3d, 0x01, 0xf2, 0x0b, 0xa0, 0xa0,
|
||||
0x02, 0x0b, 0x76, 0x9e, 0xf7, 0x66, 0xe6, 0xcd, 0x98, 0xee, 0xce, 0xad, 0x4e, 0x55, 0x11, 0x2e,
|
||||
0x9f, 0xc0, 0x01, 0xdb, 0xcb, 0x56, 0xd6, 0x2d, 0x60, 0x15, 0x3a, 0x5d, 0x38, 0x1c, 0x1d, 0x8d,
|
||||
0x8d, 0x75, 0x0f, 0x65, 0x12, 0xde, 0x43, 0x16, 0x19, 0x30, 0x10, 0xe1, 0x38, 0x29, 0xe7, 0x88,
|
||||
0x10, 0xe0, 0xab, 0x72, 0xe0, 0x57, 0x74, 0xe7, 0x4e, 0x3b, 0xa9, 0xa4, 0x93, 0x6c, 0x4c, 0x89,
|
||||
0x93, 0xa6, 0xe8, 0x05, 0xc3, 0xff, 0xa3, 0xf6, 0xb4, 0xbf, 0x59, 0x0f, 0x0e, 0x53, 0x30, 0x31,
|
||||
0x5e, 0xbc, 0xe6, 0x99, 0x76, 0x32, 0xf6, 0x7b, 0x2e, 0x90, 0xc6, 0x2f, 0x68, 0x6b, 0x66, 0x73,
|
||||
0x73, 0xab, 0xd8, 0x29, 0x6d, 0x58, 0xd5, 0x0b, 0x86, 0xc1, 0xa8, 0x39, 0xed, 0x6e, 0xd6, 0x83,
|
||||
0xfd, 0x9a, 0x6c, 0x69, 0x73, 0x13, 0x5b, 0xc5, 0x45, 0xc3, 0x2a, 0x7e, 0x43, 0x89, 0x97, 0xb0,
|
||||
0xb3, 0x4f, 0x41, 0x67, 0xd2, 0x0f, 0xb7, 0x4a, 0x84, 0x95, 0xaf, 0x97, 0xb0, 0x03, 0xda, 0x7c,
|
||||
0x96, 0x69, 0xa9, 0x7b, 0x8d, 0x61, 0x30, 0x6a, 0x8b, 0x0a, 0xf0, 0x05, 0xed, 0x78, 0x8e, 0xd0,
|
||||
0x8f, 0xa5, 0x2e, 0x1c, 0x3b, 0xa7, 0xc4, 0xdf, 0x79, 0x77, 0xec, 0xfe, 0xe0, 0x28, 0x90, 0xc4,
|
||||
0x22, 0x4a, 0x7c, 0x17, 0x34, 0xec, 0x4c, 0x8e, 0xbf, 0x21, 0x7f, 0xfc, 0x88, 0x40, 0x22, 0x8f,
|
||||
0x28, 0x99, 0x41, 0x6e, 0x6a, 0x35, 0xdb, 0xdb, 0x35, 0xa1, 0x56, 0xd3, 0xa7, 0x83, 0xaf, 0xe9,
|
||||
0xe0, 0xf7, 0x74, 0x80, 0xe9, 0xe0, 0x0f, 0xe9, 0xa6, 0xe4, 0xe5, 0xf5, 0xe4, 0x5f, 0xd2, 0xc2,
|
||||
0xdd, 0xe5, 0x5b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x67, 0x44, 0x3b, 0x20, 0x02, 0x00, 0x00,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user