As integrações do Elastic Agent permitem que os usuários façam a ingestão de dados para o Elasticsearch a partir de uma ampla variedade de fontes. Eles combinam lógica de coleta, pipelines de ingestão, dashboards e outros artefatos em um pacote que pode ser instalado e gerenciado a partir da interface web do Kibana.
Integrações configuram entradas do Filebeat para realizar a coleta de dados. Para coletar dados de APIs HTTP, usamos a entrada HTTP JSON. No entanto, mesmo APIs básicas de listagem podem diferir bastante nos detalhes, e o modelo de transformações configuradas em YAML da entrada HTTP JSON pode tornar difícil e, às vezes, impossível expressar a lógica de coleta necessária.
A entrada em linguagem de expressão comum (CEL) foi introduzida para permitir uma interação mais flexível com as APIs HTTP. A CEL é uma linguagem projetada para ser incorporada em aplicações que exigem uma maneira rápida, segura e extensível de expressar condições e transformações de dados. A entrada CEL permite que um desenvolvedor de integrações escreva uma expressão capaz de ler configurações, controlar o próprio estado, fazer solicitações, processar respostas e, por fim, retornar eventos prontos para serem ingeridos.
Neste artigo, vamos analisar como a CEL difere de outras linguagens de programação, como a estendemos para a entrada CEL, e a flexibilidade e o poder que isso oferece para expressar sua lógica de coleta de dados.
CEL e como funciona na entrada
A CEL é uma linguagem de expressões. Não possui instruções. Quando você escreve CEL, não indica o que fazer escrevendo instruções, mas sim qual valor produzir ao escrever uma expressão. Cada expressão CEL produz um valor, e expressões menores podem ser combinadas em uma expressão maior para produzir um resultado de acordo com regras mais complexas. Mais adiante, veremos como usar expressões para coisas que podem ser escritas com instruções em outras linguagens.
CEL é intencionalmente uma linguagem não Turing completa. Não permite loops ilimitados. Posteriormente, veremos como você pode processar listas e mapas usando macros, mas, ao evitar loops ilimitados, a linguagem garante um tempo de execução previsível e limitado para expressões individuais.
A entrada CEL é configurada com um programa CEL (uma expressão) e algum estado inicial. O estado será fornecido como entrada para o programa. O programa é avaliado para produzir um estado de saída. Se o estado de saída incluir uma lista de eventos, esses serão removidos e publicados. O restante do estado de saída será usado como entrada para a próxima avaliação. Se o estado de saída incluir um ou mais eventos e a bandeira want_more: true, a próxima avaliação será realizada imediatamente; caso contrário, ele irá hibernar pelo restante do intervalo configurado antes de continuar. Veja um diagrama simplificado do fluxo de controle da entrada:

A saída de cada avaliação será passada como entrada para a próxima avaliação, enquanto a entrada for executada. Os dados de saída com a chave "cursor" serão mantidos no disco e recarregados após a reinicialização da entrada, mas o restante do estado não será preservado durante as reinicializações.
A linguagem CEL em si tem funcionalidade limitada e evita efeitos colaterais, mas é extensível. A implementação do cel-go acrescenta algumas funcionalidades, como sintaxe e tipos opcionais. A biblioteca Mito se baseia no cel-go e adiciona mais funcionalidades, incluindo a capacidade de fazer solicitações HTTP. A entrada da CEL usa a versão da CEL do Mito.
Trabalhando com Mito
Para criar ou fazer debug de uma integração usando a entrada CEL, o mais importante é entender qual estado de saída o seu programa CEL produzirá para um determinado estado de entrada. Durante o desenvolvimento, pode ser complicado executar o seu programa CEL pela entrada, cercado pela stack completa do Elastic. Uma maneira de ter um ciclo de feedback mais rápido é usar a ferramenta de linha de comando do Mito, que permite executar diretamente um programa CEL e ver a saída que ele produz para uma entrada específica.
Mito é escrito em Go e pode ser instalado da seguinte forma:
Quando você executa um programa CEL com o Mito, normalmente fornece a ele dois arquivos: um arquivo JSON com o estado de entrada inicial e outro arquivo com o código-fonte do seu programa CEL:
Para facilitar o copiar e colar, os exemplos neste artigo são escritos como comandos únicos que fazem com que o shell crie arquivos temporários em tempo real, agrupando o conteúdo de cada arquivo em <(echo '...content...'). No seu próprio desenvolvimento, trabalhar com arquivos reais será mais fácil.
Busca de dados de problemas do GitHub
O exemplo a seguir inclui um programa CEL completo que buscará dados sobre problemas na API do GitHub. Seu estado de entrada inicial tem uma URL para o endpoint da API e algumas informações sobre como se deve lidar com a paginação. O programa CEL usa os dados no estado de entrada para gerar uma solicitação. Ele decodificará a resposta, produzirá eventos a partir dela e os retornará como parte de seu estado de saída.
Sua primeira avaliação produz a seguinte saída:
Os eventos serão removidos e, quando executados na entrada CEL, serão publicados para ingestão. O restante da saída será fornecido para a próxima avaliação do programa CEL como seu estado de entrada.
Para entender como esse programa CEL funciona, vamos analisar alguns exemplos menores de CEL e discutir mais detalhes sobre como a entrada CEL funciona.
Noções básicas do CEL
Na linguagem CEL, não há declarações; só existem expressões. Cada expressão CEL bem-sucedida é avaliada para um valor final. Aqui está uma das menores expressões de CEL que você pode escrever, junto com sua saída:
Muitas expressões simples são intuitivas. Operações matemáticas são suportadas apenas em valores do mesmo tipo (por exemplo, int com int), então converta os tipos conforme necessário (aqui de int para double):
Não há variáveis na linguagem CEL, mas uma expressão pode receber um nome e ser usada em uma expressão maior com a ajuda da macro as de Mito. Neste exemplo, a expressão (1 + 1) avalia para o valor 2, e .as(n, ...) dá a esse valor o nome n para uso na expressão "one plus one is "+string(n):
Também é possível acumular informações em um mapa e utilizá-las posteriormente na expressão, como demonstrado aqui usando with:
Observe esse exemplo novamente. Note que a parte aninhada, ({ "data": data, "size": size(data), }), nos dá a forma do valor final. É um mapa com as chaves "data" e "size". Os valores dessas chaves dependem de data, que é definido pela parte externa da expressão. Ler as expressões da CEL de dentro para fora pode ajudar a ver o que elas vão retornar.
A CEL não possui instruções de fluxo de controle, como if, mas a ramificação condicional pode ser feita com o operador ternário:
Ciclos ilimitados e recursão não são compatíveis, pois a CEL não é uma linguagem de Turing completa. Isso torna o tempo de execução previsível e proporcional ao tamanho dos dados de entrada e à complexidade da expressão.
Embora ciclos ilimitados não sejam possíveis em expressões CEL individuais, você pode processar listas e mapas usando macros como map:
Nesta seção, abordamos:
- Strings, números, listas e mapas.
- Concatenação de strings.
- Operações matemáticas.
- Conversão de tipo.
- Condicionais.
- Nomeando subexpressões.
- Processando coleções.
Em seguida, veremos como fazer solicitações HTTP.
Requisições
O Mito estende a CEL com a capacidade de fazer requisições HTTP:
As requisições podem ser construídas explicitamente antes de serem executadas. Isso possibilita o uso de diferentes métodos HTTP e a adição de cabeçalhos e corpo da requisição.
Neste exemplo, criamos uma URL com a ajuda de format_query, adicionamos um cabeçalho à solicitação e analisamos o corpo da resposta com decode_json. Quando você receber a opção -log_requests, o Mito loga informações detalhadas no formato JSON sobre cada requisição e resposta.
Gestão do estado e avaliações
Agora que abordamos como fazer requisições e os fundamentos da CEL necessários para produzir o estado de saída desejado, vamos dar uma olhada mais detalhada no que devemos colocar no estado de saída e como isso nos permite direcionar o processamento posterior.
O programa CEL de uma integração precisa garantir que o estado de saída seja adequado para uso como entrada na próxima avaliação. A configuração define o estado inicial, que deve ser repetido na saída com quaisquer mudanças apropriadas. Uma maneira fácil de fazer isso é usar state.with({ ... }), para repetir o mapa de estados com algumas sobrescrições. Um padrão comum para programas pequenos é envolver o programa inteiro em state.with(), para que a propagação de estados não precise ser repetida em cada ramo que gera dados de saída (por exemplo, sucesso, erros).
Quando há valores de estado inicializados por uma avaliação em vez de codificados fixamente no estado inicial de entrada, o programa precisará verificar um valor existente antes de definir o inicial. Isso é algo em que o suporte para sintaxe e tipos opcionais pode ajudar. Ao usar um ponto de interrogação antes do nome do campo em uma chave de mapa, o acesso torna-se opcional: pode ou não resolver para um valor, mas acessos opcionais adicionais são possíveis e é fácil fornecer um padrão se não houver valor presente:
Nesse exemplo, o valor contador lido do estado é convertido para int porque todos os números são serializados no estado como números de ponto flutuante, de acordo com as convenções estabelecidas pelo tipo de Number do JSON e JavaScript. Também deve ser notado que "want_more": true é respeitado aqui pelo Mito, mas quando executado na entrada CEL, a avaliação só será repetida se a saída também contiver eventos.
É uma exigência dos programas CEL executados pela entrada CEL que retornem uma chave "events" no mapa de saída. O valor pode ser uma lista de mapas de eventos, uma lista vazia ou um único mapa de eventos. O caso de evento único geralmente é usado para erros. O evento será publicado pela entrada, mas seu valor também será log, e se definir um valor error.message , ele será usado para atualizar o status de saúde da Fleet da integração. Se seu programa gerar um único evento que não seja erro, é melhor agrupá-lo em uma lista.
Confira novamente a saída anterior do nosso programa de problemas do GitHub:
O programa gerenciou efetivamente o estado por:
- Repetindo valores de estado inicial em
url,per_pageemax_pages. - Adicionando estados que devem ser mantidos durante reinícios em
cursor.page. - Eventos de retorno prontos para serem publicados na lista
events. - Solicitar reavaliação imediata com
want_more: true.
Agora que você entende o acesso opcional e o gerenciamento de estado, bem como os conceitos básicos da CEL e as solicitações HTTP, o programa completo de problemas do GitHub deve estar legível. Tente executar o programa com o Mito e experimente algumas alterações.
Revisão e recursos
Neste artigo, analisamos o que é a CEL e como ela foi estendida na biblioteca Mito para uso na entrada CEL. Observamos a flexibilidade da CEL em um programa de exemplo que busca informações de problemas na API do GitHub e analisamos todos os detalhes necessários para entender esse programa, abrangendo o acesso às configurações no estado inicial, a interação com APIs HTTP, o retorno de eventos a serem ingeridos e o gerenciamento do estado para execuções posteriores do programa.
Para aprender mais e construir integrações usando a entrada da CEL, há vários recursos que valem a pena explorar:
- Entrada CEL - Documentação do Filebeat
- Documentação Mito
- Linguagem de expressão comum - website cel.dev
- Crie uma Integração - Documentação Elastic
E talvez o recurso mais valioso para criar integrações com a entrada CEL seja o código CEL das integrações existentes do Elastic, que pode ser encontrado no GitHub:
cel.yml.hbs arquivos no repositório de integrações Elastic - GitHub




