-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from MissterHao/feature/logging
Add logger module and use it in dysession
- Loading branch information
Showing
14 changed files
with
262 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,16 @@ | ||
from typing import Optional | ||
|
||
from dysession.logger import get_logger | ||
|
||
|
||
class DynamodbTableNotFound(Exception): | ||
pass | ||
def __init__(self, table_name: Optional[str] = None, *args: object) -> None: | ||
super().__init__(*args) | ||
|
||
logger = get_logger() | ||
if table_name: | ||
logger.error(f"'{table_name}' is not found in current region.") | ||
|
||
|
||
class DynamodbItemNotFound(Exception): | ||
pass | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import logging | ||
import sys | ||
from enum import Enum, auto | ||
from functools import lru_cache | ||
from typing import Literal | ||
|
||
from .handler.colorful_console import ColorfulConsoleLoggerHandler | ||
|
||
|
||
class LoggingType(Enum): | ||
|
||
PLAINTEXT_CONSOLE = auto() | ||
COLOR_CONSOLE = auto() | ||
FILE = auto() | ||
CONSOLE = auto() | ||
|
||
|
||
@lru_cache | ||
def is_tty() -> bool: | ||
"""Hepler function with lru_cache""" | ||
return sys.stdout.isatty() | ||
|
||
|
||
def get_logger( | ||
logger_name: str = "dysession", | ||
logger_type: Literal[LoggingType.CONSOLE, LoggingType.FILE] = LoggingType.CONSOLE, | ||
level: int = logging.DEBUG, | ||
) -> logging.Logger: | ||
""" | ||
This function return a logging.Logger with handlers. | ||
Handlers could be `ColorfulConsoleLoggerHandler`, `StreamHandler`, `FileHandler`. | ||
``` | ||
logger = get_logger() | ||
logger.debug("This is a DEBUG log.") | ||
logger.info("This is a INFO log.") | ||
logger.warning("This is a WARNING log.") | ||
logger.critical("This is a CRITICAL log.") | ||
logger.fatal("This is a FATAL log.") | ||
``` | ||
""" | ||
|
||
logger = logging.getLogger(logger_name) | ||
format = logging.Formatter( | ||
"[%(asctime)-s] [%(levelname)-8s] %(name)s %(message)s ... ( %(filename)s:%(levelno)s )" | ||
) | ||
logger.setLevel(level) | ||
|
||
if not logger.handlers: | ||
if logger_type == LoggingType.CONSOLE: | ||
if is_tty(): | ||
handler = ColorfulConsoleLoggerHandler() | ||
else: | ||
handler = logging.StreamHandler() | ||
elif logger_type == LoggingType.FILE: | ||
handler = logging.FileHandler("session.log", "a", encoding="utf-8") | ||
|
||
handler.setFormatter(format) | ||
logger.addHandler(handler) | ||
else: | ||
logger.warning( | ||
f"Dysession logger had already initialized with logger({logger.handlers})" | ||
) | ||
|
||
return logger | ||
|
||
|
||
__all__ = ( | ||
LoggingType, | ||
get_logger, | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from enum import Enum | ||
|
||
|
||
class ANSIColor(Enum): | ||
HEADER = "\033[95m" | ||
OKBLUE = "\033[94m" | ||
OKCYAN = "\033[96m" | ||
OKGREEN = "\033[92m" | ||
SUCCESSFUL = "\033[38;5;107m" # 7b9246 | ||
WARNING = "\033[93m" | ||
FAIL = "\033[91m" | ||
DEBUG = "\033[37m" | ||
ENDC = "\033[0m" | ||
BOLD = "\033[1m" | ||
UNDERLINE = "\033[4m" | ||
|
||
|
||
def colorful_it(color: ANSIColor, content: str) -> str: | ||
return f"{color.value}{content}{ANSIColor.ENDC.value}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import logging | ||
|
||
from .ansi import ANSIColor | ||
|
||
LOGLEVEL_TRANSFORM = { | ||
logging.DEBUG: ANSIColor.DEBUG.value, | ||
logging.INFO: ANSIColor.OKCYAN.value, | ||
logging.WARNING: ANSIColor.WARNING.value, | ||
logging.ERROR: ANSIColor.FAIL.value, | ||
logging.CRITICAL: ANSIColor.FAIL.value, | ||
logging.FATAL: ANSIColor.FAIL.value, | ||
} | ||
|
||
|
||
class ColorfulConsoleLoggerHandler(logging.StreamHandler): | ||
""" | ||
A handler class which allows the cursor to stay on | ||
one line for selected messages | ||
""" | ||
|
||
def emit(self, record): | ||
try: | ||
record.levelname = ( | ||
LOGLEVEL_TRANSFORM[record.levelno] | ||
+ f"{record.levelname:>8}" | ||
+ ANSIColor.ENDC.value | ||
) | ||
|
||
msg = self.format(record) | ||
self.stream.write(msg) | ||
self.stream.write(self.terminator) | ||
self.flush() | ||
except (KeyboardInterrupt, SystemExit): | ||
raise | ||
except: | ||
self.handleError(record) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
1.0.3 | ||
1.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import logging | ||
import sys | ||
from unittest import mock | ||
|
||
from django.test import TestCase | ||
from parameterized import parameterized | ||
|
||
from dysession.logger import LoggingType, get_logger, is_tty | ||
from dysession.logger.handler.colorful_console import ColorfulConsoleLoggerHandler | ||
|
||
|
||
class LoggerTestCase(TestCase): | ||
def test_get_logger_with_second_logger_type_passed_in(self): | ||
|
||
with self.assertLogs("test_get_logger_with_second_logger_type_passed_in") as cm: | ||
logger = get_logger("test_get_logger_with_second_logger_type_passed_in") | ||
logger.info("This is a test content") | ||
self.assertIn("This is a test content", "\n".join(cm.output)) | ||
|
||
with self.assertLogs("test_get_logger_with_second_logger_type_passed_in") as cm: | ||
logger = get_logger("test_get_logger_with_second_logger_type_passed_in", logger_type=LoggingType.FILE) | ||
logger.info("This is a test content") | ||
self.assertIn("WARNING", "\n".join(cm.output)) | ||
|
||
@mock.patch("sys.stdout.isatty") | ||
def test_logger_is_tty_handlers(self, mock_is_tty): | ||
is_tty.cache_clear() | ||
mock_is_tty.return_value = True | ||
|
||
logger = get_logger("test_logger_is_tty_handlers") | ||
self.assertEqual(len(logger.handlers), 1) | ||
self.assertEqual(logger.name, "test_logger_is_tty_handlers") | ||
self.assertIs(type(logger.handlers[0]), ColorfulConsoleLoggerHandler) | ||
|
||
logger.handlers = [] | ||
|
||
@mock.patch("sys.stdout.isatty") | ||
def test_logger_is_not_tty_handlers(self, mock_is_tty): | ||
is_tty.cache_clear() | ||
mock_is_tty.return_value = False | ||
|
||
logger = get_logger("test_logger_is_not_tty_handlers") | ||
self.assertEqual(len(logger.handlers), 1) | ||
self.assertEqual(logger.name, "test_logger_is_not_tty_handlers") | ||
self.assertIs(type(logger.handlers[0]), logging.StreamHandler) | ||
|
||
logger.handlers = [] | ||
|
||
def test_logger_file_handler(self): | ||
|
||
logger = get_logger("test_logger_file_handler", logger_type=LoggingType.FILE) | ||
|
||
self.assertEqual(len(logger.handlers), 1) | ||
self.assertEqual(logger.name, "test_logger_file_handler") | ||
self.assertIsInstance(logger.handlers[0], logging.FileHandler) | ||
|
||
with self.assertLogs("test_logger_file_handler") as cm: | ||
logger.info("This is a test content") | ||
self.assertIn("This is a test content", "\n".join(cm.output)) | ||
|
||
logger.handlers = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from typing import Any | ||
|
||
from django.test import TestCase | ||
from parameterized import parameterized | ||
|
||
from dysession.logger import get_logger | ||
from dysession.logger.handler.ansi import ANSIColor, colorful_it | ||
|
||
|
||
class ANSITestCase(TestCase): | ||
@parameterized.expand( | ||
[ | ||
[ANSIColor.HEADER], | ||
[ANSIColor.OKBLUE], | ||
[ANSIColor.OKCYAN], | ||
[ANSIColor.OKGREEN], | ||
[ANSIColor.SUCCESSFUL], | ||
[ANSIColor.WARNING], | ||
[ANSIColor.FAIL], | ||
[ANSIColor.DEBUG], | ||
[ANSIColor.BOLD], | ||
[ANSIColor.UNDERLINE], | ||
] | ||
) | ||
def test_ansi_colorful_it_func(self, ansi_color: ANSIColor): | ||
|
||
self.assertEqual( | ||
colorful_it(ansi_color, "Content"), | ||
f"{ansi_color.value}Content{ANSIColor.ENDC.value}", | ||
) |