Improved api logging #85

This commit is contained in:
Sven Heidemann 2022-10-25 20:19:05 +02:00
parent bf2a412dec
commit 6d8aa26883
6 changed files with 61 additions and 20 deletions

View File

@ -13,7 +13,7 @@
}, },
"BotLoggingSettings": { "BotLoggingSettings": {
"Api": { "Api": {
"Path": "logs/", "Path": "logs/$date_now/",
"Filename": "api.log", "Filename": "api.log",
"ConsoleLogLevel": "TRACE", "ConsoleLogLevel": "TRACE",
"FileLogLevel": "TRACE" "FileLogLevel": "TRACE"

View File

@ -8,7 +8,7 @@
"LoggingSettings": { "LoggingSettings": {
"Path": "logs/", "Path": "logs/",
"Filename": "bot.log", "Filename": "bot.log",
"ConsoleLogLevel": "TRACE", "ConsoleLogLevel": "DEBUG",
"FileLogLevel": "TRACE" "FileLogLevel": "TRACE"
}, },
"BotLoggingSettings": { "BotLoggingSettings": {
@ -27,7 +27,7 @@
"Database": { "Database": {
"Path": "logs/", "Path": "logs/",
"Filename": "database.log", "Filename": "database.log",
"ConsoleLogLevel": "TRACE", "ConsoleLogLevel": "DEBUG",
"FileLogLevel": "TRACE" "FileLogLevel": "TRACE"
}, },
"Message": { "Message": {

View File

@ -13,7 +13,7 @@
}, },
"BotLoggingSettings": { "BotLoggingSettings": {
"Api": { "Api": {
"Path": "logs/", "Path": "logs/$date_now/",
"Filename": "api.log", "Filename": "api.log",
"ConsoleLogLevel": "ERROR", "ConsoleLogLevel": "ERROR",
"FileLogLevel": "INFO" "FileLogLevel": "INFO"

View File

@ -13,7 +13,7 @@
}, },
"BotLoggingSettings": { "BotLoggingSettings": {
"Api": { "Api": {
"Path": "logs/", "Path": "logs/$date_now/",
"Filename": "api.log", "Filename": "api.log",
"ConsoleLogLevel": "INFO", "ConsoleLogLevel": "INFO",
"FileLogLevel": "DEBUG" "FileLogLevel": "DEBUG"

View File

@ -1,13 +1,14 @@
import json
import sys import sys
import textwrap
import uuid import uuid
from functools import partial from functools import partial
from typing import Union
import eventlet import eventlet
from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.dependency_injection import ServiceProviderABC
from cpl_core.utils import CredentialManager from cpl_core.utils import CredentialManager
from eventlet import wsgi from eventlet import wsgi
from flask import Flask, request, jsonify, Response, make_response from flask import Flask, request, jsonify, Response
from flask_cors import CORS from flask_cors import CORS
from flask_socketio import SocketIO from flask_socketio import SocketIO
from werkzeug.exceptions import NotFound from werkzeug.exceptions import NotFound
@ -40,21 +41,36 @@ class Api(Flask):
self._logger = logger self._logger = logger
self._services = services self._services = services
self._apt_settings = api_settings self._api_settings = api_settings
self._auth_settings = auth_settings self._auth_settings = auth_settings
self._cors = CORS(self, support_credentials=True) self._cors = CORS(self, support_credentials=True)
# register before request # register hooks
self.before_request_funcs.setdefault(None, []).append(self.before_request) self.before_request(self.before_request_hook)
self.after_request(self.after_request_hook)
# register error handler
exc_class, code = self._get_exc_class_and_code(Exception) exc_class, code = self._get_exc_class_and_code(Exception)
self.error_handler_spec[None][code][exc_class] = self.handle_exception self.register_error_handler(exc_class, self.handle_exception)
# websockets # websockets
self._socketio = SocketIO(self, cors_allowed_origins='*', path='/api/socket.io') self._socketio = SocketIO(self, cors_allowed_origins='*', path='/api/socket.io')
self._socketio.on_event('connect', self.on_connect) self._socketio.on_event('connect', self.on_connect)
self._socketio.on_event('disconnect', self.on_disconnect) self._socketio.on_event('disconnect', self.on_disconnect)
self._requests = {}
@staticmethod
def _get_methods_from_registered_route() -> Union[list[str], str]:
methods = ['Unknown']
if request.path in Route.registered_routes and len(Route.registered_routes[request.path]) >= 1 and 'methods' in Route.registered_routes[request.path][1]:
methods = Route.registered_routes[request.path][1]['methods']
if len(methods) == 1:
return methods[0]
return methods
def _register_routes(self): def _register_routes(self):
for path, f in Route.registered_routes.items(): for path, f in Route.registered_routes.items():
route = f[0] route = f[0]
@ -88,20 +104,46 @@ class Api(Flask):
error = ErrorDTO(None, user_message) error = ErrorDTO(None, user_message)
return jsonify(error.to_dict()), 400 return jsonify(error.to_dict()), 400
def before_request(self, *args, **kwargs): def before_request_hook(self):
self._logger.debug(__name__, f'Received GET @{request.url}') request_id = uuid.uuid4()
headers = str(request.headers).replace("\n", "\n\t") self._requests[request] = request_id
self._logger.trace(__name__, f'Headers: \n\t{headers}') method = request.access_control_request_method
self._logger.info(__name__, f'Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}')
headers = str(request.headers).replace('\n', '\n\t\t')
data = request.get_data()
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8"))
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}')
self._logger.trace(__name__, text)
def after_request_hook(self, response: Response):
method = request.access_control_request_method
request_id = f'{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}'
if request in self._requests:
request_id = self._requests[request]
self._logger.info(__name__, f'Answered {request_id}')
headers = str(request.headers).replace('\n', '\n\t\t')
data = request.get_data()
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8"))
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}')
self._logger.trace(__name__, text)
return response
def start(self): def start(self):
self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') self._logger.info(__name__, f'Starting API {self._api_settings.host}:{self._api_settings.port}')
self._register_routes() self._register_routes()
self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key) self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key)
# from waitress import serve # from waitress import serve
# https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html # https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html
# serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) # serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10)
wsgi.server( wsgi.server(
eventlet.listen((self._apt_settings.host, self._apt_settings.port)), eventlet.listen((self._api_settings.host, self._api_settings.port)),
self, self,
log_output=False log_output=False
) )

View File

@ -1,19 +1,18 @@
{ {
"Api": { "Api": {
"Port": 5000, "Port": 8044,
"Host": "0.0.0.0", "Host": "0.0.0.0",
"RedirectToHTTPS": false "RedirectToHTTPS": false
}, },
"Authentication": { "Authentication": {
"SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG", "SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG",
"Issuer": "http://localhost:5000", "Issuer": "http://localhost:8084",
"Audience": "http://localhost:4200", "Audience": "http://localhost:4200",
"TokenExpireTime": 1, "TokenExpireTime": 1,
"RefreshTokenExpireTime": 7 "RefreshTokenExpireTime": 7
}, },
"DiscordAuthentication": { "DiscordAuthentication": {
"ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=", "ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=",
"_RedirectURL": "http://localhost:5000/api/auth/discord/register",
"RedirectURL": "http://localhost:4200/auth/register", "RedirectURL": "http://localhost:4200/auth/register",
"Scope": [ "Scope": [
"identify", "identify",