Logging

Standard / Custom logging

# -*- coding: utf-8 -*-
import os
import sys
from datetime import datetime,timedelta
import logging
from functools import wraps
from time import time


"""
LOGGING LEVEL PRIORITY
CRITICAL   50
ERROR      40
WARNING    30
INFO       20
DEBUG      10
NOTSET     0
"""

LOG_FOLDER = 'logs'
LOG_LEVEL = logging.INFO   
TODAY = datetime.now().strftime("%Y%m%d")
LOG_FORMAT = '%(asctime)s|{}.py|%(levelname)s|%(message)s'
PARIS_TIME_ZONE = "Europe/Paris"
DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"


def datetime2Utc(dt:datetime, timeformat:str=DEFAULT_TIME_FORMAT)->str:
    """
    Receive a datetime object and return utc time with string format
    """
    return dt.astimezone(timezone('utc')).strftime(timeformat)

def datetime2Paris(dt:datetime, timeformat:str=DEFAULT_TIME_FORMAT)->str:
    """
    Receive a datetime object and return Paris time with string format
    """
    return dt.astimezone(timezone(PARIS_TIME_ZONE)).strftime(timeformat)

class funcLogging:
    """
    Create a logger object and check if it has a filehandle attached, if not add one, Singleton design pattern.
    """

    def __init__(self,filename:str)->None:
        self.folderPath = f"{LOG_FOLDER}"
        self.completePath = f"./{LOG_FOLDER}/{TODAY}.log"
        if not os.path.exists(self.folderPath):
            os.makedirs(self.folderPath)
        self.logger = logging.getLogger()
        self.logger.setLevel(LOG_LEVEL)
        self.fileHandler = logging.FileHandler(self.completePath, 'a')
        self.fileHandler.setFormatter(logging.Formatter(LOG_FORMAT.format(filename))) 

    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(funcLogging, cls).__new__(cls)
        return cls.instance

    def critical(self, msg:str)->None:
        self.logger.critical(msg)
        
    def error(self, msg:str)->None:
        self.logger.error(msg)
        
    def warning(self, msg:str)->None:
        self.logger.warning(msg)    
        
    def info(self, msg:str)->None:
        self.logger.info(msg)

    def debug(self, msg:str)->None:
        self.logger.debug(msg)


class customLogging:

    def __init__(self, filename:str)->None:
        self.fileName = filename
        self.folderPath = f"{LOG_FOLDER}"
        self.completePath = f"./{LOG_FOLDER}/{TODAY}.log"
        if not os.path.exists(self.folderPath):
            os.makedirs(self.folderPath)
        self.format ="{0}|{1}.py|{2}|{3}"
        self.dateTimeFormat = '%Y-%m-%d %H:%M:%S'

    def write(self, msg)->None:
        today = datetime.now().strftime("%Y%m%d")
        self.completePath = f"./{LOG_FOLDER}/{today}.log"
        with open(self.completePath,encoding='utf-8', mode='a') as file:
            file.write(str(msg)+'\n')
        sys.stdout.write(str(msg)+'\n')
    
    def getDateTimeNow(self)->str:
        return datetime.now()

    def critical(self, msg:str)->None:
        msg = self.format.format(datetime2Paris(self.getDateTimeNow()), self.fileName, "CRITICAL", msg)
        self.write(msg)
        
    def error(self, msg:str)->None:
        msg = self.format.format(datetime2Paris(self.getDateTimeNow()), self.fileName, "ERROR", msg)      
        self.write(msg)  

    def warning(self, msg:str)->None:
        msg = self.format.format(datetime2Paris(self.getDateTimeNow()), self.fileName, "WARNING", msg)   
        self.write(msg) 
        
    def info(self, msg:str)->None:
        msg = self.format.format(datetime2Paris(self.getDateTimeNow()), self.fileName, "INFO", msg) 
        self.write(msg)
    
    def debug(self, msg:str)->None:
        msg = self.format.format(datetime2Paris(self.getDateTimeNow()), self.fileName, "DEBUG", msg)
        self.write(msg)
        
    def timer(self, f):
        @wraps(f)
        def wrap(*args, **kw):
            ts = time()
            result = f(*args, **kw)
            te = time()
            fctAndDuration = str({"functionName":str(f.__name__),"duration":f"{str(timedelta(seconds=te-ts))}"})
            msg = self.format.format(self.getDateTimeNow(), self.fileName, "INFO", fctAndDuration)
            self.write(msg)
            return result
        return wrap

    

Basic use:

from pathlib import Path

log = customLogging(Path(__file__).stem)
log.info("info message")
try:
    foo()
except Excepction as e:
    log.error({'comment':'K8s worker is down','error':e})

Last updated