mirror of
https://github.com/fullstorydev/grpcurl.git
synced 2026-05-25 21:21:46 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcd3253f6 | ||
|
|
4c9c82cec3 | ||
|
|
5082a1dc68 | ||
|
|
d641a66208 | ||
|
|
ce84976d3c | ||
|
|
b292d5aef8 | ||
|
|
5516a45602 | ||
|
|
4a329f3b13 | ||
|
|
1c6532c060 |
@@ -10,6 +10,7 @@ matrix:
|
||||
env: GO111MODULE=off
|
||||
- go: "1.11"
|
||||
env: GO111MODULE=on
|
||||
- go: "1.12"
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
|
||||
@@ -49,6 +49,11 @@ files (containing compiled descriptors, produced by `protoc`) to `grpcurl`.
|
||||
|
||||
Download the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.
|
||||
|
||||
On macOS, `grpcurl` is available via Homebrew:
|
||||
```shell
|
||||
brew install grpcurl
|
||||
```
|
||||
|
||||
### From Source
|
||||
You can use the `go` tool to install `grpcurl`:
|
||||
```shell
|
||||
@@ -102,7 +107,7 @@ If you want to include `grpcurl` in a command pipeline, such as when using `jq`
|
||||
create a request body, you can use `-d @`, which tells `grpcurl` to read the actual
|
||||
request body from stdin:
|
||||
```shell
|
||||
grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<<EOM
|
||||
grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM
|
||||
{
|
||||
"id": 1234,
|
||||
"tags": [
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -32,22 +31,24 @@ var (
|
||||
|
||||
isUnixSocket func() bool // nil when run on non-unix platform
|
||||
|
||||
help = flag.Bool("help", false, prettify(`
|
||||
flags = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
help = flags.Bool("help", false, prettify(`
|
||||
Print usage instructions and exit.`))
|
||||
printVersion = flag.Bool("version", false, prettify(`
|
||||
printVersion = flags.Bool("version", false, prettify(`
|
||||
Print version.`))
|
||||
plaintext = flag.Bool("plaintext", false, prettify(`
|
||||
plaintext = flags.Bool("plaintext", false, prettify(`
|
||||
Use plain-text HTTP/2 when connecting to server (no TLS).`))
|
||||
insecure = flag.Bool("insecure", false, prettify(`
|
||||
insecure = flags.Bool("insecure", false, prettify(`
|
||||
Skip server certificate and domain verification. (NOT SECURE!) Not
|
||||
valid with -plaintext option.`))
|
||||
cacert = flag.String("cacert", "", prettify(`
|
||||
cacert = flags.String("cacert", "", prettify(`
|
||||
File containing trusted root certificates for verifying the server.
|
||||
Ignored if -insecure is specified.`))
|
||||
cert = flag.String("cert", "", prettify(`
|
||||
cert = flags.String("cert", "", prettify(`
|
||||
File containing client certificate (public key), to present to the
|
||||
server. Not valid with -plaintext option. Must also provide -key option.`))
|
||||
key = flag.String("key", "", prettify(`
|
||||
key = flags.String("key", "", prettify(`
|
||||
File containing client private key, to present to the server. Not valid
|
||||
with -plaintext option. Must also provide -cert option.`))
|
||||
protoset multiString
|
||||
@@ -56,15 +57,15 @@ var (
|
||||
addlHeaders multiString
|
||||
rpcHeaders multiString
|
||||
reflHeaders multiString
|
||||
authority = flag.String("authority", "", prettify(`
|
||||
authority = flags.String("authority", "", prettify(`
|
||||
Value of :authority pseudo-header to be use with underlying HTTP/2
|
||||
requests. It defaults to the given address.`))
|
||||
data = flag.String("d", "", prettify(`
|
||||
data = flags.String("d", "", prettify(`
|
||||
Data for request contents. If the value is '@' then the request contents
|
||||
are read from stdin. For calls that accept a stream of requests, the
|
||||
contents should include all such request messages concatenated together
|
||||
(possibly delimited; see -format).`))
|
||||
format = flag.String("format", "json", prettify(`
|
||||
format = flags.String("format", "json", prettify(`
|
||||
The format of request data. The allowed values are 'json' or 'text'. For
|
||||
'json', the input data must be in JSON format. Multiple request values
|
||||
may be concatenated (messages with a JSON representation other than
|
||||
@@ -74,43 +75,46 @@ var (
|
||||
ASCII character: 0x1E. The stream should not end in a record separator.
|
||||
If it does, it will be interpreted as a final, blank message after the
|
||||
separator.`))
|
||||
connectTimeout = flag.String("connect-timeout", "", prettify(`
|
||||
connectTimeout = flags.Float64("connect-timeout", 0, prettify(`
|
||||
The maximum time, in seconds, to wait for connection to be established.
|
||||
Defaults to 10 seconds.`))
|
||||
keepaliveTime = flag.String("keepalive-time", "", prettify(`
|
||||
keepaliveTime = flags.Float64("keepalive-time", 0, prettify(`
|
||||
If present, the maximum idle time in seconds, after which a keepalive
|
||||
probe is sent. If the connection remains idle and no keepalive response
|
||||
is received for this same period then the connection is closed and the
|
||||
operation fails.`))
|
||||
maxTime = flag.String("max-time", "", prettify(`
|
||||
The maximum total time the operation can take. This is useful for
|
||||
preventing batch jobs that use grpcurl from hanging due to slow or bad
|
||||
network links or due to incorrect stream method usage.`))
|
||||
emitDefaults = flag.Bool("emit-defaults", false, prettify(`
|
||||
maxTime = flags.Float64("max-time", 0, prettify(`
|
||||
The maximum total time the operation can take, in seconds. This is
|
||||
useful for preventing batch jobs that use grpcurl from hanging due to
|
||||
slow or bad network links or due to incorrect stream method usage.`))
|
||||
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
|
||||
The maximum encoded size of a response message, in bytes, that grpcurl
|
||||
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
||||
emitDefaults = flags.Bool("emit-defaults", false, prettify(`
|
||||
Emit default values for JSON-encoded responses.`))
|
||||
msgTemplate = flag.Bool("msg-template", false, prettify(`
|
||||
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
||||
When describing messages, show a template of input data.`))
|
||||
verbose = flag.Bool("v", false, prettify(`
|
||||
verbose = flags.Bool("v", false, prettify(`
|
||||
Enable verbose output.`))
|
||||
serverName = flag.String("servername", "", prettify(`
|
||||
serverName = flags.String("servername", "", prettify(`
|
||||
Override server name when validating TLS certificate.`))
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Var(&addlHeaders, "H", prettify(`
|
||||
flags.Var(&addlHeaders, "H", prettify(`
|
||||
Additional headers in 'name: value' format. May specify more than one
|
||||
via multiple flags. These headers will also be included in reflection
|
||||
requests requests to a server.`))
|
||||
flag.Var(&rpcHeaders, "rpc-header", prettify(`
|
||||
flags.Var(&rpcHeaders, "rpc-header", prettify(`
|
||||
Additional RPC headers in 'name: value' format. May specify more than
|
||||
one via multiple flags. These headers will *only* be used when invoking
|
||||
the requested RPC method. They are excluded from reflection requests.`))
|
||||
flag.Var(&reflHeaders, "reflect-header", prettify(`
|
||||
flags.Var(&reflHeaders, "reflect-header", prettify(`
|
||||
Additional reflection headers in 'name: value' format. May specify more
|
||||
than one via multiple flags. These headers will *only* be used during
|
||||
reflection requests and will be excluded when invoking the requested RPC
|
||||
method.`))
|
||||
flag.Var(&protoset, "protoset", prettify(`
|
||||
flags.Var(&protoset, "protoset", prettify(`
|
||||
The name of a file containing an encoded FileDescriptorSet. This file's
|
||||
contents will be used to determine the RPC schema instead of querying
|
||||
for it from the remote server via the gRPC reflection API. When set: the
|
||||
@@ -119,7 +123,7 @@ func init() {
|
||||
symbols found in the given descriptors. May specify more than one via
|
||||
multiple -protoset flags. It is an error to use both -protoset and
|
||||
-proto flags.`))
|
||||
flag.Var(&protoFiles, "proto", prettify(`
|
||||
flags.Var(&protoFiles, "proto", prettify(`
|
||||
The name of a proto source file. Source files given will be used to
|
||||
determine the RPC schema instead of querying for it from the remote
|
||||
server via the gRPC reflection API. When set: the 'list' action lists
|
||||
@@ -129,7 +133,7 @@ func init() {
|
||||
-proto flags. Imports will be resolved using the given -import-path
|
||||
flags. Multiple proto files can be specified by specifying multiple
|
||||
-proto flags. It is an error to use both -protoset and -proto flags.`))
|
||||
flag.Var(&importPaths, "import-path", prettify(`
|
||||
flags.Var(&importPaths, "import-path", prettify(`
|
||||
The path to a directory from which proto sources can be imported, for
|
||||
use with -proto flags. Multiple import paths can be configured by
|
||||
specifying multiple -import-path flags. Paths will be searched in the
|
||||
@@ -150,8 +154,8 @@ func (s *multiString) Set(value string) error {
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.CommandLine.Usage = usage
|
||||
flag.Parse()
|
||||
flags.Usage = usage
|
||||
flags.Parse(os.Args[1:])
|
||||
if *help {
|
||||
usage()
|
||||
os.Exit(0)
|
||||
@@ -162,6 +166,18 @@ func main() {
|
||||
}
|
||||
|
||||
// Do extra validation on arguments and figure out what user asked us to do.
|
||||
if *connectTimeout < 0 {
|
||||
fail(nil, "The -connect-timeout argument must not be negative.")
|
||||
}
|
||||
if *keepaliveTime < 0 {
|
||||
fail(nil, "The -keepalive-time argument must not be negative.")
|
||||
}
|
||||
if *maxTime < 0 {
|
||||
fail(nil, "The -max-time argument must not be negative.")
|
||||
}
|
||||
if *maxMsgSz < 0 {
|
||||
fail(nil, "The -max-msg-sz argument must not be negative.")
|
||||
}
|
||||
if *plaintext && *insecure {
|
||||
fail(nil, "The -plaintext and -insecure arguments are mutually exclusive.")
|
||||
}
|
||||
@@ -181,7 +197,7 @@ func main() {
|
||||
warn("The -emit-defaults is only used when using json format.")
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
args := flags.Args()
|
||||
|
||||
if len(args) == 0 {
|
||||
fail(nil, "Too few arguments.")
|
||||
@@ -246,38 +262,29 @@ func main() {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if *maxTime != "" {
|
||||
t, err := strconv.ParseFloat(*maxTime, 64)
|
||||
if err != nil {
|
||||
fail(nil, "The -max-time argument must be a valid number.")
|
||||
}
|
||||
timeout := time.Duration(t * float64(time.Second))
|
||||
if *maxTime > 0 {
|
||||
timeout := time.Duration(*maxTime * float64(time.Second))
|
||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||
}
|
||||
|
||||
dial := func() *grpc.ClientConn {
|
||||
dialTime := 10 * time.Second
|
||||
if *connectTimeout != "" {
|
||||
t, err := strconv.ParseFloat(*connectTimeout, 64)
|
||||
if err != nil {
|
||||
fail(nil, "The -connect-timeout argument must be a valid number.")
|
||||
}
|
||||
dialTime = time.Duration(t * float64(time.Second))
|
||||
if *connectTimeout > 0 {
|
||||
dialTime = time.Duration(*connectTimeout * float64(time.Second))
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
||||
defer cancel()
|
||||
var opts []grpc.DialOption
|
||||
if *keepaliveTime != "" {
|
||||
t, err := strconv.ParseFloat(*keepaliveTime, 64)
|
||||
if err != nil {
|
||||
fail(nil, "The -keepalive-time argument must be a valid number.")
|
||||
}
|
||||
timeout := time.Duration(t * float64(time.Second))
|
||||
if *keepaliveTime > 0 {
|
||||
timeout := time.Duration(*keepaliveTime * float64(time.Second))
|
||||
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: timeout,
|
||||
Timeout: timeout,
|
||||
}))
|
||||
}
|
||||
if *maxMsgSz > 0 {
|
||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
||||
}
|
||||
if *authority != "" {
|
||||
opts = append(opts, grpc.WithAuthority(*authority))
|
||||
}
|
||||
@@ -546,7 +553,7 @@ path to the domain socket.
|
||||
|
||||
Available flags:
|
||||
`, os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
|
||||
func prettify(docString string) string {
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
package main
|
||||
|
||||
import "flag"
|
||||
|
||||
var (
|
||||
unix = flag.Bool("unix", false, prettify(`
|
||||
unix = flags.Bool("unix", false, prettify(`
|
||||
Indicates that the server address is the path to a Unix domain socket.`))
|
||||
)
|
||||
|
||||
|
||||
8
go.mod
8
go.mod
@@ -1,8 +1,8 @@
|
||||
module github.com/fullstorydev/grpcurl
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.1.0
|
||||
github.com/jhump/protoreflect v1.1.0
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022
|
||||
google.golang.org/grpc v1.12.0
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/jhump/protoreflect v1.2.0
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d
|
||||
google.golang.org/grpc v1.19.0
|
||||
)
|
||||
|
||||
29
go.sum
29
go.sum
@@ -1,13 +1,28 @@
|
||||
github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/jhump/protoreflect v1.1.0 h1:h+zsMrsiq0vIl7yWmeowmd8e8VtnWk75U04GgXA2s6Y=
|
||||
github.com/jhump/protoreflect v1.1.0/go.mod h1:kG/zRVeS2M91gYaCvvUbPkMjjtFQS4qqjcPFzFkh2zE=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022 h1:MVYFTUmVD3/+ERcvRRI+P/C2+WOUimXh+Pd8LVsklZ4=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/jhump/protoreflect v1.2.0 h1:W4HDXsqxgmE9OUmEXWaU2ZoUap/A29J5hW+zRXWbdC8=
|
||||
github.com/jhump/protoreflect v1.2.0/go.mod h1:kG/zRVeS2M91gYaCvvUbPkMjjtFQS4qqjcPFzFkh2zE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/genproto v0.0.0-20170818100345-ee236bd376b0 h1:jgaHBfsPDMBDKsth1hPtI1HcOyecWndWOFSGW21VgaM=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20170818100345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.12.0 h1:Mm8atZtkT+P6R43n/dqNDWkPPu5BwRVu/1rJnJCeZH8=
|
||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
10
grpcurl.go
10
grpcurl.go
@@ -17,7 +17,6 @@ import (
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
@@ -574,11 +573,8 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||
}
|
||||
}
|
||||
|
||||
dialer := func(address string, timeout time.Duration) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
conn, err := (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
|
||||
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
||||
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
return nil, err
|
||||
@@ -601,7 +597,7 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||
opts = append(opts,
|
||||
grpc.WithBlock(),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
grpc.WithDialer(dialer),
|
||||
grpc.WithContextDialer(dialer),
|
||||
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
|
||||
)
|
||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||
|
||||
@@ -311,6 +311,7 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error getting request data: %v", err)
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -321,7 +322,6 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||
|
||||
if err != nil {
|
||||
sendErr.Store(err)
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func unaryLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
|
||||
} else {
|
||||
code = codes.Unknown
|
||||
}
|
||||
grpclog.Infof("completed <%d>: %v (%d) %v\n", i, code, code, time.Now().Sub(start))
|
||||
grpclog.Infof("completed <%d>: %v (%d) %v\n", i, code, code, time.Since(start))
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func streamLogger(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServer
|
||||
} else {
|
||||
code = codes.Unknown
|
||||
}
|
||||
grpclog.Infof("completed <%d>: %v(%d) %v\n", i, code, code, time.Now().Sub(start))
|
||||
grpclog.Infof("completed <%d>: %v(%d) %v\n", i, code, code, time.Since(start))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user