FileLog 可让您:
- 订阅日志文件。
- 在新记录追加到已订阅的日志文件时对其进行处理。
创建表
path_to_logs– 要订阅的日志文件路径。可以是包含日志文件的目录路径,也可以是单个日志文件的路径。与 file 表函数一样,相对路径会基于user_files_path目录进行解析;如果某个相对路径从服务器工作目录来看已位于user_files_path内,则保持其原有含义 (例如,服务器在其数据目录中运行时 (如官方 Docker 镜像) 的路径user_files/my_app/app.log) 。路径中的文件名可以包含通配符 (*、?、{abc,def}、{N..M}) ,以仅读取目录中匹配的文件,参见使用通配符选择文件。请注意,ClickHouse 只允许使用user_files目录内的路径。format_name- 记录格式。请注意,FileLog 会将文件中的每一行视为一条单独的记录,因此并非所有数据格式都适用。
poll_timeout_ms- 从日志文件执行单次轮询的超时时间。默认值:stream_poll_timeout_ms。poll_max_batch_size— 单次轮询可拉取的最大记录数。默认值:max_block_size。max_block_size— 轮询的最大批次大小 (按记录数计) 。默认值:max_insert_block_size。max_threads- 用于解析文件的最大线程数,默认值为 0,这表示该值将为 max(1, physical_cpu_cores / 4)。poll_directory_watch_events_backoff_init- 目录监视线程的初始休眠时间。默认值:500。poll_directory_watch_events_backoff_max- 目录监视线程的最大休眠时间。默认值:32000。poll_directory_watch_events_backoff_factor- 退避速度,默认采用指数退避。默认值:2。handle_error_mode— FileLog 引擎 的错误处理方式。可选值:default (若某条记录解析失败,直接执行的SELECT会抛出异常;而当表以流式方式写入 materialized view 时,该记录会被跳过,错误则写入服务器日志) ,stream (异常消息和原始记录将保存在虚拟列_error和_raw_record中) 。
说明
已传递的记录会被自动跟踪,因此日志文件中的每条记录都只会被统计一次。如果某个文件在下次读取时的长度小于为其记录的偏移量 (例如使用copytruncate 方式执行 logrotate 之后) ,则会从头重新读取该文件。如果文件在下次读取之前又增长到不小于该偏移量的长度,则无法检测到截断。如果某个文件在目录中有多个名称 (硬链接) ,则只会通过其中一个名称读取该文件。
无法打开的文件 (目标已被删除的符号链接、服务器无法读取的文件,或已被删除的单文件表所对应的文件) 会被跳过,并在服务器日志中记录一条错误;在表加载期间,系统会持续重试,直到该文件能够打开为止。之前缺失的文件随后将从头开始读取。只有在表发现符号链接时其目标已存在,才会读取该符号链接。
SELECT 并不特别适合用来读取记录 (调试 除外) ,因为每条记录只能读取一次。更实用的做法是使用 materialized views 创建实时流。为此,请执行以下操作:
- 使用该引擎创建一个 FileLog 表,并将其视为数据流。
- 创建一个具有所需结构的表。
- 创建一个 materialized view,对来自该引擎的数据进行转换,并将其写入前面创建的表中。
MATERIALIZED VIEW 连接到该引擎后,它会开始在后台收集数据。这样,你就可以持续接收来自日志文件的记录,并使用 SELECT 将其转换为所需的格式。
一个 FileLog 表可以拥有任意多个 materialized views;它们不会直接从该表读取数据,而是接收新记录 (以块的形式) 。这样,你就可以将数据写入多个明细程度不同的表中 (带分组/aggregation 和不带分组) 。
示例:
ALTER 修改目标表,我们建议先禁用materialized view,以避免目标表与视图数据之间出现不一致。
使用通配符选择文件
当path_to_logs 中的文件名包含通配符时,表会读取该目录下所有名称匹配的文件,包括之后新出现的文件。路径中的目录部分不支持通配符。
对于表正在读取的文件,即使被重命名为不匹配的名称,也会继续被读取,直到该文件从目录中移除为止。如果文件是在表处于分离状态或服务器停止期间创建,并被重命名为不匹配的名称,则不会被读取。在 macOS 上,系统通过比较目录的列举结果来监视目录,因此,如果文件在表列举目录之前就已创建并被重命名为不匹配的名称,同样不会被读取。这正是日志轮转所需的行为。例如,使用带有 compress 和 delaycompress 选项的 logrotate 时,目录会保持如下状态:
FileLog('/var/lib/clickhouse/user_files/my_app/*.log', 'JSONEachRow') 创建的表会读取 app.log;日志轮转后,它会继续读取 app.log.1,因此应用程序在重新打开日志文件之前写入该文件的行不会丢失,而且该表永远不会读取压缩文件。请确保通配符不会匹配压缩文件的文件名。
虚拟列
_filename- 日志文件名称。数据类型:LowCardinality(String)。_offset- 日志文件中的偏移量。数据类型:UInt64。
handle_error_mode='stream' 时,还会有以下附加虚拟列:
_raw_record- 无法成功解析的原始记录。数据类型:Nullable(String)。_error- 解析失败期间产生的异常消息。数据类型:Nullable(String)。
_raw_record 和 _error 这两个虚拟列;如果消息解析成功,它们始终为 NULL。
数据持久性
FileLog 引擎会在所属插入操作提交前记录已消费分片的偏移量,因此服务器中断时,记录的偏移量可能会超过已写入目标表的数据。重启后,每个日志文件都会从其元数据目录中记录的偏移量继续读取,因此这些行永远不会被再次读取:它们会在不报错的情况下丢失,count() 结果也会相应变小。普通的进程故障就足以触发这一问题,无需断电,因为偏移量会记录在元数据文件中,而目标分片仍在写入时,该文件就会通过重命名替换到位。
OS 页缓存丢失还可能导致已写入目标表的数据被丢弃;例如设备级断电,以及主机或内核的非正常重置。保存偏移量的元数据文件在写入时本身也不会对文件或其目录执行 fsync,因此同样不具备任何持久性保证。
与消息 broker 引擎不同,无法通过先确保目标持久化来保护 FileLog 免受这一问题影响。由于偏移量是在读取管道内部记录的,并且发生在所属插入操作完成之前,因此在目标 MergeTree 表上设置 fsync_after_insert = 1,无法确保插入的分片在偏移量推进前已经持久化。应将 FileLog 消费视为对本地文件的尽力尾随:如果不能丢失任何行,请保留源日志文件,直到已消费的数据在目标中得到验证,以便能够重新消费。执行 SYSTEM RESET FILELOG,或删除并重新创建该表,都会丢弃记录的偏移量,并从头重新读取文件。