Trilhas
/
Go
Go
/
Exercícios
/
Análise de arquivos de log
Análise de arquivos de log

Análise de arquivos de log

Exercício de aprendizagem

Introdução

O pacote regexp oferece suporte a expressões regulares em Go.

Sintaxe

A sintaxe das expressões regulares aceitas é a mesma sintaxe geral usada por Perl, Python e outras linguagens.

Tanto os padrões de busca quanto os textos de entrada são interpretados como UTF-8.

Quando usamos crases (\) para criar strings, as barras invertidas (\) não têm nenhum significado especial e não marcam o início de caracteres especiais como tabulações \t ou quebras de linha \n:

"\t\n" // regular string literal with 2 characters: a tab and a newline
`\t\n`// raw string literal with 4 characters: two backslashes, a 't', and an 'n'

Por causa disso, usar crases é desejável para criar expressões regulares, pois significa que não precisamos escapar as barras invertidas:

"\\" // string with a single backslash
`\\` // string with 2 backslashes

Compilando padrões - tipo RegExp

Para usar uma expressão regular, primeiro precisamos compilar o padrão em string. Compilar aqui significa pegar o padrão em string da expressão regular e convertê-lo em uma representação interna com a qual é mais fácil trabalhar. Só precisamos compilar cada padrão uma vez; depois disso, podemos usar a versão compilada da expressão regular quantas vezes quisermos. O tipo regexp.Regexp representa uma expressão regular compilada. Podemos compilar um padrão em string para um regexp.Regexp usando a função regexp.Compile. Essa função retorna nil e um erro se a compilação falhar:

re, err := regexp.Compile(`(a|b)+`)
fmt.Println(re, err) // => (a|b)+ <nil>
re, err = regexp.Compile(`a|b)+`)
fmt.Println(re, err) // => <nil> error parsing regexp: unexpected ): `a|b)+`

A função MustCompile é uma alternativa prática a Compile:

re = regexp.MustCompile(`[a-z]+\d*`)

Usando essa função, não é preciso tratar um erro.

Caution

MustCompile só deve ser usado quando tivermos certeza de que o padrão compila, caso contrário o programa vai entrar em pânico.

Métodos de expressões regulares

Existem 16 métodos de Regexp que correspondem a uma expressão regular e identificam o texto correspondido. Seus nomes correspondem a esta expressão regular:

Find(All)?(String)?(Submatch)?(Index)?
  • Se All estiver presente, o método encontra correspondências sucessivas e não sobrepostas de toda a expressão.
  • Se String estiver presente, o argumento é uma string; caso contrário, é um slice de bytes; os valores de retorno são ajustados conforme o caso.
  • Se Submatch estiver presente, o valor de retorno é um slice que identifica as subcorrespondências sucessivas da expressão.
  • Se Index estiver presente, as correspondências e subcorrespondências são identificadas por pares de índices de byte dentro da string de entrada.

Também há métodos para:

  • substituir correspondências de expressões regulares por strings de substituição e
  • dividir strings separadas por expressões regulares.

No total, o pacote regexp define mais de 40 funções e métodos. Vamos demonstrar o uso de alguns métodos abaixo. Consulte a documentação da API para mais detalhes sobre essas e outras funções.

Exemplos de MatchString

O método MatchString informa se uma string contém alguma correspondência de uma expressão regular.

re = regexp.MustCompile(`[a-z]+\d*`)
b = re.MatchString("[a12]")       // => true
b = re.MatchString("12abc34(ef)") // => true
b = re.MatchString(" abc!")       // => true
b = re.MatchString("123 456")     // => false

Exemplos de FindString

O método FindString retorna uma string com o texto da correspondência mais à esquerda da expressão regular.

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.FindString("[a12]")       // => "a12"
s = re.FindString("12abc34(ef)") // => "abc34"
s = re.FindString(" abc!")       // => "abc"
s = re.FindString("123 456")     // => ""

Exemplos de FindStringSubmatch

O método FindStringSubmatch retorna um slice de strings com o texto da correspondência mais à esquerda da expressão regular e as correspondências, se houver, de suas subexpressões. Isso pode ser usado para identificar as strings que correspondem a grupos de captura. Um valor de retorno nil indica que não houve correspondência.

re = regexp.MustCompile(`[a-z]+(\d*)`)
sl = re.FindStringSubmatch("[a12]")       // => []string{"a12","12"}
sl = re.FindStringSubmatch("12abc34(ef)") // => []string{"abc34","34"}
sl = re.FindStringSubmatch(" abc!")       // => []string{"abc",""}
sl = re.FindStringSubmatch("123 456")     // => <nil>

Exemplos de ReplaceAllString

O método re.ReplaceAllString(src,repl) retorna uma cópia de src, substituindo as correspondências da expressão regular re pela string de substituição repl.

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.ReplaceAllString("[a12]", "X")       // => "[X]"
s = re.ReplaceAllString("12abc34(ef)", "X") // => "12X(X)"
s = re.ReplaceAllString(" abc!", "X")       // => " X!"
s = re.ReplaceAllString("123 456", "X")     // => "123 456"

Exemplos de Split

O método re.Split(s,n) divide um texto s em substrings separadas pela expressão e retorna um slice das substrings entre essas correspondências da expressão. A contagem n determina o número máximo de substrings a retornar. Se n<0, o método retorna todas as substrings.

re = regexp.MustCompile(`[a-z]+\d*`)
sl = re.Split("[a12]", -1)      // => []string{"[","]"}
sl = re.Split("12abc34(ef)", 2) // => []string{"12","(ef)"}
sl = re.Split(" abc!", -1)      // => []string{" ","!"}
sl = re.Split("123 456", -1)    // => []string{"123 456"}

Instruções

Este exercício aborda a análise de arquivos de log.

Depois de uma revisão de segurança recente, pediram que você fizesse uma limpeza nos arquivos de log arquivados da organização.

Todas as strings passadas para as funções têm garantia de não serem nulas e de não terem espaços no início nem no fim.

1. Identifique linhas de log corrompidas

Você precisa ter uma ideia de quantas linhas de log do seu arquivo não seguem os padrões atuais. Você acredita que um teste simples revela se uma linha de log é válida. Para ser considerada válida, uma linha deve começar com uma das seguintes strings:

  • [TRC]
  • [DBG]
  • [INF]
  • [WRN]
  • [ERR]
  • [FTL]

Implemente a função IsValidLine para retornar false se uma string não for válida e true caso contrário.

IsValidLine("[ERR] A good error here")
// => true
IsValidLine("Any old [ERR] text")
// => false
IsValidLine("[BOB] Any old text")
// => false

2. Divida a linha de log

Uma nova equipe entrou na organização, e você descobre que os arquivos de log dela usam um separador estranho para "campos". Em vez de algo sensato como dois-pontos ":", elas usam uma string como "<--->" ou "<=>" (porque é mais bonito), na verdade qualquer string que tenha "<" como primeiro caractere e ">" como último, com qualquer combinação dos seguintes caracteres no meio: "~", "*", "=" e "-".

Implemente a função SplitLogLine, que recebe uma linha e retorna um array de strings, cada uma contendo um campo.

SplitLogLine("section 1<*>section 2<~~~>section 3")
// => []string{"section 1", "section 2", "section 3"},

3. Conte o número de linhas que contêm password em texto entre aspas

A equipe precisa saber das referências a senhas em texto entre aspas para que elas possam ser examinadas manualmente.

Implemente a função CountQuotedPasswords para ter uma indicação da provável escala do trabalho manual.

Identifique as linhas de log em que a string "password", que pode estar em qualquer combinação de maiúsculas e minúsculas, está cercada por aspas. Você deve levar em conta a possibilidade de haver conteúdo adicional entre as aspas, antes e depois de "password". Cada linha conterá no máximo duas aspas.

As linhas passadas para a rotina podem ser válidas ou não, conforme definido na tarefa 1. Nós as processamos da mesma forma, sejam válidas ou não.

lines := []string{
    `[INF] passWord`, // contains 'password' but not surrounded by quotation marks
    `"passWord"`,  // count this one
    `[INF] User saw error message "Unexpected Error" on page load.`, // does not contain 'password'
    `[INF] The message "Please reset your password" was ignored by the user`, // count this one
}
// => 2

4. Remova artefatos do log

Você descobriu que certo processamento anterior dos logs vem espalhando pelos logs o texto "end-of-line" seguido de um número de linha (sem espaço entre eles).

Implemente a função RemoveEndOfLineText para receber uma string, remover o texto de fim de linha e retornar uma string "limpa".

As linhas que não contêm o texto de fim de linha devem ser retornadas sem modificação.

Remova apenas a string de fim de linha. Não tente ajustar os espaços em branco.

RemoveEndOfLineText("[INF] end-of-line23033 Network Failure end-of-line27")
// => "[INF]  Network Failure "

5. Marque linhas com nomes de usuário

Você notou que algumas linhas de log incluem frases que se referem a usuários. Essas frases sempre contêm a string "User", seguida de um ou mais espaços e, depois, de um nome de usuário. Você decide marcar essas linhas.

Implemente uma função TagWithUserName que processa linhas de log:

  • As linhas que não contêm a string "User " permanecem inalteradas.
  • Nas linhas que contêm a string "User ", coloque [USR] seguido do nome de usuário no início da linha.

Por exemplo:

result := TagWithUserName([]string{
    "[WRN] User James123 has exceeded storage space.",
	"[WRN] Host down. User   Michelle4 lost connection.",
	"[INF] Users can login again after 23:00.",
	"[DBG] We need to check that user names are at least 6 chars long.",
})
// => []string {
//  "[USR] James123 [WRN] User James123 has exceeded storage space.",
//  "[USR] Michelle4 [WRN] Host down. User   Michelle4 lost connection.",
//  "[INF] Users can login again after 23:00.",
//  "[DBG] We need to check that user names are at least 6 chars long."
// }

Você pode assumir que:

  • Os nomes de usuário são seguidos de pelo menos um espaço em branco no log.
  • Há no máximo uma ocorrência da string "User " em cada linha.
  • Os nomes de usuário são strings não vazias que não contêm espaços em branco.
Editar via GitHub O link abre em uma nova janela ou aba
Go Exercism

Tudo pronto para começar Análise de arquivos de log?

Crie sua conta no Exercism para aprender e dominar Go com 34 conceitos165 exercícios e mentoria humana de verdade, tudo de graça.