diff --git a/.gitignore b/.gitignore index eb674d3e..e4927c23 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ /.vs /Tic_Tac_Toe/tic_tac_toe -OOP.py /.vscode +.venv +.log +/__pycache__/ diff --git a/LoggerLibrary/README.md b/LoggerLibrary/README.md new file mode 100644 index 00000000..dd1e1aae --- /dev/null +++ b/LoggerLibrary/README.md @@ -0,0 +1,72 @@ + +![Star Badge](https://img.shields.io/static/v1?label=%F0%9F%8C%9F&message=If%20Useful&style=style=flat&color=BC4E99) +![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103) + +# LoggerLibrary + +## Description + +A simple Python logging utility with console/file output and automatic log cleanup. This library provides an easy-to-use interface for setting up logging in your Python projects with minimal configuration. + +**Key Features:** +- Simple setup with one function call +- Session tracking with auto-generated UUID to trace logs from the same execution +- Dual output: log to console for debugging and file for persistent records +- Auto cleanup: keep only recent logs (default: 3 days), no manual deletion needed + +## Languages or Frameworks Used + +This project uses Python 3.x with only standard library modules (no external dependencies required). + +## How to run + +### Quick Start + +1. create `demo.py` + +2. Import the logger setup function: +```python +from logger import setup_logger +``` + +3. Choose your setup method: + +**Method 1: Customized Configuration** +```python +logger = setup_logger( + name=__name__, + enable_console=True, + enable_file=True, + level=logging.INFO, + dir='./logs' +) +logger.debug('Debug message') +logger.info('Info message') +``` + +**Method 2: Default Configuration** +```python +logger = setup_logger() +logger.debug('Debug message') +logger.info('Info message') +``` + +4. Run your Python script: +```bash +python3 demo.py +``` + +### Log Output Format + +``` +2025-11-16 00:41:00,951 | demo.py | INFO | 49e1e65d-2734-4807-81de-306ee628d788 | Your message +``` + +## Demo + +See the result of running the logger with both console and file output: + + +## Author + +Shyin Lim -> https://github.com/shyinlim \ No newline at end of file diff --git a/LoggerLibrary/demo.py b/LoggerLibrary/demo.py new file mode 100644 index 00000000..45be13f0 --- /dev/null +++ b/LoggerLibrary/demo.py @@ -0,0 +1,14 @@ +from logger import setup_logger + +logger = setup_logger(name=__name__, enable_console=True, enable_file=False) + +class Sample: + def print_log(self): + logger.debug(f'[PRINT LOG] DEBUG') + logger.info(f'[PRINT LOG] INFO') + logger.warning(f'[PRINT LOG] WARNING') + logger.error(f'[PRINT LOG] ERROR') + + +s = Sample() +s.print_log() \ No newline at end of file diff --git a/LoggerLibrary/logger.py b/LoggerLibrary/logger.py new file mode 100644 index 00000000..46c7ab9b --- /dev/null +++ b/LoggerLibrary/logger.py @@ -0,0 +1,102 @@ +import logging +import os +import re +import sys +import uuid as uuid_module +from datetime import datetime, timedelta + +# === Configuration === +ENABLE_CONSOLE = True +ENABLE_FILE = True +LOG_LEVEL = logging.DEBUG +LOG_DIR = './.log' +KEEP_DAYS = 3 + + +def setup_logger( + name: str = None, + enable_console: bool = ENABLE_CONSOLE, + enable_file: bool = ENABLE_FILE, + level: int = LOG_LEVEL, + dir: str = LOG_DIR + ) -> logging.Logger: + """Setup logger with console and file handlers + + Args: + name: Logger name (use __name__ to get current file), default None (root logger) + enable_console: Enable console output, default True + enable_file: Enable file output, default True + level: Logging level, default DEBUG + dir: Log file directory, default './.log' + + Returns: + Configured logger instance + + Example: + from logger import setup_logger + + logger = setup_logger(name=__name__, enable_console=True, enable_file=False) + logger.info('Test message') + """ + # Setup logger + logger = logging.getLogger(name=name if name else '') + logger.setLevel(level=level) + + + logger.handlers.clear() # Clear existing handlers to prevent duplicate logs + + session_uuid = uuid_module.uuid4() + formatter = logging.Formatter( + f'%(asctime)s | %(filename)s | %(levelname)s | {session_uuid} | %(message)s' + ) + + # Console handler + if enable_console: + console_handler = logging.StreamHandler(stream=sys.stderr) + console_handler.setLevel(level=level) + console_handler.setFormatter(fmt=formatter) + logger.addHandler(hdlr=console_handler) + + # File handler + if enable_file: + os.makedirs(name=dir, mode=0o777, exist_ok=True) + timestamp = datetime.now().strftime('%Y%m%d') + file_path = os.path.join(dir, f'{timestamp}.log') + + file_handler = logging.FileHandler(filename=file_path, encoding='utf-8') + file_handler.setLevel(level=level) + file_handler.setFormatter(fmt=formatter) + logger.addHandler(hdlr=file_handler) + + logger.info(f'Log file created: {file_path}') + + # Clean old log files + cleanup_old_logs(directory=dir, keep_days=KEEP_DAYS, logger=logger) + + return logger + + +def cleanup_old_logs(directory: str, keep_days: int = 3, logger: logging.Logger = None) -> None: + """Clean up log files older than specified days + + Args: + directory: Directory containing log files + keep_days: Number of days to keep log files, default 3 + logger: Logger instance for logging cleanup results + """ + now = datetime.now() + pattern = re.compile(r'(\d{8})_\d{6}\.log') + + for filename in os.listdir(directory): + match = pattern.match(filename) + if match: + date_str = match.group(1) + try: + file_date = datetime.strptime(date_str, '%Y%m%d') + if now - file_date > timedelta(days=keep_days): + os.remove(path=os.path.join(directory, filename)) + if logger: + logger.info(f'Deleted old log: {filename}') + except Exception as e: + if logger: + logger.warning(f'Failed to delete log: {filename}; {e}') diff --git a/LoggerLibrary/result.jpg b/LoggerLibrary/result.jpg new file mode 100644 index 00000000..ed203be7 Binary files /dev/null and b/LoggerLibrary/result.jpg differ