logging - Elixir / Phoenix / Ecto:如何自定义SQL查询日志格式?
问题描述
在我的 Phoenix/Ecto 应用程序中,当我将日志级别设置为 时:debug
,我会看到 Ecto 发出的每个 SQL 查询的有用日志条目,如下所示:
[debug] QUERY OK source="users" db=1.9ms
SELECT u0."id", u0."full_name", u0."email", u0."uuid", u0."auth0_uid", u0."last_signed_in_at", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) LIMIT 1 [1]
[debug] QUERY OK source="projects" db=11.8ms
SELECT p0."id", p0."name", p0."uuid", p0."settings", p0."inserted_at", p0."updated_at" FROM "projects" AS p0 WHERE (p0."uuid" = $1) LIMIT 1 ["7qDjSk"]
[debug] QUERY OK source="project_admin_joins" db=1.9ms
SELECT p0."id", p0."project_id", p0."admin_id", p0."inserted_at", p0."updated_at", p0."project_id" FROM "project_admin_joins" AS p0 WHERE (p0."project_id" = $1) ORDER BY p0."project_id" [2]
但我想对这种格式进行一些调整:
- 每个 sql 查询都会产生两行日志输出。将所有这些信息放在一行中对我来说很重要,因为这样可以更轻松地在Papertrail等日志管理服务中查找事件。
- 理想情况下,我想去掉字段名称周围的引号,这样查询就更容易在视觉上扫描。
如何自定义 Ecto sql 查询日志的格式?
解决方案
似乎没有自定义 Ecto 的日志格式这样的事情;相反,我需要禁用内置日志记录并编写我自己的日志记录函数。但这在 Ecto v2 与 Ecto v3 中的工作方式非常不同。
Ecto v2
在config.exs
中,配置 MyApp.Repo 以使用新的自定义记录器功能。该功能可以在任何地方使用,但就我而言,我已将其放入MyApp.Repo.log_query/2
:
config :my_app, MyApp.Repo,
# ...
loggers: [{MyApp.Repo, :log_query, []}]
在lib/my_app/repo.ex
中,定义log_query
函数:(注意:在我的例子中,我已经硬编码了:debug
日志级别。)
...
require Logger
# Inspired by https://github.com/elixir-ecto/ecto/blob/v2.2.11/lib/ecto/log_entry.ex
def log_query(entry) do
Logger.log(:debug, fn ->
{ok, _} = entry.result
source = inspect(entry.source)
time_us = System.convert_time_unit(entry.query_time, :native, :microsecond)
time_ms = div(time_us, 100) / 10
# Strip out unnecessary quotes from the query for readability
query = Regex.replace(~r/(\d\.)"([^"]+)"/, entry.query, "\\1\\2")
params = inspect(entry.params, charlists: false)
"SQL query: #{ok} source=#{source} db=#{time_ms}ms #{query} params=#{params}"
end)
end
而已!确保您的日志级别设置正确,重新启动您的应用程序,您应该会看到以这种新格式而不是旧格式记录的所有 Ecto 查询。
Ecto v3
Ecto v3 鼓励您使用遥测事件而不是那些讨厌的旧配置。
在lib/my_app/application.ex
MyApp.Application.start/2
中,您需要设置遥测事件。Supervisor.start_link/2
在调用之前添加此代码段:
# Subscribe to Ecto queries for logging
# See https://hexdocs.pm/ecto/Ecto.Repo.html#module-telemetry-events
# and https://github.com/beam-telemetry/telemetry
handler = &MyApp.Telemetry.handle_event/4
:ok = :telemetry.attach("my_app-ecto", [:my_app, :repo, :query], handler, %{})
然后定义您的 Telemetry 模块,该模块目前只有一个事件处理程序。我把我的保存在lib/my_app/telemetry.ex
:
defmodule MyApp.Telemetry do
require Logger
# Thanks to https://hexdocs.pm/ecto/Ecto.Repo.html#module-telemetry-events
def handle_event([:my_app, :repo, :query], measurements, metadata, _config) do
Logger.log(:debug, fn ->
{ok, _} = metadata.result
source = inspect(metadata.source)
time = div(measurements.query_time, 100_000) / 10
# Strip out unnecessary quotes from the query for readability
query = Regex.replace(~r/(\d\.)"([^"]+)"/, metadata.query, "\\1\\2")
params = inspect(metadata.params, charlists: false)
"SQL query: #{ok} source=#{source} db=#{time}ms #{query} params=#{params}"
end)
end
end
在config/config.exs
中,配置 MyApp.Repo 以禁用标准 Ecto 日志记录:
config :my_app, MyApp.Repo,
# ...
log: false
而已!确保您的日志级别设置正确,重新启动您的应用程序,您应该会看到以这种新格式而不是旧格式记录的所有 Ecto 查询。
推荐阅读
- ruby - 如何使用 Fog::Compute::AWS 附加 Elastic Inference 加速器
- python - 在 Python 中同时进行多个泊松回归
- django - Django - 如何通过 Ajax 调用将 javascript 变量保存到 Django 数据库中?
- youtube-api - 通过 YouTube 数据 API 对评论进行分页
- marzipano - Marzipano - 如何打开/关闭图层?
- flutter - listview.builder 的每一项都有不同的数据
- sharepoint - 如何将 Power BI 表数据作为列表导出到 sharepoint
- ballerina - 模拟 nil 结果时测试失败
- c# - 当表上有任何此类实体要删除时,为什么 EF Extensions 不会引发异常?
- reactjs - 如何将反应日期选择器时间设置为 UTC