Skip to content

Commit 4fb6cd7

Browse files
committed
server logging
1 parent c1c30b0 commit 4fb6cd7

File tree

6 files changed

+116
-33
lines changed

6 files changed

+116
-33
lines changed

config_examples/proxy_py.nginx.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ server {
44

55
location / {
66
proxy_pass http://127.0.0.1:55555;
7+
proxy_set_header X-Real-IP $remote_addr;
78
}
89
}

server/api_v1/api_request_handler.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import aiohttp
2+
3+
from server.base_app import BaseApp
14
from .requests_to_models.request_parser import RequestParser, ParseError
25
from .requests_to_models.request_executor import RequestExecutor, ExecutionError
36
from proxy_py import settings
47

58

69
class ApiRequestHandler:
7-
def __init__(self, logger):
10+
def __init__(self, app: BaseApp):
811
self.request_parser = RequestParser(settings.PROXY_PROVIDER_SERVER_API_CONFIG)
912
self.request_executor = RequestExecutor()
10-
self._logger = logger
13+
self.app = app
1114

12-
async def handle(self, client_address: tuple, post_data: dict):
15+
async def handle(self, request: aiohttp.ClientRequest, post_data: dict):
1316
try:
1417
req_dict = self.request_parser.parse(post_data)
1518

@@ -18,9 +21,9 @@ async def handle(self, client_address: tuple, post_data: dict):
1821
}
1922
response.update(await self.request_executor.execute(req_dict))
2023
except ParseError as ex:
21-
self._logger.warning(
22-
"Error during parsing request. \nClient: {} \nRequest: {} \nException: {}".format(
23-
client_address,
24+
self.app.log_info(
25+
request,
26+
"Error during parsing request. Request: {} Exception: {}".format(
2427
post_data,
2528
ex
2629
)
@@ -32,11 +35,12 @@ async def handle(self, client_address: tuple, post_data: dict):
3235
'error_message': str(ex)
3336
}
3437
except ExecutionError as ex:
35-
self._logger.warning(
36-
"Error during execution request. \nClient: {} \nRequest: {} \nException: {}".format(
37-
client_address,
38+
self.app.log_error(
39+
request,
40+
"Error during execution request. Request: {} Exception: {}".format(
3841
post_data,
39-
ex)
42+
ex
43+
)
4044
)
4145

4246
response = {

server/api_v1/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class App(BaseApp):
1010
def __init__(self, *args, **kwargs):
1111
super(App, self).__init__(*args, **kwargs)
1212

13-
self.request_handler = ApiRequestHandler(self.logger)
13+
self.request_handler = ApiRequestHandler(self)
1414

1515
async def setup_router(self):
1616
self.app.router.add_post('/', self.post)
@@ -32,7 +32,7 @@ async def post(self, request):
3232
try:
3333
data = json.loads(data.decode())
3434

35-
response = await self.request_handler.handle(client_address, data)
35+
response = await self.request_handler.handle(request, data)
3636
except ValueError:
3737
response = {
3838
'status': 'error',

server/api_v1/requests_to_models/request_parser.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ class RequestParser:
1111
ALLOWED_KEYS = {'model', 'method', 'fields', 'filter', 'order_by', 'limit', 'offset'}
1212
MAXIMUM_KEY_LENGTH = 64
1313
MAXIMUM_VALUE_LENGTH = 512
14+
# TODO: move to settings
15+
MINIMUM_LIMIT_VALUE = 1
16+
MAXIMUM_LIMIT_VALUE = 1024
1417

1518
def __init__(self, config):
1619
self.config = config
@@ -31,6 +34,9 @@ def parse(self, request: dict):
3134

3235
self.validate_value(key, request[key])
3336

37+
if 'limit' not in request:
38+
request['limit'] = self.MAXIMUM_LIMIT_VALUE
39+
3440
return self.parse_dict(request)
3541

3642
def validate_value(self, key: str, value):
@@ -60,6 +66,13 @@ def validate_value(self, key: str, value):
6066
self._validate_value_type(key, value, int)
6167
if value < 0:
6268
raise ValidationError('Value of key "{}" should be positive'.format(key))
69+
70+
if key == 'limit' and (value < self.MINIMUM_LIMIT_VALUE or value > self.MAXIMUM_LIMIT_VALUE):
71+
raise ValidationError(
72+
'Value of key limit should be from {} to {} (inclusive)'.format(
73+
self.MINIMUM_LIMIT_VALUE, self.MAXIMUM_LIMIT_VALUE
74+
)
75+
)
6376
else:
6477
# It means I forget to add validation of field
6578
raise ValidationError('Server Error')

server/base_app.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import aiohttp
99
import aiohttp_jinja2
1010
import jinja2
11+
import inspect
1112

1213

1314
class BaseApp:
@@ -34,3 +35,32 @@ async def setup_middlewares(self):
3435
@property
3536
def app(self):
3637
return self._app
38+
39+
def log_critical(self, *args, **kwargs):
40+
self.log('critical', *args, **kwargs)
41+
42+
def log_error(self, *args, **kwargs):
43+
self.log('error', *args, **kwargs)
44+
45+
def log_warning(self, *args, **kwargs):
46+
self.log('warning', *args, **kwargs)
47+
48+
def log_info(self, *args, **kwargs):
49+
self.log('info', *args, **kwargs)
50+
51+
def log_debug(self, *args, **kwargs):
52+
self.log('debug', *args, **kwargs)
53+
54+
def log(self, level, request, message):
55+
if settings.DEBUG:
56+
client_host, client_port = request.transport.get_extra_info("peername")
57+
client_ip = "{}:{}".format(client_host, client_port)
58+
else:
59+
# behind nginx or other reverse proxy
60+
client_ip = str(request.headers.get('X-Real-IP', None))
61+
if client_ip is None:
62+
self.logger.error("Your reverse proxy doesn't present user's IP")
63+
64+
getattr(self.logger, level)(message, extra={
65+
"client_ip": client_ip,
66+
})

server/proxy_provider_server.py

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
from proxy_py import settings
24
from .base_app import BaseApp
35
from .api_v1.app import App as ApiV1App
@@ -6,36 +8,32 @@
68

79
import logging
810
import aiohttp
9-
import jinja2
1011
import aiohttp_jinja2
1112

1213

13-
# TODO: refactor it
14-
_proxy_provider_server = None
15-
_logger = logging.getLogger('proxy_py/server')
16-
_logger.setLevel(logging.DEBUG)
17-
debug_file_handler = logging.FileHandler('logs/server.debug.log')
18-
debug_file_handler.setLevel(logging.DEBUG)
19-
error_file_handler = logging.FileHandler('logs/server.error.log')
20-
error_file_handler.setLevel(logging.ERROR)
21-
error_file_handler = logging.FileHandler('logs/server.warning.log')
22-
error_file_handler.setLevel(logging.WARNING)
23-
info_file_handler = logging.FileHandler('logs/server.log')
24-
info_file_handler.setLevel(logging.INFO)
14+
class ProxyProviderServer(BaseApp):
15+
def __init__(self, host, port, processor):
16+
logger = logging.getLogger("proxy_py/server")
2517

26-
_logger.addHandler(debug_file_handler)
27-
_logger.addHandler(error_file_handler)
28-
_logger.addHandler(info_file_handler)
18+
if settings.DEBUG:
19+
logger.setLevel(logging.DEBUG)
20+
else:
21+
logger.setLevel(logging.INFO)
2922

23+
logger_file_handler = logging.FileHandler("logs/server.log")
24+
logger_file_handler.setLevel(logging.DEBUG)
25+
logger_file_handler.setFormatter(logging.Formatter(
26+
"%(levelname)s ~ %(asctime)s ~ %(client_ip)s ~ %(message)s"
27+
))
3028

31-
class ProxyProviderServer(BaseApp):
32-
def __init__(self, host, port, processor):
29+
logger.addHandler(logger_file_handler)
3330

34-
super(ProxyProviderServer, self).__init__(_logger)
31+
super(ProxyProviderServer, self).__init__(logger)
3532

3633
self._processor = processor
3734
self.host = host
3835
self.port = port
36+
self._request_number = 0
3937

4038
async def start(self, loop):
4139
await self.init()
@@ -63,10 +61,47 @@ async def setup_middlewares(self):
6361
500: self.handle_500,
6462
})
6563

66-
self._app.middlewares.append(error_middleware)
64+
self.app.middlewares.append(error_middleware)
65+
self.app.middlewares.append(self.logging_middleware)
66+
67+
@web.middleware
68+
async def logging_middleware(self, request: aiohttp.ClientRequest, handler):
69+
self._request_number += 1
70+
71+
current_request_number = self._request_number
72+
73+
request_data = {
74+
"request_number": current_request_number,
75+
"method": request.method,
76+
"url": str(request.url),
77+
"user-agent": request.headers.get("User-Agent", None),
78+
}
79+
80+
if request.body_exists:
81+
request_data["body"] = (await request.read()).decode()
82+
83+
self.log_info(request, "-> data={}".format(json.dumps(request_data)))
84+
85+
status_code = None
86+
87+
try:
88+
response = await handler(request)
89+
status_code = response.status
90+
except web.web_exceptions.HTTPException as ex:
91+
status_code = ex.status
92+
raise ex
93+
except BaseException as ex:
94+
raise ex
95+
finally:
96+
self.log_info(request, "<- data={}".format(json.dumps({
97+
"request_number": current_request_number,
98+
"status_code": status_code,
99+
})))
100+
101+
return response
67102

68103
def error_pages_handler(self, overrides):
69-
@aiohttp.web.middleware
104+
@web.middleware
70105
async def middleware(request, handler):
71106
try:
72107
response = await handler(request)

0 commit comments

Comments
 (0)