Skip to content

Commit ffc3609

Browse files
authored
feat: add logging options to use :level instead of deprecated :log_level (#779)
1 parent ae700a0 commit ffc3609

File tree

3 files changed

+204
-18
lines changed

3 files changed

+204
-18
lines changed

lib/tesla/middleware/logger.ex

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ defmodule Tesla.Middleware.Logger do
7676
7777
## Options
7878
79-
- `:log_level` - custom function for calculating log level (see below)
79+
- `:level` - custom function for calculating log level or atom for fixed level (see below)
80+
- `:log_level` - (deprecated) custom function for calculating log level (see below)
8081
- `:filter_headers` - sanitizes sensitive headers before logging in debug mode (see below)
8182
- `:debug` - use `Logger.debug/2` to log request/response details
8283
- `:format` - custom string template or function for log message (see below)
@@ -120,7 +121,43 @@ defmodule Tesla.Middleware.Logger do
120121
- `:warn` or `:warning` - for 3xx responses
121122
- `:info` - for 2xx responses
122123
123-
You can customize this setting by providing your own `log_level/1` function:
124+
You can customize this setting by providing your own level function that accepts
125+
both success and error cases:
126+
127+
```elixir
128+
defmodule MyClient do
129+
def client do
130+
Tesla.client([
131+
{Tesla.Middleware.Logger, level: &my_level/1}
132+
])
133+
end
134+
135+
def my_level({:ok, env}) do
136+
case env.status do
137+
404 -> :info
138+
_ -> :default
139+
end
140+
end
141+
142+
def my_level({:error, _reason}) do
143+
:error
144+
end
145+
end
146+
```
147+
148+
Or provide a fixed log level:
149+
150+
```elixir
151+
defmodule MyClient do
152+
def client do
153+
Tesla.client([
154+
{Tesla.Middleware.Logger, level: :debug}
155+
])
156+
end
157+
end
158+
```
159+
160+
You can also use the deprecated `log_level` option (will show a deprecation warning):
124161
125162
```elixir
126163
defmodule MyClient do
@@ -139,6 +176,13 @@ defmodule Tesla.Middleware.Logger do
139176
end
140177
```
141178
179+
To disable the deprecation warning for `:log_level`, add this to your config:
180+
181+
```elixir
182+
# config/config.exs
183+
config :tesla, disable_log_level_warning: true
184+
```
185+
142186
## Logger Debug output
143187
144188
`Tesla` will use `Logger.debug/2` to log request & response details using
@@ -233,28 +277,54 @@ defmodule Tesla.Middleware.Logger do
233277
response
234278
end
235279

236-
defp log_level({:error, _}, _), do: :error
280+
defp log_level(response, config) do
281+
log_level_option = Keyword.get(config, :log_level)
282+
level_option = Keyword.get(config, :level)
283+
284+
cond do
285+
log_level_option != nil and level_option != nil ->
286+
raise ArgumentError, "cannot provide both :log_level and :level options"
287+
288+
log_level_option != nil ->
289+
IO.warn(":log_level option is deprecated, use :level option instead")
290+
291+
apply_level_function(response, &legacy_log_level_wrapper(log_level_option, &1))
237292

238-
defp log_level({:ok, env}, config) do
239-
case Keyword.get(config, :log_level) do
240-
nil ->
241-
default_log_level(env)
293+
level_option != nil ->
294+
apply_level_function(response, level_option)
242295

243-
fun when is_function(fun) ->
244-
case fun.(env) do
245-
:default -> default_log_level(env)
246-
warning when warning in [:warn, :warning] -> @warning_level
247-
level -> level
248-
end
296+
true ->
297+
default_response_log_level(response)
298+
end
299+
end
249300

250-
warning when warning in [:warn, :warning] ->
251-
@warning_level
301+
defp apply_level_function(response, fun) when is_function(fun) do
302+
case fun.(response) do
303+
:default -> default_response_log_level(response)
304+
warning when warning in [:warn, :warning] -> @warning_level
305+
level -> level
306+
end
307+
end
252308

253-
atom when is_atom(atom) ->
254-
atom
309+
defp apply_level_function(_response, atom) when is_atom(atom) do
310+
case atom do
311+
warning when warning in [:warn, :warning] -> @warning_level
312+
level -> level
255313
end
256314
end
257315

316+
# Wrapper function to adapt old log_level functions to the new response tuple format
317+
defp legacy_log_level_wrapper(log_level_function, {:ok, env}) do
318+
log_level_function.(env)
319+
end
320+
321+
defp legacy_log_level_wrapper(_log_level_function, {:error, _}) do
322+
:error
323+
end
324+
325+
defp default_response_log_level({:error, _}), do: :error
326+
defp default_response_log_level({:ok, env}), do: default_log_level(env)
327+
258328
@spec default_log_level(Tesla.Env.t()) :: log_level
259329
def default_log_level(env) do
260330
cond do

test/tesla/middleware/logger_test.exs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,122 @@ defmodule Tesla.Middleware.LoggerTest do
223223
end
224224
end
225225

226+
describe "with level" do
227+
defmodule ClientWithLevel do
228+
use Tesla
229+
230+
plug Tesla.Middleware.Logger, level: &level/1
231+
232+
defp level({:ok, env}) do
233+
cond do
234+
env.status == 404 -> :info
235+
env.status >= 500 -> :error
236+
true -> :debug
237+
end
238+
end
239+
240+
defp level({:error, _reason}) do
241+
:warn
242+
end
243+
244+
adapter fn env ->
245+
case env.url do
246+
"/connection-error" ->
247+
{:error, :econnrefused}
248+
249+
"/server-error" ->
250+
{:ok, %{env | status: 500, body: "server error"}}
251+
252+
"/not-found" ->
253+
{:ok, %{env | status: 404, body: "not found"}}
254+
255+
"/ok" ->
256+
{:ok, %{env | status: 200, body: "ok"}}
257+
end
258+
end
259+
end
260+
261+
test "connection error logs at warn level" do
262+
log = capture_log(fn -> ClientWithLevel.get("/connection-error") end)
263+
assert log =~ "[warning] GET /connection-error -> error: :econnrefused"
264+
end
265+
266+
test "server error logs at error level" do
267+
log = capture_log(fn -> ClientWithLevel.get("/server-error") end)
268+
assert log =~ "[error] GET /server-error -> 500"
269+
end
270+
271+
test "not found logs at info level" do
272+
log = capture_log(fn -> ClientWithLevel.get("/not-found") end)
273+
assert log =~ "[info] GET /not-found -> 404"
274+
end
275+
276+
test "ok logs at debug level" do
277+
Logger.configure(level: :debug)
278+
log = capture_log(fn -> ClientWithLevel.get("/ok") end)
279+
assert log =~ "[debug] GET /ok -> 200"
280+
end
281+
end
282+
283+
describe "with level as atom" do
284+
defmodule ClientWithFixedLevel do
285+
use Tesla
286+
287+
plug Tesla.Middleware.Logger, level: :warn
288+
289+
adapter fn env ->
290+
case env.url do
291+
"/any-request" ->
292+
{:ok, %{env | status: 200, body: "ok"}}
293+
end
294+
end
295+
end
296+
297+
test "always logs at the specified level" do
298+
log = capture_log(fn -> ClientWithFixedLevel.get("/any-request") end)
299+
assert log =~ "[warning] GET /any-request -> 200"
300+
end
301+
end
302+
303+
describe "conflicting level options" do
304+
test "raises error when both :level and :log_level are provided" do
305+
assert_raise ArgumentError, "cannot provide both :log_level and :level options", fn ->
306+
client =
307+
Tesla.client(
308+
[
309+
{Tesla.Middleware.Logger,
310+
level: :info, log_level: &Tesla.Middleware.Logger.default_log_level/1}
311+
],
312+
fn env ->
313+
{:ok, %{env | status: 200, body: "ok"}}
314+
end
315+
)
316+
317+
Tesla.get(client, "/test")
318+
end
319+
end
320+
end
321+
322+
describe "deprecation warning configuration" do
323+
test "log_level deprecation warning shows by default" do
324+
# Capture both log and warnings
325+
output =
326+
ExUnit.CaptureIO.capture_io(:stderr, fn ->
327+
client =
328+
Tesla.client(
329+
[
330+
{Tesla.Middleware.Logger, log_level: fn _env -> :info end}
331+
],
332+
fn env -> {:ok, %{env | status: 200}} end
333+
)
334+
335+
Tesla.get(client, "/test")
336+
end)
337+
338+
assert output =~ ":log_level option is deprecated"
339+
end
340+
end
341+
226342
alias Tesla.Middleware.Logger.Formatter
227343

228344
defmodule CompileMod do

test/tesla/middleware/retry_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ defmodule Tesla.Middleware.RetryTest do
351351
finish_time = :os.system_time(:millisecond)
352352

353353
# need to allow some time for the request handling; should be small relative to max_delay to minimize probability of false negatives
354-
allowed_execution_ms = 100
354+
allowed_execution_ms = 200
355355

356356
%{retries: retries, start_time: start_time} = Agent.get(LaggyAdapter, fn state -> state end)
357357

0 commit comments

Comments
 (0)