Include `db.system` attribute in `opentelemetry_ecto` (#187)

* Run mix format

* Upgrade dependencies to build on OTP 26

* Add db.system attribute

* Add support for mssql and fallback to other_sql

* Fallback to not including db_system if adapter is unrecognised

---------

Co-authored-by: Tristan Sloughter <t@crashfast.com>
This commit is contained in:
Tom Taylor 2023-09-23 12:56:42 +01:00 committed by GitHub
parent 98d0bab4a9
commit 8068002c17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 50 deletions

View File

@ -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

View File

@ -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"},

View File

@ -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