{"action":"created","issue":{"url":"https://api.github.com/repos/grpc/grpc-go/issues/349","labels_url":"https://api.github.com/repos/grpc/grpc-go/issues/349/labels{/name}","comments_url":"https://api.github.com/repos/grpc/grpc-go/issues/349/comments","events_url":"https://api.github.com/repos/grpc/grpc-go/issues/349/events","html_url":"https://github.com/grpc/grpc-go/pull/349","id":107103051,"number":349,"title":"Proposal: gprc.intercept.intercept ServiceOption/MethodOption","user":{"login":"peter-edge","id":4228796,"avatar_url":"https://avatars.githubusercontent.com/u/4228796?v=3","gravatar_id":"","url":"https://api.github.com/users/peter-edge","html_url":"https://github.com/peter-edge","followers_url":"https://api.github.com/users/peter-edge/followers","following_url":"https://api.github.com/users/peter-edge/following{/other_user}","gists_url":"https://api.github.com/users/peter-edge/gists{/gist_id}","starred_url":"https://api.github.com/users/peter-edge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peter-edge/subscriptions","organizations_url":"https://api.github.com/users/peter-edge/orgs","repos_url":"https://api.github.com/users/peter-edge/repos","events_url":"https://api.github.com/users/peter-edge/events{/privacy}","received_events_url":"https://api.github.com/users/peter-edge/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-09-17T23:50:24Z","updated_at":"2015-10-07T01:21:37Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/grpc/grpc-go/pulls/349","html_url":"https://github.com/grpc/grpc-go/pull/349","diff_url":"https://github.com/grpc/grpc-go/pull/349.diff","patch_url":"https://github.com/grpc/grpc-go/pull/349.patch"},"body":"This is a proposal, not actual mergeable code.\r\n\r\nA lot of the time, I find myself wishing I could have method interceptors server-side for every RPC call I make. gRPC has a lot of concentration on round-trip time, which is good, but a lot of applications might want to do things (like custom logging/metrics) for each method, in ways that would not be appropriate to merge into the main stream. In Java/Guice world, this would not be as tough, as you could bind a method interceptor at runtime using annotations, but golang doesn't really have an equivalent. This is a proposal to add a google.protobuf.ServiceOption, and two google.protobuf.MethodOptions, that denote whether methods can be intercepted, along with a golang Interface to represent an interceptor.\r\n\r\nBelow is the general concept, including the actual proposed options and an example. Instead of the intercept package, this could just be the grpc package, so there's no redundancy in the package and option names. What would happen is the protoc-gen-go compiler would need some changing, so (taking an example from https://github.com/pachyderm/pachyderm/blob/c25d2ccec5a0ed7f396b61d1bec62e835db1fbf6/src/pfs/pfs.pb.go#L840), this would go from:\r\n\r\n```go\r\nfunc _Api_RepoCreate_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {\r\n\tin := new(RepoCreateRequest)\r\n\tif err := codec.Unmarshal(buf, in); err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\tout, err := srv.(ApiServer).RepoCreate(ctx, in)\r\n\tif err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\treturn out, nil\r\n}\r\n```\r\n\r\nto:\r\n\r\n```go\r\nfunc _Api_RepoCreate_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte, interceptors ...grpc.Interceptor) (out interface{}, err error) {\r\n\tvar in *RepoCreateRequest\r\n\tif len(interceptors) != 0 {\r\n\t\tstart := time.Now()\r\n\t\tdefer func() {\r\n\t\t\tfor _, interceptor := range interceptors {\r\n\t\t\t\tinterceptor.Intercept(\"pfs.RepoCreate\", in, out, err, time.Since(start))\r\n\t\t\t}\r\n\t\t}()\r\n\t}\r\n\tin = new(RepoCreateRequest)\r\n\tif err = codec.Unmarshal(buf, in); err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\tout, err = srv.(ApiServer).RepoCreate(ctx, in)\r\n\tif err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\treturn out, nil\r\n}\r\n```\r\n\r\nThere are other ways to do this. You don't need to use protocol buffer ServiceOptions/MethodOptions, and have all the logic in the golang code. I played that fork for a while though and this ended up making more sense.\r\n\r\nAny thoughts?"},"comment":{"url":"https://api.github.com/repos/grpc/grpc-go/issues/comments/146049617","html_url":"https://github.com/grpc/grpc-go/pull/349#issuecomment-146049617","issue_url":"https://api.github.com/repos/grpc/grpc-go/issues/349","id":146049617,"user":{"login":"iamqizhao","id":10121772,"avatar_url":"https://avatars.githubusercontent.com/u/10121772?v=3","gravatar_id":"","url":"https://api.github.com/users/iamqizhao","html_url":"https://github.com/iamqizhao","followers_url":"https://api.github.com/users/iamqizhao/followers","following_url":"https://api.github.com/users/iamqizhao/following{/other_user}","gists_url":"https://api.github.com/users/iamqizhao/gists{/gist_id}","starred_url":"https://api.github.com/users/iamqizhao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iamqizhao/subscriptions","organizations_url":"https://api.github.com/users/iamqizhao/orgs","repos_url":"https://api.github.com/users/iamqizhao/repos","events_url":"https://api.github.com/users/iamqizhao/events{/privacy}","received_events_url":"https://api.github.com/users/iamqizhao/received_events","type":"User","site_admin":false},"created_at":"2015-10-07T01:21:37Z","updated_at":"2015-10-07T01:21:37Z","body":"+ @dsymonds the owner of go protobuf"}}