Atualização: substituí o código original por uma versão atualizada com inúmeras melhorias. Vamos chamar isso de qualidade alfa (real?).
Esta versão inclui:
- manipulação de opção de linha de comando
- validação de formato de data da linha de comandos
- alguns
try
blocos
- leitura de linha movida para uma função
Texto original:
Bem, o que você sabe? "Procure" e você encontrará! Aqui está um programa Python que procura no arquivo e usa uma pesquisa binária mais ou menos. É consideravelmente mais rápido que o script AWK que outro cara escreveu.
É (pré?) Qualidade alfa. Deveria ter try
blocos e validação de entrada e muitos testes e sem dúvida poderia ser mais Pythonic. Mas aqui é para sua diversão. Ah, e está escrito para Python 2.6.
Novo Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# timegrep.py by Dennis Williamson 20100113
# in response to http://serverfault.com/questions/101744/fast-extraction-of-a-time-range-from-syslog-logfile
# thanks to serverfault user http://serverfault.com/users/1545/mike
# for the inspiration
# Perform a binary search through a log file to find a range of times
# and print the corresponding lines
# tested with Python 2.6
# TODO: Make sure that it works if the seek falls in the middle of
# the first or last line
# TODO: Make sure it's not blind to a line where the sync read falls
# exactly at the beginning of the line being searched for and
# then gets skipped by the second read
# TODO: accept arbitrary date
# done: add -l long and -s short options
# done: test time format
version = "0.01a"
import os, sys
from stat import *
from datetime import date, datetime
import re
from optparse import OptionParser
# Function to read lines from file and extract the date and time
def getdata():
"""Read a line from a file
Return a tuple containing:
the date/time in a format such as 'Jan 15 20:14:01'
the line itself
The last colon and seconds are optional and
not handled specially
"""
try:
line = handle.readline(bufsize)
except:
print("File I/O Error")
exit(1)
if line == '':
print("EOF reached")
exit(1)
if line[-1] == '\n':
line = line.rstrip('\n')
else:
if len(line) >= bufsize:
print("Line length exceeds buffer size")
else:
print("Missing newline")
exit(1)
words = line.split(' ')
if len(words) >= 3:
linedate = words[0] + " " + words[1] + " " + words[2]
else:
linedate = ''
return (linedate, line)
# End function getdata()
# Set up option handling
parser = OptionParser(version = "%prog " + version)
parser.usage = "\n\t%prog [options] start-time end-time filename\n\n\
\twhere times are in the form hh:mm[:ss]"
parser.description = "Search a log file for a range of times occurring yesterday \
and/or today using the current time to intelligently select the start and end. \
A date may be specified instead. Seconds are optional in time arguments."
parser.add_option("-d", "--date", action = "store", dest = "date",
default = "",
help = "NOT YET IMPLEMENTED. Use the supplied date instead of today.")
parser.add_option("-l", "--long", action = "store_true", dest = "longout",
default = False,
help = "Span the longest possible time range.")
parser.add_option("-s", "--short", action = "store_true", dest = "shortout",
default = False,
help = "Span the shortest possible time range.")
parser.add_option("-D", "--debug", action = "store", dest = "debug",
default = 0, type = "int",
help = "Output debugging information.\t\t\t\t\tNone (default) = %default, Some = 1, More = 2")
(options, args) = parser.parse_args()
if not 0 <= options.debug <= 2:
parser.error("debug level out of range")
else:
debug = options.debug # 1 = print some debug output, 2 = print a little more, 0 = none
if options.longout and options.shortout:
parser.error("options -l and -s are mutually exclusive")
if options.date:
parser.error("date option not yet implemented")
if len(args) != 3:
parser.error("invalid number of arguments")
start = args[0]
end = args[1]
file = args[2]
# test for times to be properly formatted, allow hh:mm or hh:mm:ss
p = re.compile(r'(^[2][0-3]|[0-1][0-9]):[0-5][0-9](:[0-5][0-9])?$')
if not p.match(start) or not p.match(end):
print("Invalid time specification")
exit(1)
# Determine Time Range
yesterday = date.fromordinal(date.today().toordinal()-1).strftime("%b %d")
today = datetime.now().strftime("%b %d")
now = datetime.now().strftime("%R")
if start > now or start > end or options.longout or options.shortout:
searchstart = yesterday
else:
searchstart = today
if (end > start > now and not options.longout) or options.shortout:
searchend = yesterday
else:
searchend = today
searchstart = searchstart + " " + start
searchend = searchend + " " + end
try:
handle = open(file,'r')
except:
print("File Open Error")
exit(1)
# Set some initial values
bufsize = 4096 # handle long lines, but put a limit them
rewind = 100 # arbitrary, the optimal value is highly dependent on the structure of the file
limit = 75 # arbitrary, allow for a VERY large file, but stop it if it runs away
count = 0
size = os.stat(file)[ST_SIZE]
beginrange = 0
midrange = size / 2
oldmidrange = midrange
endrange = size
linedate = ''
pos1 = pos2 = 0
if debug > 0: print("File: '{0}' Size: {1} Today: '{2}' Now: {3} Start: '{4}' End: '{5}'".format(file, size, today, now, searchstart, searchend))
# Seek using binary search
while pos1 != endrange and oldmidrange != 0 and linedate != searchstart:
handle.seek(midrange)
linedate, line = getdata() # sync to line ending
pos1 = handle.tell()
if midrange > 0: # if not BOF, discard first read
if debug > 1: print("...partial: (len: {0}) '{1}'".format((len(line)), line))
linedate, line = getdata()
pos2 = handle.tell()
count += 1
if debug > 0: print("#{0} Beg: {1} Mid: {2} End: {3} P1: {4} P2: {5} Timestamp: '{6}'".format(count, beginrange, midrange, endrange, pos1, pos2, linedate))
if searchstart > linedate:
beginrange = midrange
else:
endrange = midrange
oldmidrange = midrange
midrange = (beginrange + endrange) / 2
if count > limit:
print("ERROR: ITERATION LIMIT EXCEEDED")
exit(1)
if debug > 0: print("...stopping: '{0}'".format(line))
# Rewind a bit to make sure we didn't miss any
seek = oldmidrange
while linedate >= searchstart and seek > 0:
if seek < rewind:
seek = 0
else:
seek = seek - rewind
if debug > 0: print("...rewinding")
handle.seek(seek)
linedate, line = getdata() # sync to line ending
if debug > 1: print("...junk: '{0}'".format(line))
linedate, line = getdata()
if debug > 0: print("...comparing: '{0}'".format(linedate))
# Scan forward
while linedate < searchstart:
if debug > 0: print("...skipping: '{0}'".format(linedate))
linedate, line = getdata()
if debug > 0: print("...found: '{0}'".format(line))
if debug > 0: print("Beg: {0} Mid: {1} End: {2} P1: {3} P2: {4} Timestamp: '{5}'".format(beginrange, midrange, endrange, pos1, pos2, linedate))
# Now that the preliminaries are out of the way, we just loop,
# reading lines and printing them until they are
# beyond the end of the range we want
while linedate <= searchend:
print line
linedate, line = getdata()
if debug > 0: print("Start: '{0}' End: '{1}'".format(searchstart, searchend))
handle.close()
if debug > 0: print("File: '{0}' Size: {1} Today: '{2}' Now: {3} Start: '{4}' End: '{5}'".format(file, size, today, now, searchstar$
. Ésearchstar
suposto terminar com um$
, ou é um erro de digitação? Eu recebo um erro de sintaxe nesta linha (Linha 159)))
.$
deve sert, searchend))
o que diz... searchstart, searchend))
Em uma pesquisa rápida na rede, há coisas que são extraídas com base em palavras-chave (como FIRE ou algo semelhante :), mas nada que extrai um intervalo de datas do arquivo.
Não parece difícil fazer o que você propõe:
Parece simples, e eu poderia escrever para você se você não se importa com Ruby :)
fonte
Isso imprimirá o intervalo de entradas entre a hora de início e a hora de término, com base em como elas se relacionam com a hora atual ("agora").
Uso:
Exemplo:
A
-l
opção (longa) causa a saída mais longa possível. O horário de início será interpretado como ontem se o valor de horas e minutos do horário de início for menor que o horário de término e agora. O horário de término será interpretado como hoje, se os valores de horário de início e de término de HH: MM forem maiores que "agora".Supondo que "now" seja "11 de janeiro às 19:00", é assim que vários exemplos de horários de início e término serão interpretados (sem
-l
exceção, conforme indicado):Quase todo o script está configurado. As duas últimas linhas fazem todo o trabalho.
Aviso: nenhuma validação de argumento ou verificação de erro é feita. Os casos Edge não foram exaustivamente testados. Isso foi escrito usando
gawk
outras versões do AWK pode gritar.Eu acho que o AWK é muito eficiente na pesquisa de arquivos. Acho que nada mais será necessariamente mais rápido na pesquisa de um arquivo de texto não indexado .
fonte
Um programa C ++ que aplica uma pesquisa binária - precisaria de algumas modificações simples (por exemplo, chamada strptime) para trabalhar com datas de texto.
http://gitorious.org/bs_grep/
Eu tinha uma versão anterior com suporte para datas de texto, mas ainda era muito lenta para a escala de nossos arquivos de log; A criação de perfil dizia que mais de 90% do tempo era gasto no tempo de execução, portanto, apenas modificamos o formato do log para incluir também um carimbo de data / hora numérico unix.
fonte
Mesmo que essa resposta seja tarde demais, pode ser benéfica para alguns.
Eu converti o código de @Dennis Williamson em uma classe Python que pode ser usada para outras coisas em python.
Eu adicionei suporte para vários suportes de data.
fonte