【问题标题】:convert incoming text timestamp from rsyslog to timestamp for postrgesql将传入的文本时间戳从 syslog 转换为 postgresql 的时间戳
【发布时间】:2020-10-13 11:59:24
【问题描述】:

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

在数据库日志记录表的原始测试设置中,我将该时间戳字段创建为“文本”。我需要解析的大多数东西都可以正常工作,所以我希望将时间戳表列从文本转换为时间戳数据类型(并尽可能保留亚秒和时区)。

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

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

提前感谢您提供任何指针、建议、查看地点或代码的 sn-ps。

相关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)
)

一些样本数据已经输入表格(忽略消息中的时间戳,它们是由我的前任使用独立的手工记录系统完成的):

【问题讨论】:

    标签: postgresql rsyslog timestamp-with-timezone


    【解决方案1】:

    理论上,您可以使用ALTER TABLE .. ALTER COLUMN ... SET DATA TYPE ... USINGTEXT 列转换为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)
    

    但请注意,表中的任何不良数据(即无法解析为时间戳的值)都会导致ALTER TABLE 操作失败,因此您应该考虑在转换数据之前验证这些值。像 SELECT "timestamp"::TIMESTAMPTZ FROM public.log 这样的东西会失败并出现像 invalid input syntax for type timestamp with time zone: "somebadvalue" 这样的错误。

    还请记住,这种ALTER TABLE 需要重写表,这可能需要一些时间才能完成(取决于表的大小),并且需要ACCESS EXCLUSIVE 锁定,从而在持续时间内无法访问表操作。

    如果你想避免长时间运行的ACCESS EXCLUSIVE 锁,你可能会做这样的事情(未经测试):

    • 添加一个新的TIMESTAMPTZ 列(添加一个列不会重写表,并且如果您不使用易失的默认值,则相当便宜)
    • 创建触发器以复制插入到原始列中的任何值
    • 复制现有值(使用一组批量更新,如UPDATE public.foo SET newlog = log::TIMESTAMPTZ
    • (在单个事务中)删除触发器和现有列,并将新列重命名为旧列

    【讨论】:

    • 此时,日志系统仍在不断变化,因此我可以删除所有当前数据,而不必担心数据不正确。问题仍然存在:更改列 now 然后清除表,或者先清除然后更改。我想这并不重要?
    • 好吧,我抓住了这个机会,只是在现场运行了 alter table 命令。工作得很好,所以我认为我什至不需要清理桌子 - 到目前为止。感谢您的回复!
    猜你喜欢
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多