Como processar este ficheiro de registo personalizado em Python
estou a usar o registo Python para gerar ficheiros de Registo ao processar e estou a tentar ler esses ficheiros de registo numa lista/dict que será então convertida em JSON e carregada numa base de dados nosql para processamento.
o ficheiro é gerado com o seguinte formato.
2015-05-22 16:46:46,985 - __main__ - INFO - Starting to Wait for Files
2015-05-22 16:46:56,645 - __main__ - INFO - Starting: Attempt 1 Checking for New Files from gs://folder/folder/
2015-05-22 16:47:46,488 - __main__ - INFO - Success: Downloading the Files from Cloud Storage: Return Code - 0 and FileCount 1
2015-05-22 16:48:48,180 - __main__ - ERROR - Failed: Waiting for files the Files from Cloud Storage: gs://folder/folder/
Traceback (most recent call last):
File "<ipython-input-16-132cda1c011d>", line 10, in <module>
if numFilesDownloaded == 0:
NameError: name 'numFilesDownloaded' is not defined
2015-05-22 16:49:17,918 - __main__ - INFO - Starting to Wait for Files
2015-05-22 16:49:32,160 - __main__ - INFO - Starting: Attempt 1 Checking for New Files from gs://folder/folder/
2015-05-22 16:49:39,329 - __main__ - INFO - Success: Downloading the Files from Cloud Storage: Return Code - 0 and FileCount 1
2015-05-22 16:53:30,706 - __main__ - INFO - Starting to Wait for Files
Nota: Existem intervalos antes de cada nova data que vê, mas não consegue representá-la aqui.
Basicamente, estou a tentar ler neste ficheiro de texto e produzir um objecto json que parece isto:
{
'Date': '2015-05-22 16:46:46,985',
'Type': 'INFO',
'Message':'Starting to Wait for Files'
}
...
{
'Date': '2015-05-22 16:48:48,180',
'Type': 'ERROR',
'Message':'Failed: Waiting for files the Files from Cloud Storage: gs://folder/anotherfolder/ Traceback (most recent call last):
File "<ipython-input-16-132cda1c011d>", line 10, in <module> if numFilesDownloaded == 0: NameError: name 'numFilesDownloaded' is not defined '
}
o problema que tenho:
posso adicionar cada linha numa lista ou dict etc, mas a mensagem de erro às vezes passa por várias linhas, por isso acabo por dividi-la incorrectamente.
tentei:
tentei usar um código como o abaixo para apenas dividir as linhas em datas válidas, mas parece que não consigo obter as mensagens de erro que atravessam várias linhas. Eu também tentei expressões regulares e acho que é uma solução possível, mas não posso parece encontrar a expressão certa para use...NO pista de como ele funciona assim tentei um monte de pasta de cópia, mas sem qualquer sucesso.
with open(filename,'r') as f:
for key,group in it.groupby(f,lambda line: line.startswith('2015')):
if key:
for line in group:
listNew.append(line)
Tentei um regex maluco, mas também não tive sorte.
logList = re.split(r'(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])', fileData)
Agradecia qualquer ajuda...obrigado.
editar:
Postou uma solução abaixo para qualquer um que lute com a mesma coisa.4 answers
Principais Pontos Tratados:
- Os meus ficheiros de Registo seguem sempre a mesma estrutura: {Data} - {Tipo} - Por isso usei o corte de cordas e a divisão para acabar com os itens. precisava deles. Exemplo: o {data} é sempre de 23 caracteres e eu só quero os primeiros 19 caracteres.
- usando a linha.startswith ("2015") é louco como as datas vão mudar, eventualmente, criou um novo função que usa algum regex para corresponder a um formato de data que eu estou esperando. Mais uma vez, as minhas datas de Registo seguem um padrão específico para que eu possa ser específico.
- o ficheiro é lido na primeira função " generateDicts () "e depois chama a função" matchDate () " para ver se a linha a ser processada corresponde a um formato {Date} que estou à procura.
- é criado um novo dict sempre que é encontrado um formato {Date} válido e tudo é processado até à próxima data {Date} válida encontrar.
Função para dividir os ficheiros de Registo.
def generateDicts(log_fh):
currentDict = {}
for line in log_fh:
if line.startswith(matchDate(line)):
if currentDict:
yield currentDict
currentDict = {"date":line.split("__")[0][:19],"type":line.split("-",5)[3],"text":line.split("-",5)[-1]}
else:
currentDict["text"] += line
yield currentDict
with open("/Users/stevenlevey/Documents/out_folder/out_loyaltybox/log_CardsReport_20150522164636.logs") as f:
listNew= list(generateDicts(f))
Função para ver se a linha a ser processada começa com um {data} que corresponde ao formato que procuro
def matchDate(line):
matchThis = ""
matched = re.match(r'\d\d\d\d-\d\d-\d\d\ \d\d:\d\d:\d\d',line)
if matched:
#matches a date and adds it to matchThis
matchThis = matched.group()
else:
matchThis = "NONE"
return matchThis
def generateDicts(log_fh):
currentDict = {}
for line in log_fh:
if line.startswith("2015"): #you might want a better check here
if currentDict:
yield currentDict
currentDict = {"date":line.split("-")[0],"type":line.split("-")[2],"text":line.split("-")[-1]}
else:
currentDict["text"] += line
yield currentDict
with open("logfile.txt") as f:
print list(generateDicts(f))
Pode haver alguns erros menores... Eu não dirigi isto.
Você pode obter os campos que está à procura directamente da expressão regular usando grupos. Você pode até nomeá-los:
>>> import re
>>> date_re = re.compile('(?P<a_year>\d{2,4})-(?P<a_month>\d{2})-(?P<a_day>\d{2}) (?P<an_hour>\d{2}):(?P<a_minute>\d{2}):(?P<a_second>\d{2}[.\d]*)')
>>> found = date_re.match('2016-02-29 12:34:56.789')
>>> if found is not None:
... print found.groupdict()
...
{'a_year': '2016', 'a_second': '56.789', 'a_day': '29', 'a_minute': '34', 'an_hour': '12', 'a_month': '02'}
>>> found.groupdict()['a_month']
'02'
Em seguida, crie uma classe de data onde os kwargs do construtor correspondem aos nomes dos grupos. Use um pouco de magia **para criar uma instância do objeto diretamente a partir do groupdict regex e você está cozinhando com gás. No construtor você pode então descobrir se 2016 é um ano bissexto e 29 de Fevereiro sai.
- lrm
A solução fornecida por @steven.o levey é perfeito. Uma adição a ele que eu gostaria de fazer é usar este padrão regex para determinar se a linha é adequada e extrair os valores necessários. Para que não tenhamos que trabalhar em dividir as linhas mais uma vez depois de determinar o formato usando o regex.
pattern = '(^[0-9\-\s\:\,]+)\s-\s__main__\s-\s([A-Z]+)\s-\s([\s\S]+)'