diff --git a/odex25_base/odoo_log_viewer/__init__.py b/odex25_base/odoo_log_viewer/__init__.py new file mode 100644 index 000000000..38718f084 --- /dev/null +++ b/odex25_base/odoo_log_viewer/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import controllers \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/__manifest__.py b/odex25_base/odoo_log_viewer/__manifest__.py new file mode 100644 index 000000000..6bb975988 --- /dev/null +++ b/odex25_base/odoo_log_viewer/__manifest__.py @@ -0,0 +1,22 @@ +{ + 'name': 'Odoo Log Viewer', + 'version': '17.0.0.1.0', + 'category': 'Tools', + 'summary': 'View Odoo log in real-time as a web page', + 'sequence': 10, + 'author': 'Abdelrahman Eltayar', + 'depends': ['base', 'web'], + 'data': [ + 'views/log_viewer_template.xml', + 'views/assets.xml', + 'data/ir_config_parameter.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'custom_log_viewer/static/src/css/log_viewer_style.css', + ], + }, + 'installable': True, + 'application': False, + 'auto_install': False, +} \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/controllers/__init__.py b/odex25_base/odoo_log_viewer/controllers/__init__.py new file mode 100644 index 000000000..deec4a8b8 --- /dev/null +++ b/odex25_base/odoo_log_viewer/controllers/__init__.py @@ -0,0 +1 @@ +from . import main \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/controllers/main.py b/odex25_base/odoo_log_viewer/controllers/main.py new file mode 100644 index 000000000..f95201165 --- /dev/null +++ b/odex25_base/odoo_log_viewer/controllers/main.py @@ -0,0 +1,25 @@ +from odoo import http, _ +from odoo.http import request + +class LogViewer(http.Controller): + + @http.route('/log_viewer', type='http', auth='public') + def log_viewer(self, password=None): + correct_password = request.env['ir.config_parameter'].sudo().get_param('odoo_log_viewer.password') + + if password == correct_password: + return request.render('odoo_log_viewer.log_viewer_template', {}) + else: + return request.render('odoo_log_viewer.password_template', {}) + + @http.route('/log_viewer/get_logs', type='json', auth='public') + def get_logs(self, password=None): + correct_password = request.env['ir.config_parameter'].sudo().get_param('odoo_log_viewer.password') + + if password == correct_password: + log_entries = request.env['log.handler.manager'].sudo().get_log_entries() + return {'log_content': '\n'.join(log_entries)} + + else: + return {'error': 'Invalid password'} + diff --git a/odex25_base/odoo_log_viewer/data/ir_config_parameter.xml b/odex25_base/odoo_log_viewer/data/ir_config_parameter.xml new file mode 100644 index 000000000..05c1e2f8e --- /dev/null +++ b/odex25_base/odoo_log_viewer/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + odoo_log_viewer.password + $ecret@)24 + + + diff --git a/odex25_base/odoo_log_viewer/models/__init__.py b/odex25_base/odoo_log_viewer/models/__init__.py new file mode 100644 index 000000000..e33386e53 --- /dev/null +++ b/odex25_base/odoo_log_viewer/models/__init__.py @@ -0,0 +1 @@ +from . import log_handler \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/models/log_handler.py b/odex25_base/odoo_log_viewer/models/log_handler.py new file mode 100644 index 000000000..643044448 --- /dev/null +++ b/odex25_base/odoo_log_viewer/models/log_handler.py @@ -0,0 +1,46 @@ +import logging +from odoo import api, models +from werkzeug._internal import _log + +class MemoryLogHandler(logging.Handler): + def __init__(self, max_entries=1000): + super().__init__() + self.max_entries = max_entries + self.entries = [] + + def emit(self, record): + if '/log_viewer/get_logs' in getattr(record, 'message', ''): + return + self.entries.append(self.format(record)) + if len(self.entries) > self.max_entries: + self.entries.pop(0) + +class LogHandlerManager(models.AbstractModel): + _name = 'log.handler.manager' + _description = 'Log Handler Manager' + + @api.model + def _get_memory_handler(self): + if not hasattr(LogHandlerManager, '_memory_handler'): + LogHandlerManager._memory_handler = MemoryLogHandler() + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + LogHandlerManager._memory_handler.setFormatter(formatter) + logging.getLogger().addHandler(LogHandlerManager._memory_handler) + + + def custom_log(type, message, *args, **kwargs): + if '/log_viewer/get_logs' not in message: + _log(type, message, *args, **kwargs) + + import werkzeug + werkzeug._internal._log = custom_log + + return LogHandlerManager._memory_handler + + @api.model + def _register_hook(self): + self._get_memory_handler() + + @api.model + def get_log_entries(self): + return self._get_memory_handler().entries \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/static/description/icon.png b/odex25_base/odoo_log_viewer/static/description/icon.png new file mode 100644 index 000000000..eb4606020 Binary files /dev/null and b/odex25_base/odoo_log_viewer/static/description/icon.png differ diff --git a/odex25_base/odoo_log_viewer/static/src/css/log_viewer_style.css b/odex25_base/odoo_log_viewer/static/src/css/log_viewer_style.css new file mode 100644 index 000000000..6dfa8e767 --- /dev/null +++ b/odex25_base/odoo_log_viewer/static/src/css/log_viewer_style.css @@ -0,0 +1,12 @@ +form label { + font-weight: 700; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +body { + background-color: #464646; +} diff --git a/odex25_base/odoo_log_viewer/views/assets.xml b/odex25_base/odoo_log_viewer/views/assets.xml new file mode 100644 index 000000000..65b908453 --- /dev/null +++ b/odex25_base/odoo_log_viewer/views/assets.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/odex25_base/odoo_log_viewer/views/log_viewer_template.xml b/odex25_base/odoo_log_viewer/views/log_viewer_template.xml new file mode 100644 index 000000000..996ed90bb --- /dev/null +++ b/odex25_base/odoo_log_viewer/views/log_viewer_template.xml @@ -0,0 +1,66 @@ + + + + + +