Criar Expressões Regulares de Desempenho

Aqui discutimos alguns dos aspectos importantes que você deve considerar ao criar expressões regulares de desempenho.

Para as construções de expressão regular que podem ser usadas com parsers, labels e datafilters, consulte Documentação do Java Platform Standard Ed. 8.

No caso de lookups e regex de tempo de consulta, consulte a sintaxe RE2J em Implementação Java de RE2.

Classes de Caractere

As classes de caractere especificam os caracteres que você está tentando ou não corresponder. Certifique-se de substituir o . em seu .*s por um caractere mais específico. O .* mudará de forma invariavelmente para o fim da sua entrada e, em seguida, recuará, retornando ao estado salvo anteriormente para continuar procurando uma correspondência. Ao usar uma classe de caractere específica, você tem controle sobre quantos caracteres o * fará com que o mecanismo regex consuma, dando a você o poder de interromper o processo de recuo excessivo.

Considere o seguinte exemplo de expressão regular:

(\d{4})(\d{2})(\d{2}),(\S+),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Para a seguinte entrada:

20150220,201502,16798186260,tvN,Entertainment/Music,Female,67,2,Individual,ollehtv Economic,Commercial Housing,5587,0,2,0,1,1

Como resultado da expressão regular especificada, a correspondência pode encontrar um recuo. Essa situação é detectada pelo Oracle Log Analytics e a operação de correspondência é abortada.

Alterando a expressão regular no exemplo abaixo, você pode garantir que a correspondência seja concluída mais rapidamente. Observe que [\S\s]* é alterado para [^,], o que evita o recuo desnecessário.

(\d{4})(\d{2})(\d{2}),(\S+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Quantificadores Lentos

Em muitos regexes, quantificadores rápidos (.*s) podem ser substituídos com segurança por quantificadores lentos (.*?s), dando ao regex um ganho de desempenho sem alterar o resultado.

Considere a entrada:

Trace file /u01/app/oracle/diag/rdbms/navisdb/NAVISDB/trace/NAVISDB_arc0_3941.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.2.0/db_1
System name: Linux
Node name: NAVISDB
Release: 2.6.18-308.el5
Version: #1 SMP Fri Jan 27 17:17:51 EST 2012
Machine: x86_64
Instance name: NAVISDB
Redo thread mounted by this instance: 1
Oracle process number: 21
Unix process pid: 3941, image: oracle@NAVISDB (ARC0)

Considere o seguinte regex rápido para a entrada em questão:

Trace\sfile\s(\S*).*ORACLE_HOME\s*[:=]\s*(\S*).*System\sname:\s*(\S*).*Node\sname:\s*(\S*).*Release:\s*(\S*).*Machine:\s*(\S*).*Instance\sname:\s*(\S*).*Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*Oracle\sprocess\snumber:\s*(\d*).*Unix\sprocess\spid:\s(\d*).*image:\s+([^\n\r]*)

O mecanismo regex vai até o final da entrada toda vez que ele encontra .*.. A primeira vez que o .* aparece, ele consome toda a entrada e, em seguida, recua até chegar a ORACLE_HOME. Essa é uma maneira ineficiente de correspondência. O regex lento alternativo é mostrado a seguir:

Trace\sfileRelease:\s*(\S*).*?Machine:\s*(\S*).*?Instance\sname:\s*(\S*).*?Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*?Oracle\sprocess\snumber:\s*(\d*).*?Unix\sprocess\spid:\s(\d*).*?image:\s+([^\n\r]*)

O regex lento acima consome o início do começo da string até chegar a ORACLE_HOME, ponto do qual ele pode prosseguir para a correspondência do restante da string.

Observação: Se o campo ORACLE_HOME aparecer no início da entrada, o quantificador lento deverá ser usado. Se o campo ORACLE_HOME aparecer no fim, talvez seja apropriado usar o quantificador rápido.

Âncoras

As âncoras informam ao mecanismo regex que você pretende que o cursor esteja em um local específico na entrada. As âncoras mais comuns são ^ e $, indicando o início e o fim da entrada.

Considere os seguintes regexes para encontrar um endereço IPv4:

\d{1,3}\.d{1,3}.\d{1,3}.\d{1,3}
^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

Observe que o segundo regex começa com ^ e é específico sobre o endereço IP que aparece no início da entrada.

Estamos procurando o regex na entrada semelhante ao seguinte exemplo:

107.21.20.1 - - [07/Dec/2012:18:55:53 -0500] "GET
      /extension/bsupport/design/cl/images/btn_letschat.png HTTP/1.1" 200
    2144

Uma entrada não correspondente seria semelhante ao seguinte exemplo:

[07/Dec/2012:23:57:13 +0000] 1354924633 GET "/favicon.ico" "" HTTP/1.1 200 82726 "-"
      "ELB-HealthChecker/1.0"

O segundo regex (que começa com ^) é executado mais rápido na entrada sem correspondência porque descarta essa entrada imediatamente.

A Importância da Alternância

A ordem das contagens de alternância; portanto, coloque as opções mais comuns na frente para que elas possam ser correspondidas mais rapidamente. Se as opções mais raras forem colocadas primeiro, o mecanismo de regex perderá tempo na verificação delas antes de verificar as opções mais comuns que têm mais chance de sucesso. Além disso, tente extrair padrões comuns. Por exemplo, em vez de (abcd|abef), use ab(cd|ef).

Observe os seguintes regexes:

{TIMEDATE}\s{0,1}:\s*(?:|\[)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*
{TIMEDATE}\s{0,1}:\s*(?:\[|)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*

Na seguinte entrada:

2014-06-16 12:13:46.743: [UiServer][1166092608] {0:7:2} Done for
ctx=0x2aaab45d8330

O segundo regex corresponde mais rápido, já que a alternância procura o caractere [ primeiro, seguido de nulo. Como a entrada tem [, a correspondência é executada mais rapidamente.

Expressões de Parsing de Amostra

Você pode consultar as seguintes expressões de parsing de amostra para criar uma expressão de parsing adequada para extrair valores do seu arquivo de log.

Um arquivo de log abrange as entradas que são geradas pela concatenação de diversos valores de campo. Talvez você não precise exibir todos os valores de campo para analisar um arquivo de log de um formato específico. Usando um parser, você pode extrair os valores apenas dos campos que deseja exibir.

Um parser extrai campos de um arquivo de log com base na expressão de parsing definida. Uma expressão de parsing é criada na forma de uma expressão regular que define um padrão de pesquisa. Em uma expressão de parsing, você coloca os padrões de pesquisa entre parênteses () para cada campo correspondente que você deseja extrair de uma entrada de log. Qualquer valor que corresponda a um padrão de pesquisa que esteja fora dos parênteses não será extraído.

Exemplo 1

Se quiser fazer parsing das seguintes entradas de log de amostra:

Jun 20 15:19:29 hostabc rpc.gssd[2239]: ERROR: can't open clnt5aa9: No such file or directory
Jul 29 11:26:28 hostabc kernel: FS-Cache: Loaded
Jul 29 11:26:28 hostxyz kernel: FS-Cache: Netfs 'nfs' registered for caching

Veja como deverá ser sua expressão de parsing:

(\S+)\s+(\d+)\s(\d+):(\d+):(\d+)\s(\S+)\s(?:([^:\[]+)(?:\[(\d+)\])?:\s+)?(.+)

No exemplo anterior, alguns dos valores que a expressão de parsing captura são:

  • (\S+): Vários caracteres sem espaço em branco para o mês

  • (\d+): Vários caracteres sem espaço em branco para o dia

  • (?:([^:\[]+): (Opcional) Todos os caracteres, exceto ^, :, \, []; isso é para o nome do serviço

  • (.+): (Opcional) Conteúdo da mensagem principal

Exemplo 2

Se quiser fazer parsing das seguintes entradas de log de amostra:

####<Apr 27, 2014 4:01:42 AM PDT> <Info> <EJB> <host> <AdminServer> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <OracleSystemUser> <BEA1-13E2AD6CAC583057A4BD> <b3c34d62475d5b0b:6e1e6d7b:143df86ae85:-8000-000000000000cac6> <1398596502577> <BEA-010227> <EJB Exception occurred during invocation from home or business: weblogic.ejb.container.internal.StatelessEJBHomeImpl@2f9ea244 threw exception: javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec. The exception is:java.lang.StackOverflowError>
####<Jul 30, 2014 8:43:48 AM PDT> <Info> <RJVM> <example.com> <> <Thread-9> <> <> <> <1406735028770> <BEA-000570> <Network Configuration for Channel "AdminServer" Listen Address example.com:7002 (SSL) Public Address N/A Http Enabled true Tunneling Enabled false Outbound Enabled false Admin Traffic Enabled true ResolveDNSName Enabled false> 

Veja como deverá ser sua expressão de parsing:

####<(\p{Upper}\p{Lower}{2})\s+([\d]{1,2}),\s+([\d]{4})\s+([\d]{1,2}):([\d]{2}):([\d]{2})\s+(\p{Upper}{2})(?:\s+(\w+))?>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<\d{10}\d{3}>\s+<(.*?)>\s+<(.*?)(?:\n(.*))?>\s*

No exemplo anterior, alguns dos valores que a expressão de parsing captura são:

  • (\p{Upper}\p{Lower}{2}): Nome abreviado de três letras para o mês; com a primeira letra em maiúscula seguida de duas letras minúsculas

  • ([\d]{1,2}): Dia com 1 ou 2 dígitos

  • ([\d]{4}): Ano com 4 dígitos

  • ([\d]{1,2}): Hora com 1 ou 2 dígitos

  • ([\d]{2}): Minuto com 2 dígitos

  • ([\d]{2}): Segundo com 2 dígitos

  • (\p{Upper}{2}): AM/PM com 2 letras maiúsculas

  • (?:\s+(\w+)): (Opcional, algumas entradas não podem retornar qualquer valor para isso) Vários caracteres alfanuméricos para o fuso horário

  • (.*?): (Opcional, algumas entradas não podem retornar qualquer valor para isso) Um ou vários caracteres para o nível de severidade; nesse caso, <INFO>

  • (.*): Qualquer detalhe adicional junto com a mensagem

Padrões de Pesquisa

Alguns dos padrões mais usados são explicados na seguinte tabela:

Padrão Descrição Exemplo
. Qualquer caractere, exceto quebra de linha d.f corresponde a def, daf, dbf etc.
* Zero ou mais vezes D*E*F* corresponde a DDEEFF, DEF, DDFF, EEFF etc.
? Uma ou nenhuma vez; opcional colou?r corresponde a cor e colorido
+ Um ou mais Stage \w-\w+ corresponde a Estágio A-b1_1, Estágio B-a2 e assim por diante
{2} Exatamente duas vezes [\d]{2} corresponde a 01, 11, 21 e assim por diante
{1,2} Duas a quatro vezes [\d]{1,2} corresponde a 1, 12 e assim por diante
{3,} Três ou mais vezes [\w]{3,} corresponde a dez, olá, h2134 e assim por diante
[ ... ] Um dos caracteres entre colchetes [AEIOU] corresponde a uma vogal maiúscula
[x-y] Um dos caracteres na faixa de x a y [A-Z]+ corresponde a ACT, ACTION, BAT, e assim por diante
[^x] Um caractere que não é x [^/d]{2} corresponde a AA, BB, AC e assim por diante
[^x-y] Um dos caracteres não na faixa de x a y [^a-z]{2} corresponde a A1, BB, B2 e assim por diante
[\d\D] Um caractere que é um dígito ou não dígito [\d\D]+ corresponde a qualquer caractere, incluindo novas linhas, que o ponto regular não corresponde
\s Um espaço em branco (\S+)\s+(\d+) corresponde a AA 123, a_ 221 e assim por diante
\S Um caractere que não é um espaço em branco (\S+) corresponde a abcd, ABC, A1B2C3 e assim por diante
\n Uma nova linha (\d)\n(\w) corresponde a:

1

A

\w Um caractere alfanumérico [\w-\w\w\w] corresponde a a-123, 1-aaa e assim por diante
\p{Lower} Letras minúsculas \p{Lower}{2} corresponde a aa, ab, ac, bb e assim por diante
\p{Upper} Letras maiúsculas \p{Upper} corresponde a A, B, C e assim por diante
\ seguido por ?, [], *, . Caractere de escape; para usar os caracteres após \ como literais \? retorna ?