elastic stack: corrigindo erros de timezone
No Elasticsearch todos os campos do tipo date
são sempre armazenados em UTC.
Além disso, quando usamos o Kibana para visualizar os dados no Elasticsearch, e estamos em uma timezone diferente de UTC, o Kibana por padrão irá converter a string de data em UTC para a timezone padrão do navegador.
Quando indexamos documentos onde os campos do tipo date
não foram convertidos para UTC e não possuem informação sobre a timezone na qual foram gerados, podemos causar uma confusão na hora de visualizar esses dados, já que uma string de data não UTC será interpretada como estando em UTC pelo Elasticsearch e convertida para a timezone do navegador pelo Kibana.
Para evitar esse problema devemos ajustar a string de data original durante a ingestão, convertendo para UTC ou informando a timezone original da string de data.
String de data sem timezone
Como exemplo vamos considerar que temos uma string de data com as seguintes características.
- foi gerada na timezone UTC -3.
- não possui informação sobre a timezone na qual foi gerada.
Nesse caso a string de data 2023-11-19T10:30:00
na timezone UTC -3 corresponde a string de data 2023-11-19T13:30:00
em UTC, um offset de -3 horas.
Se indexarmos essa string de data sem informar esse offset, ela será indexada como já estando em UTC e ao visualizarmos esse documento no Kibana, em um navegador na timezone UTC -3, a string de data original irá sofrer outro offset de -3 horas, sendo exibida como Nov 19, 2023 @ 07:30:00.000
.
Para exemplificar essa situação usaramos o seguinte template:
PUT _index_template/timezone-example
{
"index_patterns": ["timezone-example"],
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"dateString": {
"type": "keyword"
},
"message": {
"type": "text"
}
}
}
}
}
E o seguinte documento:
POST timezone-example/_doc/
{
"@timestamp": "2023-11-19T10:30:00",
"dateString": "2023-11-19T10:30:00",
"message": "date string example"
}
Nesse caso o campo @timestamp
por ser do tipo date
será convertido pelo Kibana que está na timezone UTC-3
, mas o campo dateString
por ser do tipo keyword
será tratado como uma string e não será convertido.
Usando agora um documento com a string de data convertida para UTC, não teremos esse problema no Kibana.
POST timezone-example/_doc/
{
"@timestamp": "2023-11-19T13:30:00",
"dateString": "2023-11-19T10:30:00",
"message": "date string example"
}
String de data com timezone
Quando temos a informação da timezone na String de data podemos verificar que esse problema não ocorre, já que o Elasticsearch converte a data corretamente para UTC.
POST timezone-example/_doc/
{
"@timestamp": "2023-11-19T10:30:00-03:00",
"dateString": "2023-11-19T10:30:00-03:00",
"message": "date string example with timezone"
}
Informando a timezone da string de data
A melhor forma de resolver esse problema é informar a timezone diretamente na string de data, mas nem sempre isso possível, nesses casos precisamos informar ao Elasticsearch que a string de data possui um offset de timezone e como isso será feito depende de como a ingestão é feita.
Ingestão via Logstash
Quando utilizamos o Logstash podemos usar o filtro date
com a opção timezone
, indicando que a string de data está em uma timezone diferente de UTC.
filter {
date {
match => ["dateString", "yyyy-MM-dd'T'HH:mm:ss]
target => "@timestamp"
timezone => "America/Sao_Paulo
}
}
O filtro date
acima irá fazer o parse do campo dateString
e validar se o padrão corresponde ao especificado e considerar que a string de data foi gerada na timezone America/Sao_Paulo
, em caso de sucesso o valor convertido para UTC será armazenado no campo @timestamp
.
Como exemplo de output temos:
{
"event" => {
"original" => "2023-11-19T10:30:00"
},
"@version" => "1",
"@timestamp" => 2023-11-19T13:30:00.000Z,
"dateString" => "2023-11-19T10:30:00"
}
O valor para a opção timezone
precisa estar no formato canônico (America/Sao_Paulo
) ou no formato numérico (-0300
).
Ingestão via Elasticsearch
Quando estamos enviando os dados diretamente ao Elasticsearch podemos utilizar um Ingest Pipeline com um date
processor para informar que a string de data está em uma timezone diferente de UTC.
PUT _ingest/pipeline/parse-date
{
"description": "parse date field",
"processors": [
{
"date" : {
"field" : "dateString",
"target_field" : "@timestamp",
"formats" : ["yyyy-MM-dd'T'HH:mm:ss"],
"timezone": "America/Sao_Paulo"
}
}
]
}
O pipeline acima funciona da mesma forma que o filtro date
do Logstash do exemplo anterior, para utilizar esse pipeline podemos fazer o seguinte request:
POST timezone-example/_doc?pipeline=parse-date
{
"@timestamp": "2023-11-19T10:30:00",
"dateString": "2023-11-19T10:30:00",
"message": "date string with ingest pipeline"
}
Podemos também configurar esse ingest pipeline como um final pipeline, fazendo com que ele seja sempre executado para qualquer request para esse índice, fazemos isso alterando o template.
PUT _index_template/timezone-example
{
"index_patterns": ["timezone-example"],
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"dateString": {
"type": "keyword"
},
"message": {
"type": "text"
}
}
},
"settings": {
"index.final_pipeline": "parse-date"
}
}
}
Quando temos uma string de data que foi gerada em uma timezone diferente de UTC, é necessário informarmos de alguma forma o offset em relação a UTC.