Repository: elixir-grpc/grpc (elixir)

Issue:
consider show only one type of log when using level option on logger
**Is your feature request related to a problem? Please describe.**

When using `intercept GRPC.Logger.Server, level: :info`. I want to show only this level of the log.

If I do it now, it will transform all logs, even `info`, `warn`, and `error` to the option I specified.

**Describe the solution you'd like**

I think we could have an option, `only: true`. This will say that I want only log of this type, and not other types.

**Describe alternatives you've considered**

None.

**Additional context**

Not applicable.


Pull request description:
feat: accept more combos of log level
Closes #208 by allowing the user to also provide the list of desired comparators

also deprecates Elixir 1.10 in favor of using `import Config` instead of `use Mix.Config`

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6ac0036..53b4616 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,10 +32,8 @@ jobs:
     strategy:
       matrix:
         otp: [22.x, 23.x, 24.x, 25.x]
-        elixir: [1.10.x, 1.11.x, 1.12.x, 1.13.x]
+        elixir: [1.11.x, 1.12.x, 1.13.x]
         exclude:
-          - otp: 25.x
-            elixir: 1.10.x
           - otp: 25.x
             elixir: 1.11.x
           - otp: 25.x
diff --git a/benchmark/config/config.exs b/benchmark/config/config.exs
index db5eb57..1c01285 100644
--- a/benchmark/config/config.exs
+++ b/benchmark/config/config.exs
@@ -1,4 +1,4 @@
-use Mix.Config
+import Config
 
 config :logger, level: :info
 
diff --git a/benchmark/config/dev.exs b/benchmark/config/dev.exs
index d2d855e..becde76 100644
--- a/benchmark/config/dev.exs
+++ b/benchmark/config/dev.exs
@@ -1,1 +1,1 @@
-use Mix.Config
+import Config
diff --git a/benchmark/config/prod.exs b/benchmark/config/prod.exs
index 28c45b1..3da7d10 100644
--- a/benchmark/config/prod.exs
+++ b/benchmark/config/prod.exs
@@ -1,3 +1,3 @@
-use Mix.Config
+import Config
 
 config :logger, level: :warn
diff --git a/config/config.exs b/config/config.exs
index ef59900..54bbf49 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,3 +1,9 @@
 import Config
 
 config :grpc, http2_client_adapter: GRPC.Adapter.Gun
+
+config_file = Path.expand("#{config_env()}.exs", __DIR__)
+
+if File.exists?(config_file) do
+  import_config config_file
+end
diff --git a/examples/helloworld/config/config.exs b/examples/helloworld/config/config.exs
index c2f683e..ef385b1 100644
--- a/examples/helloworld/config/config.exs
+++ b/examples/helloworld/config/config.exs
@@ -1,6 +1,6 @@
-use Mix.Config
+import Config
 
 # Start server in OTP
-# config :grpc, start_server: true
+config :grpc, start_server: true
 
 import_config "#{Mix.env}.exs"
diff --git a/examples/helloworld/config/dev.exs b/examples/helloworld/config/dev.exs
index d2d855e..becde76 100644
--- a/examples/helloworld/config/dev.exs
+++ b/examples/helloworld/config/dev.exs
@@ -1,1 +1,1 @@
-use Mix.Config
+import Config
diff --git a/examples/helloworld/config/prod.exs b/examples/helloworld/config/prod.exs
index 9d6d504..2a16500 100644
--- a/examples/helloworld/config/prod.exs
+++ b/examples/helloworld/config/prod.exs
@@ -1,4 +1,4 @@
-use Mix.Config
+import Config
 
 config :grpc, start_server: true
 
diff --git a/examples/helloworld/mix.exs b/examples/helloworld/mix.exs
index ff2c94c..31103d5 100644
--- a/examples/helloworld/mix.exs
+++ b/examples/helloworld/mix.exs
@@ -18,8 +18,6 @@ defmodule Helloworld.Mixfile do
   defp deps do
     [
       {:grpc, path: "../../"},
-      {:protobuf, github: "tony612/protobuf-elixir", override: true},
-      {:cowlib, "~> 2.8.0", hex: :grpc_cowlib, override: true},
       {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
     ]
   end
diff --git a/examples/helloworld/mix.lock b/examples/helloworld/mix.lock
index 010aed5..508e4b5 100644
--- a/examples/helloworld/mix.lock
+++ b/examples/helloworld/mix.lock
@@ -1,8 +1,8 @@
 %{
-  "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
-  "cowlib": {:hex, :grpc_cowlib, "2.8.1", "ddaf77f3b89bd8e6c76df67b28a4b069688eef91c0c497a246cf9bfcdf87f7d3", [:rebar3], [], "hexpm"},
-  "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
-  "gun": {:hex, :grpc_gun, "2.0.0", "f99678a2ab975e74372a756c86ec30a8384d3ac8a8b86c7ed6243ef4e61d2729", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm"},
-  "protobuf": {:git, "https://github.com/tony612/protobuf-elixir.git", "384f97ca03aa4f874e527e9f28f5ebbae2f142f1", []},
-  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
+  "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
+  "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
+  "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"},
+  "gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
+  "protobuf": {:hex, :protobuf, "0.9.0", "9c1633ecc098f3d7ec0a00503e070541b0e1868114fff41523934888442319e7", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "15fb7cddc5f85b8055fedaf81a9093020e4cd283647a21deb8f7de8d243abb9d"},
+  "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
 }
diff --git a/examples/route_guide/config/config.exs b/examples/route_guide/config/config.exs
index f346b5d..0234afa 100644
--- a/examples/route_guide/config/config.exs
+++ b/examples/route_guide/config/config.exs
@@ -1,3 +1,3 @@
-use Mix.Config
+import Config
 
 # config :grpc, start_server: true
diff --git a/interop/config/config.exs b/interop/config/config.exs
index c659714..c4e7cbc 100644
--- a/interop/config/config.exs
+++ b/interop/config/config.exs
@@ -8,5 +8,4 @@ config :prometheus, GRPCPrometheus.ClientInterceptor,
 
 # config :grpc, start_server: true
 
-# config :logger, level: :debug
 config :logger, level: :warn
diff --git a/lib/grpc/logger/client.ex b/lib/grpc/logger/client.ex
index 0dab721..28c3f8b 100644
--- a/lib/grpc/logger/client.ex
+++ b/lib/grpc/logger/client.ex
@@ -7,21 +7,34 @@ defmodule GRPC.Logger.Client do
       17:13:33.021 [info]  Call say_hello of helloworld.Greeter
       17:13:33.079 [info]  Got :ok in 58ms
 
+  ## Options
+
+    * `:level` - the desired log level. Defaults to `:info`
+    * `:accepted_comparators` - a list with the accepted `Logger.compare_levels(configured_level, Logger.level())` results.
+    Defaults to `[:lt, :eq]`
+
   ## Usage
 
       {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Logger.Client])
+      # This will log on `:info` and greater priority
       {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, level: :info}])
+      # This will log only on `:info`
+      {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [{GRPC.Logger.Client, 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]}])
   """
 
   def init(opts) do
     level = Keyword.get(opts, :level) || :info
-    [level: level]
+    accepted_comparators = Keyword.get(opts, :accepted_comparators) || [:lt, :eq]
+    [level: level, accepted_comparators: accepted_comparators]
   end
 
   def call(%{grpc_type: grpc_type} = stream, req, next, opts) do
     level = opts[:level]
+    accepted_comparators = opts[:accepted_comparators]
 
-    if Logger.compare_levels(level, Logger.level()) != :lt do
+    if Logger.compare_levels(level, Logger.level()) in accepted_comparators do
       Logger.log(level, fn ->
         ["Call ", to_string(elem(stream.rpc, 0)), " of ", stream.service_name]
       end)
diff --git a/lib/grpc/logger/server.ex b/lib/grpc/logger/server.ex
index dbfbcfa..ba52ffa 100644
--- a/lib/grpc/logger/server.ex
+++ b/lib/grpc/logger/server.ex
@@ -5,6 +5,12 @@ defmodule GRPC.Logger.Server do
       17:18:45.151 [info]  Handled by HelloServer.say_hello
       17:18:45.151 [info]  Response :ok in 11µs
 
+  ## Options
+
+    * `:level` - the desired log level. Defaults to `:info`
+    * `:accepted_comparators` - a list with the accepted `Logger.compare_levels(configured_level, Logger.level())` results.
+    Defaults to `[:lt, :eq]`
+
   ## Usage
 
       defmodule Your.Endpoint do
@@ -12,36 +18,47 @@ defmodule GRPC.Logger.Server do
 
         intercept GRPC.Logger.Server, 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]
+      end
+
+      defmodule Your.Endpoint do
+        use GRPC.Endpoint
+
+        # logs only on :error
+        intercept GRPC.Logger.Server, level: :error, accepted_comparators: [:eq]
+      end
   """
   require Logger
   @behaviour GRPC.ServerInterceptor
 
   def init(opts) do
     level = Keyword.get(opts, :level) || :info
-    [level: level]
+    accepted_comparators = Keyword.get(opts, :accepted_comparators) || [:lt, :eq]
+    [level: level, accepted_comparators: accepted_comparators]
   end
 
   def call(req, stream, next, opts) do
     level = Keyword.fetch!(opts, :level)
+    accepted_comparators = opts[:accepted_comparators]
 
-    if Logger.compare_levels(level, Logger.level()) != :lt do
+    if Logger.compare_levels(level, Logger.level()) in accepted_comparators do
       Logger.metadata(request_id: Logger.metadata()[:request_id] || stream.request_id)
 
-      Logger.log(level, fn ->
-        ["Handled by ", inspect(stream.server), ".", to_string(elem(stream.rpc, 0))]
-      end)
+      Logger.log(level, "Handled by #{inspect(stream.server)}.#{elem(stream.rpc, 0)}")
 
       start = System.monotonic_time()
       result = next.(req, stream)
       stop = System.monotonic_time()
 
       status = elem(result, 0)
+      diff = System.convert_time_unit(stop - start, :native, :microsecond)
 
-      Logger.log(level, fn ->
-        diff = System.convert_time_unit(stop - start, :native, :microsecond)
-
-        ["Response ", inspect(status), " in ", formatted_diff(diff)]
-      end)
+      Logger.log(level, "Response #{inspect(status)} in #{formatted_diff(diff)}")
 
       result
     else
diff --git a/mix.exs b/mix.exs
index 73c1465..f300c4d 100644
--- a/mix.exs
+++ b/mix.exs
@@ -7,7 +7,7 @@ defmodule GRPC.Mixfile do
     [
       app: :grpc,
       version: @version,
-      elixir: "~> 1.10",
+      elixir: "~> 1.11",
       elixirc_paths: elixirc_paths(Mix.env()),
       build_embedded: Mix.env() == :prod,
       start_permanent: Mix.env() == :prod,


Test patch:
diff --git a/config/test.exs b/config/test.exs
new file mode 100644
index 0000000..477c907
--- /dev/null
+++ b/config/test.exs
@@ -0,0 +1,3 @@
+import Config
+
+config :logger, level: :info
diff --git a/test/grpc/integration/endpoint_test.exs b/test/grpc/integration/endpoint_test.exs
index f5055bd..a0015cf 100644
--- a/test/grpc/integration/endpoint_test.exs
+++ b/test/grpc/integration/endpoint_test.exs
@@ -13,7 +13,7 @@ defmodule GRPC.Integration.EndpointTest do
   defmodule HelloEndpoint do
     use GRPC.Endpoint
 
-    intercept GRPC.Logger.Server, level: :info
+    intercept GRPC.Logger.Server, level: :info, accepted_comparators: [:lt, :eq, :gt]
     run HelloServer
   end
 
@@ -51,14 +51,14 @@ defmodule GRPC.Integration.EndpointTest do
   defmodule FeatureEndpoint do
     use GRPC.Endpoint
 
-    intercept GRPC.Logger.Server
+    intercept GRPC.Logger.Server, accepted_comparators: [:lt, :eq, :gt]
     run FeatureServer
   end
 
   defmodule FeatureAndHelloHaltEndpoint do
     use GRPC.Endpoint
 
-    intercept GRPC.Logger.Server
+    intercept GRPC.Logger.Server, accepted_comparators: [:lt, :eq, :gt]
     run HelloServer, interceptors: [HelloHaltInterceptor]
     run FeatureServer
   end
diff --git a/test/grpc/logger/server_test.exs b/test/grpc/logger/server_test.exs
index 6740d43..b666762 100644
--- a/test/grpc/logger/server_test.exs
+++ b/test/grpc/logger/server_test.exs
@@ -1,5 +1,7 @@
 defmodule GRPC.Logger.ServerTest do
-  use ExUnit.Case, async: true
+  use ExUnit.Case, async: false
+
+  import ExUnit.CaptureLog
 
   alias GRPC.Logger.Server
 
@@ -10,12 +12,66 @@ 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(:request, stream, fn :request, ^stream -> {:ok, :ok} end, level: :info)
+
+    Server.call(
+      :request,
+      stream,
+      fn :request, ^stream -> {:ok, :ok} end,
+      Server.init(level: :info)
+    )
 
     assert [request_id: request_id] == Logger.metadata()
 
     stream = %{stream | request_id: nil}
-    Server.call(:request, stream, fn :request, ^stream -> {:ok, :ok} end, level: :info)
+
+    Server.call(
+      :request,
+      stream,
+      fn :request, ^stream -> {:ok, :ok} end,
+      Server.init(level: :info)
+    )
+
     assert request_id == Logger.metadata()[:request_id]
   end
+
+  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
+      server_name = :"server_#{System.unique_integer()}"
+
+      logger_level = Logger.level()
+      assert logger_level == :info
+
+      logs =
+        capture_log(fn ->
+          stream = %Stream{server: server_name, rpc: {1, 2, 3}, request_id: "1234"}
+
+          Server.call(
+            :request,
+            stream,
+            fn :request, ^stream -> {:ok, :ok} end,
+            Server.init(
+              level: configured_level,
+              accepted_comparators: accepted_comparators
+            )
+          )
+        end)
+
+      if should_log do
+        assert Regex.match?(
+                 ~r/\[#{configured_level}\]\s+Handled by #{inspect(server_name)}/,
+                 logs
+               )
+      else
+        assert logs == ""
+      end
+    end
+  end
 end