Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally log last known call, publish stacktrace in telemetry #3798

Merged
merged 11 commits into from Feb 15, 2022
2 changes: 1 addition & 1 deletion integration_test/cases/repo.exs
Expand Up @@ -20,7 +20,7 @@ defmodule Ecto.Integration.RepoTest do

test "supports unnamed repos" do
assert {:ok, pid} = TestRepo.start_link(name: nil)
assert Ecto.Repo.Queryable.all(pid, Post, []) == []
assert Ecto.Repo.Queryable.all(pid, Post, Ecto.Repo.Supervisor.triplet(pid, [])) == []
end

test "all empty" do
Expand Down
92 changes: 62 additions & 30 deletions lib/ecto/repo.ex
Expand Up @@ -56,6 +56,9 @@ defmodule Ecto.Repo do
use the `:repo` property in the event metadata for distinguishing
between repos.

* `:stacktrace`- publishes the stacktrace in telemetry events and
allows more advanced logging.

Comment on lines +59 to +61
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a bit of a gray area because, well, this is how ecto_sql uses it, but it may be used differently elsewhere :~

## URLs

Repositories by default support URLs. For example, the configuration
Expand Down Expand Up @@ -242,7 +245,7 @@ defmodule Ecto.Repo do
adapter.checked_out?(meta)
end

@compile {:inline, get_dynamic_repo: 0, with_default_options: 2}
@compile {:inline, get_dynamic_repo: 0, prepare_opts: 2}

def get_dynamic_repo() do
Process.get({__MODULE__, :dynamic_repo}, @default_dynamic_repo)
Expand All @@ -255,15 +258,18 @@ defmodule Ecto.Repo do
def default_options(_operation), do: []
defoverridable default_options: 1

defp with_default_options(operation_name, opts) do
Keyword.merge(default_options(operation_name), opts)
defp prepare_opts(operation_name, opts) do
operation_name
|> default_options()
|> Keyword.merge(opts)
end

## Transactions

if Ecto.Adapter.Transaction in behaviours do
def transaction(fun_or_multi, opts \\ []) do
Ecto.Repo.Transaction.transaction(__MODULE__, get_dynamic_repo(), fun_or_multi, with_default_options(:transaction, opts))
repo = get_dynamic_repo()
Ecto.Repo.Transaction.transaction(__MODULE__, repo, fun_or_multi, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:transaction, opts)))
end

def in_transaction? do
Expand All @@ -280,39 +286,48 @@ defmodule Ecto.Repo do

if Ecto.Adapter.Schema in behaviours and not @read_only do
def insert(struct, opts \\ []) do
Ecto.Repo.Schema.insert(__MODULE__, get_dynamic_repo(), struct, with_default_options(:insert, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.insert(__MODULE__, repo, struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:insert, opts)))
end

def update(struct, opts \\ []) do
Ecto.Repo.Schema.update(__MODULE__, get_dynamic_repo(), struct, with_default_options(:update, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.update(__MODULE__, get_dynamic_repo(), struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:update, opts)))
end

def insert_or_update(changeset, opts \\ []) do
Ecto.Repo.Schema.insert_or_update(__MODULE__, get_dynamic_repo(), changeset, with_default_options(:insert_or_update, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.insert_or_update(__MODULE__, get_dynamic_repo(), changeset, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:insert_or_update, opts)))
end

def delete(struct, opts \\ []) do
Ecto.Repo.Schema.delete(__MODULE__, get_dynamic_repo(), struct, with_default_options(:delete, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.delete(__MODULE__, get_dynamic_repo(), struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:delete, opts)))
end

def insert!(struct, opts \\ []) do
Ecto.Repo.Schema.insert!(__MODULE__, get_dynamic_repo(), struct, with_default_options(:insert, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.insert!(__MODULE__, get_dynamic_repo(), struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:insert, opts)))
end

def update!(struct, opts \\ []) do
Ecto.Repo.Schema.update!(__MODULE__, get_dynamic_repo(), struct, with_default_options(:update, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.update!(__MODULE__, get_dynamic_repo(), struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:update, opts)))
end

def insert_or_update!(changeset, opts \\ []) do
Ecto.Repo.Schema.insert_or_update!(__MODULE__, get_dynamic_repo(), changeset, with_default_options(:insert_or_update, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.insert_or_update!(__MODULE__, get_dynamic_repo(), changeset, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:insert_or_update, opts)))
end

def delete!(struct, opts \\ []) do
Ecto.Repo.Schema.delete!(__MODULE__, get_dynamic_repo(), struct, with_default_options(:delete, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.delete!(__MODULE__, get_dynamic_repo(), struct, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:delete, opts)))
end

def insert_all(schema_or_source, entries, opts \\ []) do
Ecto.Repo.Schema.insert_all(__MODULE__, get_dynamic_repo(), schema_or_source, entries, with_default_options(:insert_all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Schema.insert_all(__MODULE__, get_dynamic_repo(), schema_or_source, entries, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:insert_all, opts)))
end
end

Expand All @@ -321,77 +336,94 @@ defmodule Ecto.Repo do
if Ecto.Adapter.Queryable in behaviours do
if not @read_only do
def update_all(queryable, updates, opts \\ []) do
Ecto.Repo.Queryable.update_all(get_dynamic_repo(), queryable, updates, with_default_options(:update_all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.update_all(get_dynamic_repo(), queryable, updates, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:update_all, opts)))
end

def delete_all(queryable, opts \\ []) do
Ecto.Repo.Queryable.delete_all(get_dynamic_repo(), queryable, with_default_options(:delete_all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.delete_all(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:delete_all, opts)))
end
end

def all(queryable, opts \\ []) do
Ecto.Repo.Queryable.all(get_dynamic_repo(), queryable, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.all(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def stream(queryable, opts \\ []) do
Ecto.Repo.Queryable.stream(get_dynamic_repo(), queryable, with_default_options(:stream, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.stream(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:stream, opts)))
end

def get(queryable, id, opts \\ []) do
Ecto.Repo.Queryable.get(get_dynamic_repo(), queryable, id, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.get(get_dynamic_repo(), queryable, id, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def get!(queryable, id, opts \\ []) do
Ecto.Repo.Queryable.get!(get_dynamic_repo(), queryable, id, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.get!(get_dynamic_repo(), queryable, id, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def get_by(queryable, clauses, opts \\ []) do
Ecto.Repo.Queryable.get_by(get_dynamic_repo(), queryable, clauses, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.get_by(get_dynamic_repo(), queryable, clauses, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def get_by!(queryable, clauses, opts \\ []) do
Ecto.Repo.Queryable.get_by!(get_dynamic_repo(), queryable, clauses, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.get_by!(get_dynamic_repo(), queryable, clauses, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def reload(queryable, opts \\ []) do
Ecto.Repo.Queryable.reload(get_dynamic_repo(), queryable, opts)
repo = get_dynamic_repo()
Ecto.Repo.Queryable.reload(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:reload, opts)))
end

def reload!(queryable, opts \\ []) do
Ecto.Repo.Queryable.reload!(get_dynamic_repo(), queryable, opts)
repo = get_dynamic_repo()
Ecto.Repo.Queryable.reload!(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:reload, opts)))
end

def one(queryable, opts \\ []) do
Ecto.Repo.Queryable.one(get_dynamic_repo(), queryable, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.one(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def one!(queryable, opts \\ []) do
Ecto.Repo.Queryable.one!(get_dynamic_repo(), queryable, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.one!(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def aggregate(queryable, aggregate, opts \\ [])

def aggregate(queryable, aggregate, opts)
when aggregate in [:count] and is_list(opts) do
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def aggregate(queryable, aggregate, field)
when aggregate in @aggregates and is_atom(field) do
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, field, with_default_options(:all, []))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, field, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, [])))
end

def aggregate(queryable, aggregate, field, opts)
when aggregate in @aggregates and is_atom(field) and is_list(opts) do
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, field, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.aggregate(get_dynamic_repo(), queryable, aggregate, field, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def exists?(queryable, opts \\ []) do
Ecto.Repo.Queryable.exists?(get_dynamic_repo(), queryable, with_default_options(:all, opts))
repo = get_dynamic_repo()
Ecto.Repo.Queryable.exists?(get_dynamic_repo(), queryable, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:all, opts)))
end

def preload(struct_or_structs_or_nil, preloads, opts \\ []) do
Ecto.Repo.Preloader.preload(struct_or_structs_or_nil, get_dynamic_repo(), preloads, with_default_options(:preload, opts))
repo = get_dynamic_repo()
Ecto.Repo.Preloader.preload(struct_or_structs_or_nil, get_dynamic_repo(), preloads, Ecto.Repo.Supervisor.triplet(repo, prepare_opts(:preload, opts)))
end

def prepare_query(operation, query, opts), do: {query, opts}
Expand Down