-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdaemon.py
More file actions
141 lines (121 loc) · 4.72 KB
/
daemon.py
File metadata and controls
141 lines (121 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import time
import requests
import json
import os
import argparse
import glob
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from dotenv import load_dotenv
load_dotenv()
host = os.getenv('API_HOST')
secret = os.getenv('API_SECRET')
class LogManifest(object):
def __init__(self, file: str):
self.file = file
self.accepted = set()
self.rejected = set()
try:
data = json.load(open(file, 'r'))
self.accepted.update(data['accepted'])
self.rejected.update(data['rejected'])
except FileNotFoundError:
pass
def is_handled(self, file) -> bool:
return file in self.accepted or file in self.rejected
def is_rejected(self, file) -> bool:
return file in self.rejected
def add_accepted(self, file):
self.accepted.add(file)
self.save()
def add_rejected(self, file):
self.rejected.add(file)
self.save()
def save(self):
data = dict()
data['accepted'] = list(self.accepted)
data['rejected'] = list(self.rejected)
json.dump(data, open(self.file, 'w'), indent=2)
manifest = LogManifest('manifest.json')
def send_log(file):
print('Sending {}...'.format(file))
url = host + 'logs/'
data = {
'secret': secret
}
files = {
'log': open(file, 'rb')
}
response = requests.post(url, data, files=files)
if response.status_code in [201, 409]:
# The server accepted it (201) or rejected it as a log that has already
# been parsed (409). Let's throw it in the acepted pile.
print('Log file processed ({})'.format(response.status_code))
manifest.add_accepted(os.path.basename(file))
elif response.status_code == 500:
print('Log file rejected ({})'.format(response.status_code))
# Internal server error while parsing the log, throw it in the rejected pile.
# The server should probably be *storing* the logs before we get here, the
# server could keep track of what the error is and correct it if need be.
manifest.add_rejected(os.path.basename(file))
pass
else:
# The server rejected it for an unknown reason. We will do nothing
# and the daemon can attempt to send it up again in the future.
print('Communication breakdown, forget it! (' + str(response.status_code) + ')')
pass
class DaemonFileHandler(FileSystemEventHandler):
def on_moved(self, event):
# The game writes to a .log.tmp file then later renames (moves) it
# with the correct extension.
if os.path.splitext(event.dest_path)[1] == '.log':
print('A wild log file appeared!')
send_log(event.dest_path)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--path', action='append')
parser.add_argument('--skip_backlog', action='store_true')
parser.add_argument('--retry_rejected', action='store_true', default=False)
args = parser.parse_args()
paths = args.path
if paths is None or len(paths) == 0:
parser.print_usage()
raise Exception('No paths specified.')
# For each path, go through all the *.log files (perhaps a CRC-based method
# would be better instead of filename, since file names can overlap.
# The other option would be to put the manifest in the path itself
# then we can still do file based stuff.
if not args.skip_backlog:
print('Clearing backlog...')
for path in paths:
# For each path, check if there are unhandled log
# files and then attempt to send them all to
# the server.
for file in glob.glob(os.path.join(path, '*.log')):
if not manifest.is_handled(os.path.basename(file)):
send_log(file)
if args.retry_rejected:
print('Retrying {} rejected files...'.format(len(manifest.rejected)))
for path in paths:
for file in glob.glob(os.path.join(path, '*.log')):
if manifest.is_rejected(os.path.basename(file)):
send_log(file)
print('Backlog cleared.')
event_handler = DaemonFileHandler()
observers = []
for path in paths:
# Set up a new file observer in each specified path.
observer = Observer()
print('Listening at "{}"'.format(path))
observer.schedule(event_handler, path=path, recursive=False)
observer.start()
observers.append(observer)
# Sit and spin until we are interrupted.
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
for observer in observers:
observer.stop()
for observer in observers:
observer.join()