From 506ea925d0e2c7e9ccc3971ebef3d78582c60b2f Mon Sep 17 00:00:00 2001 From: yashlamba Date: Thu, 12 Dec 2024 15:17:16 +0100 Subject: [PATCH] custom_logging: implement custom data logger --- site/zenodo_rdm/custom_logging/__init__.py | 7 +++ site/zenodo_rdm/custom_logging/config.py | 11 ++++ site/zenodo_rdm/custom_logging/ext.py | 53 +++++++++++++++++++ site/zenodo_rdm/custom_logging/handlers.py | 43 +++++++++++++++ .../os-v2/custom-logs-template.json | 29 ++++++++++ 5 files changed, 143 insertions(+) create mode 100644 site/zenodo_rdm/custom_logging/__init__.py create mode 100644 site/zenodo_rdm/custom_logging/config.py create mode 100644 site/zenodo_rdm/custom_logging/ext.py create mode 100644 site/zenodo_rdm/custom_logging/handlers.py create mode 100644 site/zenodo_rdm/index_templates/os-v2/custom-logs-template.json diff --git a/site/zenodo_rdm/custom_logging/__init__.py b/site/zenodo_rdm/custom_logging/__init__.py new file mode 100644 index 00000000..859fbc14 --- /dev/null +++ b/site/zenodo_rdm/custom_logging/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2023 CERN. +# +# Zenodo-RDM is free software; you can redistribute it and/or modify +# it under the terms of the MIT License; see LICENSE file for more details. +"""Custom logging module.""" diff --git a/site/zenodo_rdm/custom_logging/config.py b/site/zenodo_rdm/custom_logging/config.py new file mode 100644 index 00000000..2118f317 --- /dev/null +++ b/site/zenodo_rdm/custom_logging/config.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# ZenodoRDM is free software; you can redistribute it and/or modify +# it under the terms of the MIT License; see LICENSE file for more details. + +"""Logging config.""" + +CUSTOM_LOGGING_INDEX = "custom-logs" +"""Custom logs index.""" diff --git a/site/zenodo_rdm/custom_logging/ext.py b/site/zenodo_rdm/custom_logging/ext.py new file mode 100644 index 00000000..79df95f1 --- /dev/null +++ b/site/zenodo_rdm/custom_logging/ext.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# ZenodoRDM is free software; you can redistribute it and/or modify +# it under the terms of the MIT License; see LICENSE file for more details. + +"""ZenodoRDM custom logging module.""" + +import logging + +from zenodo_rdm.custom_logging.handlers import OpenSearchHandler + +from . import config + +CUSTOM_LOGGING_LEVEL = 1000 + + +def custom(self, key, message, *args, **kws): + """Log level custom method.""" + if not isinstance(message, dict): + raise ValueError("Message should be a dict") + if self.isEnabledFor(CUSTOM_LOGGING_LEVEL): + message["key"] = key + self._log(CUSTOM_LOGGING_LEVEL, message, args, **kws) + + +class ZenodoCustomLogging: + """Zenodo custom logging extension.""" + + def __init__(self, app=None): + """Extension initialization.""" + if app: + self.init_app(app) + + @staticmethod + def init_config(app): + """Initialize configuration.""" + for k in dir(config): + if k.startswith("CUSTOM_LOGGING_"): + app.config.setdefault(k, getattr(config, k)) + + def init_app(self, app): + """Flask application initialization.""" + logging.addLevelName(CUSTOM_LOGGING_LEVEL, "CUSTOM") + logging.Logger.custom = custom + handler = OpenSearchHandler() + for h in app.logger.handlers: + if isinstance(h, handler.__class__): + break + else: + app.logger.addHandler(handler) + self.init_config(app) diff --git a/site/zenodo_rdm/custom_logging/handlers.py b/site/zenodo_rdm/custom_logging/handlers.py new file mode 100644 index 00000000..cbd8d70d --- /dev/null +++ b/site/zenodo_rdm/custom_logging/handlers.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# ZenodoRDM is free software; you can redistribute it and/or modify +# it under the terms of the MIT License; see LICENSE file for more details. + +"""Custom logging handler for ZenodoRDM.""" + +import logging +from datetime import datetime, timezone + +from flask import current_app +from invenio_search.proxies import current_search_client + + +class OpenSearchHandler(logging.Handler): + """Open search handler for logs.""" + + def __init__(self): + """Constructor.""" + logging.Handler.__init__(self) + + def _build_index_name(self): + """Build index name.""" + return f"{current_app.config.get('SEARCH_INDEX_PREFIX')}-{current_app.config.get('CUSTOM_LOGGING_INDEX')}" + + def emit(self, record): + """Emit, take log action.""" + if record.levelname == "CUSTOM": + key = record.msg.pop("key") + document = { + "timestamp": datetime.fromtimestamp( + record.created, timezone.utc + ).isoformat(), + "key": key, + "context": record.msg, + } + + current_search_client.index( + index=self._build_index_name(), + body=document, + ) diff --git a/site/zenodo_rdm/index_templates/os-v2/custom-logs-template.json b/site/zenodo_rdm/index_templates/os-v2/custom-logs-template.json new file mode 100644 index 00000000..0f64cf87 --- /dev/null +++ b/site/zenodo_rdm/index_templates/os-v2/custom-logs-template.json @@ -0,0 +1,29 @@ +{ + "index_patterns": [ + "__SEARCH_INDEX_PREFIX__custom-logs-*" + ], + "template": { + "aliases": { + "__SEARCH_INDEX_PREFIX__custom-logs": {} + }, + "settings": { + "index": { + "refresh_interval": "5s" + } + }, + "mappings": { + "properties": { + "timestamp": { + "type": "date" + }, + "key": { + "type": "keyword" + }, + "context": { + "type": "object", + "dynamic": true + } + } + } + } +}