/
log.py
226 lines (167 loc) · 6.28 KB
/
log.py
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import logging
import socket
import threading
from logging.handlers import RotatingFileHandler
from traceback import format_exc
from config import cfg
from requests import post
_initalised = False
_LOGGER_NAME = 'LegitLogger'
class CountsHandler(logging.Handler):
def __init__(self, level=logging.NOTSET):
self.counts = {
}
self._countLock = threading.Lock()
super(CountsHandler, self).__init__(level)
def emit(self, record):
self._countLock.acquire()
self.counts[record.levelname] = self.counts.get(record.levelname, 0) + 1
self._countLock.release()
pass
def get_counts(self):
return self.counts
def reset_counts(self):
self.counts = {}
def getLogger():
return logging.getLogger(_LOGGER_NAME)
logger = getLogger()
def setup_logging(log_file_name=None, verbose=False, interactive_only=False):
global _initalised
if _initalised:
return logging.getLogger(_LOGGER_NAME)
if not verbose:
# Quieten other loggers down a bit (particularly requests and google api client)
for logger_str in logging.Logger.manager.loggerDict:
try:
logging.getLogger(logger_str).setLevel(logging.WARNING)
except:
pass
logFormatter = logging.Formatter(
"%(asctime)s [%(filename)-20.20s:%(lineno)-4.4s - %(funcName)-20.20s() [%(threadName)-12.12s] [%(levelname)-8.8s] %(message).5000s")
logger = logging.getLogger(_LOGGER_NAME)
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
logger.addHandler(consoleHandler)
# Add logger to count number of errors
countsHandler = CountsHandler()
logger.addHandler(countsHandler)
if verbose:
consoleHandler.setLevel(logging.DEBUG)
else:
consoleHandler.setLevel(logging.INFO)
if not interactive_only and log_file_name:
fileHandler = RotatingFileHandler(log_file_name, maxBytes=20000000, backupCount=20, encoding="UTF-8")
fileHandler.setFormatter(logFormatter)
if verbose:
fileHandler.setLevel(logging.DEBUG)
else:
fileHandler.setLevel(logging.INFO)
logger.addHandler(fileHandler)
# Add
if verbose:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
_initalised = True
return logger
run_summary = {
'summary_log_messages': [],
'summary_counts': {},
}
def init_run_summary():
global run_summary
run_summary = {
'summary_log_messages': [],
'summary_counts': {},
}
def increment_run_summary(variable_name, value=1):
global run_summary
run_summary['summary_counts'][variable_name] = run_summary['summary_counts'].get(variable_name, 0) + value
def log_run_summary(summary_msg, module_name=None):
global run_summary
if module_name:
summary_msg = "[{}] {}\n".format(module_name, summary_msg)
run_summary['summary_log_messages'].append(summary_msg)
logger.info(summary_msg)
def get_log_summary(reset_summary=False):
global run_summary
message_body = ""
for line in run_summary['summary_log_messages']:
message_body += str(line) + "\n"
for key, value in run_summary['summary_counts'].items():
message_body += "{key}: {value}\n".format(key=key, value=value)
logger = getLogger()
for handlerobj in logger.handlers:
if isinstance(handlerobj, CountsHandler):
counts = handlerobj.get_counts()
message_body += "\n\nLog messages:\n"
for key, value in counts.items():
message_body += "{key} messages: {value}\n".format(key=key, value=value)
if reset_summary:
handlerobj.reset_counts()
if reset_summary:
run_summary = {
'summary_log_messages': [],
'summary_counts': {},
}
return message_body
def print_run_summary(subject, log_file=None, reset_summary=True, send_email=False):
message_body = get_log_summary(reset_summary=reset_summary)
logger.info('\n\n')
logger.info('----------------------')
logger.info(subject)
logger.info(message_body)
logger.info('----------------------')
if send_email:
send_update_mail(subject, message_body)
if reset_summary:
init_run_summary() # Not sure why the run summary is not being reset in the get_log_summary() function.
def send_update_mail(subject, message):
try:
hostname = socket.gethostname()
full_address = socket.gethostbyname_ex(hostname)
send_mail({
"subject": subject,
"text": str(message) + "\n" +
"From {host}.".format(
host=full_address
)
}
)
except Exception as e:
logger.exception("Unable to send error mail: {}".format(e), exc_info=True)
def send_exception(module_name=None, message=None, message_body=None):
if message:
logger.exception(message, exc_info=True)
if message_body:
message = message_body[:2500]
try:
hostname = socket.gethostname()
full_address = socket.gethostbyname_ex(hostname)
subject = "[{}] Unexpected Error: {}".format(module_name, message)
send_mail({
"subject": subject,
"text":
"Unexpected Error, please check your instance {host}.\n{message}\n{traceback}".format(
host=full_address,
message=message_body,
traceback=format_exc()[:2500],
)
}
)
except Exception as e:
logger.error("Unable to send error mail: {}".format(e))
def send_mail(message_dict):
if cfg['mailgun']:
api_base_url = cfg['mailgun']['mailgun_api_base_url'] + '/messages'
auth = ('api', cfg['mailgun']['mailgun_api_key'])
data = {
"from": "Legit-Platforms <%s>" % cfg['mailgun']['mailgun_default_smtp_login'],
"to": cfg['mailgun']['email_to_notify']
}
data.update(message_dict)
logger.info("Sent error email to {}.".format(cfg['mailgun']['email_to_notify']))
return post(api_base_url, auth=auth, data=data)
else:
logger.info("Not sending email - mailgun is not configured.")
return None