将传入的文本时间戳从 syslog 转换为 postgresql 的时间戳
convert incoming text timestamp from rsyslog to timestamp for postrgesql
我有来自各种 linux 服务器的日志,这些日志由 rsyslog 提供给 PostgreSQL 数据库。传入的时间戳是一个 rsyslog'd RFC3339 格式的时间,如下所示:2020-10-12T12:01:18.162329+02:00
.
在数据库日志记录 table 的原始测试设置中,我将时间戳字段创建为 'text'。我需要解析的大部分内容都正常工作,所以我希望将该时间戳 table 列从文本转换为时间戳数据类型(并尽可能保留亚秒和时区)。
最终结果应该是时间戳数据类型,这样我就可以使用 PostgreSQL 数据函数进行日期范围查询。
这在 PostgreSQL 11 中可行吗?还是以正确的时间戳列数据类型重新创建 table 更好?
在此先感谢您提供任何指示、建议、查找位置或代码片段。
相关的 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="')")
}
和日志table结构:
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)
)
一些示例数据已经输入 table(忽略消息中的时间戳,它们是我的前任使用独立的手工日志系统完成的):
理论上您可以使用 ALTER TABLE .. ALTER COLUMN ... SET DATA TYPE ... USING
将 TEXT
列转换为 TIMESTAMP WITH TIME ZONE
,例如:
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)
但请注意 table 中的任何错误数据(即无法解析为时间戳的值)将导致 ALTER TABLE
操作失败,因此您应该考虑在转换之前验证这些值数据。像 SELECT "timestamp"::TIMESTAMPTZ FROM public.log
这样的东西会失败并出现像 invalid input syntax for type timestamp with time zone: "somebadvalue"
.
这样的错误
还要记住,这种 ALTER TABLE
需要 table 重写,这可能需要一些时间才能完成(取决于 table 的大小),并且需要ACCESS EXCLUSIVE
锁定,使 table 在操作期间无法访问。
如果你想避免 long-running ACCESS EXCLUSIVE
锁,你可以这样做(未测试):
- 添加一个新的
TIMESTAMPTZ
列(添加一个列不会重写 table,并且如果您不使用易变的默认值,则相当便宜)
- 创建触发器以复制插入到原始列中的任何值
- 复制现有值(使用一堆批量更新,如
UPDATE public.foo SET newlog = log::TIMESTAMPTZ
- (在单个事务中)删除触发器和现有列,并将新列重命名为旧列
我有来自各种 linux 服务器的日志,这些日志由 rsyslog 提供给 PostgreSQL 数据库。传入的时间戳是一个 rsyslog'd RFC3339 格式的时间,如下所示:2020-10-12T12:01:18.162329+02:00
.
在数据库日志记录 table 的原始测试设置中,我将时间戳字段创建为 'text'。我需要解析的大部分内容都正常工作,所以我希望将该时间戳 table 列从文本转换为时间戳数据类型(并尽可能保留亚秒和时区)。
最终结果应该是时间戳数据类型,这样我就可以使用 PostgreSQL 数据函数进行日期范围查询。
这在 PostgreSQL 11 中可行吗?还是以正确的时间戳列数据类型重新创建 table 更好?
在此先感谢您提供任何指示、建议、查找位置或代码片段。
相关的 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="')")
}
和日志table结构:
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)
)
一些示例数据已经输入 table(忽略消息中的时间戳,它们是我的前任使用独立的手工日志系统完成的):
理论上您可以使用 ALTER TABLE .. ALTER COLUMN ... SET DATA TYPE ... USING
将 TEXT
列转换为 TIMESTAMP WITH TIME ZONE
,例如:
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)
但请注意 table 中的任何错误数据(即无法解析为时间戳的值)将导致 ALTER TABLE
操作失败,因此您应该考虑在转换之前验证这些值数据。像 SELECT "timestamp"::TIMESTAMPTZ FROM public.log
这样的东西会失败并出现像 invalid input syntax for type timestamp with time zone: "somebadvalue"
.
还要记住,这种 ALTER TABLE
需要 table 重写,这可能需要一些时间才能完成(取决于 table 的大小),并且需要ACCESS EXCLUSIVE
锁定,使 table 在操作期间无法访问。
如果你想避免 long-running ACCESS EXCLUSIVE
锁,你可以这样做(未测试):
- 添加一个新的
TIMESTAMPTZ
列(添加一个列不会重写 table,并且如果您不使用易变的默认值,则相当便宜) - 创建触发器以复制插入到原始列中的任何值
- 复制现有值(使用一堆批量更新,如
UPDATE public.foo SET newlog = log::TIMESTAMPTZ
- (在单个事务中)删除触发器和现有列,并将新列重命名为旧列