DefinetlyNotAI/AlgoPy

View on GitHub
jupyter/log.ipynb

Summary

Maintainability
Test Coverage
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "initial_id",
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import inspect\n",
    "import logging\n",
    "import os\n",
    "from datetime import datetime\n",
    "from typing import Type\n",
    "\n",
    "import colorlog\n",
    "\n",
    "\n",
    "class Log:\n",
    "    \"\"\"\n",
    "    A logging class that supports colored output using the colorlog library.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, config: dict = None):\n",
    "        \"\"\"\n",
    "        Initializes the Log class with the given configuration.\n",
    "\n",
    "        :param config: A dictionary containing configuration options.\n",
    "        \"\"\"\n",
    "        config = config or {\n",
    "            \"filename\": \"../ACCESS/LOGS/Logicytics.log\",\n",
    "            \"use_colorlog\": True,\n",
    "            \"log_level\": \"INFO\",\n",
    "            \"debug_color\": \"cyan\",\n",
    "            \"info_color\": \"green\",\n",
    "            \"warning_color\": \"yellow\",\n",
    "            \"error_color\": \"red\",\n",
    "            \"critical_color\": \"red\",\n",
    "            \"exception_color\": \"red\",\n",
    "            \"colorlog_fmt_parameters\": \"%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s\",\n",
    "        }\n",
    "        self.EXCEPTION_LOG_LEVEL = 45\n",
    "        self.INTERNAL_LOG_LEVEL = 15\n",
    "        logging.addLevelName(self.EXCEPTION_LOG_LEVEL, \"EXCEPTION\")\n",
    "        logging.addLevelName(self.INTERNAL_LOG_LEVEL, \"INTERNAL\")\n",
    "        self.color = config.get(\"use_colorlog\", True)\n",
    "        self.filename = config.get(\"filename\", \"../ACCESS/LOGS/Logicytics.log\")\n",
    "        if self.color:\n",
    "            logger = colorlog.getLogger()\n",
    "            logger.setLevel(getattr(logging, config[\"log_level\"].upper(), logging.INFO))\n",
    "            handler = colorlog.StreamHandler()\n",
    "            log_colors = {\n",
    "                \"INTERNAL\": \"cyan\",\n",
    "                \"DEBUG\": config.get(\"debug_color\", \"cyan\"),\n",
    "                \"INFO\": config.get(\"info_color\", \"green\"),\n",
    "                \"WARNING\": config.get(\"warning_color\", \"yellow\"),\n",
    "                \"ERROR\": config.get(\"error_color\", \"red\"),\n",
    "                \"CRITICAL\": config.get(\"critical_color\", \"red\"),\n",
    "                \"EXCEPTION\": config.get(\"exception_color\", \"red\"),\n",
    "            }\n",
    "\n",
    "            formatter = colorlog.ColoredFormatter(\n",
    "                config.get(\n",
    "                    \"colorlog_fmt_parameters\",\n",
    "                    \"%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s\",\n",
    "                ),\n",
    "                log_colors=log_colors,\n",
    "            )\n",
    "\n",
    "            handler.setFormatter(formatter)\n",
    "            logger.addHandler(handler)\n",
    "            try:\n",
    "                getattr(logging, config[\"log_level\"].upper())\n",
    "            except AttributeError as AE:\n",
    "                self.__internal(\n",
    "                    f\"Log Level {config['log_level']} not found, setting default level to INFO -> {AE}\"\n",
    "                )\n",
    "\n",
    "        if not os.path.exists(self.filename):\n",
    "            self.newline()\n",
    "            self.raw(\n",
    "                \"|     Timestamp     |  LOG Level  |\"\n",
    "                + \" \" * 71\n",
    "                + \"LOG Messages\"\n",
    "                + \" \" * 71\n",
    "                + \"|\"\n",
    "            )\n",
    "        self.newline()\n",
    "\n",
    "    @staticmethod\n",
    "    def __timestamp() -> str:\n",
    "        \"\"\"\n",
    "        Returns the current timestamp as a string.\n",
    "\n",
    "        :return: Current timestamp in 'YYYY-MM-DD HH:MM:SS' format.\n",
    "        \"\"\"\n",
    "        return datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n",
    "\n",
    "    @staticmethod\n",
    "    def __pad_message(message: str) -> str:\n",
    "        \"\"\"\n",
    "        Pads or truncates the message to fit the log format.\n",
    "\n",
    "        :param message: The log message to be padded or truncated.\n",
    "        :return: The padded or truncated message.\n",
    "        \"\"\"\n",
    "        return (\n",
    "            message + \" \" * (153 - len(message))\n",
    "            if len(message) < 153\n",
    "            else message[:150] + \"...\"\n",
    "        ) + \"|\"\n",
    "\n",
    "    def raw(self, message):\n",
    "        \"\"\"\n",
    "        Logs a raw message directly to the log file.\n",
    "\n",
    "        :param message: The raw message to be logged.\n",
    "        \"\"\"\n",
    "        frame = inspect.stack()[1]\n",
    "        if frame.function == \"<module>\":\n",
    "            self.__internal(\n",
    "                f\"Raw message called from a non-function - This is not recommended\"\n",
    "            )\n",
    "        if message != \"None\" and message is not None:\n",
    "            with open(self.filename, \"a\") as f:\n",
    "                f.write(f\"{str(message)}\\n\")\n",
    "\n",
    "    def newline(self):\n",
    "        \"\"\"\n",
    "        Logs a newline separator in the log file.\n",
    "        \"\"\"\n",
    "        with open(self.filename, \"a\") as f:\n",
    "            f.write(\"|\" + \"-\" * 19 + \"|\" + \"-\" * 13 + \"|\" + \"-\" * 154 + \"|\" + \"\\n\")\n",
    "\n",
    "    def info(self, message):\n",
    "        \"\"\"\n",
    "        Logs an info message.\n",
    "\n",
    "        :param message: The info message to be logged.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            colorlog.info(str(message))\n",
    "        self.raw(\n",
    "            f\"[{self.__timestamp()}] > INFO:     | {self.__pad_message(str(message))}\"\n",
    "        )\n",
    "\n",
    "    def warning(self, message):\n",
    "        \"\"\"\n",
    "        Logs a warning message.\n",
    "\n",
    "        :param message: The warning message to be logged.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            colorlog.warning(str(message))\n",
    "        self.raw(\n",
    "            f\"[{self.__timestamp()}] > WARNING:  | {self.__pad_message(str(message))}\"\n",
    "        )\n",
    "\n",
    "    def error(self, message):\n",
    "        \"\"\"\n",
    "        Logs an error message.\n",
    "\n",
    "        :param message: The error message to be logged.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            colorlog.error(str(message))\n",
    "        self.raw(\n",
    "            f\"[{self.__timestamp()}] > ERROR:    | {self.__pad_message(str(message))}\"\n",
    "        )\n",
    "\n",
    "    def critical(self, message):\n",
    "        \"\"\"\n",
    "        Logs a critical message.\n",
    "\n",
    "        :param message: The critical message to be logged.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            colorlog.critical(str(message))\n",
    "        self.raw(\n",
    "            f\"[{self.__timestamp()}] > CRITICAL: | {self.__pad_message(str(message))}\"\n",
    "        )\n",
    "\n",
    "    @staticmethod\n",
    "    def debug(message):\n",
    "        \"\"\"\n",
    "        Logs a debug message.\n",
    "\n",
    "        :param message: The debug message to be logged.\n",
    "        \"\"\"\n",
    "        if message != \"None\" and message is not None:\n",
    "            colorlog.debug(str(message))\n",
    "\n",
    "    def string(self, message, type: str):\n",
    "        \"\"\"\n",
    "        Logs a message with a specified type. Supported types are 'debug', 'info', 'warning', 'error', 'critical'\n",
    "        as well as the aliases 'err', 'warn', and 'crit'.\n",
    "\n",
    "        :param message: The message to be logged.\n",
    "        :param type: The type of the log message.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            type_map = {\"err\": \"error\", \"warn\": \"warning\", \"crit\": \"critical\"}\n",
    "            type = type_map.get(type.lower(), type)\n",
    "            try:\n",
    "                getattr(self, type.lower())(str(message))\n",
    "            except AttributeError as AE:\n",
    "                self.__internal(\n",
    "                    f\"A wrong Log Type was called: {type} not found. -> {AE}\"\n",
    "                )\n",
    "                getattr(self, \"Debug\".lower())(str(message))\n",
    "\n",
    "    def exception(self, message, exception_type: Type = Exception):\n",
    "        \"\"\"\n",
    "        Logs an exception message.\n",
    "\n",
    "        :param message: The exception message to be logged.\n",
    "        :param exception_type: The type of exception to raise.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            self.raw(\n",
    "                f\"[{self.__timestamp()}] > EXCEPTION:| {self.__pad_message(f'{message} -> Exception provoked: {str(exception_type)}')}\"\n",
    "            )\n",
    "        raise exception_type(message)\n",
    "\n",
    "    def __internal(self, message):\n",
    "        \"\"\"\n",
    "        Logs an internal message.\n",
    "\n",
    "        :param message: The internal message to be logged.\n",
    "        \"\"\"\n",
    "        if self.color and message != \"None\" and message is not None:\n",
    "            colorlog.log(self.INTERNAL_LOG_LEVEL, str(message))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}