Skip to content

Commit 7d041e6

Browse files
committed
Add metadata to logger
1 parent d3ea190 commit 7d041e6

File tree

2 files changed

+140
-8
lines changed

2 files changed

+140
-8
lines changed

lib/tesla/middleware/logger.ex

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,16 @@ defmodule Tesla.Middleware.Logger.Formatter do
4343
Enum.map(format, &output(&1, request, response, time))
4444
end
4545

46-
defp output(:query, env, _, _), do: env.query |> Tesla.encode_query()
47-
defp output(:method, env, _, _), do: env.method |> to_string() |> String.upcase()
48-
defp output(:url, env, _, _), do: env.url
49-
defp output(:status, _, {:ok, env}, _), do: to_string(env.status)
50-
defp output(:status, _, {:error, reason}, _), do: "error: " <> inspect(reason)
51-
defp output(:time, _, _, time), do: :io_lib.format("~.3f", [time / 1000])
52-
defp output(binary, _, _, _), do: binary
46+
def output(:query, env, _, _), do: env.query |> Tesla.encode_query()
47+
def output(:method, env, _, _), do: env.method |> to_string() |> String.upcase()
48+
def output(:url, env, _, _), do: env.url
49+
def output(:status, _, {:ok, env}, _), do: to_string(env.status)
50+
def output(:status, _, {:error, reason}, _), do: "error: " <> inspect(reason)
51+
def output(:time, _, _, time), do: :io_lib.format("~.3f", [time / 1000])
52+
def output(:request_body, env, _, _), do: env.body
53+
def output(:response_body, _, {:ok, env}, _), do: env.body
54+
def output(:response_body, _, {:error, _}, _), do: nil
55+
def output(binary, _, _, _), do: binary
5356
end
5457

5558
defmodule Tesla.Middleware.Logger do
@@ -74,6 +77,7 @@ defmodule Tesla.Middleware.Logger do
7477
- `:filter_headers` - sanitizes sensitive headers before logging in debug mode (see below)
7578
- `:debug` - show detailed request/response logging
7679
- `:format` - custom string template or function for log message (see below)
80+
- `:metadata` - configure logger metadata
7781
7882
## Custom log format
7983
@@ -176,6 +180,27 @@ defmodule Tesla.Middleware.Logger do
176180
config :tesla, Tesla.Middleware.Logger,
177181
filter_headers: ["authorization"]
178182
```
183+
184+
### Configure Logger metadata
185+
186+
Set `metadata: true` to include metadata in the log output.
187+
188+
```
189+
plug Tesla.Middleware.Logger, metadata: true
190+
```
191+
192+
Pass a list of atoms to `metadata` to include only specific metadata.
193+
194+
```
195+
plug Tesla.Middleware.Logger, metadata: [:url, :status, :request_body]
196+
```
197+
198+
Use `:conceal` request option to conceal sensitive requests.
199+
200+
```
201+
Tesla.get(client, opts: [conceal: true]])
202+
```
203+
179204
"""
180205

181206
@behaviour Tesla.Middleware
@@ -208,7 +233,12 @@ defmodule Tesla.Middleware.Logger do
208233
if optional_runtime_format, do: Formatter.compile(optional_runtime_format), else: @format
209234

210235
level = log_level(response, config)
211-
Logger.log(level, fn -> Formatter.format(env, response, time, format) end)
236+
237+
Logger.log(
238+
level,
239+
fn -> Formatter.format(env, response, time, format) end,
240+
metadata(env, response, time, Keyword.get(config, :metadata, false))
241+
)
212242

213243
if Keyword.get(config, :debug, true) do
214244
Logger.debug(fn -> debug(env, response, config) end)
@@ -248,6 +278,20 @@ defmodule Tesla.Middleware.Logger do
248278
end
249279
end
250280

281+
@metadata_keys [:method, :url, :query, :status, :request_body, :response_body, :time]
282+
283+
defp metadata(req, res, time, keys) when is_list(keys) do
284+
if req.opts[:conceal] do
285+
[]
286+
else
287+
[tesla: Enum.into(keys, %{}, fn key -> {key, Formatter.output(key, req, res, time)} end)]
288+
end
289+
end
290+
291+
defp metadata(req, res, time, true), do: metadata(req, res, time, @metadata_keys)
292+
293+
defp metadata(_req, _res, _time, false), do: []
294+
251295
@debug_no_query "(no query)"
252296
@debug_no_headers "(no headers)"
253297
@debug_no_body "(no body)"

test/tesla/middleware/logger_test.exs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,92 @@ defmodule Tesla.Middleware.LoggerTest do
289289
assert Formatter.format(nil, nil, nil, {CompileMod, :format}) == "message"
290290
end
291291
end
292+
293+
describe "Metadata" do
294+
defmodule TestLoggerBackend do
295+
def init(_mod), do: {:ok, []}
296+
297+
def handle_event({level, _pid, {_mod, _msg, _time, meta}}, state) do
298+
send(meta[:pid], {:metadata, meta})
299+
{:ok, state}
300+
end
301+
302+
def handle_event(_, state), do: {:ok, state}
303+
end
304+
305+
setup do
306+
{:ok, _} = Logger.add_backend(TestLoggerBackend)
307+
on_exit(fn -> Logger.remove_backend(TestLoggerBackend) end)
308+
309+
adapter = fn env ->
310+
case env.url do
311+
"/connection-error" -> {:error, :econnrefused}
312+
"/server-error" -> {:ok, %{env | status: 500, body: "error"}}
313+
"/client-error" -> {:ok, %{env | status: 404, body: "error"}}
314+
"/redirect" -> {:ok, %{env | status: 301, body: "moved"}}
315+
"/ok" -> {:ok, %{env | status: 200, body: "ok"}}
316+
end
317+
end
318+
319+
[adapter: adapter]
320+
end
321+
322+
test "do not include metadata by default", %{adapter: adapter} do
323+
middleware = [
324+
Tesla.Middleware.Logger
325+
]
326+
327+
client = Tesla.client(middleware, adapter)
328+
capture_log(fn -> Tesla.get(client, "/ok") end)
329+
assert_received {:metadata, meta}
330+
refute meta[:tesla]
331+
end
332+
333+
test "include all metadata when set to true", %{adapter: adapter} do
334+
middleware = [
335+
{Tesla.Middleware.Logger, metadata: true}
336+
]
337+
338+
client = Tesla.client(middleware, adapter)
339+
capture_log(fn -> Tesla.post(client, "/ok", "reqdata") end)
340+
assert_received {:metadata, meta}
341+
342+
assert meta[:tesla] == %{
343+
status: "200",
344+
time: ~c"0.000",
345+
url: "/ok",
346+
query: "",
347+
method: "POST",
348+
request_body: "reqdata",
349+
response_body: "ok"
350+
}
351+
end
352+
353+
test "include only selected metadata", %{adapter: adapter} do
354+
middleware = [
355+
{Tesla.Middleware.Logger, metadata: [:status, :method]}
356+
]
357+
358+
client = Tesla.client(middleware, adapter)
359+
capture_log(fn -> Tesla.post(client, "/ok", "reqdata") end)
360+
assert_received {:metadata, meta}
361+
362+
assert meta[:tesla] == %{
363+
status: "200",
364+
method: "POST"
365+
}
366+
end
367+
368+
test "do not include metadata for concealed reqeusts", %{adapter: adapter} do
369+
middleware = [
370+
{Tesla.Middleware.Logger, metadata: true}
371+
]
372+
373+
client = Tesla.client(middleware, adapter)
374+
capture_log(fn -> Tesla.post(client, "/ok", "reqdata", opts: [conceal: true]) end)
375+
assert_received {:metadata, meta}
376+
377+
refute meta[:tesla]
378+
end
379+
end
292380
end

0 commit comments

Comments
 (0)