Prompt

How to make a logging script that works globally for an entire python program. I want it to use OOP, have decorators, and is refactored. Make sure that I can call the same instance of the logger across multiple scripts, and that I can input a message type and message into the decorator and it all logs into the same log file. Add an option so I can also time the execution functions as well. Make the logging decorator a child class of the Logger class, make sure that I can call LoggerDecorator into any script and it will all refer to the same instance

Answer

Certainly! Here's an updated version where the logging decorator is implemented as a child class of the Logger class, and you can call LoggerDecorator into any script, and it will refer to the same instance of the logger:

import logging
import timeit
from functools import wraps

class Logger:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.logger = logging.getLogger(__name__)
            cls._instance.logger.setLevel(logging.DEBUG)

            formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")

            # Define the log file name and create a file handler
            file_handler = logging.FileHandler("app.log")
            file_handler.setLevel(logging.DEBUG)
            file_handler.setFormatter(formatter)
            cls._instance.logger.addHandler(file_handler)

            # Create a stream handler to print log messages to the console
            stream_handler = logging.StreamHandler()
            stream_handler.setLevel(logging.INFO)
            stream_handler.setFormatter(formatter)
            cls._instance.logger.addHandler(stream_handler)

        return cls._instance

    def log(self, level, message):
        self.logger.log(level, message)

    def debug(self, message):
        self.log(logging.DEBUG, message)

    def info(self, message):
        self.log(logging.INFO, message)

    def warning(self, message):
        self.log(logging.WARNING, message)

    def error(self, message):
        self.log(logging.ERROR, message)

    def critical(self, message):
        self.log(logging.CRITICAL, message)

class LoggerDecorator(Logger):
    def log_execution(self, message_type, message, time_execution=False):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                function_name = func.__name__
                args_string = ", ".join(repr(arg) for arg in args)
                kwargs_string = ", ".join(f"{key}={repr(value)}" for key, value in kwargs.items())
                all_args = ", ".join(filter(None, [args_string, kwargs_string]))

                self.log(logging.INFO, f"{message_type}: {message}")
                self.log(logging.INFO, f"Executing {function_name}({all_args})")

                if time_execution:
                    start_time = timeit.default_timer()

                result = func(*args, **kwargs)

                if time_execution:
                    end_time = timeit.default_timer()
                    elapsed_time = end_time - start_time
                    self.log(logging.INFO, f"{function_name} executed in {elapsed_time:.6f} seconds")

                return result
            return wrapper
        return decorator

In this updated version, the LoggerDecorator class is a child class of Logger, and it includes the log_execution method as the decorator. By inheriting from Logger, LoggerDecorator has access to the logging functionality provided by the Logger class.

You can now use LoggerDecorator as follows in any script:

from logger import LoggerDecorator

logger = LoggerDecorator()

@logger.log_execution("Custom", "Custom log message", time_execution=True)
def my_function():
    logger.info("This is a log message from my_function.")

# Usage in other scripts
# ...

In any script where you import and create an instance of LoggerDecorator as logger, it will refer to the same logging instance as other scripts. Therefore, all log messages logged through the LoggerDecorator instance will be directed to the same log file ("app.log" in this case).