Logstash执行JDBC查询时遇时区偏移异常的解决方法
解决Logstash JDBC输入的时区与夏令时异常问题
问题概述
你的Logstash JDBC输入配置存在两个核心问题:
- 时区配置错误:
sql_last_value生成的起始时间为'1970-01-01 02:00:00',但伊斯坦布尔时区(Europe/Istanbul)无夏令时,正确起始时间应为'1970-01-01 03:00:00' - 旧日期夏令时间隙异常:当查询到
log_date为1949-04-10这类历史日期时,触发JodaTime的IllegalInstantException,原因是该日期对应伊斯坦布尔时区的夏令时切换间隙,时间点不存在
你的原始JDBC配置:
input { jdbc { id => "mypipeline" jdbc_driver_class => "com.mysql.jdbc.Driver" jdbc_driver_library => "/usr/share/logstash/drivers/mysql-connector-j-8.0.32.jar" jdbc_connection_string => "my-connection-string" jdbc_password => "my-user" # 配置错误:此处应为密码,当前写了用户名 jdbc_user => "my-pass" # 配置错误:此处应为用户名,当前写了密码 jdbc_fetch_size => 5000 schedule => "*/5 * * * *" statement => "SELECT l.id, l.name, l.log_date FROM logs l WHERE l.created_at >= :sql_last_value" } }
触发的错误日志:
[2023-06-02T11:50:30,470][WARN ][logstash.inputs.jdbc ][main][mypipeline] Exception when executing JDBC query {:exception=>Sequel::DatabaseError, :message=>"Java::OrgJodaTime::IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 1949-04-10T00:00:00.000 (Europe/Istanbul)", :cause=>"#<Java::OrgJodaTime::IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 1949-04-10T00:00:00.000 (Europe/Istanbul)>"}
解决方案
1. 修正时区配置,确保sql_last_value使用正确时区
需要在JDBC连接和Logstash JDBC输入中明确指定伊斯坦布尔时区:
- 在
jdbc_connection_string中添加MySQL时区参数,确保数据库连接使用正确时区 - 添加
jdbc_default_timezone参数,强制Logstash生成sql_last_value时使用指定时区
修改后的配置片段:
jdbc_connection_string => "my-connection-string?serverTimezone=Europe/Istanbul" jdbc_default_timezone => "Europe/Istanbul"
2. 纠正用户名/密码的配置错误
将配置中搞反的用户名和密码修正:
jdbc_user => "my-user" jdbc_password => "my-pass"
3. 解决旧日期的夏令时间隙异常
由于1949年伊斯坦布尔时区的夏令时切换导致1949-04-10 00:00:00这个时间点不存在,JDBC驱动解析log_date字段时会触发异常,推荐采用以下方式解决:
方式一:在SQL查询中将日期转换为字符串
修改statement,把log_date用DATE_FORMAT转换为字符串,避免JDBC驱动解析为时间对象:
statement => "SELECT l.id, l.name, DATE_FORMAT(l.log_date, '%Y-%m-%d') AS log_date FROM logs l WHERE l.created_at >= :sql_last_value"
后续如果需要在Logstash中处理这个日期字段,可通过date过滤器将字符串转换为时间类型,并指定时区:
filter { date { match => ["log_date", "yyyy-MM-dd"] timezone => "Europe/Istanbul" target => "log_date" } }
完整修正后的配置示例
input { jdbc { id => "mypipeline" jdbc_driver_class => "com.mysql.jdbc.Driver" jdbc_driver_library => "/usr/share/logstash/drivers/mysql-connector-j-8.0.32.jar" jdbc_connection_string => "my-connection-string?serverTimezone=Europe/Istanbul" jdbc_user => "my-user" jdbc_password => "my-pass" jdbc_fetch_size => 5000 jdbc_default_timezone => "Europe/Istanbul" schedule => "*/5 * * * *" statement => "SELECT l.id, l.name, DATE_FORMAT(l.log_date, '%Y-%m-%d') AS log_date FROM logs l WHERE l.created_at >= :sql_last_value" } } filter { date { match => ["log_date", "yyyy-MM-dd"] timezone => "Europe/Istanbul" target => "log_date" } }
内容的提问来源于stack exchange,提问作者Hasan Can Saral
相关产品推荐
相关产品推荐

