diff --git a/instrumentation/opentelemetry_ecto/lib/opentelemetry_ecto.ex b/instrumentation/opentelemetry_ecto/lib/opentelemetry_ecto.ex index bce5906..c6f0513 100644 --- a/instrumentation/opentelemetry_ecto/lib/opentelemetry_ecto.ex +++ b/instrumentation/opentelemetry_ecto/lib/opentelemetry_ecto.ex @@ -104,37 +104,14 @@ defmodule OpentelemetryEcto do "total_time_#{time_unit}s": System.convert_time_unit(total_time, :native, time_unit) } - base_attributes = - case Keyword.fetch(config, :db_statement) do - {:ok, :enabled} -> - Map.put(base_attributes, :"db.statement", query) - - {:ok, :disabled} -> - base_attributes - - {:ok, sanitizer} -> - Map.put(base_attributes, :"db.statement", sanitizer.(query)) - - :error -> - base_attributes - end + db_statement_config = Keyword.get(config, :db_statement, :disabled) attributes = - measurements - |> Enum.reduce(%{}, fn - {k, v}, acc - when not is_nil(v) and k in [:decode_time, :query_time, :queue_time, :idle_time] -> - Map.put( - acc, - String.to_atom("#{k}_#{time_unit}s"), - System.convert_time_unit(v, :native, time_unit) - ) - - _, acc -> - acc - end) - |> Map.merge(base_attributes) - |> Map.merge(additional_attributes) + base_attributes + |> add_measurements(measurements, time_unit) + |> maybe_add_db_statement(db_statement_config, query) + |> maybe_add_db_system(repo.__adapter__()) + |> add_additional_attributes(additional_attributes) parent_context = case OpentelemetryProcessPropagator.fetch_ctx(self()) do @@ -179,4 +156,60 @@ defmodule OpentelemetryEcto do end defp format_error(_), do: "" + + defp add_measurements(attributes, measurements, time_unit) do + measurements + |> Enum.reduce(attributes, fn + {k, v}, acc + when not is_nil(v) and k in [:decode_time, :query_time, :queue_time, :idle_time] -> + Map.put( + acc, + String.to_atom("#{k}_#{time_unit}s"), + System.convert_time_unit(v, :native, time_unit) + ) + + _, acc -> + acc + end) + end + + defp maybe_add_db_statement(attributes, :enabled, query) do + Map.put(attributes, :"db.statement", query) + end + + defp maybe_add_db_statement(attributes, :disabled, _query) do + attributes + end + + defp maybe_add_db_statement(attributes, sanitizer, query) when is_function(sanitizer, 1) do + Map.put(attributes, :"db.statement", sanitizer.(query)) + end + + defp maybe_add_db_statement(attributes, _, _query) do + attributes + end + + defp maybe_add_db_system(attributes, Ecto.Adapters.Postgres) do + Map.put(attributes, :"db.system", :postgresql) + end + + defp maybe_add_db_system(attributes, Ecto.Adapters.MyXQL) do + Map.put(attributes, :"db.system", :mysql) + end + + defp maybe_add_db_system(attributes, Ecto.Adapters.SQLite3) do + Map.put(attributes, :"db.system", :sqlite) + end + + defp maybe_add_db_system(attributes, Ecto.Adapters.Tds) do + Map.put(attributes, :"db.system", :mssql) + end + + defp maybe_add_db_system(attributes, _) do + attributes + end + + defp add_additional_attributes(attributes, additional_attributes) do + Map.merge(attributes, additional_attributes) + end end diff --git a/instrumentation/opentelemetry_ecto/mix.lock b/instrumentation/opentelemetry_ecto/mix.lock index f8683db..0d6fc2f 100644 --- a/instrumentation/opentelemetry_ecto/mix.lock +++ b/instrumentation/opentelemetry_ecto/mix.lock @@ -1,12 +1,10 @@ %{ "acceptor_pool": {:hex, :acceptor_pool, "1.0.0", "43c20d2acae35f0c2bcd64f9d2bde267e459f0f3fd23dab26485bf518c281b21", [:rebar3], [], "hexpm", "0cbcd83fdc8b9ad2eee2067ef8b91a14858a5883cb7cd800e6fcd5803e158788"}, "chatterbox": {:hex, :ts_chatterbox, "0.13.0", "6f059d97bcaa758b8ea6fffe2b3b81362bd06b639d3ea2bb088335511d691ebf", [:rebar3], [{:hpack, "~>0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}], "hexpm", "b93d19104d86af0b3f2566c4cba2a57d2e06d103728246ba1ac6c3c0ff010aa7"}, - "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"}, - "earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"}, "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, @@ -21,7 +19,6 @@ "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "opentelemetry": {:hex, :opentelemetry, "1.3.0", "988ac3c26acac9720a1d4fb8d9dc52e95b45ecfec2d5b5583276a09e8936bc5e", [:rebar3], [{:opentelemetry_api, "~> 1.2.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "8e09edc26aad11161509d7ecad854a3285d88580f93b63b0b1cf0bac332bfcc0"}, "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.1", "7b69ed4f40025c005de0b74fce8c0549625d59cb4df12d15c32fe6dc5076ff42", [:mix, :rebar3], [{:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "6d7a27b7cad2ad69a09cabf6670514cafcec717c8441beb5c96322bac3d05350"}, - "opentelemetry_ecto": {:git, "https://github.com/bryannaegele/opentelemetry-erlang-contrib.git", "9730da9300cdd08138d3455deb270b06f3a440fa", [sparse: "propagators/opentelemetry_process_propagator", branch: "otel-process-walker"]}, "opentelemetry_exporter": {:hex, :opentelemetry_exporter, "1.6.0", "f4fbf69aa9f1541b253813221b82b48a9863bc1570d8ecc517bc510c0d1d3d8c", [:rebar3], [{:grpcbox, ">= 0.0.0", [hex: :grpcbox, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.3", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.2", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:tls_certificate_check, "~> 1.18", [hex: :tls_certificate_check, repo: "hexpm", optional: false]}], "hexpm", "1802d1dca297e46f21e5832ecf843c451121e875f73f04db87355a6cb2ba1710"}, "opentelemetry_process_propagator": {:hex, :opentelemetry_process_propagator, "0.2.0", "d549f257fc7e20f0ff8674efb46f0dba1b3c5fe4ee6e4bd38d9873c7e18c6be1", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "4062d3e982c5c7cd01b8b7189fc5c53dfacd666e47797483bc2310e5f0fa7f9c"}, "opentelemetry_semantic_conventions": {:hex, :opentelemetry_semantic_conventions, "0.2.0", "b67fe459c2938fcab341cb0951c44860c62347c005ace1b50f8402576f241435", [:mix, :rebar3], [], "hexpm", "d61fa1f5639ee8668d74b527e6806e0503efc55a42db7b5f39939d84c07d6895"}, diff --git a/instrumentation/opentelemetry_ecto/test/opentelemetry_ecto_test.exs b/instrumentation/opentelemetry_ecto/test/opentelemetry_ecto_test.exs index 77b17fe..9e35646 100644 --- a/instrumentation/opentelemetry_ecto/test/opentelemetry_ecto_test.exs +++ b/instrumentation/opentelemetry_ecto/test/opentelemetry_ecto_test.exs @@ -49,6 +49,7 @@ defmodule OpentelemetryEctoTest do )} assert %{ + "db.system": :postgresql, "db.instance": "opentelemetry_ecto_test", "db.type": :sql, "db.url": "ecto://localhost", @@ -73,7 +74,9 @@ defmodule OpentelemetryEctoTest do Repo.all(User) assert_receive {:span, span(attributes: attributes)} - assert %{"db.statement": "SELECT u0.\"id\", u0.\"email\" FROM \"users\" AS u0"} = :otel_attributes.map(attributes) + + assert %{"db.statement": "SELECT u0.\"id\", u0.\"email\" FROM \"users\" AS u0"} = + :otel_attributes.map(attributes) end test "include santized query with sanitizer function" do @@ -81,15 +84,20 @@ defmodule OpentelemetryEctoTest do Repo.all(User) assert_receive {:span, span(attributes: attributes)} - assert %{"db.statement": " u0.\"id\", u0.\"email\" FROM \"users\" AS u0"} = :otel_attributes.map(attributes) + + assert %{"db.statement": " u0.\"id\", u0.\"email\" FROM \"users\" AS u0"} = + :otel_attributes.map(attributes) end test "include additional_attributes" do attach_handler(additional_attributes: %{"config.attribute": "special value", "db.instance": "my_instance"}) + Repo.all(User) assert_receive {:span, span(attributes: attributes)} - assert %{"config.attribute": "special value", "db.instance": "my_instance"} = :otel_attributes.map(attributes) + + assert %{"config.attribute": "special value", "db.instance": "my_instance"} = + :otel_attributes.map(attributes) end test "changes the time unit" do @@ -104,6 +112,7 @@ defmodule OpentelemetryEctoTest do )} assert %{ + "db.system": :postgresql, "db.instance": "opentelemetry_ecto_test", "db.type": :sql, "db.url": "ecto://localhost", @@ -141,7 +150,7 @@ defmodule OpentelemetryEctoTest do attach_handler() try do - Repo.all(from u in "users", select: u.non_existant_field) + Repo.all(from(u in "users", select: u.non_existant_field)) rescue _ -> :ok end @@ -167,9 +176,24 @@ defmodule OpentelemetryEctoTest do end assert_receive {:span, span(span_id: root_span_id, name: "parent span")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:users")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:posts")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:comments")} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:users" + )} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:posts" + )} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:comments" + )} end test "preloads in parallel are tied to the parent span" do @@ -184,9 +208,24 @@ defmodule OpentelemetryEctoTest do end assert_receive {:span, span(span_id: root_span_id, name: "parent span")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:users")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:posts")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:comments")} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:users" + )} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:posts" + )} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:comments" + )} end test "nested query preloads are tied to the parent span" do @@ -197,21 +236,45 @@ defmodule OpentelemetryEctoTest do attach_handler() Tracer.with_span "parent span" do - users_query = from u in User, preload: [:posts, :comments] - comments_query = from c in Comment, preload: [user: ^users_query] + users_query = from(u in User, preload: [:posts, :comments]) + comments_query = from(c in Comment, preload: [user: ^users_query]) Repo.all(Query.from(User, preload: [:posts, comments: ^comments_query])) end assert_receive {:span, span(span_id: root_span_id, name: "parent span")} # root query - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:users")} + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:users" + )} + # comments preload - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:comments")} + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:comments" + )} + # users preload - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:users")} + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:users" + )} + # preloads of user - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:posts")} - assert_receive {:span, span(parent_span_id: ^root_span_id, name: "opentelemetry_ecto.test_repo.query:comments")} + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:posts" + )} + + assert_receive {:span, + span( + parent_span_id: ^root_span_id, + name: "opentelemetry_ecto.test_repo.query:comments" + )} end test "nested query within Task does not skip parent span" do @@ -234,7 +297,12 @@ defmodule OpentelemetryEctoTest do assert_receive {:span, span(span_id: _root_span_id, name: "root span")} assert_receive {:span, span(span_id: parent_span_id, name: "parent span")} - assert_receive {:span, span(parent_span_id: ^parent_span_id, name: "opentelemetry_ecto.test_repo.query:users")} + + assert_receive {:span, + span( + parent_span_id: ^parent_span_id, + name: "opentelemetry_ecto.test_repo.query:users" + )} end def attach_handler(config \\ []) do