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.

Author: steven.levey, 2015-06-03

4 answers

Usando a resposta de @Joran Beasley, encontrei a seguinte solução e parece funcionar:

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
 7
Author: steven.levey, 2015-06-04 14:50:13
Criar um gerador (Im em uma curva de gerador hoje)
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.
 2
Author: Joran Beasley, 2015-06-03 18:34:38

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

 2
Author: Lincoln Randall McFarland, 2016-11-12 04:05:10

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]+)'
 0
Author: Deepak, 2016-03-20 17:43:52