Skip to content

Commit c7d9132

Browse files
committed
Allow configuring stacktrace depth for logging
A lot of times, more stack items can be helpful in identifying the source of a query. This commit demonstrates how we can support it. But the API has problems: 1. the `:stacktrace` option is used not only by logging but also for general telemetry events. Making it an integer doesn't make sense for telemetry (as the full stacktrace is published there anyway). 2. the `:stacktrace` option is overriden Ecto-side, so Ecto.SQL can't accept integers on a per-query basis, only on global configuration.
1 parent 8dc5a77 commit c7d9132

File tree

2 files changed

+45
-29
lines changed

2 files changed

+45
-29
lines changed

integration_test/sql/logging.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ defmodule Ecto.Integration.LoggingTest do
137137

138138
:ok
139139
end) =~ stacktrace_entry(__ENV__.line)
140+
141+
# Bigger stacktrace size
142+
# This fails because we don't receive per-query :stacktrace ecto-sql side,
143+
# only the global config from adapter_meta
144+
out = capture_log(fn ->
145+
TestRepo.all(Post, Keyword.put(@stacktrace_opts, :stacktrace, 2))
146+
147+
:ok
148+
end)
149+
150+
assert out =~ stacktrace_entry(__ENV__.line - 2)
151+
assert out =~ ~r/ ↳ ExUnit.CaptureLog.with_log\/2, at: lib\/ex_unit\/capture_log.ex:113/
140152
end
141153

142154
test "with custom log level" do

lib/ecto/adapters/sql.ex

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -658,10 +658,11 @@ defmodule Ecto.Adapters.SQL do
658658
sql_call(adapter_meta, :query_many, [sql], params, opts)
659659
end
660660

661+
661662
defp sql_call(adapter_meta, callback, args, params, opts) do
662-
%{pid: pool, telemetry: telemetry, sql: sql, opts: default_opts} = adapter_meta
663+
%{pid: pool, telemetry: telemetry, sql: sql, opts: default_opts, stacktrace: stacktrace_config} = adapter_meta
663664
conn = get_conn_or_pool(pool, adapter_meta)
664-
opts = with_log(telemetry, params, opts ++ default_opts)
665+
opts = with_log(telemetry, stacktrace_config, params, opts ++ default_opts)
665666
args = args ++ [params, opts]
666667
apply(sql, callback, [conn | args])
667668
end
@@ -872,7 +873,7 @@ defmodule Ecto.Adapters.SQL do
872873
"""
873874
end
874875

875-
stacktrace = Keyword.get(config, :stacktrace, nil)
876+
stacktrace = Keyword.get(config, :stacktrace)
876877
telemetry_prefix = Keyword.fetch!(config, :telemetry_prefix)
877878
telemetry = {config[:repo], log, telemetry_prefix ++ [:query]}
878879

@@ -1097,8 +1098,8 @@ defmodule Ecto.Adapters.SQL do
10971098

10981099
@doc false
10991100
def reduce(adapter_meta, statement, params, opts, acc, fun) do
1100-
%{pid: pool, telemetry: telemetry, sql: sql, opts: default_opts} = adapter_meta
1101-
opts = with_log(telemetry, params, opts ++ default_opts)
1101+
%{pid: pool, telemetry: telemetry, sql: sql, stacktrace: stacktrace_config, opts: default_opts} = adapter_meta
1102+
opts = with_log(telemetry, stacktrace_config, params, opts ++ default_opts)
11021103

11031104
case get_conn(pool) do
11041105
%DBConnection{conn_mode: :transaction} = conn ->
@@ -1113,8 +1114,8 @@ defmodule Ecto.Adapters.SQL do
11131114

11141115
@doc false
11151116
def into(adapter_meta, statement, params, opts) do
1116-
%{pid: pool, telemetry: telemetry, sql: sql, opts: default_opts} = adapter_meta
1117-
opts = with_log(telemetry, params, opts ++ default_opts)
1117+
%{pid: pool, telemetry: telemetry, sql: sql, opts: default_opts, stacktrace: stacktrace_config} = adapter_meta
1118+
opts = with_log(telemetry, stacktrace_config, params, opts ++ default_opts)
11181119

11191120
case get_conn(pool) do
11201121
%DBConnection{conn_mode: :transaction} = conn ->
@@ -1231,11 +1232,11 @@ defmodule Ecto.Adapters.SQL do
12311232

12321233
## Log
12331234

1234-
defp with_log(telemetry, params, opts) do
1235-
[log: &log(telemetry, params, &1, opts)] ++ opts
1235+
defp with_log(telemetry, stacktrace_config, params, opts) do
1236+
[log: &log(telemetry, stacktrace_config, params, &1, opts)] ++ opts
12361237
end
12371238

1238-
defp log({repo, log, event_name}, params, entry, opts) do
1239+
defp log({repo, log, event_name}, stacktrace_config, params, entry, opts) do
12391240
%{
12401241
connection_time: query_time,
12411242
decode_time: decode_time,
@@ -1286,21 +1287,24 @@ defmodule Ecto.Adapters.SQL do
12861287
{true, level} ->
12871288
Logger.log(
12881289
level,
1289-
fn -> log_iodata(measurements, repo, source, query, log_params, result, stacktrace) end,
1290+
fn -> log_iodata(measurements, repo, source, query, log_params, result, stacktrace, stacktrace_size(stacktrace_config)) end,
12901291
ansi_color: sql_color(query)
12911292
)
12921293

12931294
{opts_level, args_level} ->
12941295
Logger.log(
12951296
opts_level || args_level,
1296-
fn -> log_iodata(measurements, repo, source, query, log_params, result, stacktrace) end,
1297+
fn -> log_iodata(measurements, repo, source, query, log_params, result, stacktrace, stacktrace_size(stacktrace_config)) end,
12971298
ansi_color: sql_color(query)
12981299
)
12991300
end
13001301

13011302
:ok
13021303
end
13031304

1305+
defp stacktrace_size(val) when is_integer(val), do: val
1306+
defp stacktrace_size(_), do: 1
1307+
13041308
defp log_measurements([{_, nil} | rest], total, acc),
13051309
do: log_measurements(rest, total, acc)
13061310

@@ -1310,7 +1314,7 @@ defmodule Ecto.Adapters.SQL do
13101314
defp log_measurements([], total, acc),
13111315
do: Map.new([total_time: total] ++ acc)
13121316

1313-
defp log_iodata(measurements, repo, source, query, params, result, stacktrace) do
1317+
defp log_iodata(measurements, repo, source, query, params, result, stacktrace, stacktrace_size) do
13141318
[
13151319
"QUERY",
13161320
?\s,
@@ -1324,7 +1328,7 @@ defmodule Ecto.Adapters.SQL do
13241328
query,
13251329
?\s,
13261330
inspect(params, charlists: false),
1327-
log_stacktrace(stacktrace, repo)
1331+
log_stacktrace(stacktrace, repo, stacktrace_size)
13281332
]
13291333
end
13301334

@@ -1351,19 +1355,19 @@ defmodule Ecto.Adapters.SQL do
13511355
end
13521356
end
13531357

1354-
defp log_stacktrace(stacktrace, repo) do
1355-
with [_ | _] <- stacktrace,
1356-
{module, function, arity, info} <- last_non_ecto(Enum.reverse(stacktrace), repo, nil) do
1358+
defp log_stacktrace([], _, _), do: []
1359+
1360+
defp log_stacktrace(stacktrace, repo, size) do
1361+
for {{module, function, arity, info}, idx} <- Enum.with_index(last_non_ecto(Enum.reverse(stacktrace), repo, size)) do
13571362
[
13581363
?\n,
13591364
IO.ANSI.light_black(),
1365+
List.duplicate(?\s, 2 * idx),
13601366
"↳ ",
13611367
Exception.format_mfa(module, function, arity),
13621368
log_stacktrace_info(info),
13631369
IO.ANSI.reset()
13641370
]
1365-
else
1366-
_ -> []
13671371
end
13681372
end
13691373

@@ -1377,21 +1381,21 @@ defmodule Ecto.Adapters.SQL do
13771381

13781382
@repo_modules [Ecto.Repo.Queryable, Ecto.Repo.Schema, Ecto.Repo.Transaction]
13791383

1380-
defp last_non_ecto([{mod, _, _, _} | _stacktrace], repo, last)
1381-
when mod == repo or mod in @repo_modules,
1382-
do: last
1383-
1384-
defp last_non_ecto([last | stacktrace], repo, _last),
1385-
do: last_non_ecto(stacktrace, repo, last)
1384+
defp last_non_ecto(stacktrace, repo, size) do
1385+
stacktrace
1386+
|> last_non_ecto_entries(repo, [])
1387+
|> Enum.take(size)
1388+
end
13861389

1387-
defp last_non_ecto([], _repo, last),
1388-
do: last
1390+
defp last_non_ecto_entries([{mod, _, _, _} | _], repo, acc) when mod == repo or mod in @repo_modules, do: acc
1391+
defp last_non_ecto_entries([entry | rest], repo, acc), do: last_non_ecto_entries(rest, repo, [entry | acc])
1392+
defp last_non_ecto_entries([], _, acc), do: acc
13891393

13901394
## Connection helpers
13911395

13921396
defp checkout_or_transaction(fun, adapter_meta, opts, callback) do
1393-
%{pid: pool, telemetry: telemetry, opts: default_opts} = adapter_meta
1394-
opts = with_log(telemetry, [], opts ++ default_opts)
1397+
%{pid: pool, telemetry: telemetry, opts: default_opts, stacktrace: stacktrace_config} = adapter_meta
1398+
opts = with_log(telemetry, stacktrace_config, [], opts ++ default_opts)
13951399

13961400
callback = fn conn ->
13971401
previous_conn = put_conn(pool, conn)

0 commit comments

Comments
 (0)