319 lines
9.5 KiB
Elixir
319 lines
9.5 KiB
Elixir
defmodule OpentelemetryEctoTest do
|
|
use ExUnit.Case
|
|
import Ecto.Query
|
|
require OpenTelemetry.Tracer
|
|
|
|
alias OpentelemetryEcto.TestRepo, as: Repo
|
|
alias OpentelemetryEcto.TestModels.{Comment, User, Post}
|
|
|
|
require Ecto.Query, as: Query
|
|
require OpenTelemetry.Tracer, as: Tracer
|
|
|
|
@event_name [:opentelemetry_ecto, :test_repo]
|
|
|
|
require Record
|
|
|
|
for {name, spec} <- Record.extract_all(from_lib: "opentelemetry/include/otel_span.hrl") do
|
|
Record.defrecord(name, spec)
|
|
end
|
|
|
|
setup do
|
|
:application.stop(:opentelemetry)
|
|
:application.set_env(:opentelemetry, :tracer, :otel_tracer_default)
|
|
|
|
:application.set_env(:opentelemetry, :processors, [
|
|
{:otel_batch_processor, %{scheduled_delay_ms: 1}}
|
|
])
|
|
|
|
:application.start(:opentelemetry)
|
|
|
|
:otel_batch_processor.set_exporter(:otel_exporter_pid, self())
|
|
|
|
OpenTelemetry.Tracer.start_span("test")
|
|
|
|
on_exit(fn ->
|
|
OpenTelemetry.Tracer.end_span()
|
|
end)
|
|
end
|
|
|
|
test "captures basic query events" do
|
|
attach_handler()
|
|
|
|
Repo.all(User)
|
|
|
|
assert_receive {:span,
|
|
span(
|
|
name: "opentelemetry_ecto.test_repo.query:users",
|
|
attributes: attributes,
|
|
kind: :client
|
|
)}
|
|
|
|
assert %{
|
|
"db.system": :postgresql,
|
|
"db.instance": "opentelemetry_ecto_test",
|
|
"db.type": :sql,
|
|
"db.url": "ecto://localhost",
|
|
decode_time_microseconds: _,
|
|
query_time_microseconds: _,
|
|
queue_time_microseconds: _,
|
|
source: "users",
|
|
total_time_microseconds: _
|
|
} = :otel_attributes.map(attributes)
|
|
end
|
|
|
|
test "exclude unsantized query" do
|
|
attach_handler()
|
|
Repo.all(User)
|
|
|
|
assert_receive {:span, span(attributes: attributes)}
|
|
assert !Map.has_key?(:otel_attributes.map(attributes), :"db.statement")
|
|
end
|
|
|
|
test "include unsanitized query when enabled" do
|
|
attach_handler(db_statement: :enabled)
|
|
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)
|
|
end
|
|
|
|
test "include santized query with sanitizer function" do
|
|
attach_handler(db_statement: fn str -> String.replace(str, "SELECT", "") end)
|
|
Repo.all(User)
|
|
|
|
assert_receive {:span, span(attributes: 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)
|
|
end
|
|
|
|
test "changes the time unit" do
|
|
attach_handler(time_unit: :millisecond)
|
|
|
|
Repo.all(Post)
|
|
|
|
assert_receive {:span,
|
|
span(
|
|
name: "opentelemetry_ecto.test_repo.query:posts",
|
|
attributes: attributes
|
|
)}
|
|
|
|
assert %{
|
|
"db.system": :postgresql,
|
|
"db.instance": "opentelemetry_ecto_test",
|
|
"db.type": :sql,
|
|
"db.url": "ecto://localhost",
|
|
decode_time_milliseconds: _,
|
|
query_time_milliseconds: _,
|
|
queue_time_milliseconds: _,
|
|
source: "posts",
|
|
total_time_milliseconds: _
|
|
} = :otel_attributes.map(attributes)
|
|
end
|
|
|
|
test "changes the span name prefix" do
|
|
attach_handler(span_prefix: "Ecto")
|
|
|
|
Repo.all(User)
|
|
|
|
assert_receive {:span, span(name: "Ecto:users")}
|
|
end
|
|
|
|
test "collects multiple spans" do
|
|
user = Repo.insert!(%User{email: "opentelemetry@erlang.org"})
|
|
Repo.insert!(%Post{body: "We got traced!", user: user})
|
|
|
|
attach_handler()
|
|
|
|
User
|
|
|> Repo.all()
|
|
|> Repo.preload([:posts])
|
|
|
|
assert_receive {:span, span(name: "opentelemetry_ecto.test_repo.query:users")}
|
|
assert_receive {:span, span(name: "opentelemetry_ecto.test_repo.query:posts")}
|
|
end
|
|
|
|
test "sets error message on error" do
|
|
attach_handler()
|
|
|
|
try do
|
|
Repo.all(from(u in "users", select: u.non_existant_field))
|
|
rescue
|
|
_ -> :ok
|
|
end
|
|
|
|
assert_receive {:span,
|
|
span(
|
|
name: "opentelemetry_ecto.test_repo.query:users",
|
|
status: {:status, :error, message}
|
|
)}
|
|
|
|
assert message =~ "non_existant_field does not exist"
|
|
end
|
|
|
|
test "preloads in sequence are tied to the parent span" do
|
|
user = Repo.insert!(%User{email: "opentelemetry@erlang.org"})
|
|
Repo.insert!(%Post{body: "We got traced!", user: user})
|
|
Repo.insert!(%Comment{body: "We got traced!", user: user})
|
|
|
|
attach_handler()
|
|
|
|
Tracer.with_span "parent span" do
|
|
Repo.all(Query.from(User, preload: [:posts, :comments]), in_parallel: false)
|
|
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"
|
|
)}
|
|
end
|
|
|
|
test "preloads in parallel are tied to the parent span" do
|
|
user = Repo.insert!(%User{email: "opentelemetry@erlang.org"})
|
|
Repo.insert!(%Post{body: "We got traced!", user: user})
|
|
Repo.insert!(%Comment{body: "We got traced!", user: user})
|
|
|
|
attach_handler()
|
|
|
|
Tracer.with_span "parent span" do
|
|
Repo.all(Query.from(User, preload: [:posts, :comments]))
|
|
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"
|
|
)}
|
|
end
|
|
|
|
test "nested query preloads are tied to the parent span" do
|
|
user = Repo.insert!(%User{email: "opentelemetry@erlang.org"})
|
|
Repo.insert!(%Post{body: "We got traced!", user: user})
|
|
Repo.insert!(%Comment{body: "We got traced!", user: user})
|
|
|
|
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])
|
|
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"
|
|
)}
|
|
|
|
# comments preload
|
|
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"
|
|
)}
|
|
|
|
# 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"
|
|
)}
|
|
end
|
|
|
|
test "nested query within Task does not skip parent span" do
|
|
user = Repo.insert!(%User{email: "opentelemetry@erlang.org"})
|
|
Repo.insert!(%Post{body: "We got traced!", user: user})
|
|
Repo.insert!(%Comment{body: "We got traced!", user: user})
|
|
|
|
attach_handler()
|
|
|
|
Tracer.with_span "root span" do
|
|
task =
|
|
Task.async(fn ->
|
|
Tracer.with_span "parent span" do
|
|
Repo.all(User)
|
|
end
|
|
end)
|
|
|
|
Task.await(task)
|
|
end
|
|
|
|
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"
|
|
)}
|
|
end
|
|
|
|
def attach_handler(config \\ []) do
|
|
# For now setup the handler manually in each test
|
|
handler = {__MODULE__, self()}
|
|
|
|
:telemetry.attach(handler, @event_name ++ [:query], &OpentelemetryEcto.handle_event/4, config)
|
|
|
|
on_exit(fn ->
|
|
:telemetry.detach(handler)
|
|
end)
|
|
end
|
|
end
|