Como usar o Painless em campos com script do Kibana

O Kibana fornece maneiras poderosas de buscar e visualizar dados armazenados no Elasticsearch. Para fins de visualização, o Kibana procura campos definidos nos mapeamentos do Elasticsearch e os apresenta como opções para o usuário que está montando um gráfico. Mas o que acontecerá se você se esquecer de definir um valor importante como um campo separado no seu esquema? Ou se você quiser combinar dois campos e tratá-los como um só? É aqui que entram os campos com script do Kibana.

Os campos com script existem desde os primórdios do Kibana 4. Na época em que foram introduzidos, a única maneira de defini-los dependia das expressões do Lucene, uma linguagem de script no Elasticsearch que lida exclusivamente com valores numéricos. Como resultado, o poder dos campos com script era limitado a um subconjunto de casos de uso. No 5.0, o Elasticsearch introduziu o Painless, uma linguagem de script segura e poderosa que permite operar em uma variedade de tipos de dados. Como resultado, os campos com script no Kibana 5.0 são muito mais poderosos.

No restante deste post do blog, mostraremos como criar campos com script para casos de uso comuns. Faremos isso com base em um conjunto de dados do tutorial de noções básicas do Kibana  (em inglês) e usaremos uma instância do Elasticsearch e do Kibana em execução no Elastic Cloud, que você pode ativar gratuitamente.

O vídeo a seguir explica como criar uma instância pessoal do Elasticsearch e do Kibana no Elastic Cloud e carregar um conjunto de dados de amostra nela. 

Como funcionam os campos com script

O Elasticsearch permite que você especifique campos com script em cada solicitação. O Kibana melhora isso permitindo que você defina um campo com script uma única vez na seção Management (Gerenciamento), para que possa ser usado em vários locais na UI dali em adiante. Observe que, embora o Kibana armazene campos com script junto com sua outra configuração no índice .kibana, essa configuração é específica do Kibana, e os campos com script do Kibana não são expostos aos usuários da API do Elasticsearch.

Ao definir um campo com script no Kibana, você terá a opção de usar várias linguagens de script, podendo escolher entre todas as linguagens instaladas nos nós do Elasticsearch que tenham script dinâmico habilitado. Por padrão, são “expressão” e “painless” na versão 5.0 e somente “expressão” na versão 2.x. Você pode instalar outras linguagens de script e habilitar o script dinâmico para elas, mas isso não é recomendado porque elas não podem ter sido suficientementetestadas em sandbox e estarem obsoletas.

Os campos com script operam em um único documento do Elasticsearch por vez, mas podem fazer referência a vários campos nesse documento. Como resultado, é apropriado usar campos com script para combinar ou transformar campos em um único documento, mas não realizar cálculos com base em vários documentos (por exemplo, matemática de série temporal). Tanto o Painless quanto as expressões do Lucene operam em campos armazenados em doc_values. Portanto, para dados de string, você precisará ter a string a ser armazenada na palavra-chave do tipo de dados. Campos com script baseados em Painless também não podem operar diretamente em _source.

Depois que os campos com script são definidos em “Management” (Gerenciamento), o usuário pode interagir com eles da mesma forma que com outros campos no restante do Kibana. Os campos com script aparecem automaticamente na lista de campos do Discover e estão disponíveis em Visualize (Visualizar) para fins de criação de visualizações. O Kibana simplesmente passa as definições dos campos com script para o Elasticsearch no momento da consulta para avaliação. O conjunto de dados resultante é combinado com outros resultados provenientes do Elasticsearch e apresentado ao usuário em uma tabela ou gráfico.

Existem algumas limitações conhecidas ao trabalhar com campos com script no momento em que escrevo este post. Você pode aplicar a maioria das agregações do Elasticsearch disponíveis no construtor visual do Kibana a campos com script, sendo a exceção mais notável a agregação de termos significativos. Você também pode filtrar campos com script por meio da barra de filtro no Discover, em Visualize (Visualizar) e no Dashboard, embora seja necessário ter o cuidado de escrever scripts adequados que retornem valores bem definidos, conforme mostramos a seguir. Também é importante consultar a seção “Práticas recomendadas” abaixo para garantir que você não desestabilize seu ambiente ao usar campos com script.

O vídeo a seguir mostra como usar o Kibana para criar campos com script.

Exemplos de campos com script

Esta seção apresenta alguns exemplos de campos com script do Painless e de expressões do Lucene no Kibana em cenários comuns. Conforme mencionamos acima, esses exemplos foram desenvolvidos com base em um conjunto de dados do tutorial de noções básicas do Kibana e pressupõem que você esteja usando o Elasticsearch e o Kibana 5.1.1, pois há alguns problemas conhecidos relacionados à filtragem e à classificação em certos tipos de campos com script em versões anteriores.

Na maioria das vezes, os campos com script devem funcionar imediatamente, pois as expressões do Lucene e o Painless estão habilitados por padrão no Elasticsearch 5.0. A única exceção são os scripts que exigem análise de campos baseada em regex, o que exigirá que você defina a seguinte configuração no arquivo elasticsearch.yml para ativar a correspondência de regex para Painless: script.painless.regex.enabled: true

Executar um cálculo em um único campo

  • Exemplo: calcular quilobytes a partir de bytes
  • Linguagem: expressões
  • Tipo de retorno: número
 doc['bytes'].value / 1024

Observação: lembre-se de que os campos com script do Kibana funcionam apenas em um único documento por vez, portanto, não há como fazer matemática de série temporal em um campo com script.

Matemática de data resultando em um número

  • Exemplo: analisar data em hora do dia
  • Linguagem: expressões
  • Tipo de retorno: número

As expressões do Lucene fornecem uma série de funções de manipulação de data prontas para uso. No entanto, como as expressões do Lucene retornam apenas valores numéricos, teremos de usar o Painless para retornar um dia da semana baseado em string (abaixo).

 doc['@timestamp'].date.hourOfDay

Observação: o script acima retornará de 1 a 24

doc['@timestamp'].date.dayOfWeek

Observação: o script acima retornará de 1 a 7

Combinar dois valores de string

  • Exemplo: combinar origem e destino ou nome e sobrenome
  • Linguagem: Painless
  • Tipo de retorno: string
 doc['geo.dest.keyword'].value + ':' + doc['geo.src.keyword'].value

Observação: como os campos com script precisam operar em campos em doc_values, estamos usando versões .keyword das strings acima.

Introdução de lógica

  • Exemplo: retornar rótulo “big download” para qualquer documento acima de 10.000 bytes
  • Linguagem: Painless
  • Tipo de retorno: string
 if (doc['bytes'].value > 10000) { 
return "big download";
}
return "";

Observação: ao introduzir a lógica, certifique-se de que cada caminho de execução tenha uma instrução de retorno bem definida e um valor de retorno bem definido (não nulo). Por exemplo, o campo com script acima falhará com um erro de compilação quando usado em filtros do Kibana sem a instrução return no final ou se a instrução retornar nulo. Lembre-se também de que não há suporte para dividir a lógica em funções nos campos com script do Kibana. 

Retornar substring

  • Exemplo: retornar a parte após a última barra no URL
  • Linguagem: Painless
  • Tipo de retorno: string
 def path = doc['url.keyword'].value;
if (path != null) {
int lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex > 0) {
return path.substring(lastSlashIndex+1);
}
}
return "";

Observação: sempre que possível, evite usar expressões regex para extrair substrings, pois as operações indexOf() consomem menos recursos e são menos propensas a erros. 

Corresponder uma string usando regex e executar uma ação em uma correspondência

  • Exemplo: retornar uma string “error” se uma substring “error” for encontrada no campo “referer”; caso contrário, retornar uma string “no error”.
  • Linguagem: Painless
  • Tipo de retorno: string
if (doc['referer.keyword'].value =~ /pt/error/) { 
return "error"
} else {
return "no error"
}

Observação: a sintaxe de regex simplificada é útil para condicionais com base em uma correspondência de regex. 

Corresponder uma string e retornar essa correspondência

  • Exemplo: retornar o domínio, a string após o último ponto no campo “host”.
  • Linguagem: Painless
  • Tipo de retorno: string
def m = /^.*\.([a-z]+)$/.matcher(doc['host.keyword'].value);
if ( m.matches() ) {
return m.group(1)
} else {
return "no match"
}

Observação: definir um objeto por meio das funções regex matcher() permite extrair grupos de caracteres que corresponderam ao regex e retorná-los. 

Corresponder um número e retornar essa correspondência

  • Exemplo: retornar o primeiro octeto do endereço IP (armazenado como uma string) e trate-o como um número.
  • Linguagem: Painless
  • Tipo de retorno: número
 def m = /^([0-9]+)\..*$/.matcher(doc['clientip.keyword'].value);
if ( m.matches() ) {
return Integer.parseInt(m.group(1))
} else {
return 0
}

Observação: é importante retornar o tipo de dados correto em um script. A correspondência de regex retorna uma string, mesmo que um número seja correspondido, portanto, você deve convertê-lo explicitamente em um número inteiro no retorno. 

Matemática de data resultando em strings

  • Exemplo: analisar data em dia da semana em string
  • Linguagem: Painless
  • Tipo de retorno: string
LocalDateTime.ofInstant(Instant.ofEpochMilli(doc['@timestamp'].value), ZoneId.of('Z')).getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())

Observação: como o Painless oferece suporte para todos os tipos nativos do Java, ele fornece acesso a funções nativas relacionadas a esses tipos, como LocalDateTime(), útil para realizar matemática de data mais avançada.

Práticas recomendadas

Como podemos ver, a linguagem com script Painless oferece maneiras poderosas de extrair informações úteis de campos arbitrários armazenados no Elasticsearch por meio de campos com script do Kibana. Entretanto, com grandes poderes vêm grandes responsabilidades. 

Abaixo, descrevemos algumas práticas recomendadas sobre o uso de campos com script do Kibana.

  • Sempre use um ambiente de desenvolvimento para fazer experimentos com campos com script. Como os campos com script ficam ativos imediatamente após serem salvos na seção Management (Gerenciamento) do Kibana (por exemplo, eles aparecem na tela Discover desse padrão de indexação para todos os usuários), você não deve desenvolver campos com script diretamente em produção. Recomendamos que você experimente sua sintaxe primeiro em um ambiente de desenvolvimento, avalie o impacto dos campos com script em conjuntos de dados realistas e volumes de dados em teste, e só então os promova para produção. 
  • Depois de ganhar confiança de que o campo com script fornece valor para seus usuários, considere modificar sua ingestão para extrair o campo no momento do índice para novos dados. Isso economizará o processamento do Elasticsearch no momento da consulta e resultará em resposta mais rápida para os usuários do Kibana. Você também pode usar a API _reindex no Elasticsearch para reindexar os dados existentes.