@@ -659,9 +659,16 @@ defmodule Ecto.Adapters.SQL do
659659 end
660660
661661 defp sql_call ( adapter_meta , callback , args , params , opts ) do
662- % { pid: pool , telemetry: telemetry , sql: sql , opts: default_opts } = adapter_meta
662+ % {
663+ pid: pool ,
664+ telemetry: telemetry ,
665+ sql: sql ,
666+ opts: default_opts ,
667+ log_stacktrace_mfa: log_stacktrace_mfa
668+ } = adapter_meta
669+
663670 conn = get_conn_or_pool ( pool , adapter_meta )
664- opts = with_log ( telemetry , params , opts ++ default_opts )
671+ opts = with_log ( telemetry , log_stacktrace_mfa , params , opts ++ default_opts )
665672 args = args ++ [ params , opts ]
666673 apply ( sql , callback , [ conn | args ] )
667674 end
@@ -861,6 +868,9 @@ defmodule Ecto.Adapters.SQL do
861868
862869 log = Keyword . get ( config , :log , :debug )
863870
871+ log_stacktrace_mfa =
872+ Keyword . get ( config , :log_stacktrace_mfa , { __MODULE__ , :first_non_ecto_stacktrace , [ 1 ] } )
873+
864874 if log not in @ valid_log_levels do
865875 raise """
866876 invalid value for :log option in Repo config
@@ -872,7 +882,7 @@ defmodule Ecto.Adapters.SQL do
872882 """
873883 end
874884
875- stacktrace = Keyword . get ( config , :stacktrace , nil )
885+ stacktrace = Keyword . get ( config , :stacktrace )
876886 telemetry_prefix = Keyword . fetch! ( config , :telemetry_prefix )
877887 telemetry = { config [ :repo ] , log , telemetry_prefix ++ [ :query ] }
878888
@@ -885,6 +895,7 @@ defmodule Ecto.Adapters.SQL do
885895 telemetry: telemetry ,
886896 sql: connection ,
887897 stacktrace: stacktrace ,
898+ log_stacktrace_mfa: log_stacktrace_mfa ,
888899 opts: Keyword . take ( config , @ pool_opts )
889900 }
890901
@@ -1097,8 +1108,15 @@ defmodule Ecto.Adapters.SQL do
10971108
10981109 @ doc false
10991110 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 )
1111+ % {
1112+ pid: pool ,
1113+ telemetry: telemetry ,
1114+ sql: sql ,
1115+ log_stacktrace_mfa: log_stacktrace_mfa ,
1116+ opts: default_opts
1117+ } = adapter_meta
1118+
1119+ opts = with_log ( telemetry , log_stacktrace_mfa , params , opts ++ default_opts )
11021120
11031121 case get_conn ( pool ) do
11041122 % DBConnection { conn_mode: :transaction } = conn ->
@@ -1113,8 +1131,15 @@ defmodule Ecto.Adapters.SQL do
11131131
11141132 @ doc false
11151133 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 )
1134+ % {
1135+ pid: pool ,
1136+ telemetry: telemetry ,
1137+ sql: sql ,
1138+ opts: default_opts ,
1139+ log_stacktrace_mfa: log_stacktrace_mfa
1140+ } = adapter_meta
1141+
1142+ opts = with_log ( telemetry , log_stacktrace_mfa , params , opts ++ default_opts )
11181143
11191144 case get_conn ( pool ) do
11201145 % DBConnection { conn_mode: :transaction } = conn ->
@@ -1231,11 +1256,11 @@ defmodule Ecto.Adapters.SQL do
12311256
12321257 ## Log
12331258
1234- defp with_log ( telemetry , params , opts ) do
1235- [ log: & log ( telemetry , params , & 1 , opts ) ] ++ opts
1259+ defp with_log ( telemetry , log_stacktrace_mfa , params , opts ) do
1260+ [ log: & log ( telemetry , log_stacktrace_mfa , params , & 1 , opts ) ] ++ opts
12361261 end
12371262
1238- defp log ( { repo , log , event_name } , params , entry , opts ) do
1263+ defp log ( { repo , log , event_name } , log_stacktrace_mfa , params , entry , opts ) do
12391264 % {
12401265 connection_time: query_time ,
12411266 decode_time: decode_time ,
@@ -1286,14 +1311,36 @@ defmodule Ecto.Adapters.SQL do
12861311 { true , level } ->
12871312 Logger . log (
12881313 level ,
1289- fn -> log_iodata ( measurements , repo , source , query , log_params , result , stacktrace ) end ,
1314+ fn ->
1315+ log_iodata (
1316+ measurements ,
1317+ repo ,
1318+ source ,
1319+ query ,
1320+ log_params ,
1321+ result ,
1322+ stacktrace ,
1323+ opts [ :log_stacktrace_mfa ] || log_stacktrace_mfa
1324+ )
1325+ end ,
12901326 ansi_color: sql_color ( query )
12911327 )
12921328
12931329 { opts_level , args_level } ->
12941330 Logger . log (
12951331 opts_level || args_level ,
1296- fn -> log_iodata ( measurements , repo , source , query , log_params , result , stacktrace ) end ,
1332+ fn ->
1333+ log_iodata (
1334+ measurements ,
1335+ repo ,
1336+ source ,
1337+ query ,
1338+ log_params ,
1339+ result ,
1340+ stacktrace ,
1341+ opts [ :log_stacktrace_mfa ] || log_stacktrace_mfa
1342+ )
1343+ end ,
12971344 ansi_color: sql_color ( query )
12981345 )
12991346 end
@@ -1310,7 +1357,7 @@ defmodule Ecto.Adapters.SQL do
13101357 defp log_measurements ( [ ] , total , acc ) ,
13111358 do: Map . new ( [ total_time: total ] ++ acc )
13121359
1313- defp log_iodata ( measurements , repo , source , query , params , result , stacktrace ) do
1360+ defp log_iodata ( measurements , repo , source , query , params , result , stacktrace , stacktrace_mfa ) do
13141361 [
13151362 "QUERY" ,
13161363 ?\s ,
@@ -1324,7 +1371,7 @@ defmodule Ecto.Adapters.SQL do
13241371 query ,
13251372 ?\s ,
13261373 inspect ( params , charlists: false ) ,
1327- log_stacktrace ( stacktrace , repo )
1374+ log_stacktrace ( stacktrace , repo , stacktrace_mfa )
13281375 ]
13291376 end
13301377
@@ -1351,22 +1398,23 @@ defmodule Ecto.Adapters.SQL do
13511398 end
13521399 end
13531400
1354- defp log_stacktrace ( stacktrace , repo ) do
1355- with [ _ | _ ] <- stacktrace ,
1356- { module , function , arity , info } <- last_non_ecto ( Enum . reverse ( stacktrace ) , repo , nil ) do
1401+ defp log_stacktrace ( [ _ | _ ] = stacktrace , repo , { module , function , args } ) do
1402+ entries = apply ( module , function , [ stacktrace , % { repo: repo } | args ] )
1403+
1404+ Enum . with_index ( entries , fn { module , function , arity , info } , idx ->
13571405 [
13581406 ?\n ,
13591407 IO.ANSI . light_black ( ) ,
1360- "↳ " ,
1408+ if ( idx == 0 , do: "↳ " , else: " " ) ,
13611409 Exception . format_mfa ( module , function , arity ) ,
13621410 log_stacktrace_info ( info ) ,
13631411 IO.ANSI . reset ( )
13641412 ]
1365- else
1366- _ -> [ ]
1367- end
1413+ end )
13681414 end
13691415
1416+ defp log_stacktrace ( _ , _ , _ ) , do: [ ]
1417+
13701418 defp log_stacktrace_info ( [ file: file , line: line ] ++ _ ) do
13711419 [ ", at: " , file , ?: , Integer . to_string ( line ) ]
13721420 end
@@ -1377,21 +1425,40 @@ defmodule Ecto.Adapters.SQL do
13771425
13781426 @ repo_modules [ Ecto.Repo.Queryable , Ecto.Repo.Schema , Ecto.Repo.Transaction ]
13791427
1380- defp last_non_ecto ( [ { mod , _ , _ , _ } | _stacktrace ] , repo , last )
1428+ @ doc """
1429+ Receives a stacktrace, and return the first N items before Ecto entries
1430+
1431+ This function is used by default in the `:log_stacktrace_mfa` config, with
1432+ a size of 1.
1433+ """
1434+ @ spec first_non_ecto_stacktrace (
1435+ Exception . stacktrace ( ) ,
1436+ % { repo: Ecto.Repo . t ( ) } ,
1437+ non_neg_integer ( )
1438+ ) :: Exception . stacktrace ( )
1439+ def first_non_ecto_stacktrace ( stacktrace , % { repo: repo } , size ) do
1440+ stacktrace
1441+ |> Enum . reverse ( )
1442+ |> last_non_ecto_entries ( repo , [ ] )
1443+ |> Enum . take ( size )
1444+ end
1445+
1446+ defp last_non_ecto_entries ( [ { mod , _ , _ , _ } | _ ] , repo , acc )
13811447 when mod == repo or mod in @ repo_modules ,
1382- do: last
1448+ do: acc
13831449
1384- defp last_non_ecto ( [ last | stacktrace ] , repo , _last ) ,
1385- do: last_non_ecto ( stacktrace , repo , last )
1450+ defp last_non_ecto_entries ( [ entry | rest ] , repo , acc ) ,
1451+ do: last_non_ecto_entries ( rest , repo , [ entry | acc ] )
13861452
1387- defp last_non_ecto ( [ ] , _repo , last ) ,
1388- do: last
1453+ defp last_non_ecto_entries ( [ ] , _ , acc ) , do: acc
13891454
13901455 ## Connection helpers
13911456
13921457 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 )
1458+ % { pid: pool , telemetry: telemetry , opts: default_opts , log_stacktrace_mfa: log_stacktrace_mfa } =
1459+ adapter_meta
1460+
1461+ opts = with_log ( telemetry , log_stacktrace_mfa , [ ] , opts ++ default_opts )
13951462
13961463 callback = fn conn ->
13971464 previous_conn = put_conn ( pool , conn )
0 commit comments