lib/grpc/logger.ex

defmodule GRPC.Logger.Server do
  @moduledoc """
  Print log around server rpc calls, like:

      17:18:45.151 [info]  Handled by HelloServer.say_hello
      17:18:45.151 [info]  Response :ok in 11µs

  ## Usage

      defmodule Your.Endpoint do
        use GRPC.Endpoint

        intercept GRPC.Logger.Server, level: :info
      end
  """
  require Logger
  @behaviour GRPC.ServerInterceptor

  def init(opts) do
    Keyword.get(opts, :level, :info)
  end

  def call(req, stream, next, level) do
    if Logger.compare_levels(level, Logger.level()) != :lt do
      Logger.log(level, fn ->
        ["Handled by ", inspect(stream.server), ".", to_string(elem(stream.rpc, 0))]
      end)

      start = System.monotonic_time()
      result = next.(req, stream)
      stop = System.monotonic_time()

      status = elem(result, 0)

      Logger.log(level, fn ->
        diff = System.convert_time_unit(stop - start, :native, :microsecond)

        ["Response ", inspect(status), " in ", formatted_diff(diff)]
      end)

      result
    else
      next.(req, stream)
    end
  end

  def formatted_diff(diff) when diff > 1000, do: [diff |> div(1000) |> Integer.to_string(), "ms"]
  def formatted_diff(diff), do: [Integer.to_string(diff), "µs"]
end

defmodule GRPC.Logger.Client do
  require Logger

  @moduledoc """
  Print log around client rpc calls, like

      17:13:33.021 [info]  Call say_hello of helloworld.Greeter
      17:13:33.079 [info]  Got :ok in 58ms

  ## Usage

      {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
      {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, level: :info}])
  """

  def init(opts) do
    Keyword.get(opts, :level, :info)
  end

  def call(%{grpc_type: grpc_type} = stream, req, next, level) do
    if Logger.compare_levels(level, Logger.level()) != :lt do
      Logger.log(level, fn ->
        ["Call ", to_string(elem(stream.rpc, 0)), " of ", stream.service_name]
      end)

      start = System.monotonic_time()
      result = next.(stream, req)
      stop = System.monotonic_time()

      if grpc_type == :unary do
        status = elem(result, 0)

        Logger.log(level, fn ->
          diff = System.convert_time_unit(stop - start, :native, :microsecond)

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

      result
    else
      next.(stream, req)
    end
  end
end