diff options
author | Jacob Vosmaer <jacob@gitlab.com> | 2018-04-12 16:42:12 +0300 |
---|---|---|
committer | Jacob Vosmaer <jacob@gitlab.com> | 2018-04-12 16:42:12 +0300 |
commit | 2195e6ca9544764cf0b64daafd11d591de1ae4ea (patch) | |
tree | bee50cc9d62936930584e81e74a2c20d007ae083 /vendor/github.com/grpc-ecosystem | |
parent | 1429da85cb13d6d2105b475dac600832d756affb (diff) |
Add more gRPC dependencies
Diffstat (limited to 'vendor/github.com/grpc-ecosystem')
61 files changed, 5749 insertions, 0 deletions
diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md new file mode 100644 index 00000000..511d953a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock new file mode 100644 index 00000000..ebdcb75a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml new file mode 100644 index 00000000..0a7d4c1c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml @@ -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" diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE new file mode 100644 index 00000000..b2b06503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE @@ -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.
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md new file mode 100644 index 00000000..4fe246f2 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md @@ -0,0 +1,86 @@ +# Go gRPC Middleware + +[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware) +[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-middleware)](https://goreportcard.com/report/github.com/grpc-ecosystem/go-grpc-middleware) +[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware) +[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/?badge) +[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware) +[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) +[![quality: production](https://img.shields.io/badge/quality-production-orange.svg)](#status) + + +[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. diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md new file mode 100644 index 00000000..5834112b --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md new file mode 100644 index 00000000..5834112b --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go new file mode 100644 index 00000000..a35c95fa --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go @@ -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) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go new file mode 100644 index 00000000..0550f023 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go new file mode 100644 index 00000000..50020653 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go @@ -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 +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go new file mode 100644 index 00000000..45a2f5f4 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go @@ -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...)) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go new file mode 100644 index 00000000..71689503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md new file mode 100644 index 00000000..9deb4722 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md new file mode 100644 index 00000000..9deb4722 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go new file mode 100644 index 00000000..f725d038 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go new file mode 100644 index 00000000..ce8bb298 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md new file mode 100644 index 00000000..77ebb64c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md @@ -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 +``` +PayloadStreamServerInterceptor 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 +``` +PayloadUnaryServerInterceptor 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 +``` +PayloadUnaryServerInterceptor 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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md new file mode 100644 index 00000000..77ebb64c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md @@ -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 +``` +PayloadStreamServerInterceptor 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 +``` +PayloadUnaryServerInterceptor 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 +``` +PayloadUnaryServerInterceptor 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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go new file mode 100644 index 00000000..f7655ff6 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go @@ -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, + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go new file mode 100644 index 00000000..a39bb772 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go @@ -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) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md new file mode 100644 index 00000000..90918029 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md new file mode 100644 index 00000000..fc1d0a92 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go new file mode 100644 index 00000000..ff3e3353 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go @@ -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) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go new file mode 100644 index 00000000..95803fb6 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go new file mode 100644 index 00000000..7fcc0f64 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go @@ -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, + } +) diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go new file mode 100644 index 00000000..df8785f5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go new file mode 100644 index 00000000..c0f6c5ab --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go @@ -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)) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go new file mode 100644 index 00000000..b2b35189 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go @@ -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 +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go new file mode 100644 index 00000000..6ac4d04a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go @@ -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 + } +} + +// PayloadUnaryServerInterceptor 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 + } +} + +// PayloadStreamServerInterceptor 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 +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go new file mode 100644 index 00000000..0711d881 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go @@ -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" +) + +// PayloadUnaryServerInterceptor 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) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile new file mode 100644 index 00000000..4ad54c37 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile @@ -0,0 +1,19 @@ +SHELL="/bin/bash" + +GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/) + +all: vet fmt docs test + +docs: + ./scripts/fixup.sh + +fmt: + go fmt $(GOFILES_NOVENDOR) + +vet: + go vet $(GOFILES_NOVENDOR) + +test: vet + ./scripts/test_all.sh + +.PHONY: all docs validate test diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md new file mode 100644 index 00000000..d4ef78af --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md new file mode 100644 index 00000000..d4ef78af --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md @@ -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)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go new file mode 100644 index 00000000..da40190c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go @@ -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 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go new file mode 100644 index 00000000..c0fb5ac8 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go @@ -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) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go new file mode 100644 index 00000000..e482d7a5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go @@ -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 + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md new file mode 100644 index 00000000..b954b1f9 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md @@ -0,0 +1,188 @@ +# grpc_ctxtags +`import "github.com/grpc-ecosystem/go-grpc-middleware/tags"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +### Request Context Tags +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/peer](https://godoc.org/google.golang.org/grpc/peer) + +## <a name="pkg-index">Index</a> +* [func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}](#CodeGenRequestFieldExtractor) +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithFieldExtractor(f RequestFieldExtractorFunc) Option](#WithFieldExtractor) + * [func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option](#WithFieldExtractorForInitialReq) +* [type RequestFieldExtractorFunc](#RequestFieldExtractorFunc) + * [func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc](#TagBasedRequestFieldExtractor) +* [type Tags](#Tags) + * [func Extract(ctx context.Context) \*Tags](#Extract) + * [func (t \*Tags) Has(key string) bool](#Tags.Has) + * [func (t \*Tags) Set(key string, value interface{}) \*Tags](#Tags.Set) + * [func (t \*Tags) Values() map[string]interface{}](#Tags.Values) + +#### <a name="pkg-examples">Examples</a> +* [Package (InitialisationWithOptions)](#example__initialisationWithOptions) +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [fieldextractor.go](./fieldextractor.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="CodeGenRequestFieldExtractor">func</a> [CodeGenRequestFieldExtractor](./fieldextractor.go#L23) +``` go +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} +``` +CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +These are usually coming from a protoc-plugin that generates additional information based on custom field options. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L26) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L14) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. + +## <a name="Option">type</a> [Option](./options.go#L26) +``` go +type Option func(*options) +``` + +### <a name="WithFieldExtractor">func</a> [WithFieldExtractor](./options.go#L30) +``` go +func WithFieldExtractor(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +unary and server-streamed methods only. + +### <a name="WithFieldExtractorForInitialReq">func</a> [WithFieldExtractorForInitialReq](./options.go#L39) +``` go +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +extracted from the first message from the client. + +## <a name="RequestFieldExtractorFunc">type</a> [RequestFieldExtractorFunc](./fieldextractor.go#L13) +``` go +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} +``` +RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +It is called from tags middleware on arrival of unary request or a server-stream request. +Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. + +### <a name="TagBasedRequestFieldExtractor">func</a> [TagBasedRequestFieldExtractor](./fieldextractor.go#L43) +``` go +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc +``` +TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +These are usualy coming from a protoc-plugin, such as Gogo protobuf. + + message Metadata { + repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; + } + +The tagName is configurable using the tagName variable. Here it would be "log_field". + +## <a name="Tags">type</a> [Tags](./context.go#L17-L19) +``` go +type Tags struct { + // contains filtered or unexported fields +} +``` +Tags is the struct used for storing request tags between Context calls. +This object is *not* thread safe, and should be handled only in the context of the request. + +### <a name="Extract">func</a> [Extract](./context.go#L41) +``` go +func Extract(ctx context.Context) *Tags +``` +Extracts returns a pre-existing Tags object in the Context. +If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. + +### <a name="Tags.Has">func</a> (\*Tags) [Has](./context.go#L28) +``` go +func (t *Tags) Has(key string) bool +``` +Has checks if the given key exists. + +### <a name="Tags.Set">func</a> (\*Tags) [Set](./context.go#L22) +``` go +func (t *Tags) Set(key string, value interface{}) *Tags +``` +Set sets the given key in the metadata tags. + +### <a name="Tags.Values">func</a> (\*Tags) [Values](./context.go#L35) +``` go +func (t *Tags) Values() map[string]interface{} +``` +Values returns a map of key to values. +Do not modify the underlying map, please use Set instead. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md new file mode 100644 index 00000000..b954b1f9 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md @@ -0,0 +1,188 @@ +# grpc_ctxtags +`import "github.com/grpc-ecosystem/go-grpc-middleware/tags"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +### Request Context Tags +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/peer](https://godoc.org/google.golang.org/grpc/peer) + +## <a name="pkg-index">Index</a> +* [func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}](#CodeGenRequestFieldExtractor) +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithFieldExtractor(f RequestFieldExtractorFunc) Option](#WithFieldExtractor) + * [func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option](#WithFieldExtractorForInitialReq) +* [type RequestFieldExtractorFunc](#RequestFieldExtractorFunc) + * [func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc](#TagBasedRequestFieldExtractor) +* [type Tags](#Tags) + * [func Extract(ctx context.Context) \*Tags](#Extract) + * [func (t \*Tags) Has(key string) bool](#Tags.Has) + * [func (t \*Tags) Set(key string, value interface{}) \*Tags](#Tags.Set) + * [func (t \*Tags) Values() map[string]interface{}](#Tags.Values) + +#### <a name="pkg-examples">Examples</a> +* [Package (InitialisationWithOptions)](#example__initialisationWithOptions) +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [fieldextractor.go](./fieldextractor.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="CodeGenRequestFieldExtractor">func</a> [CodeGenRequestFieldExtractor](./fieldextractor.go#L23) +``` go +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} +``` +CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +These are usually coming from a protoc-plugin that generates additional information based on custom field options. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L26) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L14) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. + +## <a name="Option">type</a> [Option](./options.go#L26) +``` go +type Option func(*options) +``` + +### <a name="WithFieldExtractor">func</a> [WithFieldExtractor](./options.go#L30) +``` go +func WithFieldExtractor(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +unary and server-streamed methods only. + +### <a name="WithFieldExtractorForInitialReq">func</a> [WithFieldExtractorForInitialReq](./options.go#L39) +``` go +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +extracted from the first message from the client. + +## <a name="RequestFieldExtractorFunc">type</a> [RequestFieldExtractorFunc](./fieldextractor.go#L13) +``` go +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} +``` +RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +It is called from tags middleware on arrival of unary request or a server-stream request. +Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. + +### <a name="TagBasedRequestFieldExtractor">func</a> [TagBasedRequestFieldExtractor](./fieldextractor.go#L43) +``` go +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc +``` +TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +These are usualy coming from a protoc-plugin, such as Gogo protobuf. + + message Metadata { + repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; + } + +The tagName is configurable using the tagName variable. Here it would be "log_field". + +## <a name="Tags">type</a> [Tags](./context.go#L17-L19) +``` go +type Tags struct { + // contains filtered or unexported fields +} +``` +Tags is the struct used for storing request tags between Context calls. +This object is *not* thread safe, and should be handled only in the context of the request. + +### <a name="Extract">func</a> [Extract](./context.go#L41) +``` go +func Extract(ctx context.Context) *Tags +``` +Extracts returns a pre-existing Tags object in the Context. +If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. + +### <a name="Tags.Has">func</a> (\*Tags) [Has](./context.go#L28) +``` go +func (t *Tags) Has(key string) bool +``` +Has checks if the given key exists. + +### <a name="Tags.Set">func</a> (\*Tags) [Set](./context.go#L22) +``` go +func (t *Tags) Set(key string, value interface{}) *Tags +``` +Set sets the given key in the metadata tags. + +### <a name="Tags.Values">func</a> (\*Tags) [Values](./context.go#L35) +``` go +func (t *Tags) Values() map[string]interface{} +``` +Values returns a map of key to values. +Do not modify the underlying map, please use Set instead. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go new file mode 100644 index 00000000..583025ce --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go @@ -0,0 +1,78 @@ +package grpc_ctxtags + +import ( + "context" +) + +type ctxMarker struct{} + +var ( + // ctxMarkerKey is the Context value marker used by *all* logging middleware. + // The logging middleware object must interf + ctxMarkerKey = &ctxMarker{} + // NoopTags is a trivial, minimum overhead implementation of Tags for which all operations are no-ops. + NoopTags = &noopTags{} +) + +// Tags is the interface used for storing request tags between Context calls. +// The default implementation is *not* thread safe, and should be handled only in the context of the request. +type Tags interface { + // Set sets the given key in the metadata tags. + Set(key string, value interface{}) Tags + // Has checks if the given key exists. + Has(key string) bool + // Values returns a map of key to values. + // Do not modify the underlying map, please use Set instead. + Values() map[string]interface{} +} + +type mapTags struct { + values map[string]interface{} +} + +func (t *mapTags) Set(key string, value interface{}) Tags { + t.values[key] = value + return t +} + +func (t *mapTags) Has(key string) bool { + _, ok := t.values[key] + return ok +} + +func (t *mapTags) Values() map[string]interface{} { + return t.values +} + +type noopTags struct{} + +func (t *noopTags) Set(key string, value interface{}) Tags { + return t +} + +func (t *noopTags) Has(key string) bool { + return false +} + +func (t *noopTags) Values() map[string]interface{} { + return nil +} + +// Extracts returns a pre-existing Tags object in the Context. +// If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. +func Extract(ctx context.Context) Tags { + t, ok := ctx.Value(ctxMarkerKey).(Tags) + if !ok { + return NoopTags + } + + return t +} + +func setInContext(ctx context.Context, tags Tags) context.Context { + return context.WithValue(ctx, ctxMarkerKey, tags) +} + +func newTags() Tags { + return &mapTags{values: make(map[string]interface{})} +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go new file mode 100644 index 00000000..960638d0 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go @@ -0,0 +1,22 @@ +/* +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +Request Context Tags + +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +https://github.com/opentracing/specification/blob/master/semantic_conventions.md +*/ +package grpc_ctxtags diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go new file mode 100644 index 00000000..d87ee438 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go @@ -0,0 +1,85 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +import ( + "reflect" +) + +// RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +// It is called from tags middleware on arrival of unary request or a server-stream request. +// Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} + +type requestFieldsExtractor interface { + // ExtractRequestFields is a method declared on a Protobuf message that extracts fields from the interface. + // The values from the extracted fields should be set in the appendToMap, in order to avoid allocations. + ExtractRequestFields(appendToMap map[string]interface{}) +} + +// CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +// These are usually coming from a protoc-plugin that generates additional information based on custom field options. +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} { + if ext, ok := req.(requestFieldsExtractor); ok { + retMap := make(map[string]interface{}) + ext.ExtractRequestFields(retMap) + if len(retMap) == 0 { + return nil + } + return retMap + } + return nil +} + +// TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +// These are usualy coming from a protoc-plugin, such as Gogo protobuf. +// +// message Metadata { +// repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; +// } +// +// The tagName is configurable using the tagName variable. Here it would be "log_field". +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc { + return func(fullMethod string, req interface{}) map[string]interface{} { + retMap := make(map[string]interface{}) + reflectMessageTags(req, retMap, tagName) + if len(retMap) == 0 { + return nil + } + return retMap + } +} + +func reflectMessageTags(msg interface{}, existingMap map[string]interface{}, tagName string) { + v := reflect.ValueOf(msg) + // Only deal with pointers to structs. + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return + } + // Deref the pointer get to the struct. + v = v.Elem() + t := v.Type() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + kind := field.Kind() + // Only recurse down direct pointers, which should only be to nested structs. + if kind == reflect.Ptr { + reflectMessageTags(field.Interface(), existingMap, tagName) + } + // In case of arrays/splices (repeated fields) go down to the concrete type. + if kind == reflect.Array || kind == reflect.Slice { + if field.Len() == 0 { + continue + } + kind = field.Index(0).Kind() + } + // Only be interested in + if (kind >= reflect.Bool && kind <= reflect.Float64) || kind == reflect.String { + if tag := t.Field(i).Tag.Get(tagName); tag != "" { + existingMap[tag] = field.Interface() + } + } + } + return +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go new file mode 100644 index 00000000..038afd26 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go @@ -0,0 +1,83 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" +) + +// UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor { + o := evaluateOptions(opts) + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := newTagsForCtx(ctx) + if o.requestFieldsFunc != nil { + setRequestFieldTags(newCtx, o.requestFieldsFunc, info.FullMethod, req) + } + return handler(newCtx, req) + } +} + +// StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor { + o := evaluateOptions(opts) + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + newCtx := newTagsForCtx(stream.Context()) + if o.requestFieldsFunc == nil { + // Short-circuit, don't do the expensive bit of allocating a wrappedStream. + wrappedStream := grpc_middleware.WrapServerStream(stream) + wrappedStream.WrappedContext = newCtx + return handler(srv, wrappedStream) + } + wrapped := &wrappedStream{stream, info, o, newCtx, true} + err := handler(srv, wrapped) + return err + } +} + +// wrappedStream is a thin wrapper around grpc.ServerStream that allows modifying context and extracts log fields from the initial message. +type wrappedStream struct { + grpc.ServerStream + info *grpc.StreamServerInfo + opts *options + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context + initial bool +} + +// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() +func (w *wrappedStream) Context() context.Context { + return w.WrappedContext +} + +func (w *wrappedStream) RecvMsg(m interface{}) error { + err := w.ServerStream.RecvMsg(m) + // We only do log fields extraction on the single-request of a server-side stream. + if !w.info.IsClientStream || w.opts.requestFieldsFromInitial && w.initial { + w.initial = false + + setRequestFieldTags(w.Context(), w.opts.requestFieldsFunc, w.info.FullMethod, m) + } + return err +} + +func newTagsForCtx(ctx context.Context) context.Context { + t := newTags() + if peer, ok := peer.FromContext(ctx); ok { + t.Set("peer.address", peer.Addr.String()) + } + return setInContext(ctx, t) +} + +func setRequestFieldTags(ctx context.Context, f RequestFieldExtractorFunc, fullMethodName string, req interface{}) { + if valMap := f(fullMethodName, req); valMap != nil { + t := Extract(ctx) + for k, v := range valMap { + t.Set("grpc.request."+k, v) + } + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go new file mode 100644 index 00000000..2596be89 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go @@ -0,0 +1,25 @@ +package ctx_logrus + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +// AddFields adds logrus fields to the logger. +// Deprecated: should use the ctxlogrus.Extract instead +func AddFields(ctx context.Context, fields logrus.Fields) { + ctxlogrus.AddFields(ctx, fields) +} + +// Extract takes the call-scoped logrus.Entry from grpc_logrus middleware. +// Deprecated: should use the ctxlogrus.Extract instead +func Extract(ctx context.Context) *logrus.Entry { + return ctxlogrus.Extract(ctx) +} + +// ToContext adds the logrus.Entry to the context for extraction later. +// Depricated: should use ctxlogrus.ToContext instead +func ToContext(ctx context.Context, entry *logrus.Entry) context.Context { + return ctxlogrus.ToContext(ctx, entry) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go new file mode 100644 index 00000000..952775f8 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go @@ -0,0 +1,44 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +var ( + defaultOptions = &options{ + requestFieldsFunc: nil, + } +) + +type options struct { + requestFieldsFunc RequestFieldExtractorFunc + requestFieldsFromInitial bool +} + +func evaluateOptions(opts []Option) *options { + optCopy := &options{} + *optCopy = *defaultOptions + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +type Option func(*options) + +// WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +// unary and server-streamed methods only. +func WithFieldExtractor(f RequestFieldExtractorFunc) Option { + return func(o *options) { + o.requestFieldsFunc = f + } +} + +// WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +// for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +// extracted from the first message from the client. +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option { + return func(o *options) { + o.requestFieldsFunc = f + o.requestFieldsFromInitial = true + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md new file mode 100644 index 00000000..a02cde31 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md @@ -0,0 +1,114 @@ +# metautils +`import "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc/metadata](https://godoc.org/google.golang.org/grpc/metadata) + +## <a name="pkg-index">Index</a> +* [type NiceMD](#NiceMD) + * [func ExtractIncoming(ctx context.Context) NiceMD](#ExtractIncoming) + * [func ExtractOutgoing(ctx context.Context) NiceMD](#ExtractOutgoing) + * [func (m NiceMD) Add(key string, value string) NiceMD](#NiceMD.Add) + * [func (m NiceMD) Clone(copiedKeys ...string) NiceMD](#NiceMD.Clone) + * [func (m NiceMD) Del(key string) NiceMD](#NiceMD.Del) + * [func (m NiceMD) Get(key string) string](#NiceMD.Get) + * [func (m NiceMD) Set(key string, value string) NiceMD](#NiceMD.Set) + * [func (m NiceMD) ToIncoming(ctx context.Context) context.Context](#NiceMD.ToIncoming) + * [func (m NiceMD) ToOutgoing(ctx context.Context) context.Context](#NiceMD.ToOutgoing) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [nicemd.go](./nicemd.go) [single_key.go](./single_key.go) + +## <a name="NiceMD">type</a> [NiceMD](./nicemd.go#L14) +``` go +type NiceMD metadata.MD +``` +NiceMD is a convenience wrapper definiting extra functions on the metadata. + +### <a name="ExtractIncoming">func</a> [ExtractIncoming](./nicemd.go#L20) +``` go +func ExtractIncoming(ctx context.Context) NiceMD +``` +ExtractIncoming extracts an inbound metadata from the server-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="ExtractOutgoing">func</a> [ExtractOutgoing](./nicemd.go#L32) +``` go +func ExtractOutgoing(ctx context.Context) NiceMD +``` +ExtractOutgoing extracts an outbound metadata from the client-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="NiceMD.Add">func</a> (NiceMD) [Add](./nicemd.go#L122) +``` go +func (m NiceMD) Add(key string, value string) NiceMD +``` +Add retrieves a single value from the metadata. + +It works analogously to http.Header.Add, as it appends to any existing values associated with key. + +The function is binary-key safe. + +### <a name="NiceMD.Clone">func</a> (NiceMD) [Clone](./nicemd.go#L44) +``` go +func (m NiceMD) Clone(copiedKeys ...string) NiceMD +``` +Clone performs a *deep* copy of the metadata.MD. + +You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +all keys get copied. + +### <a name="NiceMD.Del">func</a> (NiceMD) [Del](./nicemd.go#L100) +``` go +func (m NiceMD) Del(key string) NiceMD +``` + +### <a name="NiceMD.Get">func</a> (NiceMD) [Get](./nicemd.go#L85) +``` go +func (m NiceMD) Get(key string) string +``` +Get retrieves a single value from the metadata. + +It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +an empty string is returned. + +The function is binary-key safe. + +### <a name="NiceMD.Set">func</a> (NiceMD) [Set](./nicemd.go#L111) +``` go +func (m NiceMD) Set(key string, value string) NiceMD +``` +Set sets the given value in a metadata. + +It works analogously to http.Header.Set, overwriting all previous metadata values. + +The function is binary-key safe. + +### <a name="NiceMD.ToIncoming">func</a> (NiceMD) [ToIncoming](./nicemd.go#L75) +``` go +func (m NiceMD) ToIncoming(ctx context.Context) context.Context +``` +ToIncoming sets the given NiceMD as a server-side context for dispatching. + +This is mostly useful in ServerInterceptors.. + +### <a name="NiceMD.ToOutgoing">func</a> (NiceMD) [ToOutgoing](./nicemd.go#L68) +``` go +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context +``` +ToOutgoing sets the given NiceMD as a client-side context for dispatching. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md new file mode 100644 index 00000000..a02cde31 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md @@ -0,0 +1,114 @@ +# metautils +`import "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc/metadata](https://godoc.org/google.golang.org/grpc/metadata) + +## <a name="pkg-index">Index</a> +* [type NiceMD](#NiceMD) + * [func ExtractIncoming(ctx context.Context) NiceMD](#ExtractIncoming) + * [func ExtractOutgoing(ctx context.Context) NiceMD](#ExtractOutgoing) + * [func (m NiceMD) Add(key string, value string) NiceMD](#NiceMD.Add) + * [func (m NiceMD) Clone(copiedKeys ...string) NiceMD](#NiceMD.Clone) + * [func (m NiceMD) Del(key string) NiceMD](#NiceMD.Del) + * [func (m NiceMD) Get(key string) string](#NiceMD.Get) + * [func (m NiceMD) Set(key string, value string) NiceMD](#NiceMD.Set) + * [func (m NiceMD) ToIncoming(ctx context.Context) context.Context](#NiceMD.ToIncoming) + * [func (m NiceMD) ToOutgoing(ctx context.Context) context.Context](#NiceMD.ToOutgoing) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [nicemd.go](./nicemd.go) [single_key.go](./single_key.go) + +## <a name="NiceMD">type</a> [NiceMD](./nicemd.go#L14) +``` go +type NiceMD metadata.MD +``` +NiceMD is a convenience wrapper definiting extra functions on the metadata. + +### <a name="ExtractIncoming">func</a> [ExtractIncoming](./nicemd.go#L20) +``` go +func ExtractIncoming(ctx context.Context) NiceMD +``` +ExtractIncoming extracts an inbound metadata from the server-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="ExtractOutgoing">func</a> [ExtractOutgoing](./nicemd.go#L32) +``` go +func ExtractOutgoing(ctx context.Context) NiceMD +``` +ExtractOutgoing extracts an outbound metadata from the client-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="NiceMD.Add">func</a> (NiceMD) [Add](./nicemd.go#L122) +``` go +func (m NiceMD) Add(key string, value string) NiceMD +``` +Add retrieves a single value from the metadata. + +It works analogously to http.Header.Add, as it appends to any existing values associated with key. + +The function is binary-key safe. + +### <a name="NiceMD.Clone">func</a> (NiceMD) [Clone](./nicemd.go#L44) +``` go +func (m NiceMD) Clone(copiedKeys ...string) NiceMD +``` +Clone performs a *deep* copy of the metadata.MD. + +You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +all keys get copied. + +### <a name="NiceMD.Del">func</a> (NiceMD) [Del](./nicemd.go#L100) +``` go +func (m NiceMD) Del(key string) NiceMD +``` + +### <a name="NiceMD.Get">func</a> (NiceMD) [Get](./nicemd.go#L85) +``` go +func (m NiceMD) Get(key string) string +``` +Get retrieves a single value from the metadata. + +It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +an empty string is returned. + +The function is binary-key safe. + +### <a name="NiceMD.Set">func</a> (NiceMD) [Set](./nicemd.go#L111) +``` go +func (m NiceMD) Set(key string, value string) NiceMD +``` +Set sets the given value in a metadata. + +It works analogously to http.Header.Set, overwriting all previous metadata values. + +The function is binary-key safe. + +### <a name="NiceMD.ToIncoming">func</a> (NiceMD) [ToIncoming](./nicemd.go#L75) +``` go +func (m NiceMD) ToIncoming(ctx context.Context) context.Context +``` +ToIncoming sets the given NiceMD as a server-side context for dispatching. + +This is mostly useful in ServerInterceptors.. + +### <a name="NiceMD.ToOutgoing">func</a> (NiceMD) [ToOutgoing](./nicemd.go#L68) +``` go +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context +``` +ToOutgoing sets the given NiceMD as a client-side context for dispatching. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go new file mode 100644 index 00000000..1ed9bb49 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go @@ -0,0 +1,19 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +Package `metautils` provides convenience functions for dealing with gRPC metadata.MD objects inside +Context handlers. + +While the upstream grpc-go package contains decent functionality (see https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) +they are hard to use. + +The majority of functions center around the NiceMD, which is a convenience wrapper around metadata.MD. For example +the following code allows you to easily extract incoming metadata (server handler) and put it into a new client context +metadata. + + nmd := metautils.ExtractIncoming(serverCtx).Clone(":authorization", ":custom") + clientCtx := nmd.Set("x-client-header", "2").Set("x-another", "3").ToOutgoing(ctx) +*/ + +package metautils diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go new file mode 100644 index 00000000..a277bee3 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go @@ -0,0 +1,126 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package metautils + +import ( + "strings" + + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +// NiceMD is a convenience wrapper definiting extra functions on the metadata. +type NiceMD metadata.MD + +// ExtractIncoming extracts an inbound metadata from the server-side context. +// +// This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +// a new empty NiceMD. +func ExtractIncoming(ctx context.Context) NiceMD { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return NiceMD(metadata.Pairs()) + } + return NiceMD(md) +} + +// ExtractOutgoing extracts an outbound metadata from the client-side context. +// +// This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +// a new empty NiceMD. +func ExtractOutgoing(ctx context.Context) NiceMD { + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + return NiceMD(metadata.Pairs()) + } + return NiceMD(md) +} + +// Clone performs a *deep* copy of the metadata.MD. +// +// You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +// all keys get copied. +func (m NiceMD) Clone(copiedKeys ...string) NiceMD { + newMd := NiceMD(metadata.Pairs()) + for k, vv := range m { + found := false + if len(copiedKeys) == 0 { + found = true + } else { + for _, allowedKey := range copiedKeys { + if strings.ToLower(allowedKey) == strings.ToLower(k) { + found = true + break + } + } + } + if !found { + continue + } + newMd[k] = make([]string, len(vv)) + copy(newMd[k], vv) + } + return NiceMD(newMd) +} + +// ToOutgoing sets the given NiceMD as a client-side context for dispatching. +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context { + return metadata.NewOutgoingContext(ctx, metadata.MD(m)) +} + +// ToIncoming sets the given NiceMD as a server-side context for dispatching. +// +// This is mostly useful in ServerInterceptors.. +func (m NiceMD) ToIncoming(ctx context.Context) context.Context { + return metadata.NewIncomingContext(ctx, metadata.MD(m)) +} + +// Get retrieves a single value from the metadata. +// +// It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +// an empty string is returned. +// +// The function is binary-key safe. +func (m NiceMD) Get(key string) string { + k, _ := encodeKeyValue(key, "") + vv, ok := m[k] + if !ok { + return "" + } + return vv[0] +} + +// Del retrieves a single value from the metadata. +// +// It works analogously to http.Header.Del, deleting all values if they exist. +// +// The function is binary-key safe. + +func (m NiceMD) Del(key string) NiceMD { + k, _ := encodeKeyValue(key, "") + delete(m, k) + return m +} + +// Set sets the given value in a metadata. +// +// It works analogously to http.Header.Set, overwriting all previous metadata values. +// +// The function is binary-key safe. +func (m NiceMD) Set(key string, value string) NiceMD { + k, v := encodeKeyValue(key, value) + m[k] = []string{v} + return m +} + +// Add retrieves a single value from the metadata. +// +// It works analogously to http.Header.Add, as it appends to any existing values associated with key. +// +// The function is binary-key safe. +func (m NiceMD) Add(key string, value string) NiceMD { + k, v := encodeKeyValue(key, value) + m[k] = append(m[k], v) + return m +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go new file mode 100644 index 00000000..8a538716 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go @@ -0,0 +1,22 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package metautils + +import ( + "encoding/base64" + "strings" +) + +const ( + binHdrSuffix = "-bin" +) + +func encodeKeyValue(k, v string) (string, string) { + k = strings.ToLower(k) + if strings.HasSuffix(k, binHdrSuffix) { + val := base64.StdEncoding.EncodeToString([]byte(v)) + v = string(val) + } + return k, v +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go new file mode 100644 index 00000000..597b8624 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go @@ -0,0 +1,29 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_middleware + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context. +type WrappedServerStream struct { + grpc.ServerStream + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context +} + +// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() +func (w *WrappedServerStream) Context() context.Context { + return w.WrappedContext +} + +// WrapServerStream returns a ServerStream that has the ability to overwrite context. +func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream { + if existing, ok := stream.(*WrappedServerStream); ok { + return existing + } + return &WrappedServerStream{ServerStream: stream, WrappedContext: stream.Context()} +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE new file mode 100644 index 00000000..b2b06503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE @@ -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.
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md new file mode 100644 index 00000000..0eaa0846 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md @@ -0,0 +1,247 @@ +# Go gRPC Interceptors for Prometheus monitoring + +[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus.svg)](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus) +[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-prometheus)](http://goreportcard.com/report/grpc-ecosystem/go-grpc-prometheus) +[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-prometheus) +[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/?badge) +[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus) +[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) + +[Prometheus](https://prometheus.io/) monitoring for your [gRPC Go](https://github.com/grpc/grpc-go) servers and clients. + +A sister implementation for [gRPC Java](https://github.com/grpc/grpc-java) (same metrics, same semantics) is in [grpc-ecosystem/java-grpc-prometheus](https://github.com/grpc-ecosystem/java-grpc-prometheus). + +## Interceptors + +[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for Interceptors, i.e. middleware that is executed +by a gRPC Server before the request is passed onto the user's application logic. It is a perfect way to implement +common patterns: auth, logging and... monitoring. + +To use Interceptors in chains, please see [`go-grpc-middleware`](https://github.com/mwitkow/go-grpc-middleware). + +## Usage + +There are two types of interceptors: client-side and server-side. This package provides monitoring Interceptors for both. + +### Server-side + +```go +import "github.com/grpc-ecosystem/go-grpc-prometheus" +... + // Initialize your gRPC server's interceptor. + myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), + grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), + ) + // Register your gRPC service implementations. + myservice.RegisterMyServiceServer(s.server, &myServiceImpl{}) + // After all your registrations, make sure all of the Prometheus metrics are initialized. + grpc_prometheus.Register(myServer) + // Register Prometheus metrics handler. + http.Handle("/metrics", promhttp.Handler()) +... +``` + +### Client-side + +```go +import "github.com/grpc-ecosystem/go-grpc-prometheus" +... + clientConn, err = grpc.Dial( + address, + grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor), + grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor) + ) + client = pb_testproto.NewTestServiceClient(clientConn) + resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) +... +``` + +# Metrics + +## Labels + +All server-side metrics start with `grpc_server` as Prometheus subsystem name. All client-side metrics start with `grpc_client`. Both of them have mirror-concepts. Similarly all methods +contain the same rich labels: + + * `grpc_service` - the [gRPC service](http://www.grpc.io/docs/#defining-a-service) name, which is the combination of protobuf `package` and + the `grpc_service` section name. E.g. for `package = mwitkow.testproto` and + `service TestService` the label will be `grpc_service="mwitkow.testproto.TestService"` + * `grpc_method` - the name of the method called on the gRPC service. E.g. + `grpc_method="Ping"` + * `grpc_type` - the gRPC [type of request](http://www.grpc.io/docs/guides/concepts.html#rpc-life-cycle). + Differentiating between the two is important especially for latency measurements. + + - `unary` is single request, single response RPC + - `client_stream` is a multi-request, single response RPC + - `server_stream` is a single request, multi-response RPC + - `bidi_stream` is a multi-request, multi-response RPC + + +Additionally for completed RPCs, the following labels are used: + + * `grpc_code` - the human-readable [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go). + The list of all statuses is to long, but here are some common ones: + + - `OK` - means the RPC was successful + - `IllegalArgument` - RPC contained bad values + - `Internal` - server-side error not disclosed to the clients + +## Counters + +The counters and their up to date documentation is in [server_reporter.go](server_reporter.go) and [client_reporter.go](client_reporter.go) +the respective Prometheus handler (usually `/metrics`). + +For the purpose of this documentation we will only discuss `grpc_server` metrics. The `grpc_client` ones contain mirror concepts. + +For simplicity, let's assume we're tracking a single server-side RPC call of [`mwitkow.testproto.TestService`](examples/testproto/test.proto), +calling the method `PingList`. The call succeeds and returns 20 messages in the stream. + +First, immediately after the server receives the call it will increment the +`grpc_server_started_total` and start the handling time clock (if histograms are enabled). + +```jsoniq +grpc_server_started_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +Then the user logic gets invoked. It receives one message from the client containing the request +(it's a `server_stream`): + +```jsoniq +grpc_server_msg_received_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +The user logic may return an error, or send multiple messages back to the client. In this case, on +each of the 20 messages sent back, a counter will be incremented: + +```jsoniq +grpc_server_msg_sent_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 20 +``` + +After the call completes, it's status (`OK` or other [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go)) +and the relevant call labels increment the `grpc_server_handled_total` counter. + +```jsoniq +grpc_server_handled_total{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +## Histograms + +[Prometheus histograms](https://prometheus.io/docs/concepts/metric_types/#histogram) are a great way +to measure latency distributions of your RPCs. However, since it is bad practice to have metrics +of [high cardinality](https://prometheus.io/docs/practices/instrumentation/#do-not-overuse-labels) +the latency monitoring metrics are disabled by default. To enable them please call the following +in your server initialization code: + +```jsoniq +grpc_prometheus.EnableHandlingTimeHistogram() +``` + +After the call completes, its handling time will be recorded in a [Prometheus histogram](https://prometheus.io/docs/concepts/metric_types/#histogram) +variable `grpc_server_handling_seconds`. The histogram variable contains three sub-metrics: + + * `grpc_server_handling_seconds_count` - the count of all completed RPCs by status and method + * `grpc_server_handling_seconds_sum` - cumulative time of RPCs by status and method, useful for + calculating average handling times + * `grpc_server_handling_seconds_bucket` - contains the counts of RPCs by status and method in respective + handling-time buckets. These buckets can be used by Prometheus to estimate SLAs (see [here](https://prometheus.io/docs/practices/histograms/)) + +The counter values will look as follows: + +```jsoniq +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.005"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.01"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.025"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.05"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.1"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.25"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="1"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="2.5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="10"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="+Inf"} 1 +grpc_server_handling_seconds_sum{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 0.0003866430000000001 +grpc_server_handling_seconds_count{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + + +## Useful query examples + +Prometheus philosophy is to provide raw metrics to the monitoring system, and +let the aggregations be handled there. The verbosity of above metrics make it possible to have that +flexibility. Here's a couple of useful monitoring queries: + + +### request inbound rate +```jsoniq +sum(rate(grpc_server_started_total{job="foo"}[1m])) by (grpc_service) +``` +For `job="foo"` (common label to differentiate between Prometheus monitoring targets), calculate the +rate of requests per second (1 minute window) for each gRPC `grpc_service` that the job has. Please note +how the `grpc_method` is being omitted here: all methods of a given gRPC service will be summed together. + +### unary request error rate +```jsoniq +sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service) +``` +For `job="foo"`, calculate the per-`grpc_service` rate of `unary` (1:1) RPCs that failed, i.e. the +ones that didn't finish with `OK` code. + +### unary request error percentage +```jsoniq +sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service) + / +sum(rate(grpc_server_started_total{job="foo",grpc_type="unary"}[1m])) by (grpc_service) + * 100.0 +``` +For `job="foo"`, calculate the percentage of failed requests by service. It's easy to notice that +this is a combination of the two above examples. This is an example of a query you would like to +[alert on](https://prometheus.io/docs/alerting/rules/) in your system for SLA violations, e.g. +"no more than 1% requests should fail". + +### average response stream size +```jsoniq +sum(rate(grpc_server_msg_sent_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service) + / +sum(rate(grpc_server_started_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service) +``` +For `job="foo"` what is the `grpc_service`-wide `10m` average of messages returned for all ` +server_stream` RPCs. This allows you to track the stream sizes returned by your system, e.g. allows +you to track when clients started to send "wide" queries that ret +Note the divisor is the number of started RPCs, in order to account for in-flight requests. + +### 99%-tile latency of unary requests +```jsoniq +histogram_quantile(0.99, + sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary"}[5m])) by (grpc_service,le) +) +``` +For `job="foo"`, returns an 99%-tile [quantile estimation](https://prometheus.io/docs/practices/histograms/#quantiles) +of the handling time of RPCs per service. Please note the `5m` rate, this means that the quantile +estimation will take samples in a rolling `5m` window. When combined with other quantiles +(e.g. 50%, 90%), this query gives you tremendous insight into the responsiveness of your system +(e.g. impact of caching). + +### percentage of slow unary queries (>250ms) +```jsoniq +100.0 - ( +sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary",le="0.25"}[5m])) by (grpc_service) + / +sum(rate(grpc_server_handling_seconds_count{job="foo",grpc_type="unary"}[5m])) by (grpc_service) +) * 100.0 +``` +For `job="foo"` calculate the by-`grpc_service` fraction of slow requests that took longer than `0.25` +seconds. This query is relatively complex, since the Prometheus aggregations use `le` (less or equal) +buckets, meaning that counting "fast" requests fractions is easier. However, simple maths helps. +This is an example of a query you would like to alert on in your system for SLA violations, +e.g. "less than 1% of requests are slower than 250ms". + + +## Status + +This code has been used since August 2015 as the basis for monitoring of *production* gRPC micro services at [Improbable](https://improbable.io). + +## License + +`go-grpc-prometheus` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go new file mode 100644 index 00000000..751a4c72 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go @@ -0,0 +1,39 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Prometheus monitoring interceptors for client-side gRPC. + +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" +) + +var ( + // DefaultClientMetrics is the default instance of ClientMetrics. It is + // intended to be used in conjunction the default Prometheus metrics + // registry. + DefaultClientMetrics = NewClientMetrics() + + // UnaryClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Unary RPCs. + UnaryClientInterceptor = DefaultClientMetrics.UnaryClientInterceptor() + + // StreamClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Streaming RPCs. + StreamClientInterceptor = DefaultClientMetrics.StreamClientInterceptor() +) + +func init() { + prom.MustRegister(DefaultClientMetrics.clientStartedCounter) + prom.MustRegister(DefaultClientMetrics.clientHandledCounter) + prom.MustRegister(DefaultClientMetrics.clientStreamMsgReceived) + prom.MustRegister(DefaultClientMetrics.clientStreamMsgSent) +} + +// EnableClientHandlingTimeHistogram turns on recording of handling time of +// RPCs. Histogram metrics can be very expensive for Prometheus to retain and +// query. This function acts on the DefaultClientMetrics variable and the +// default Prometheus metrics registry. +func EnableClientHandlingTimeHistogram(opts ...HistogramOption) { + DefaultClientMetrics.EnableClientHandlingTimeHistogram(opts...) + prom.Register(DefaultClientMetrics.clientHandledHistogram) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go new file mode 100644 index 00000000..ba1a703f --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go @@ -0,0 +1,166 @@ +package grpc_prometheus + +import ( + "io" + + prom "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// ClientMetrics represents a collection of metrics to be registered on a +// Prometheus metrics registry for a gRPC client. +type ClientMetrics struct { + clientStartedCounter *prom.CounterVec + clientHandledCounter *prom.CounterVec + clientStreamMsgReceived *prom.CounterVec + clientStreamMsgSent *prom.CounterVec + clientHandledHistogramEnabled bool + clientHandledHistogramOpts prom.HistogramOpts + clientHandledHistogram *prom.HistogramVec +} + +// NewClientMetrics returns a ClientMetrics object. Use a new instance of +// ClientMetrics when not using the default Prometheus metrics registry, for +// example when wanting to control which metrics are added to a registry as +// opposed to automatically adding metrics via init functions. +func NewClientMetrics(counterOpts ...CounterOption) *ClientMetrics { + opts := counterOptions(counterOpts) + return &ClientMetrics{ + clientStartedCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_started_total", + Help: "Total number of RPCs started on the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientHandledCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_handled_total", + Help: "Total number of RPCs completed by the client, regardless of success or failure.", + }), []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"}), + + clientStreamMsgReceived: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_msg_received_total", + Help: "Total number of RPC stream messages received by the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientStreamMsgSent: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_msg_sent_total", + Help: "Total number of gRPC stream messages sent by the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientHandledHistogramEnabled: false, + clientHandledHistogramOpts: prom.HistogramOpts{ + Name: "grpc_client_handling_seconds", + Help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.", + Buckets: prom.DefBuckets, + }, + clientHandledHistogram: nil, + } +} + +// Describe sends the super-set of all possible descriptors of metrics +// collected by this Collector to the provided channel and returns once +// the last descriptor has been sent. +func (m *ClientMetrics) Describe(ch chan<- *prom.Desc) { + m.clientStartedCounter.Describe(ch) + m.clientHandledCounter.Describe(ch) + m.clientStreamMsgReceived.Describe(ch) + m.clientStreamMsgSent.Describe(ch) + if m.clientHandledHistogramEnabled { + m.clientHandledHistogram.Describe(ch) + } +} + +// Collect is called by the Prometheus registry when collecting +// metrics. The implementation sends each collected metric via the +// provided channel and returns once the last metric has been sent. +func (m *ClientMetrics) Collect(ch chan<- prom.Metric) { + m.clientStartedCounter.Collect(ch) + m.clientHandledCounter.Collect(ch) + m.clientStreamMsgReceived.Collect(ch) + m.clientStreamMsgSent.Collect(ch) + if m.clientHandledHistogramEnabled { + m.clientHandledHistogram.Collect(ch) + } +} + +// EnableClientHandlingTimeHistogram turns on recording of handling time of RPCs. +// Histogram metrics can be very expensive for Prometheus to retain and query. +func (m *ClientMetrics) EnableClientHandlingTimeHistogram(opts ...HistogramOption) { + for _, o := range opts { + o(&m.clientHandledHistogramOpts) + } + if !m.clientHandledHistogramEnabled { + m.clientHandledHistogram = prom.NewHistogramVec( + m.clientHandledHistogramOpts, + []string{"grpc_type", "grpc_service", "grpc_method"}, + ) + } + m.clientHandledHistogramEnabled = true +} + +// UnaryClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Unary RPCs. +func (m *ClientMetrics) UnaryClientInterceptor() func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + monitor := newClientReporter(m, Unary, method) + monitor.SentMessage() + err := invoker(ctx, method, req, reply, cc, opts...) + if err != nil { + monitor.ReceivedMessage() + } + monitor.Handled(grpc.Code(err)) + return err + } +} + +// StreamServerInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Streaming RPCs. +func (m *ClientMetrics) StreamClientInterceptor() func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + monitor := newClientReporter(m, clientStreamType(desc), method) + clientStream, err := streamer(ctx, desc, cc, method, opts...) + if err != nil { + monitor.Handled(grpc.Code(err)) + return nil, err + } + return &monitoredClientStream{clientStream, monitor}, nil + } +} + +func clientStreamType(desc *grpc.StreamDesc) grpcType { + if desc.ClientStreams && !desc.ServerStreams { + return ClientStream + } else if !desc.ClientStreams && desc.ServerStreams { + return ServerStream + } + return BidiStream +} + +// monitoredClientStream wraps grpc.ClientStream allowing each Sent/Recv of message to increment counters. +type monitoredClientStream struct { + grpc.ClientStream + monitor *clientReporter +} + +func (s *monitoredClientStream) SendMsg(m interface{}) error { + err := s.ClientStream.SendMsg(m) + if err == nil { + s.monitor.SentMessage() + } + return err +} + +func (s *monitoredClientStream) RecvMsg(m interface{}) error { + err := s.ClientStream.RecvMsg(m) + if err == nil { + s.monitor.ReceivedMessage() + } else if err == io.EOF { + s.monitor.Handled(codes.OK) + } else { + s.monitor.Handled(grpc.Code(err)) + } + return err +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go new file mode 100644 index 00000000..cbf15322 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go @@ -0,0 +1,46 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "time" + + "google.golang.org/grpc/codes" +) + +type clientReporter struct { + metrics *ClientMetrics + rpcType grpcType + serviceName string + methodName string + startTime time.Time +} + +func newClientReporter(m *ClientMetrics, rpcType grpcType, fullMethod string) *clientReporter { + r := &clientReporter{ + metrics: m, + rpcType: rpcType, + } + if r.metrics.clientHandledHistogramEnabled { + r.startTime = time.Now() + } + r.serviceName, r.methodName = splitMethodName(fullMethod) + r.metrics.clientStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() + return r +} + +func (r *clientReporter) ReceivedMessage() { + r.metrics.clientStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *clientReporter) SentMessage() { + r.metrics.clientStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *clientReporter) Handled(code codes.Code) { + r.metrics.clientHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc() + if r.metrics.clientHandledHistogramEnabled { + r.metrics.clientHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile new file mode 100644 index 00000000..74c08422 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile @@ -0,0 +1,16 @@ +SHELL="/bin/bash" + +GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/) + +all: vet fmt test + +fmt: + go fmt $(GOFILES_NOVENDOR) + +vet: + go vet $(GOFILES_NOVENDOR) + +test: vet + ./scripts/test_all.sh + +.PHONY: all vet test diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go new file mode 100644 index 00000000..ad8a51c5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go @@ -0,0 +1,35 @@ +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" +) + +type CounterOption func(opts *prom.CounterOpts) + +type counterOptions []CounterOption + +func (co counterOptions) apply(o prom.CounterOpts) prom.CounterOpts { + for _, f := range co { + f(&o) + } + return o +} + +func WithConstLabels(labels prom.Labels) CounterOption { + return func(o *prom.CounterOpts) { + o.ConstLabels = labels + } +} + +type HistogramOption func(*prom.HistogramOpts) + +// WithHistogramBuckets allows you to specify custom bucket ranges for histograms if EnableHandlingTimeHistogram is on. +func WithHistogramBuckets(buckets []float64) HistogramOption { + return func(o *prom.HistogramOpts) { o.Buckets = buckets } +} + +func WithHistogramConstLabels(labels prom.Labels) HistogramOption { + return func(o *prom.HistogramOpts) { + o.ConstLabels = labels + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go new file mode 100644 index 00000000..322f9904 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go @@ -0,0 +1,48 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Prometheus monitoring interceptors for server-side gRPC. + +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" + "google.golang.org/grpc" +) + +var ( + // DefaultServerMetrics is the default instance of ServerMetrics. It is + // intended to be used in conjunction the default Prometheus metrics + // registry. + DefaultServerMetrics = NewServerMetrics() + + // UnaryServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Unary RPCs. + UnaryServerInterceptor = DefaultServerMetrics.UnaryServerInterceptor() + + // StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs. + StreamServerInterceptor = DefaultServerMetrics.StreamServerInterceptor() +) + +func init() { + prom.MustRegister(DefaultServerMetrics.serverStartedCounter) + prom.MustRegister(DefaultServerMetrics.serverHandledCounter) + prom.MustRegister(DefaultServerMetrics.serverStreamMsgReceived) + prom.MustRegister(DefaultServerMetrics.serverStreamMsgSent) +} + +// Register takes a gRPC server and pre-initializes all counters to 0. This +// allows for easier monitoring in Prometheus (no missing metrics), and should +// be called *after* all services have been registered with the server. This +// function acts on the DefaultServerMetrics variable. +func Register(server *grpc.Server) { + DefaultServerMetrics.InitializeMetrics(server) +} + +// EnableHandlingTimeHistogram turns on recording of handling time +// of RPCs. Histogram metrics can be very expensive for Prometheus +// to retain and query. This function acts on the DefaultServerMetrics +// variable and the default Prometheus metrics registry. +func EnableHandlingTimeHistogram(opts ...HistogramOption) { + DefaultServerMetrics.EnableHandlingTimeHistogram(opts...) + prom.Register(DefaultServerMetrics.serverHandledHistogram) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go new file mode 100644 index 00000000..afa01428 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go @@ -0,0 +1,202 @@ +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// ServerMetrics represents a collection of metrics to be registered on a +// Prometheus metrics registry for a gRPC server. +type ServerMetrics struct { + serverStartedCounter *prom.CounterVec + serverHandledCounter *prom.CounterVec + serverStreamMsgReceived *prom.CounterVec + serverStreamMsgSent *prom.CounterVec + serverHandledHistogramEnabled bool + serverHandledHistogramOpts prom.HistogramOpts + serverHandledHistogram *prom.HistogramVec +} + +// NewServerMetrics returns a ServerMetrics object. Use a new instance of +// ServerMetrics when not using the default Prometheus metrics registry, for +// example when wanting to control which metrics are added to a registry as +// opposed to automatically adding metrics via init functions. +func NewServerMetrics(counterOpts ...CounterOption) *ServerMetrics { + opts := counterOptions(counterOpts) + return &ServerMetrics{ + serverStartedCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_started_total", + Help: "Total number of RPCs started on the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverHandledCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_handled_total", + Help: "Total number of RPCs completed on the server, regardless of success or failure.", + }), []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"}), + serverStreamMsgReceived: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_msg_received_total", + Help: "Total number of RPC stream messages received on the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverStreamMsgSent: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_msg_sent_total", + Help: "Total number of gRPC stream messages sent by the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverHandledHistogramEnabled: false, + serverHandledHistogramOpts: prom.HistogramOpts{ + Name: "grpc_server_handling_seconds", + Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", + Buckets: prom.DefBuckets, + }, + serverHandledHistogram: nil, + } +} + +// EnableHandlingTimeHistogram enables histograms being registered when +// registering the ServerMetrics on a Prometheus registry. Histograms can be +// expensive on Prometheus servers. It takes options to configure histogram +// options such as the defined buckets. +func (m *ServerMetrics) EnableHandlingTimeHistogram(opts ...HistogramOption) { + for _, o := range opts { + o(&m.serverHandledHistogramOpts) + } + if !m.serverHandledHistogramEnabled { + m.serverHandledHistogram = prom.NewHistogramVec( + m.serverHandledHistogramOpts, + []string{"grpc_type", "grpc_service", "grpc_method"}, + ) + } + m.serverHandledHistogramEnabled = true +} + +// Describe sends the super-set of all possible descriptors of metrics +// collected by this Collector to the provided channel and returns once +// the last descriptor has been sent. +func (m *ServerMetrics) Describe(ch chan<- *prom.Desc) { + m.serverStartedCounter.Describe(ch) + m.serverHandledCounter.Describe(ch) + m.serverStreamMsgReceived.Describe(ch) + m.serverStreamMsgSent.Describe(ch) + if m.serverHandledHistogramEnabled { + m.serverHandledHistogram.Describe(ch) + } +} + +// Collect is called by the Prometheus registry when collecting +// metrics. The implementation sends each collected metric via the +// provided channel and returns once the last metric has been sent. +func (m *ServerMetrics) Collect(ch chan<- prom.Metric) { + m.serverStartedCounter.Collect(ch) + m.serverHandledCounter.Collect(ch) + m.serverStreamMsgReceived.Collect(ch) + m.serverStreamMsgSent.Collect(ch) + if m.serverHandledHistogramEnabled { + m.serverHandledHistogram.Collect(ch) + } +} + +// UnaryServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Unary RPCs. +func (m *ServerMetrics) UnaryServerInterceptor() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + monitor := newServerReporter(m, Unary, info.FullMethod) + monitor.ReceivedMessage() + resp, err := handler(ctx, req) + monitor.Handled(grpc.Code(err)) + if err == nil { + monitor.SentMessage() + } + return resp, err + } +} + +// StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs. +func (m *ServerMetrics) StreamServerInterceptor() func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + monitor := newServerReporter(m, streamRpcType(info), info.FullMethod) + err := handler(srv, &monitoredServerStream{ss, monitor}) + monitor.Handled(grpc.Code(err)) + return err + } +} + +// InitializeMetrics initializes all metrics, with their appropriate null +// value, for all gRPC methods registered on a gRPC server. This is useful, to +// ensure that all metrics exist when collecting and querying. +func (m *ServerMetrics) InitializeMetrics(server *grpc.Server) { + serviceInfo := server.GetServiceInfo() + for serviceName, info := range serviceInfo { + for _, mInfo := range info.Methods { + preRegisterMethod(m, serviceName, &mInfo) + } + } +} + +// Register registers all server metrics in a given metrics registry. Depending +// on histogram options and whether they are enabled, histogram metrics are +// also registered. +// +// Deprecated: ServerMetrics implements Prometheus Collector interface. You can +// register an instance of ServerMetrics directly by using +// prometheus.Register(m). +func (m *ServerMetrics) Register(r prom.Registerer) error { + return r.Register(m) +} + +// MustRegister tries to register all server metrics and panics on an error. +// +// Deprecated: ServerMetrics implements Prometheus Collector interface. You can +// register an instance of ServerMetrics directly by using +// prometheus.MustRegister(m). +func (m *ServerMetrics) MustRegister(r prom.Registerer) { + r.MustRegister(m) +} + +func streamRpcType(info *grpc.StreamServerInfo) grpcType { + if info.IsClientStream && !info.IsServerStream { + return ClientStream + } else if !info.IsClientStream && info.IsServerStream { + return ServerStream + } + return BidiStream +} + +// monitoredStream wraps grpc.ServerStream allowing each Sent/Recv of message to increment counters. +type monitoredServerStream struct { + grpc.ServerStream + monitor *serverReporter +} + +func (s *monitoredServerStream) SendMsg(m interface{}) error { + err := s.ServerStream.SendMsg(m) + if err == nil { + s.monitor.SentMessage() + } + return err +} + +func (s *monitoredServerStream) RecvMsg(m interface{}) error { + err := s.ServerStream.RecvMsg(m) + if err == nil { + s.monitor.ReceivedMessage() + } + return err +} + +// preRegisterMethod is invoked on Register of a Server, allowing all gRPC services labels to be pre-populated. +func preRegisterMethod(metrics *ServerMetrics, serviceName string, mInfo *grpc.MethodInfo) { + methodName := mInfo.Name + methodType := string(typeFromMethodInfo(mInfo)) + // These are just references (no increments), as just referencing will create the labels but not set values. + metrics.serverStartedCounter.GetMetricWithLabelValues(methodType, serviceName, methodName) + metrics.serverStreamMsgReceived.GetMetricWithLabelValues(methodType, serviceName, methodName) + metrics.serverStreamMsgSent.GetMetricWithLabelValues(methodType, serviceName, methodName) + if metrics.serverHandledHistogramEnabled { + metrics.serverHandledHistogram.GetMetricWithLabelValues(methodType, serviceName, methodName) + } + for _, code := range allCodes { + metrics.serverHandledCounter.GetMetricWithLabelValues(methodType, serviceName, methodName, code.String()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go new file mode 100644 index 00000000..aa9db540 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go @@ -0,0 +1,46 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "time" + + "google.golang.org/grpc/codes" +) + +type serverReporter struct { + metrics *ServerMetrics + rpcType grpcType + serviceName string + methodName string + startTime time.Time +} + +func newServerReporter(m *ServerMetrics, rpcType grpcType, fullMethod string) *serverReporter { + r := &serverReporter{ + metrics: m, + rpcType: rpcType, + } + if r.metrics.serverHandledHistogramEnabled { + r.startTime = time.Now() + } + r.serviceName, r.methodName = splitMethodName(fullMethod) + r.metrics.serverStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() + return r +} + +func (r *serverReporter) ReceivedMessage() { + r.metrics.serverStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *serverReporter) SentMessage() { + r.metrics.serverStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *serverReporter) Handled(code codes.Code) { + r.metrics.serverHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc() + if r.metrics.serverHandledHistogramEnabled { + r.metrics.serverHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go new file mode 100644 index 00000000..d581c792 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go @@ -0,0 +1,50 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "strings" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +type grpcType string + +const ( + Unary grpcType = "unary" + ClientStream grpcType = "client_stream" + ServerStream grpcType = "server_stream" + BidiStream grpcType = "bidi_stream" +) + +var ( + allCodes = []codes.Code{ + codes.OK, codes.Canceled, codes.Unknown, codes.InvalidArgument, codes.DeadlineExceeded, codes.NotFound, + codes.AlreadyExists, codes.PermissionDenied, codes.Unauthenticated, codes.ResourceExhausted, + codes.FailedPrecondition, codes.Aborted, codes.OutOfRange, codes.Unimplemented, codes.Internal, + codes.Unavailable, codes.DataLoss, + } +) + +func splitMethodName(fullMethodName string) (string, string) { + fullMethodName = strings.TrimPrefix(fullMethodName, "/") // remove leading slash + if i := strings.Index(fullMethodName, "/"); i >= 0 { + return fullMethodName[:i], fullMethodName[i+1:] + } + return "unknown", "unknown" +} + +func typeFromMethodInfo(mInfo *grpc.MethodInfo) grpcType { + if mInfo.IsClientStream == false && mInfo.IsServerStream == false { + return Unary + } + if mInfo.IsClientStream == true && mInfo.IsServerStream == false { + return ClientStream + } + if mInfo.IsClientStream == false && mInfo.IsServerStream == true { + return ServerStream + } + return BidiStream +} |