Postgresql 将rsyslog中的传入文本时间戳转换为PostSQL的时间戳

Postgresql 将rsyslog中的传入文本时间戳转换为PostSQL的时间戳,postgresql,rsyslog,timestamp-with-timezone,Postgresql,Rsyslog,Timestamp With Timezone,我有来自各种linux服务器的日志,这些日志由rsyslog提供给PostgreSQL数据库。传入的时间戳是rsyslog的RFC3339格式的时间,如下所示:2020-10-12T12:01:18.162329+02:00 在数据库日志表的原始测试设置中,我将时间戳字段创建为“text”。我需要解析的大部分内容都正常工作,所以我希望将时间戳表列从文本转换为时间戳数据类型(如果可能的话,保留亚秒和时区) 最终结果应该是一个时间戳数据类型,这样我就可以使用PostgreSQL数据函数进行日期范围查

我有来自各种linux服务器的日志,这些日志由rsyslog提供给PostgreSQL数据库。传入的时间戳是rsyslog的RFC3339格式的时间,如下所示:
2020-10-12T12:01:18.162329+02:00

在数据库日志表的原始测试设置中,我将时间戳字段创建为“text”。我需要解析的大部分内容都正常工作,所以我希望将时间戳表列从文本转换为时间戳数据类型(如果可能的话,保留亚秒和时区)

最终结果应该是一个时间戳数据类型,这样我就可以使用PostgreSQL数据函数进行日期范围查询

这在PostgreSQL 11中可行吗?还是最好首先使用正确的时间戳列数据类型重新创建表

提前感谢您提供的任何指针、建议、查找位置或代码片段

相关rsyslog配置:

$template CustomFormat,"%timegenerated:::date-rfc3339% %syslogseverity-text:::uppercase% %hostname% %syslogtag% %msg%\n"
$ActionFileDefaultTemplate CustomFormat

...

template(name="rsyslog" type="list" option.sql="on") {
  constant(value="INSERT INTO log (timestamp, severity, hostname, syslogtag, message)
    values ('")
    property(name="timegenerated" dateFormat="rfc3339")  constant(value="','")
    property(name="syslogseverity-text" caseConversion="upper")  constant(value="','")
    property(name="hostname")  constant(value="','")
    property(name="syslogtag")  constant(value="','")
    property(name="msg")  constant(value="')")
}
以及日志表结构:

CREATE TABLE public.log
(
    id integer NOT NULL DEFAULT nextval('log_id_seq'::regclass),
    "timestamp" text COLLATE pg_catalog."default" DEFAULT timezone('UTC'::text, CURRENT_TIMESTAMP),
    severity character varying(10) COLLATE pg_catalog."default",
    hostname character varying(20) COLLATE pg_catalog."default",
    syslogtag character varying(24) COLLATE pg_catalog."default",
    program character varying(24) COLLATE pg_catalog."default",
    process text COLLATE pg_catalog."default",
    message text COLLATE pg_catalog."default",
    CONSTRAINT log_pkey PRIMARY KEY (id)
)
一些示例数据已经输入到表中(忽略消息中的时间戳,它们是由我的前任使用独立的手工记录系统完成的):

理论上,您可以将
文本
列转换为带时区的
时间戳
更改表。。更改列。。。设置数据类型。。。使用
,例如:

postgres=# CREATE TABLE tstest (tsval TEXT NOT NULL);
CREATE TABLE

postgres=# INSERT INTO tstest values('2020-10-12T12:01:18.162329+02:00');
INSERT 0 1

postgres=# ALTER TABLE tstest
             ALTER COLUMN tsval SET DATA TYPE TIMESTAMP WITH TIME ZONE
             USING tsval::TIMESTAMPTZ;
ALTER TABLE

postgres=# \d tstest
                       Table "public.tstest"
 Column |           Type           | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------
 tsval  | timestamp with time zone |           | not null |


postgres=# SELECT * FROM tstest ;
             tsval
-------------------------------
 2020-10-12 12:01:18.162329+02
(1 row)
PostgreSQL可以解析
RFC3339
格式,因此后续插入应该可以正常工作:

postgres=# INSERT INTO tstest values('2020-10-12T12:01:18.162329+02:00');
INSERT 0 1

postgres=# SELECT * FROM tstest ;
             tsval
-------------------------------
 2020-10-12 12:01:18.162329+02
 2020-10-12 12:01:18.162329+02
(2 rows)

但是注意表中的任何坏数据(即不能被解析为时间戳的值)将导致<代码> ALTER表< /代码>操作失败,因此您应该考虑在转换数据之前验证值。类似于

选择“timestamp”::TIMESTAMPTZ FROM public.log
将失败,并出现类似于
带有时区的timestamp类型的无效输入语法:“somebadvalue”

还要记住,这种
ALTER TABLE
需要重写表,这可能需要一些时间才能完成(取决于表的大小),并且需要
ACCESS EXCLUSIVE
锁,使得表在操作期间无法访问

如果要避免长时间运行的
ACCESS EXCLUSIVE
锁,您可能可以执行以下操作(未经测试):

  • 添加一个新的
    timestamtz
    列(添加列不会重写表,而且如果不使用易失性默认值,成本相当低廉)
  • 创建触发器以复制插入原始列中的任何值
  • 复制现有值(使用一组批处理的更新,如
    updatepublic.foo SET newlog=log::TIMESTAMPTZ
  • (在单个事务中)删除触发器和现有列,并将新列重命名为旧列

此时,日志系统仍在不断变化,因此我可以删除所有当前数据,而不必担心坏数据。问题仍然是:现在更改列,然后清除表,或者先清除,然后再更改。我想这并不重要。我抓住机会,在字段上运行alter table命令。工作性能很明显,到目前为止,我甚至不认为我需要清理桌子。谢谢你的回复!