Show More
@@ -11,6 +11,7 b' import time' | |||||
11 | import threading |
|
11 | import threading | |
12 | import traceback |
|
12 | import traceback | |
13 | import random |
|
13 | import random | |
|
14 | import socket | |||
14 | from gunicorn.glogging import Logger |
|
15 | from gunicorn.glogging import Logger | |
15 |
|
16 | |||
16 |
|
17 | |||
@@ -254,6 +255,74 b' def post_request(worker, req, environ, r' | |||||
254 | _check_memory_usage(worker) |
|
255 | _check_memory_usage(worker) | |
255 |
|
256 | |||
256 |
|
257 | |||
|
258 | def _filter_proxy(ip): | |||
|
259 | """ | |||
|
260 | Passed in IP addresses in HEADERS can be in a special format of multiple | |||
|
261 | ips. Those comma separated IPs are passed from various proxies in the | |||
|
262 | chain of request processing. The left-most being the original client. | |||
|
263 | We only care about the first IP which came from the org. client. | |||
|
264 | ||||
|
265 | :param ip: ip string from headers | |||
|
266 | """ | |||
|
267 | if ',' in ip: | |||
|
268 | _ips = ip.split(',') | |||
|
269 | _first_ip = _ips[0].strip() | |||
|
270 | return _first_ip | |||
|
271 | return ip | |||
|
272 | ||||
|
273 | ||||
|
274 | def _filter_port(ip): | |||
|
275 | """ | |||
|
276 | Removes a port from ip, there are 4 main cases to handle here. | |||
|
277 | - ipv4 eg. 127.0.0.1 | |||
|
278 | - ipv6 eg. ::1 | |||
|
279 | - ipv4+port eg. 127.0.0.1:8080 | |||
|
280 | - ipv6+port eg. [::1]:8080 | |||
|
281 | ||||
|
282 | :param ip: | |||
|
283 | """ | |||
|
284 | def is_ipv6(ip_addr): | |||
|
285 | if hasattr(socket, 'inet_pton'): | |||
|
286 | try: | |||
|
287 | socket.inet_pton(socket.AF_INET6, ip_addr) | |||
|
288 | except socket.error: | |||
|
289 | return False | |||
|
290 | else: | |||
|
291 | return False | |||
|
292 | return True | |||
|
293 | ||||
|
294 | if ':' not in ip: # must be ipv4 pure ip | |||
|
295 | return ip | |||
|
296 | ||||
|
297 | if '[' in ip and ']' in ip: # ipv6 with port | |||
|
298 | return ip.split(']')[0][1:].lower() | |||
|
299 | ||||
|
300 | # must be ipv6 or ipv4 with port | |||
|
301 | if is_ipv6(ip): | |||
|
302 | return ip | |||
|
303 | else: | |||
|
304 | ip, _port = ip.split(':')[:2] # means ipv4+port | |||
|
305 | return ip | |||
|
306 | ||||
|
307 | ||||
|
308 | def get_ip_addr(environ): | |||
|
309 | proxy_key = 'HTTP_X_REAL_IP' | |||
|
310 | proxy_key2 = 'HTTP_X_FORWARDED_FOR' | |||
|
311 | def_key = 'REMOTE_ADDR' | |||
|
312 | _filters = lambda x: _filter_port(_filter_proxy(x)) | |||
|
313 | ||||
|
314 | ip = environ.get(proxy_key) | |||
|
315 | if ip: | |||
|
316 | return _filters(ip) | |||
|
317 | ||||
|
318 | ip = environ.get(proxy_key2) | |||
|
319 | if ip: | |||
|
320 | return _filters(ip) | |||
|
321 | ||||
|
322 | ip = environ.get(def_key, '0.0.0.0') | |||
|
323 | return _filters(ip) | |||
|
324 | ||||
|
325 | ||||
257 | class RhodeCodeLogger(Logger): |
|
326 | class RhodeCodeLogger(Logger): | |
258 | """ |
|
327 | """ | |
259 | Custom Logger that allows some customization that gunicorn doesn't allow |
|
328 | Custom Logger that allows some customization that gunicorn doesn't allow | |
@@ -270,5 +339,58 b' class RhodeCodeLogger(Logger):' | |||||
270 | msecs = int((now - long(now)) * 1000) |
|
339 | msecs = int((now - long(now)) * 1000) | |
271 | return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs) |
|
340 | return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs) | |
272 |
|
341 | |||
|
342 | def atoms(self, resp, req, environ, request_time): | |||
|
343 | """ Gets atoms for log formatting. | |||
|
344 | """ | |||
|
345 | status = resp.status | |||
|
346 | if isinstance(status, str): | |||
|
347 | status = status.split(None, 1)[0] | |||
|
348 | atoms = { | |||
|
349 | 'h': get_ip_addr(environ), | |||
|
350 | 'l': '-', | |||
|
351 | 'u': self._get_user(environ) or '-', | |||
|
352 | 't': self.now(), | |||
|
353 | 'r': "%s %s %s" % (environ['REQUEST_METHOD'], | |||
|
354 | environ['RAW_URI'], | |||
|
355 | environ["SERVER_PROTOCOL"]), | |||
|
356 | 's': status, | |||
|
357 | 'm': environ.get('REQUEST_METHOD'), | |||
|
358 | 'U': environ.get('PATH_INFO'), | |||
|
359 | 'q': environ.get('QUERY_STRING'), | |||
|
360 | 'H': environ.get('SERVER_PROTOCOL'), | |||
|
361 | 'b': getattr(resp, 'sent', None) is not None and str(resp.sent) or '-', | |||
|
362 | 'B': getattr(resp, 'sent', None), | |||
|
363 | 'f': environ.get('HTTP_REFERER', '-'), | |||
|
364 | 'a': environ.get('HTTP_USER_AGENT', '-'), | |||
|
365 | 'T': request_time.seconds, | |||
|
366 | 'D': (request_time.seconds * 1000000) + request_time.microseconds, | |||
|
367 | 'M': (request_time.seconds * 1000) + int(request_time.microseconds/1000), | |||
|
368 | 'L': "%d.%06d" % (request_time.seconds, request_time.microseconds), | |||
|
369 | 'p': "<%s>" % os.getpid() | |||
|
370 | } | |||
|
371 | ||||
|
372 | # add request headers | |||
|
373 | if hasattr(req, 'headers'): | |||
|
374 | req_headers = req.headers | |||
|
375 | else: | |||
|
376 | req_headers = req | |||
|
377 | ||||
|
378 | if hasattr(req_headers, "items"): | |||
|
379 | req_headers = req_headers.items() | |||
|
380 | ||||
|
381 | atoms.update({"{%s}i" % k.lower(): v for k, v in req_headers}) | |||
|
382 | ||||
|
383 | resp_headers = resp.headers | |||
|
384 | if hasattr(resp_headers, "items"): | |||
|
385 | resp_headers = resp_headers.items() | |||
|
386 | ||||
|
387 | # add response headers | |||
|
388 | atoms.update({"{%s}o" % k.lower(): v for k, v in resp_headers}) | |||
|
389 | ||||
|
390 | # add environ variables | |||
|
391 | environ_variables = environ.items() | |||
|
392 | atoms.update({"{%s}e" % k.lower(): v for k, v in environ_variables}) | |||
|
393 | ||||
|
394 | return atoms | |||
273 |
|
395 | |||
274 | logger_class = RhodeCodeLogger |
|
396 | logger_class = RhodeCodeLogger |
General Comments 0
You need to be logged in to leave comments.
Login now