可观测性

可观测性允许根据系统生成的日志、指标和跟踪等数据来衡量系统的当前状态。

日志

PostgREST 将基本请求信息记录到 stdout,包括可用的已认证用户、请求的 IP 地址和用户代理、请求的 URL 和 HTTP 响应状态。

127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"

有关服务器本身的诊断信息,PostgREST 将记录到 stderr

  • 连接的 PostgreSQL 数据库的完整版本。

  • 模式缓存 统计信息。

  • 监听器 收到的消息。

06/May/2024:08:16:11 -0500: Starting PostgREST 12.1...
06/May/2024:08:16:11 -0500: Attempting to connect to the database...
06/May/2024:08:16:11 -0500: Successfully connected to PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
06/May/2024:08:16:11 -0500: Listening on port 3000
06/May/2024:08:16:11 -0500: Listening for notifications on the "pgrst" channel
06/May/2024:08:16:11 -0500: Config reloaded
06/May/2024:08:16:11 -0500: Schema cache queried in 3.8 milliseconds
06/May/2024:08:16:11 -0500: Schema cache loaded 15 Relations, 8 Relationships, 8 Functions, 0 Domain Representations, 4 Media Type Handlers
06/May/2024:14:11:27 -0500: Received a config reload message on the "pgrst" channel
06/May/2024:14:11:27 -0500: Config reloaded

数据库日志

目前 PostgREST 不会记录针对底层数据库执行的 SQL 命令。

要查找 SQL 操作,您可以查看数据库日志。默认情况下,PostgreSQL 不会保留这些日志,因此您需要进行以下配置更改。

在您的 PostgreSQL 数据目录中找到 postgresql.conf(要找到它,请执行命令 show data_directory;)。要么在整个文件中找到这些设置并将它们更改为以下值,要么将此代码块追加到配置文件的末尾。

# send logs where the collector can access them
log_destination = "stderr"

# collect stderr output to log files
logging_collector = on

# save logs in pg_log/ under the pg data directory
log_directory = "pg_log"

# (optional) new log file per day
log_filename = "postgresql-%Y-%m-%d.log"

# log every kind of SQL statement
log_statement = "all"

重新启动数据库并实时查看日志文件,以了解 HTTP 请求是如何转换为 SQL 命令的。

注意

在 Docker 上,您可以使用自定义的 init.sh 来启用日志。

#!/bin/sh
echo "log_statement = 'all'" >> /var/lib/postgresql/data/postgresql.conf

之后,您可以启动容器并使用 docker logs 检查日志。

docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
docker logs -f <container-id>

指标

管理服务器 端点上的 metrics 端点以 Prometheus 文本格式 提供指标。

curl "http://localhost:3001/metrics"

# HELP pgrst_schema_cache_query_time_seconds The query time in seconds of the last schema cache load
# TYPE pgrst_schema_cache_query_time_seconds gauge
pgrst_schema_cache_query_time_seconds 1.5937927e-2
# HELP pgrst_schema_cache_loads_total The total number of times the schema cache was loaded
# TYPE pgrst_schema_cache_loads_total counter
pgrst_schema_cache_loads_total 1.0
...

模式缓存指标

模式缓存 相关的指标。

pgrst_schema_cache_query_time_seconds

类型

Gauge

上次模式缓存加载的查询时间(以秒为单位)。

pgrst_schema_cache_loads_total

类型

计数器

标签

status: SUCCESS | FAIL

架构缓存加载的总次数。

连接池指标

连接池相关的指标。

pgrst_db_pool_timeouts_total

类型

计数器

池连接超时总数。

pgrst_db_pool_available

类型

Gauge

池中可用的连接。

pgrst_db_pool_waiting

类型

Gauge

等待获取池连接的请求

pgrst_db_pool_max

类型

Gauge

最大池连接数。

跟踪

服务器版本头

调试问题时,验证正在运行的 PostgREST 版本非常重要。为此,您可以查看每个请求返回的 Server HTTP 响应头。

HEAD /users HTTP/1.1

Server: postgrest/11.0.1

跟踪头

您可以通过设置 server-trace-header 来启用跟踪 HTTP 请求。在请求中指定要设置的头,服务器将在响应中包含它。

server-trace-header = "X-Request-Id"
curl "http://localhost:3000/users" \
  -H "X-Request-Id: 123"
HTTP/1.1 200 OK
X-Request-Id: 123

服务器计时头

您可以通过设置 server-timing-enabled 为 on 来启用 Server-Timing 头。此头将传达请求-响应周期中不同阶段的指标。

curl "http://localhost:3000/users" -i
HTTP/1.1 200 OK

Server-Timing: jwt;dur=14.9, parse;dur=71.1, plan;dur=109.0, transaction;dur=353.2, response;dur=4.4
  • 所有持续时间 (dur) 都是以毫秒为单位。

  • jwt 阶段,基于 JWT 的用户模拟 完成。此持续时间可以通过 JWT 缓存 来降低。

  • parse 阶段,URL 语法 被解析。

  • plan 阶段,架构缓存 用于生成事务的 主查询

  • The transaction stage corresponds to the database transaction. See Transactions.

  • The response stage is where the response status and headers are computed.

注意

We’re working on lowering the duration of the parse and plan stages on https://github.com/PostgREST/postgrest/issues/2816.

执行计划

您可以通过添加 Accept: application/vnd.pgrst.plan 头部来获取请求的 EXPLAIN 执行计划。这由 db-plan-enabled(默认情况下为 false)启用。

curl "http://localhost:3000/users?select=name&order=id" \
  -H "Accept: application/vnd.pgrst.plan"
Aggregate  (cost=73.65..73.68 rows=1 width=112)
  ->  Index Scan using users_pkey on users  (cost=0.15..60.90 rows=850 width=36)

计划的输出默认情况下以 text 格式生成,但您可以使用 +json 后缀将其更改为 JSON。

curl "http://localhost:3000/users?select=name&order=id" \
  -H "Accept: application/vnd.pgrst.plan+json"
[
  {
    "Plan": {
      "Node Type": "Aggregate",
      "Strategy": "Plain",
      "Partial Mode": "Simple",
      "Parallel Aware": false,
      "Async Capable": false,
      "Startup Cost": 73.65,
      "Total Cost": 73.68,
      "Plan Rows": 1,
      "Plan Width": 112,
      "Plans": [
        {
          "Node Type": "Index Scan",
          "Parent Relationship": "Outer",
          "Parallel Aware": false,
          "Async Capable": false,
          "Scan Direction": "Forward",
          "Index Name": "users_pkey",
          "Relation Name": "users",
          "Alias": "users",
          "Startup Cost": 0.15,
          "Total Cost": 60.90,
          "Plan Rows": 850,
          "Plan Width": 36
        }
      ]
    }
  }
]

默认情况下,假设该计划生成资源的 JSON 表示形式(application/json),但您可以通过将它们添加到 for 参数中来获取 PostgREST 支持的不同表示形式 的计划。例如,要获取 text/xml 的计划,您将使用 Accept: application/vnd.pgrst.plan; for="text/xml

其他可用参数是 analyzeverbosesettingsbufferswal,它们对应于 EXPLAIN 命令选项。例如,要使用 analyzewal 参数,您将像 Accept: application/vnd.pgrst.plan; options=analyze|wal 那样添加它们。

请注意,与 EXPLAIN 命令类似,使用 analyze 选项时,更改将被提交。为了避免这种情况,您可以使用 db-tx-endPrefer: tx=rollback 头部。

保护执行计划

建议仅在测试环境中激活 db-plan-enabled,因为它会暴露内部数据库细节。但是,如果您选择在生产环境中使用它,您可以添加一个 db-pre-request 来过滤可以使用此功能的请求。

例如,要仅允许来自特定 IP 地址的请求获取执行计划

-- Assuming a proxy(Nginx, Cloudflare, etc) passes an "X-Forwarded-For" header(https://mdn.org.cn/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)
create or replace function filter_plan_requests()
returns void as $$
declare
  headers   json := current_setting('request.headers', true)::json;
  client_ip text := coalesce(headers->>'x-forwarded-for', '');
  accept    text := coalesce(headers->>'accept', '');
begin
  if accept like 'application/vnd.pgrst.plan%' and client_ip != '144.96.121.73' then
    raise insufficient_privilege using
      message = 'Not allowed to use application/vnd.pgrst.plan';
  end if;
end; $$ language plpgsql;

-- set this function on your postgrest.conf
-- db-pre-request = filter_plan_requests