Como analisar o arquivo de log de várias linhas no awk e gerar apenas uma linha com o último endereço IP conhecido

0

Estou preso e procurando ajuda. Eu quero acionar um evento que gostaria de processar mais através de um script bash. Os dados são recuperados de um arquivo de log. Antes de começar a explicar, mostrarei algumas linhas desse arquivo de log específico para melhor compreensão.

O que parece

test.log

[...]
24/04/2017 20:14:29 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:34 [10355] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:38 [10355] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:14:47 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:50 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:53 [ 7910] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:15:08 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:15:14 [22518] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' some other colums
24/04/2017 20:15:15 [ 7910] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:15:16 [10355] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' some other colums
24/04/2017 20:15:49 [32637] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:15:53 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:15:56 [22518] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:16:05 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:16:09 [32637] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
01/05/2017 03:27:45 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:27:49 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:05 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:10 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:25 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:31 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
15/03/2018 14:49:19 [12918] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' some other colums
15/03/2018 14:49:21 [12834] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' some other colums
15/03/2018 14:49:22 [12834] [INFO] [alice] SyncCollections->CheckForChanges(): Waiting for store changes... (lifetime 470 seconds)
15/03/2018 14:55:26 [12843] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:26 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:26 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:27 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:28 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:28 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:32 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:32 [12970] [INFO] [bob] SyncCollections->CheckForChanges(): Waiting for store changes... (lifetime 470 seconds)
[...]

Meta

Estou interessado em recuperar o nome de usuário (neste exemplo "alice" ou "bob") do arquivo de log que aparece na quinta coluna e o endereço IP apropriado listado na sétima coluna. Caso o endereço IP seja diferente do último estado, uma notificação por e-mail deve ser enviada por meio de um pequeno script bash.

A condição deve ser:

  • se a linha contiver "alice" OR "bob" E a linha contiver "from=" então envie o nome de usuário e o endereço IP apropriado.

O resultado final deve ser

bob 192.168.0.166
alice 192.168.0.171

Nota: Somente o último endereço IP conhecido é desejado, portanto a saída deve gerar apenas 2 linhas neste exemplo, como mostrado acima (uma para cada usuário)

O que eu tentei até agora

Comecei com awk mas rapidamente enfrentei um obstáculo porque o awk por padrão usa o espaço em branco como separador de campo. Minha intenção era começar com uma declaração '{print $ 4, $ 6}'. Percebi que a terceira coluna às vezes interrompe essa filtragem devido a um espaço principal no ID do processo, por exemplo,

24/04/2017 20:14:50 [ 7910] ...

O que meu comando awk atualmente parece

Com o seguinte comando eu estou procurando pela string "alice" OU "bob" AND a string "from=" e então gere uma saída de duas colunas não formatadas

awk 'BEGIN { FS = "[?!([ )]+" } /alice|bob/ && /from=/ { print $5,$7 }' test.log

Saída - >

bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.171'
alice] from='192.168.0.171'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'

Estou preso aqui. Eu tentei brincar armazenando a última linha conhecida em uma variável e a saída que "{a = $ 0}", mas obviamente eu estou fazendo algo errado porque eu recebo erros ou a saída está errada. Minha próxima idéia foi usar "tac" e começar a ler o arquivo de log do seu final e sair após o primeiro jogo. Algo assim:

tac test.txt | awk 'BEGIN { FS = "[?!([ )]+" } /alice|bob/ && /from=/ { print $5,$7; exit }'

mas isso pára imediatamente após a primeira partida e a saída é:

bob] from='192.168.0.166'

Eu preciso de formatação de saída adicional removendo o colchete direito ']' e a string 'from =' e as aspas simples ao redor do endereço IP.

Qualquer ajuda realmente apreciada. Agradecemos antecipadamente.

    
por user882786 15.03.2018 / 17:11

2 respostas

0

Você pode estender seu separador de campo regex para incluir ] e ' e, em seguida, você terá o nome e o ip corretamente nos campos 5 e 9. Você pode salvá-los em uma matriz associativa indexada pelo nome e mantendo o último endereço IP. No final do arquivo, você imprime esta matriz.

awk 'BEGIN { FS = "[?!([ )\]'\'']+" }
/alice|bob/ && /from=/ { 
    user = $5; ip = $9;
    userip[user] = ip
}
END{ for(user in userip)print user,userip[user] }'
    
por 15.03.2018 / 18:36
0

Olá meuh e muito obrigado pela sua sugestão com o exemplo. Isso funciona muito bem. Mas ainda estou me perguntando se não seria melhor reverter o processamento e começar a ler do final do arquivo. Porque, nesse caso, se o arquivo de log a ser lido tiver milhares de linhas, ele consome muita energia de processamento. Eu acho que seria mais eficaz relacionado a performace para começar a ler da cauda e parar após a primeira partida para cada usuário.

Por outro lado, eu estou querendo saber se é possível incluir todo o meu projeto no awk como um one-liner.

O objetivo é executar um cron job a cada minuto e ler o arquivo de log. Se o endereço IP foi alterado e é mais recente do que o último conhecido e a sub-rede IP não está dentro da sub-rede (C), então a notificação por e-mail deve ser enviada.

/etc/cron.d/access-audit.log

*/1 * * * * root nice -n5 /usr/bin/awk 'BEGIN { FS = "[?!([ )\]'\'']+" } /alice|bob/ && /from=/ { user = $5; ip = $9; userip[user] = ip } END{ for(user in userip)print user,userip[user] }' | ...

Eu não sei como conseguir isso. Preciso tocar em um arquivo de sinalizador onde armazeno o endereço IP atual de cada usuário e, em seguida, faço uma consulta posterior? É possível fazer tudo no awk?

    
por 16.03.2018 / 13:59