adds logger utility

This commit is contained in:
happy 2023-04-25 19:58:56 -07:00
parent 841bfc8dd6
commit 67c080db68
8 changed files with 376 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

125
.gitignore vendored Normal file
View File

@ -0,0 +1,125 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Bazel stuff
bazel-*
# MacOS stuff
.DS_Store
# Django
*.sqlite3
local_settings.py
web/.env

4
BUILD Normal file
View File

@ -0,0 +1,4 @@
load("@rules_python//python:defs.bzl", "py_binary")
package(default_visibility = ["//visibility:public"])

15
WORKSPACE Normal file
View File

@ -0,0 +1,15 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
http_archive(
name = "rules_python",
sha256 = "94750828b18044533e98a129003b6a68001204038dc4749f40b195b24c38f49f",
strip_prefix = "rules_python-0.21.0",
url = "https://github.com/bazelbuild/rules_python/archive/0.21.0.tar.gz",
)
git_repository(
name = "subpar",
remote = "https://github.com/google/subpar",
tag = "2.0.0",
)

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
colorlog
diffusers
torch

11
utilities/BUILD Normal file
View File

@ -0,0 +1,11 @@
py_library(
name = "logger",
srcs = ["logger.py"],
)
py_test(
name = "logger_test",
srcs = ["logger_test.py"],
deps = [":logger"],
)

181
utilities/logger.py Normal file
View File

@ -0,0 +1,181 @@
import logging
import os
import sys
import traceback
from colorlog import ColoredFormatter
FORMATTER_STREAM = ColoredFormatter(
"%(asctime)s %(log_color)s%(levelname)-.1s[%(name)s] %(message)s%(reset)s")
FORMATTER_FILE = logging.Formatter("%(asctime)s %(levelname)-.1s. %(message)s")
VERBOSITY_V = 10
VERBOSITY_VV = 100
VERBOSITY_VVV = 1000
def touch(filepath):
'''
Behaves similarly as `touch` command in Linux system.
Creates an empty file.
'''
try:
os.makedirs(os.path.dirname(filepath), exist_ok=True)
open(filepath, 'a').close()
return True
except BaseException:
pass
return False
def get_func_name(offset=0):
"""
Returns the name of the caller function name.
Offset counts the recursion - for example, offset=1 means the caller of the caller.
"""
return sys._getframe(1+offset).f_code.co_name
class DummyLogger():
'''
DummyLogger does not do anything.
'''
def __init__(self):
pass
def get_verbosity_value(self) -> int:
return 0
def set_verbosity(self, verbosity: int):
pass
def set_log_output_filepath(self, filepath: str, level=logging.DEBUG):
pass
def debugging_on(self):
pass
def debugging_off(self):
pass
def info(self, msg=""):
pass
def error(self, msg=""):
pass
def warn(self, msg=""):
pass
def debug(self, msg="", verbosity=VERBOSITY_V):
pass
def critical(self, msg=""):
pass
class Logger(DummyLogger):
'''
Logger with specific format
'''
def __init__(
self,
name: str = "default",
filepath: str = "",
verbosity: int = VERBOSITY_V,
stream_lvl=logging.INFO,
file_lvl=logging.DEBUG
):
self.verbosity = verbosity
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)
self.__streamHandler = logging.StreamHandler()
self.__streamHandler.setLevel(stream_lvl)
self.__streamHandler.setFormatter(FORMATTER_STREAM)
self.logger.addHandler(self.__streamHandler)
self.__fileHandler = None
if filepath and touch(filepath):
file_handler = logging.FileHandler(filepath)
file_handler.setLevel(file_lvl)
file_handler.setFormatter(FORMATTER_FILE)
self.__fileHandler = file_handler
self.logger.addHandler(self.__fileHandler)
self.logger.debug("log for {} started".format(name))
self.logger.info("log output filepath: {}".format(filepath))
def get_verbosity_value(self) -> int:
return self.verbosity
def set_verbosity(self, verbosity: int):
self.verbosity = verbosity
def set_log_output_filepath(self, filepath: str, level=logging.DEBUG):
# remove prior one if any, only one handler would be allowed at a time
if self.__fileHandler is not None:
self.logger.removeHandler(self.__fileHandler)
self.__fileHandler.close()
if filepath and touch(filepath):
file_handler = logging.FileHandler(filepath)
file_handler.setLevel(level)
file_handler.setFormatter(FORMATTER_FILE)
self.__fileHandler = file_handler
self.logger.addHandler(self.__fileHandler)
def debugging_on(self):
self.__streamHandler.setLevel(logging.DEBUG)
self.logger.info(
"debug msg printing is on, verbosity: {}".format(self.verbosity))
def debugging_off(self):
self.__streamHandler.setLevel(logging.INFO)
self.logger.info("debug msg printing is off")
def info(self, msg="", verbosity: int = VERBOSITY_V):
'''
Showing info message.
@param msg: the message
@param verbosity: the higher the number is, the less important it is.
'''
if verbosity > self.verbosity:
return
self.logger.info("[{}] {}".format(get_func_name(offset=1), msg))
def warn(self, msg="", verbosity: int = VERBOSITY_V):
'''
Showing warning message.
@param msg: the message
@param verbosity: the higher the number is, the less important it is.
'''
if verbosity > self.verbosity:
return
self.logger.warning("[{}] {}".format(get_func_name(offset=1), msg))
def debug(self, msg: str = "", verbosity: int = VERBOSITY_V):
'''
Showing debug message.
@param msg: the message
@param verbosity: the higher the number is, the less important it is.
'''
if verbosity > self.verbosity:
return
self.logger.debug("[{}] {}".format(get_func_name(offset=1), msg))
def error(self, msg=""):
'''
Showing error message.
@param msg: the message
'''
self.logger.error("[{}] {}".format(get_func_name(offset=1), msg))
self.logger.error(traceback.format_exc())
def critical(self, msg=""):
'''
Showing critical message.
@param msg: the message
'''
self.logger.critical("[{}] {}".format(get_func_name(offset=1), msg))

35
utilities/logger_test.py Normal file
View File

@ -0,0 +1,35 @@
import os
import unittest
from utilities.logger import Logger
class TestLogger(unittest.TestCase):
@classmethod
def setUpClass(self):
self.logger = Logger()
self.log_filepath = "/tmp/test/tmplog.log"
def test_logger_print(self):
self.logger.set_log_output_filepath(self.log_filepath)
self.logger.debug("A quirky message only developers care about")
self.logger.info("Curious users might want to know this")
self.logger.warn("Something is wrong and any user should be informed")
self.logger.error("Serious stuff, this is red for a reason")
self.logger.critical("OH NO everything is on fire")
self.logger.debugging_on()
self.logger.debug("A quirky message only developers care about")
self.assertTrue(os.path.isfile(self.log_filepath))
@classmethod
def tearDownClass(self):
if os.path.isfile(self.log_filepath):
os.remove(self.log_filepath)
if __name__ == '__main__':
unittest.main()