日志及审计 01:日志
以下内容是来自于我的知识星球:《PostgreSQL 小课》专栏,有需要可以关注一下
PostgreSQL 提供了非常丰富的日志基础设施。能够检查日志是每个 DBA 的关键技能——日志提供了关于集群过去的操作、当前正在进行的操作以及发生了什么的提示和信息。本章将介绍 PostgreSQL 日志配置的基础知识,为我们提供如何配置日志机制以获取有关集群活动的所需信息的说明。日志可以手动分析,但 DBA 通常还会利用自动化工具,以洞察更广泛的集群活动。与日志相关的话题是审计,它是跟踪谁对哪些数据做了什么的能力。一个良好的审计系统也可以帮助管理员识别数据库中发生了什么。
在本章中,我们将介绍如下主题:
- 日志介绍
- 使用 pgBadger 抽取日志信息
日志介绍
就像许多其他服务和数据库一样,PostgreSQL 提供了自己的日志基础设施,以便管理员可以检查守护进程正在做什么以及数据库系统的当前状态。虽然日志对于数据和数据库活动并不是必不可少的,但它们代表了关于整个系统中发生的事情的非常重要的部分,并且通过日志提供的重要线索,可以帮助管理员采取相应的动作。
PostgreSQL 有一个非常灵活和可配置的日志基础设施,允许不同的日志配置、轮转、归档和后续分析。
日志以文本形式存储,以便可以使用常见的日志分析工具进行分析,包括操作系统实用程序,如 grep,sed 和文本编辑器。
在默认的安装中,日志被包含在 PGDATA 目录的特定子文件夹中,但正如我们将在以下小节中看到的,我们可以自由地将日志移动到操作系统下面的任何位置。
在数据库中发生的每个事件都会在日志中的单独一行文本中记录下来,这是一个重要且有用的方面,特别是当我们想要使用基于行的工具(例如常见的 Unix 命令grep)来分析日志时。当然,将大量信息写入日志也有缺点;它需要系统资源,并且可能填满存放日志的存储空间。因此,根据集群的目的,管理日志基础设施非常重要,只记录可以用于后续分析的最少信息量即可。
[!WARNING]
如果我们配置日志的方式不正确,日志可能会迅速填满我们的磁盘存储空间,因此我们应该确保集群产生的日志不应超过系统的处理能力。
遵循常见的 Unix 哲学,PostgreSQL 允许我们将日志发送到一个名为 syslog 的外部组件。另外一个方式是 PostgreSQL 也有自己的组件,称为“日志收集器”,用于存储日志。
实际上,在负载较重的情况下,Syslog 集中收集器可能会丢弃日志条目,而 PostgreSQL 日志收集器明确设计为不丢失任何一条日志信息。因此,通常情况下,PostgreSQL 附带的日志收集器是跟踪日志的首选方式,这样一旦我们开始分析日志,我们就可以确保集群产生的所有信息都不会丢失。
PostgreSQL 的日志记录是通过主集群配置中的可调参数进行配置的,即 postgresqlconf 文件。在接下来的小节中,我们将了解到 PostgreSQL 的日志配置,并且我们将看到如何调整日志以满足我们的需求。
日志存放哪里
配置日志系统的第一步是决定在哪里以及如何存储文本日志。控制日志系统的主要参数是 log_destination,它可以取以下一个或多个值:
stderr
意味着集群日志将被发送到 postmaster 进程的标准错误输出,通常意味着它们将出现在启动集群的控制台上。syslog
意味着日志将被发送到外部的 syslog 组件。csvlog
意味着日志将以逗号分隔的形式存储,有助于日志的自动分析(稍后详细介绍)。jsonlog
意味着日志将以 JSON 格式的形式存储,这是另一种非常适用于日志自动分析的格式(稍后会详细介绍)。eventlog
是仅在 Windows 平台上可用的特定组件,用于收集大量服务的日志。
可以设置多个日志目标,以便存储不同目的地及类型的日志。
另一个日志基础设施的重要设置是 log_collector,它是一个布尔值,会触发一个进程(称为日志收集器),该进程会捕获所有发送到标准错误的日志并将其存储在我们想要的位置。简而言之,设置 log_destination = stderr 将强制 PostgreSQL 将所有日志消息发送到标准错误,这是启动服务的控制台。由于守护程序在后台启动,因此不会附加控制台,而且很少有人愿意保持控制台打开以查看屏幕上滚动的日志消息。因此,logging_collector = on 启用了 PostgreSQL 日志捕获进程,该进程旨在读取所有在标准错误上产生的消息并将其发送到适当的目标位置。通常,目标位置可以是文本文件、CSV 文件或其他类型文件。因此,log_destination 决定了 PostgreSQL 将在哪里输出日志消息,而 logging_collector 则会启动一个特定进程来捕获这些已发出的日志并将其发送到其他地方。需要注意的是,一些日志目标也需要启用 logging_collector:cvslog 和 jsonlog 需要启用 logging_collector。
前面的两个参数有些相互依赖:我们需要选择将 PostgreSQL 产生的日志发送到哪里(log_destination),并且如果我们需要将它们发送到标准错误或自定义格式(如 csvlog),则需要启用一个专用进程(logging_collector 值)来捕获任何日志条目并将其存储在磁盘上。那么我们的日志配置通常要配置以下内容:
log_destination = 'stderr'
logging_collector = on
这里,第一行告诉集群将产生的日志发送到标准错误,它们将由一个专用进程——日志收集器进行管理和存储。
在本节的其余部分中,我们将集中讨论日志收集器的配置。日志收集器可以配置为将日志放置在我们所需的目录中,按我们的要求命名日志文件,并自动进行日志轮转。日志轮转是每个日志系统中非常常见的功能,意味着一旦单个日志文件增长到指定大小,或者经过足够的时间,该文件日志将被关闭,然后创建一个新的(具有不同名称的)日志文件。例如,我们可以决定在单个文件达到 100 MB 或每 2 天时自动轮转我们的日志文件:第一个条件发生时触发轮转,以便 PostgreSQL 至少每 2 天或每 100 MB 的文本信息生成一个不同的日志文件。
[!TIP]
日志轮转很有用,因为它允许我们生成较小的日志文件,可以限定在特定的时间段内。一方面,这会将日志分散到多个(可能很小的)文件中;另一方面,它不会生成一个可能难以分析的单个(可能很大的)日志文件。
一旦我们启用了日志收集器,我们必须配置它以按我们的要求和位置存储日志。我们可以通过在 PostgreSQL 配置文件中为以下参数设置正确的值来配置日志收集器进程:
-
log_directory:存储日志文件的目录。它可以是相对路径,相对于 PGDATA 环境变量,也可以是绝对路径(进程必须能够写入)。
-
log_filename:用于指定日志文件(在日志目录中)的名称。该模式可以按照 strftime(3) 的格式来指定,以日期和时间进行格式化。例如,值
postgresql-%Y-%m-%d.log
将生成一个带有日期(分别是年、月和日)的日志文件名,例如 postgresql-2024-05-21.log。# 可以通过如下命令来查看 strftime 的帮助 $ man 3 strftime
-
log_rotation_age:表示日志多长时间进行轮转一次。例如,1d 表示 1 天,指定日志每天轮换一次。
-
log_rotation_size: 日志文件达到多大时将发生轮转。如 50MB 意味着日志大小达到 50MB 时将发生轮转。
-
log_truncate_on_rotation: 该参数是一个布尔值,用于是否开启日志轮转。如果开启,则日志到了指定的时间或文件大小,PostgreSQL 则会强制截断(即清空并重新开始)现有文件。
所有与轮转相关的设置需要打开 logging_collector 配置。在 postgresql.conf 配置文件中关于日志的配置如下:
logging_collector = on
log_destination = 'stderr,csvlog,jsonlog'
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_rotation_age = '1d'
log_rotation_size = '50MB'
根据上述设置,集群将使用日志收集器在日志目录(相对于 PGDATA)中为每一天(或 50MB 的大小)生成一个新的日志文件,并且每个日志文件都会标明它创建的年份、月份和日期。请注意,例如,系统将同时生成文本、JSON 和 CSV 格式的日志;对于后两种格式,PostgreSQL 会智能地更改日志文件的扩展名分别为json 和 csv。
假如我们配置了上述配置,则会在日志目录产生如下的日志文件:
$ ls -l $PGDATA/log
total 24
-rw------- 1 postgres postgres 1241 May 21 10:35 postgresql-2024-05-21.csv
-rw------- 1 postgres postgres 1976 May 21 10:35 postgresql-2024-05-21.json
-rw------- 1 postgres postgres 697 May 21 10:35 postgresql-2024-05-21.log
...
何时记录日志
前面我们介绍了把日志记录到哪里,那么接下来的问题是何时记录日志?什么操作都需记录还是发生了特定的操作才需要记录?日志记录的级别是什么?
最常见的日志级别依次为 info、notice、warning、error、log、fatal 和 panic,其中 info 是最低值,fatal 是最高值。
[!TIP]
工作见到很多开发同事喜欢使用 DEBUG 级别的日志,这在开发阶段固然很方便,有时上生产时忘记修改日志级别,把 OS 的磁盘沾满的例子有很多。这里的 info 类似 Spring 框架中的 DEBUG。
如果我们决定将 warning 作为我们要接受的最低阈值,那么所有低于该阈值的日志事件(例如 info 和 notice)将不会被写入到日志中。还有最低级别的 debug1 到 debug5,用于获取有关进程执行的开发信息和内部细节(通常在与 PostgreSQL 开发时使用)。
因此,集群将在不同时间产生不同的日志事件,且具有不同的优先级,这些日志将根据我们配置的阈值写入到日志中。
有两个参数可以用来调整日志阈值:log_min_messages 和 client_min_messages。前者 log_min_messages 决定了日志系统的阈值,而后者决定了每个新用户连接的阈值。它们有什么不同?
log_min_messages 告诉集群在不考虑传入用户连接或其设置的情况下要写入日志的内容。而 client_min_messages 决定客户端在连接期间向用户报告哪些日志事件。这两个设置都可以从前面的阈值列表中选择一个值。
开发或测试环境的典型用例如下:
log_min_messages = 'info'
client_min_messages = 'debug1'
设置阈值并不是唯一的方法来决定何时触发日志写入:还有另外一些设置可以用来处理语句执行的持续时间。
如果我们对客户执行的语句(如查询语句)感兴趣,我们可以调整以下日志记录参数:
- log_min_duration_statement 是一个表示毫秒数的整数值。超过阈值的语句都将被记录。将此值设置为 0 意味着系统中的每个语句都将被记录到日志中。
- log_min_duration_sample 和 log_statement_sample_rate 两个参数需要配合使用。log_min_duration_sample 表示仅记录运行时间超过该毫秒值的语句。它类似于 log_min_duration_statement,但不是记录每个语句,而是仅记录其中一部分。要记录的语句比例由 log_statement_sample_rate 决定,它处理一个介于 0 和 1 之间的值。
- log_transaction_sample_rate 是一个介于 0 和 1 之间的值,表示有多少个事务将被完全记录(即事务的每个语句都会出现在日志中),而不考虑语句的持续时间。
取样(sample)参数背后的思想是减少日志记录活动量(和大小),同时仍提供有关集群有用的信息。为了更好地理解上述参数,我们看下以下配置:
log_min_duration_statement = 500
log_min_duration_sample = 100
log_statement_sample_rate = 0.8
log_transaction_sample_rate = 0.5
以上配置将记录每个运行时间超过 500 毫秒(log_min_duration_statement)的语句,以及每个运行时间超过 100 毫秒的语句的 80%(log_min_duration_sample 和 log_statement_sample_rate)。最后,它将记录每两个事务中的一个(log_transaction_sample_rate)。
修改上述参数后,需要重启一下 PostgreSQL 服务。接下来我们使用以下语句来测试上述内容:
forumdb=&