Show More
@@ -11,6 +11,7 b' import time' | |||
|
11 | 11 | import threading |
|
12 | 12 | import traceback |
|
13 | 13 | import random |
|
14 | import socket | |
|
14 | 15 | from gunicorn.glogging import Logger |
|
15 | 16 | |
|
16 | 17 | |
@@ -254,6 +255,74 b' def post_request(worker, req, environ, r' | |||
|
254 | 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 | 326 | class RhodeCodeLogger(Logger): |
|
258 | 327 | """ |
|
259 | 328 | Custom Logger that allows some customization that gunicorn doesn't allow |
@@ -270,5 +339,58 b' class RhodeCodeLogger(Logger):' | |||
|
270 | 339 | msecs = int((now - long(now)) * 1000) |
|
271 | 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 | 396 | logger_class = RhodeCodeLogger |
General Comments 0
You need to be logged in to leave comments.
Login now