119 lines
4.1 KiB
Python
119 lines
4.1 KiB
Python
import re
|
|
import os
|
|
import time
|
|
import random
|
|
import string
|
|
|
|
class LogReader(object):
|
|
def __init__(self):
|
|
self.log_file_sizes = {}
|
|
# (if the time between checks is greater, ignore ) - in seconds
|
|
self.max_file_time_change = 30
|
|
|
|
def read_file(self, path, retrieval_key):
|
|
# this removes old entries that are no longer valid
|
|
try:
|
|
self._clear_old_logs()
|
|
except Exception as e:
|
|
print('could not clear old logs')
|
|
print(e)
|
|
|
|
if os.name != 'nt':
|
|
path = re.sub(r'^[A-Z]\:', '', path)
|
|
path = re.sub(r'\\+', '/', path)
|
|
|
|
# prevent traversing directories
|
|
if re.search('r^.+\.\.\\.+$', path):
|
|
return self._generate_bad_response()
|
|
|
|
# must be a valid log path and log file
|
|
if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path):
|
|
return self._generate_bad_response()
|
|
|
|
# get the new file size
|
|
new_file_size = self.file_length(path)
|
|
|
|
# the log size was unable to be read (probably the wrong path)
|
|
if new_file_size < 0:
|
|
return self._generate_bad_response()
|
|
|
|
next_retrieval_key = self._generate_key()
|
|
|
|
# this is the first time the key has been requested, so we need to the next one
|
|
if retrieval_key not in self.log_file_sizes or int(time.time() - self.log_file_sizes[retrieval_key]['read']) > self.max_file_time_change:
|
|
print('retrieval key "%s" does not exist or is outdated' % retrieval_key)
|
|
last_log_info = {
|
|
'size' : new_file_size,
|
|
'previous_key' : None
|
|
}
|
|
else:
|
|
last_log_info = self.log_file_sizes[retrieval_key]
|
|
|
|
print('next key is %s' % next_retrieval_key)
|
|
expired_key = last_log_info['previous_key']
|
|
print('expired key is %s' % expired_key)
|
|
|
|
# grab the previous value
|
|
last_size = last_log_info['size']
|
|
file_size_difference = new_file_size - last_size
|
|
|
|
#print('generating info for next key %s' % next_retrieval_key)
|
|
|
|
# update the new size
|
|
self.log_file_sizes[next_retrieval_key] = {
|
|
'size' : new_file_size,
|
|
'read': time.time(),
|
|
'next_key': next_retrieval_key,
|
|
'previous_key': retrieval_key
|
|
}
|
|
|
|
if expired_key in self.log_file_sizes:
|
|
print('deleting expired key %s' % expired_key)
|
|
del self.log_file_sizes[expired_key]
|
|
|
|
#print('reading %i bytes starting at %i' % (file_size_difference, last_size))
|
|
|
|
new_log_content = self.get_file_lines(path, last_size, file_size_difference)
|
|
return {
|
|
'content': new_log_content,
|
|
'next_key': next_retrieval_key
|
|
}
|
|
|
|
def get_file_lines(self, path, start_position, length_to_read):
|
|
try:
|
|
file_handle = open(path, 'rb')
|
|
file_handle.seek(start_position)
|
|
file_data = file_handle.read(length_to_read)
|
|
file_handle.close()
|
|
# using ignore errors omits the pesky 0xb2 bytes we're reading in for some reason
|
|
return file_data.decode('utf-8', errors='ignore')
|
|
except Exception as e:
|
|
print('could not read the log file at {0}, wanted to read {1} bytes'.format(path, length_to_read))
|
|
print(e)
|
|
return False
|
|
|
|
def _clear_old_logs(self):
|
|
expired_logs = [path for path in self.log_file_sizes if int(time.time() - self.log_file_sizes[path]['read']) > self.max_file_time_change]
|
|
for key in expired_logs:
|
|
print('removing expired log with key {0}'.format(key))
|
|
del self.log_file_sizes[key]
|
|
|
|
def _generate_bad_response(self):
|
|
return {
|
|
'content': None,
|
|
'next_key': None
|
|
}
|
|
|
|
def _generate_key(self):
|
|
return ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
|
|
|
|
def file_length(self, path):
|
|
try:
|
|
return os.stat(path).st_size
|
|
except Exception as e:
|
|
print('could not get the size of the log file at {0}'.format(path))
|
|
print(e)
|
|
return -1
|
|
|
|
reader = LogReader()
|