Don't record DB statements without sanitizaiton (#166)

* Don't record DB statements without sanitizaiton

This adds an option to OpentelemetryEcto.setup/1 that allows a query
sanitization function to be provided. If it is not provided, queries
are not captured (this is the default).

* test that db.statement isnt present unless query sanitizer is configured

* rename option to `:db_statement`

* run mix format
This commit is contained in:
Josh Lee 2023-06-13 07:13:01 -04:00 committed by GitHub
parent 12532e5ff5
commit b85420afed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 7 deletions

View File

@ -31,7 +31,6 @@ defmodule OpentelemetryEcto do
* `:time_unit` - a time unit used to convert the values of query phase * `:time_unit` - a time unit used to convert the values of query phase
timings, defaults to `:microsecond`. See `System.convert_time_unit/3` timings, defaults to `:microsecond`. See `System.convert_time_unit/3`
* `:span_prefix` - the first part of the span name, as a `String.t`, * `:span_prefix` - the first part of the span name, as a `String.t`,
defaults to the concatenation of the event name with periods, e.g. defaults to the concatenation of the event name with periods, e.g.
`"blog.repo.query"`. This will always be followed with a colon and the `"blog.repo.query"`. This will always be followed with a colon and the
@ -39,6 +38,12 @@ defmodule OpentelemetryEcto do
* `:additional_attributes` - additional attributes to include in the span. If there * `:additional_attributes` - additional attributes to include in the span. If there
are conflits with default provided attributes, the ones provided with are conflits with default provided attributes, the ones provided with
this config will have precedence. this config will have precedence.
* `:db_statement` - :disabled (default) | :enabled | fun
Whether or not to include db statements.
Optionally provide a function that takes a query string and returns a
sanitized version of it. This is useful for removing sensitive information from the
query string. Unless this option is `:enabled` or a function,
query statements will not be recorded on spans.
""" """
def setup(event_prefix, config \\ []) do def setup(event_prefix, config \\ []) do
event = event_prefix ++ [:query] event = event_prefix ++ [:query]
@ -89,7 +94,6 @@ defmodule OpentelemetryEcto do
# net.peer.name or net.peer.ip and net.peer.port # net.peer.name or net.peer.ip and net.peer.port
base_attributes = %{ base_attributes = %{
"db.type": db_type, "db.type": db_type,
"db.statement": query,
source: source, source: source,
"db.instance": database, "db.instance": database,
"db.name": database, "db.name": database,
@ -97,6 +101,21 @@ defmodule OpentelemetryEcto do
"total_time_#{time_unit}s": System.convert_time_unit(total_time, :native, time_unit) "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
attributes = attributes =
measurements measurements
|> Enum.reduce(%{}, fn |> Enum.reduce(%{}, fn

View File

@ -50,8 +50,6 @@ defmodule OpentelemetryEctoTest do
assert %{ assert %{
"db.instance": "opentelemetry_ecto_test", "db.instance": "opentelemetry_ecto_test",
"db.name": "opentelemetry_ecto_test",
"db.statement": "SELECT u0.\"id\", u0.\"email\" FROM \"users\" AS u0",
"db.type": :sql, "db.type": :sql,
"db.url": "ecto://localhost", "db.url": "ecto://localhost",
decode_time_microseconds: _, decode_time_microseconds: _,
@ -62,7 +60,31 @@ defmodule OpentelemetryEctoTest do
} = :otel_attributes.map(attributes) } = :otel_attributes.map(attributes)
end end
test "include additionaL_attributes" do 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"}) attach_handler(additional_attributes: %{"config.attribute": "special value", "db.instance": "my_instance"})
Repo.all(User) Repo.all(User)
@ -83,8 +105,6 @@ defmodule OpentelemetryEctoTest do
assert %{ assert %{
"db.instance": "opentelemetry_ecto_test", "db.instance": "opentelemetry_ecto_test",
"db.name": "opentelemetry_ecto_test",
"db.statement": "SELECT p0.\"id\", p0.\"body\", p0.\"user_id\" FROM \"posts\" AS p0",
"db.type": :sql, "db.type": :sql,
"db.url": "ecto://localhost", "db.url": "ecto://localhost",
decode_time_milliseconds: _, decode_time_milliseconds: _,