Skip to content

Instantly share code, notes, and snippets.

@binarybrat
Created December 22, 2023 03:23
Show Gist options
  • Save binarybrat/41da33c43a75d08ac5fe340479b670b6 to your computer and use it in GitHub Desktop.
Save binarybrat/41da33c43a75d08ac5fe340479b670b6 to your computer and use it in GitHub Desktop.

Revisions

  1. binarybrat created this gist Dec 22, 2023.
    19 changes: 19 additions & 0 deletions exampleuse.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    import cfg
    import error
    import dbsrc
    # Each of these below hell everything in dbsrc also should be logged.
    from dbsrc import cmmentmodel, colorinfomodel, modlgmodel, mentionmodel, postmodel, snapshotmodel
    import filetils
    iport fmtutils
    import logutils
    from logutils import rainbowlogger
    import reddtutils

    # Usually logName is 'all'
    log = rainbowlogger.set_logger(fileutils.get_filePath(__file__, "all"))






    157 changes: 157 additions & 0 deletions rainbowlogger.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,157 @@
    import os
    import sys
    import traceback
    import colorama
    import logging
    import logging.handlers
    from logging.handlers import RotatingFileHandler
    from random import choice as rc
    from random import sample as rsamp


    import cfg
    from fmtutils import smlformat
    from logutils import set_prettyLogger
    from fileutils import get_pathFileName

    plog = set_prettyLogger()

    smlAnsiList = rsamp(cfg.ansiColorList, 20)

    # Simple prin function that mocs certain log outtputs

    #I want to be able to use this in other modules, but I don't want to have to pass the logger around
    def set_logger(ldir=None, lname=None, silent=False):
    """
    This function sets up the logger. It takes two optional arguments, ldir and lname.
    I have other arguments but am right now running this bare bones.
    :param ldir: This is the log directory. It is set in the cfg file, or manually passed in.
    :param lname: This is the log name. It is set in the cfg file, or manually passed in.
    :param silent: Whether or nt to give output to the terminal.
    :return: logging.Lgger object
    """

    # this looks at my cfg file (currently .py file until I work on my dataclasses config stuff
    # and sets the log directory based on the cfg file
    logDir = cfg.LOG_DIR + ldir if not cfg.TESTING else cfg.TEST_LOG_DIR + ldir
    tlogDir = cfg.TEST_LOG_DIR
    LOGDIR = logDir if not cfg.TESTING else tlogDir

    if not os.path.isdir(LOGDIR):
    # Ansi mocks the color class of the logger.
    plog.Ansi(f"Log directory does not exist: {LOGDIR} - creating it now.")
    os.mkdir(LOGDIR)
    plog.Ansi(f"Created new log directory: {os.path.isdir}")

    if lname is None:
    lname = get_pathFileName(__file__) # this ensures that the name is the actual file name
    # I hate having weird logfile names so if I am in the terminal I want it to go to a file called terminal
    if lname == "<input>":
    lname = "terminal"

    logName = lname
    logFile = os.path.join(LOGDIR, f"{logName}.log")

    if not os.path.isfile(logFile):
    # Is this the right way to do this? I was using `w` but then once I connected this project
    # with hithub it stopped seeing the log file no matter what I dod so I switched to this.
    with open(logFile, "x"):
    pass
    if cfg.TESTING:
    plog.Ansi(f"Created new log file: {logFile}")
    plog.Ansi(f"Log File Exists: {os.path.isfile(logFile)}")

    logger = logging.getLogger(logName)

    if logger.hasHandlers():
    logger.handlers.clear()

    logger.setLevel(logging.DEBUG)

    # I snagged this from somewhere years ago and I don't remember where. I think it was from a 'colorful logging' tutorial.
    class UpperThresholdFilter(logging.Filter):
    def __init__(self, threshold, *args, **kwargs):
    self._threshold = threshold
    super(UpperThresholdFilter, self).__init__(*args, **kwargs)

    def filter(self, rec):
    return rec.levelno <= self._threshold

    # This is the same as the above. I think I got it from the same place. Can I pull these out of this function or does it need to be written this way?
    class ColorFormatter(logging.Formatter):
    def __init__(self, colorfmt, *args, **kwargs):
    self._colorfmt = colorfmt
    super(ColorFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
    if record.levelno == logging.INFO:
    # This little bit changes the info from teal to random.
    # The color ansi output is from a list of ansi colors I have in my cfg file from colors that are good on my eyes.
    color = rc(smlAnsiList)
    # color = colorama.Fore.CYAN
    elif record.levelno == logging.WARNING:
    color = colorama.Fore.YELLOW
    elif record.levelno == logging.ERROR:
    color = colorama.Fore.RED
    elif record.levelno == logging.DEBUG:
    color = colorama.Fore.LIGHTBLUE_EX
    else:
    color = ""

    self._style._fmt = self._colorfmt.format(color, colorama.Style.RESET_ALL)

    return logging.Formatter.format(self, record)

    # My various log formats I have mo but even having these two would be helful
    # There are 2 other formats I want to also add in here and add log level for - eventually.
    logfmt = "[{}%(asctime)s %(levelname)s %(funcName)s{}] %(message)s"
    issuefmt = '{}[%(asctime)s] [%(levelname)s] [%(color_term)s:%(funcName)s] [%(filename)s:%(lineno)d]{} %(message)s'
    pthfmt = "%(asctime)s - %(levelname)s - %(message)s - %(pathname)s"
    dtfmt = smlformat
    dtfmt2 = "%Y-%m-%d %H:%M:%S"

    formatter = ColorFormatter(logfmt, datefmt=dtfmt)
    issueformatter = ColorFormatter(issuefmt, datefmt=dtfmt)

    # Stdout handler
    stdouthandler = logging.StreamHandler(sys.stdout)
    stdouthandler.setLevel(logging.DEBUG)
    stdouthandler.addFilter(UpperThresholdFilter(logging.INFO))
    stdouthandler.setFormatter(formatter)
    if stdouthandler not in logger.handlers:
    logger.addHandler(stdouthandler)


    # Stderr handler
    stderrhandler = logging.StreamHandler(sys.stderr)
    stderrhandler.setLevel(logging.WARNING)
    stderrhandler.setFormatter(issueformatter)
    if stderrhandler not in logger.handlers:
    logger.addHandler(stderrhandler)

    # Setup main filehandler
    filehandler = RotatingFileHandler(logFile, maxBytes=1024 * 1024 * 100, backupCount=10)
    filehandler.setLevel(logging.DEBUG)
    filehandler.setFormatter(logging.Formatter(logfmt.format("", "")))
    if filehandler not in logger.handlers:
    logger.addHandler(filehandler)

    # I usually have a warninghandler as well but it is removed right now.

    # Setup error file handler
    errorFile = os.path.join(LOGDIR, f"{logName}.error.log")
    if not os.path.isfile(errorFile):
    with open(errorFile, "x"):
    pass
    if not silent:
    plog.Ansi(f"Created error file: {errorFile}")
    plog.Ansi(f"Error File Exists: {os.path.isfile(errorFile)}")

    eh = RotatingFileHandler(errorFile, maxBytes=1024 * 1024 * 100, backupCount=10)
    eh.setLevel(logging.ERROR)
    eh.setFormatter(logging.Formatter(issuefmt.format("", "")))
    if eh not in logger.handlers:
    logger.addHandler(eh)

    return logger