Skip to content

Commit

Permalink
Split client/server interceptors (#289)
Browse files Browse the repository at this point in the history
* Split client/server interceptors

* Add client logger test
  • Loading branch information
avillen authored Jan 4, 2023
1 parent 52e1715 commit 71012b3
Show file tree
Hide file tree
Showing 19 changed files with 108 additions and 54 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ You can start the gRPC server as a supervised process. First, add `GRPC.Server.S
defmodule Helloworld.Endpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server
intercept GRPC.Server.Interceptors.Logger
run Helloworld.Greeter.Server
end

Expand All @@ -92,7 +92,7 @@ iex> request = Helloworld.HelloRequest.new(name: "grpc-elixir")
iex> {:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(request)

# With interceptors
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
...
```

Expand Down
2 changes: 1 addition & 1 deletion examples/helloworld/lib/endpoint.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Helloworld.Endpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server
intercept GRPC.Server.Interceptors.Logger
run Helloworld.Greeter.Server
end
2 changes: 1 addition & 1 deletion examples/helloworld/priv/client.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])

{:ok, reply} =
channel
Expand Down
2 changes: 1 addition & 1 deletion examples/helloworld/test/hello_world_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule HelloworldTest do
use ExUnit.Case

setup_all do
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
[channel: channel]
end

Expand Down
2 changes: 1 addition & 1 deletion examples/route_guide/lib/endpoint.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Routeguide.Endpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server
intercept GRPC.Server.Interceptors.Logger
run Routeguide.RouteGuide.Server
end
2 changes: 1 addition & 1 deletion examples/route_guide/priv/client.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
opts = [interceptors: [GRPC.Logger.Client]]
opts = [interceptors: [GRPC.Client.Interceptors.Logger]]

opts =
if System.get_env("TLS") do
Expand Down
2 changes: 1 addition & 1 deletion interop/lib/interop/endpoint.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Interop.Endpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server
intercept GRPC.Server.Interceptors.Logger
intercept GRPCPrometheus.ServerInterceptor
intercept Interop.ServerInterceptor

Expand Down
2 changes: 1 addition & 1 deletion interop/script/run.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ alias Interop.Client

1..concurrency
|> Task.async_stream(fn _cli ->
ch = Client.connect("127.0.0.1", port, interceptors: [GRPCPrometheus.ClientInterceptor, GRPC.Logger.Client])
ch = Client.connect("127.0.0.1", port, interceptors: [GRPCPrometheus.ClientInterceptor, GRPC.Client.Interceptors.Logger])

for _ <- 1..rounds do
Client.empty_unary!(ch)
Expand Down
13 changes: 13 additions & 0 deletions lib/grpc/client/interceptor.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule GRPC.Client.Interceptor do
@moduledoc """
Interceptor on client side. See `GRPC.Stub.connect/2`.
"""
alias GRPC.Client.Stream

@type options :: any()
@type req :: struct() | nil
@type next :: (Stream.t(), req -> GRPC.Stub.rpc_return())

@callback init(options) :: options
@callback call(stream :: Stream.t(), req, next, options) :: GRPC.Stub.rpc_return()
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule GRPC.Logger.Client do
defmodule GRPC.Client.Interceptors.Logger do
@moduledoc """
Print log around client rpc calls, like
Expand All @@ -13,18 +13,18 @@ defmodule GRPC.Logger.Client do
## Usage
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
# This will log on `:info` and greater priority
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, level: :info}])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Client.Interceptors.Logger, level: :info}])
# This will log only on `:info`
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, level: :info, accepted_comparators: [:eq]}])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Client.Interceptors.Logger, level: :info, accepted_comparators: [:eq]}])
# This will log on `:info` and lower priority
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, level: :info, accepted_comparators: [:eq, :gt]}])
{:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Client.Interceptors.Logger, level: :info, accepted_comparators: [:eq, :gt]}])
"""

require Logger

@behaviour GRPC.ClientInterceptor
@behaviour GRPC.Client.Interceptor

@impl true
def init(opts) do
Expand Down Expand Up @@ -53,7 +53,7 @@ defmodule GRPC.Logger.Client do
Logger.log(level, fn ->
diff = System.convert_time_unit(stop - start, :native, :microsecond)

["Got ", inspect(status), " in ", GRPC.Logger.Server.formatted_diff(diff)]
["Got ", inspect(status), " in ", GRPC.Server.Interceptors.Logger.formatted_diff(diff)]
end)
end

Expand Down
6 changes: 3 additions & 3 deletions lib/grpc/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ defmodule GRPC.Endpoint do
defmodule Your.Endpoint do
use GRPC.Endpoint
intercept GRPC.Logger.Server, level: :info
intercept GRPC.Server.Interceptors.Logger, level: :info
intercept Other.Interceptor
run HelloServer, interceptors: [HelloHaltInterceptor]
run FeatureServer
end
Interceptors will be run around your rpc calls from top to bottom. And you can even set
interceptors for some of servers. In the above example, `[GRPC.Logger.Server, Other.Interceptor,
HelloHaltInterceptor]` will be run for `HelloServer`, and `[GRPC.Logger.Server, Other.Interceptor]`
interceptors for some of servers. In the above example, `[GRPC.Server.Interceptors.Logger, Other.Interceptor,
HelloHaltInterceptor]` will be run for `HelloServer`, and `[GRPC.Server.Interceptors.Logger, Other.Interceptor]`
will be run for `FeatureServer`.
"""

Expand Down
16 changes: 1 addition & 15 deletions lib/grpc/interceptor.ex → lib/grpc/server/interceptor.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule GRPC.ServerInterceptor do
defmodule GRPC.Server.Interceptor do
@moduledoc """
Interceptor on server side. See `GRPC.Endpoint`.
"""
Expand All @@ -12,17 +12,3 @@ defmodule GRPC.ServerInterceptor do
@callback init(options) :: options
@callback call(GRPC.Server.rpc_req(), stream :: Stream.t(), next, options) :: rpc_return
end

defmodule GRPC.ClientInterceptor do
@moduledoc """
Interceptor on client side. See `GRPC.Stub.connect/2`.
"""
alias GRPC.Client.Stream

@type options :: any()
@type req :: struct() | nil
@type next :: (Stream.t(), req -> GRPC.Stub.rpc_return())

@callback init(options) :: options
@callback call(stream :: Stream.t(), req, next, options) :: GRPC.Stub.rpc_return()
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule GRPC.Logger.Server do
defmodule GRPC.Server.Interceptors.Logger do
@moduledoc """
Print log around server rpc calls, like:
Expand All @@ -16,27 +16,27 @@ defmodule GRPC.Logger.Server do
defmodule Your.Endpoint do
use GRPC.Endpoint
intercept GRPC.Logger.Server, level: :info
intercept GRPC.Server.Interceptors.Logger, level: :info
end
defmodule Your.Endpoint do
use GRPC.Endpoint
# logs on :info and higher priority (warn, error...)
intercept GRPC.Logger.Server, level: :info, accepted_comparators: [:lt, :eq]
intercept GRPC.Server.Interceptors.Logger, level: :info, accepted_comparators: [:lt, :eq]
end
defmodule Your.Endpoint do
use GRPC.Endpoint
# logs only on :error
intercept GRPC.Logger.Server, level: :error, accepted_comparators: [:eq]
intercept GRPC.Server.Interceptors.Logger, level: :error, accepted_comparators: [:eq]
end
"""

require Logger

@behaviour GRPC.ServerInterceptor
@behaviour GRPC.Server.Interceptor

@impl true
def init(opts) do
Expand Down
50 changes: 50 additions & 0 deletions test/grpc/client/interceptors/logger_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule GRPC.Client.Interceptors.LoggerTest do
use ExUnit.Case, async: false

import ExUnit.CaptureLog

alias GRPC.Client.Interceptors.Logger, as: LoggerInterceptor
alias GRPC.Client.Stream

test "accepted_comparators filter logs correctly" do
for {configured_level, accepted_comparators, should_log} <-
[
{:error, [:lt], false},
{:error, [:eq], false},
{:error, [:gt], true},
{:debug, [:eq], false},
{:debug, [:eq, :gt], false},
{:info, [:lt, :eq], true}
] do
logger_level = Logger.level()
assert logger_level == :info

service_name = "service_name"
rpc = {1, 2, 3}

logs =
capture_log(fn ->
stream = %Stream{grpc_type: :unary, rpc: rpc, service_name: service_name}

LoggerInterceptor.call(
stream,
:request,
fn ^stream, :request -> {:ok, :ok} end,
LoggerInterceptor.init(
level: configured_level,
accepted_comparators: accepted_comparators
)
)
end)

if should_log do
assert Regex.match?(
~r/\[#{configured_level}\]\s+Call #{to_string(elem(rpc, 0))} of #{service_name}/,
logs
)
else
assert logs == ""
end
end
end
end
2 changes: 2 additions & 0 deletions test/grpc/integration/client_interceptor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ defmodule GRPC.Integration.ClientInterceptorTest do
end

defmodule AddHeadersClientInterceptor do
@behaviour GRPC.Client.Interceptor

def init(label), do: label

def call(%{headers: headers} = stream, req, next, label) do
Expand Down
6 changes: 3 additions & 3 deletions test/grpc/integration/endpoint_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule GRPC.Integration.EndpointTest do
defmodule HelloEndpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server, level: :info, accepted_comparators: [:lt, :eq, :gt]
intercept GRPC.Server.Interceptors.Logger, level: :info, accepted_comparators: [:lt, :eq, :gt]
run HelloServer
end

Expand Down Expand Up @@ -51,14 +51,14 @@ defmodule GRPC.Integration.EndpointTest do
defmodule FeatureEndpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server, accepted_comparators: [:lt, :eq, :gt]
intercept GRPC.Server.Interceptors.Logger, accepted_comparators: [:lt, :eq, :gt]
run FeatureServer
end

defmodule FeatureAndHelloHaltEndpoint do
use GRPC.Endpoint

intercept GRPC.Logger.Server, accepted_comparators: [:lt, :eq, :gt]
intercept GRPC.Server.Interceptors.Logger, accepted_comparators: [:lt, :eq, :gt]
run HelloServer, interceptors: [HelloHaltInterceptor]
run FeatureServer
end
Expand Down
6 changes: 4 additions & 2 deletions test/grpc/integration/erlpack_notypes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule GRPC.Integration.ErplackNotypesTest do
{:ok, channel} =
GRPC.Stub.connect(
"localhost:#{port}",
interceptors: [GRPC.Logger.Client],
interceptors: [GRPC.Client.Interceptors.Logger],
codec: GRPC.Codec.Erlpack
)

Expand All @@ -36,7 +36,9 @@ defmodule GRPC.Integration.ErplackNotypesTest do

test "Says hello over erlpack call level" do
run_server(HelloServer, fn port ->
{:ok, channel} = GRPC.Stub.connect("localhost:#{port}", interceptors: [GRPC.Logger.Client])
{:ok, channel} =
GRPC.Stub.connect("localhost:#{port}", interceptors: [GRPC.Client.Interceptors.Logger])

name = "World"
{:ok, reply} = channel |> HelloErlpackStub.reply_hello(name, codec: GRPC.Codec.Erlpack)
assert reply == {:ok, "Hello, #{name}"}
Expand Down
4 changes: 3 additions & 1 deletion test/grpc/integration/stub_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ defmodule GRPC.Integration.StubTest do

test "body larger than 2^14 works" do
run_server(HelloServer, fn port ->
{:ok, channel} = GRPC.Stub.connect("localhost:#{port}", interceptors: [GRPC.Logger.Client])
{:ok, channel} =
GRPC.Stub.connect("localhost:#{port}", interceptors: [GRPC.Client.Interceptors.Logger])

name = String.duplicate("a", round(:math.pow(2, 15)))
req = Helloworld.HelloRequest.new(name: name)
{:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(req)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
defmodule GRPC.Logger.ServerTest do
defmodule GRPC.Server.Interceptors.LoggerTest do
use ExUnit.Case, async: false

import ExUnit.CaptureLog

alias GRPC.Logger.Server

alias GRPC.Server.Interceptors.Logger, as: LoggerInterceptor
alias GRPC.Server.Stream

test "request id is only set if not previously set" do
Expand All @@ -13,22 +12,22 @@ defmodule GRPC.Logger.ServerTest do
request_id = to_string(System.monotonic_time())
stream = %Stream{server: :server, rpc: {1, 2, 3}, request_id: request_id}

Server.call(
LoggerInterceptor.call(
:request,
stream,
fn :request, ^stream -> {:ok, :ok} end,
Server.init(level: :info)
LoggerInterceptor.init(level: :info)
)

assert [request_id: request_id] == Logger.metadata()

stream = %{stream | request_id: nil}

Server.call(
LoggerInterceptor.call(
:request,
stream,
fn :request, ^stream -> {:ok, :ok} end,
Server.init(level: :info)
LoggerInterceptor.init(level: :info)
)

assert request_id == Logger.metadata()[:request_id]
Expand All @@ -53,11 +52,11 @@ defmodule GRPC.Logger.ServerTest do
capture_log(fn ->
stream = %Stream{server: server_name, rpc: {1, 2, 3}, request_id: "1234"}

Server.call(
LoggerInterceptor.call(
:request,
stream,
fn :request, ^stream -> {:ok, :ok} end,
Server.init(
LoggerInterceptor.init(
level: configured_level,
accepted_comparators: accepted_comparators
)
Expand Down

0 comments on commit 71012b3

Please sign in to comment.