Skip to content

Commit

Permalink
Merge pull request #1 from PabloLec/dev
Browse files Browse the repository at this point in the history
Update 0.0.2
  • Loading branch information
PabloLec authored Oct 2, 2021
2 parents 12e99b9 + 564c923 commit 58c27e1
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 126 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__pycache__
__pycache__/
poetry.lock
dev.py
2 changes: 1 addition & 1 deletion livelog/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

from .logger import Logger
from livelog.logger import Logger, LoggerSingleton
61 changes: 53 additions & 8 deletions livelog/__main__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
# -*- coding: utf-8 -*-

from sys import argv
from os import _exit
from .reader import start_reader
from platform import system
from tempfile import gettempdir
from pathlib import Path
from argparse import ArgumentParser
from livelog.reader import start_reader

if __name__ == "__main__":
if len(argv) == 1:
print("! No file specified !")
_exit(1)

start_reader(file=argv[1])
def _parse_args():
"""Parse CLI arguments.
Returns:
tuple: Provided arguments
"""

parser = ArgumentParser(description="Live read a log file")
parser.add_argument(
"-f",
"--file",
action="store",
type=str,
required=False,
help="Log file to be read",
)
parser.add_argument(
"-l",
"--level",
action="store",
type=str,
default="DEBUG",
required=False,
help="Minimum log level. Default: DEBUG",
)
parser.add_argument(
"--nocolors",
action="store_true",
required=False,
help="Do not color lines",
)
args = parser.parse_args()

if args.file is not None:
file = Path(args.file)
else:
file = (
Path("/tmp/livelog.log")
if system() == "Darwin"
else Path(gettempdir()) / "livelog.log"
)

return file, args.level, args.nocolors


if __name__ == "__main__":
file, level, nocolors = _parse_args()
start_reader(file=file, level=level, nocolors=nocolors)
125 changes: 58 additions & 67 deletions livelog/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
from platform import system
from tempfile import gettempdir
from datetime import datetime
from colorama import Fore, Style
from .errors import *


class Logger:
"""Main logger object.
"""Logging handler.
Attributes:
LEVELS (dict): Log levels value for fast filtering
enabled (bool): If logging is enabled
erase (bool): If preexisting file should be erased
file (str): Log file path
level (str): Minimum log level to be displayed
Raises:
LogLevelDoesNotExist: If user provide an unknown log level
Expand All @@ -20,66 +26,57 @@ class Logger:
write to output path
"""

__instance = None
_LEVELS = {"ERROR": 3, "WARNING": 2, "INFO": 1, "DEBUG": 0}

def __new__(cls, *args, **kwargs):
is_singleton = (len(args) == 6 and args[5] == True) or kwargs.get("singleton")
if is_singleton and not Logger.__instance or not is_singleton:
Logger.__instance = object.__new__(cls)
return Logger.__instance
_LEVELS = {
"ERROR": 3,
"WARNING": 2,
"INFO": 1,
"DEBUG": 0,
}

def __init__(
self,
output_file: str = None,
level: str = "INFO",
file: str = None,
level: str = "DEBUG",
enabled: bool = True,
colors: bool = True,
erase: bool = True,
singleton: bool = False,
):
"""Logger initialization.
Args:
output_file (str, optional): Output file path. Defaults to None.
level (str, optional): Minimum log level. Defaults to "INFO".
file (str, optional): Output file path. Defaults to None.
level (str, optional): Minimum log level. Defaults to "DEBUG".
enabled (bool, optional): Is log enabled ? Defaults to True.
colors (bool, optional): Are colors enabled ? Defaults to True.
erase (bool, optional): Should preexisting file be erased ? Defaults to True.
singleton (bool, optional): Is singleton ? Defaults to False.
Raises:
LogLevelDoesNotExist: [description]
"""

self._enabled = enabled
self._colors = colors
self._erase = erase

if output_file is None:
self._output_file = Path(
if file is None:
self._file = Path(
"/tmp/livelog.log"
if system() == "Darwin"
else Path(gettempdir()) / "livelog.log"
)
else:
self._output_file = Path(output_file)
self._verify_file(self._output_file)
self._file = Path(file)

self._verify_file()

level = level.upper()
if level not in self._LEVELS:
raise LogLevelDoesNotExist(level)
self._level = level

@property
def output_file(self):
return self._output_file
def file(self):
return self._file

@output_file.setter
def set_output_file(self, value: str):
@file.setter
def set_file(self, value: str):
path = Path(value)
self._verify_file(file=path)
self._output_file = path
self._file = path

@property
def level(self):
Expand All @@ -92,16 +89,14 @@ def level(self, value: str):
raise LogLevelDoesNotExist(level)
self._level = level

def _verify_file(self, file: Path):
"""Verify if the file is a valid log file and clear its preexisting content.
Args:
file (Path): File path to verify.
def _verify_file(self):
"""Verify if provided file path is a valid log file and clear its
preexisting content.
"""

dir = file.parent.resolve()
if file.is_dir():
raise LogFileIsADirectory(path=file)
dir = self._file.parent.resolve()
if self._file.is_dir():
raise LogFileIsADirectory(path=self._file)
if not dir.is_dir():
raise LogPathDoesNotExist(path=dir)
if not access(dir, X_OK):
Expand All @@ -111,31 +106,26 @@ def _verify_file(self, file: Path):
def _clear_file(self):
"""Clear output file content."""

if not self._erase or not self._output_file.is_file():
if not self._erase or not self._file.is_file():
return
with open(self._output_file, "w") as f:
with open(self._file, "w") as f:
pass

def _write(self, content: str):
def _write(self, level: str, content: str):
"""Write provided content to output file.
Args:
level (str); Log level
content (str): Content to be written
"""

if not self._enabled:
return

if self._colors:
time = Style.DIM + datetime.now().strftime("%H:%M:%S.%f")[:-3]
dash = Style.BRIGHT + " - "
content = f"{Style.NORMAL}{content}{Style.RESET_ALL}"
else:
time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
dash = " - "
time = datetime.now().strftime("%H:%M:%S.%f")[:-3]

with open(self._output_file, "a") as f:
f.write(f"{time}{dash}{content}\n")
with open(self._file, "a") as f:
f.write(f"{level} | {time} - {content}\n")

def _is_valid_level(self, level: str):
"""Verify if the given log level should be written.
Expand All @@ -156,10 +146,7 @@ def error(self, message: str):
message (str): Log message
"""

if self._colors:
self._write(content=Fore.RED + message)
else:
self._write(content="error | " + message)
self._write(level="ERR!", content=message)

def warn(self, message: str):
"""Write warning message.
Expand All @@ -170,10 +157,7 @@ def warn(self, message: str):

if not self._is_valid_level("WARNING"):
return
if self._colors:
self._write(content=Fore.YELLOW + message)
else:
self._write(content="warning | " + message)
self._write(level="WARN", content=message)

def info(self, message: str):
"""Write info message.
Expand All @@ -184,10 +168,7 @@ def info(self, message: str):

if not self._is_valid_level("INFO"):
return
if self._colors:
self._write(content=Fore.BLUE + message)
else:
self._write(content="info | " + message)
self._write(level="INFO", content=message)

def debug(self, message: str):
"""Write debug message.
Expand All @@ -198,7 +179,17 @@ def debug(self, message: str):

if not self._is_valid_level("DEBUG"):
return
if self._colors:
self._write(content=Fore.WHITE + message)
else:
self._write(content="debug | " + message)
self._write(level="DBUG", content=message)


class Singleton(type):
_instances = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]


class LoggerSingleton(Logger, metaclass=Singleton):
pass
Loading

0 comments on commit 58c27e1

Please sign in to comment.