loggingsetup 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ """
2
+ Initializes the application's logging system.
3
+
4
+ Features:
5
+ - Automatically creates a `logs/` directory if it doesn't exist.
6
+ - Logs messages from DEBUG up to WARNING into `logs/app-YYYY-MM-DD.log`.
7
+ - Logs all ERROR and CRITICAL messages into `logs/error-YYYY-MM-DD.log`.
8
+ - Creates a new log file every day (daily rotation by date, UTC).
9
+ - Colored console output:
10
+ - DEBUG — white
11
+ - INFO — cyan
12
+ - WARNING — yellow
13
+ - ERROR — red
14
+ - CRITICAL — bright red
15
+ - STDOUT receives messages up to WARNING, STDERR receives ERROR and above.
16
+ - Works without `logging.getLogger(__name__)`: records sent through the root
17
+ logger (`logging.info(...)`) get the module name (e.g. `app.bot`) instead of `root`.
18
+ - All uncaught exceptions (`sys.excepthook`) and uncaught exceptions in threads
19
+ (`threading.excepthook`) are logged as ERRORs.
20
+ - Noisy third-party loggers (urllib3, aiohttp, selenium, telethon, pyrogram,
21
+ asyncio, apscheduler) are limited to WARNING.
22
+ - Old log files older than 30 days are deleted in the background:
23
+ once at startup and then every day (`APScheduler`, background scheduler).
24
+ - Repeated calls of `init_logging()` are ignored.
25
+
26
+ Example of generated log files:
27
+ - logs/app-2025-06-20.log
28
+ - logs/error-2025-06-20.log
29
+
30
+ Usage:
31
+ - Call once in the main entry script:
32
+ from logging_setup import init_logging
33
+ init_logging()
34
+ - In all other modules just use:
35
+ import logging
36
+ logging.info("message")
37
+ (`logger = logging.getLogger(__name__)` still works, but is optional.)
38
+
39
+ Note:
40
+ - The module name is calculated from the file path relative to the current
41
+ working directory. If the project is started from another folder, pass the
42
+ path to `install_module_name_factory(...)`.
43
+ - The entry script is shown as `main`, not `__main__`.
44
+ - The system is modular and extensible via `filters.py`, `formats.py`, `cleanup_old_logs.py`.
45
+ """
46
+ from .my_setup import init_logging
47
+
48
+ __all__ = [
49
+ 'init_logging',
50
+ ]
@@ -0,0 +1,37 @@
1
+ import logging
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+ from pathlib import Path
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def cleanup_old_logs_by_filename(
10
+ logs_dir: str = "logs",
11
+ days: int = 30,
12
+ delete_old_logs: bool = True
13
+ ) -> None:
14
+ """
15
+ The function for deleting old logging files is automatic.
16
+
17
+ :param logs_dir: The directory where the logs are stored, from where they will be deleted.
18
+ :param days: Deleting logs that are older than the specified days.
19
+ :param delete_old_logs: A bullseye value, if True, it deletes the old logs.
20
+ """
21
+ if not delete_old_logs:
22
+ return
23
+
24
+ cutoff = datetime.now(timezone.utc).date() - timedelta(days=days)
25
+ deleted_files = 0
26
+
27
+ for file in Path(logs_dir).glob("*-????-??-??.log"):
28
+ try:
29
+ file_date = datetime.strptime(file.stem[-10:], "%Y-%m-%d").date()
30
+ if file_date < cutoff:
31
+ file.unlink()
32
+ deleted_files += 1
33
+ logger.info(f"[logging-setup] The log was deleted: {file.name}")
34
+ except Exception as e:
35
+ logger.warning(f"[logging-setup] File problem {file.name}: {e}")
36
+
37
+ logger.info(f"[logging-setup] Deleted {deleted_files} logs older than {days} days.")
@@ -0,0 +1,29 @@
1
+ import logging
2
+ import sys
3
+
4
+
5
+ def max_level_filter(max_level_name: str):
6
+ max_level = getattr(logging, max_level_name.upper(), logging.WARNING)
7
+
8
+ class MaxLevelFilter(logging.Filter):
9
+ def filter(self, record):
10
+ return record.levelno <= max_level
11
+
12
+ return MaxLevelFilter()
13
+
14
+
15
+ def handle_exception(exc_type, exc_value, exc_traceback):
16
+ if issubclass(exc_type, KeyboardInterrupt):
17
+ sys.__excepthook__(exc_type, exc_value, exc_traceback)
18
+ return
19
+
20
+ logging.error("[logging-setup] Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
21
+
22
+
23
+ def handle_thread_exception(args):
24
+ if issubclass(args.exc_type, SystemExit):
25
+ return
26
+ logging.error(
27
+ "[logging-setup] Uncaught exception in thread",
28
+ exc_info=(args.exc_type, args.exc_value, args.exc_traceback),
29
+ )
@@ -0,0 +1,61 @@
1
+ import logging
2
+ from functools import lru_cache
3
+ from logging.handlers import TimedRotatingFileHandler
4
+ from pathlib import Path
5
+
6
+ from colorama import Fore, Style
7
+
8
+
9
+ class ColorFormatter(logging.Formatter):
10
+ LEVEL_COLORS = {
11
+ logging.DEBUG: Fore.WHITE,
12
+ logging.INFO: Fore.CYAN,
13
+ logging.WARNING: Fore.YELLOW,
14
+ logging.ERROR: Fore.RED,
15
+ logging.CRITICAL: Fore.RED + Style.BRIGHT
16
+ }
17
+
18
+ def format(self, record):
19
+ color = self.LEVEL_COLORS.get(record.levelno, "")
20
+ message = super().format(record)
21
+ return f"{color}{message}{Style.RESET_ALL}"
22
+
23
+
24
+ class DatedTimedRotatingFileHandler(TimedRotatingFileHandler):
25
+ def __init__(self, *args, **kwargs):
26
+ super().__init__(*args, **kwargs)
27
+
28
+ def rotation_filename(self, default_name):
29
+ date_part = Path(default_name).suffix.lstrip(".") # 2025-06-20
30
+ base = Path(self.baseFilename)
31
+ return str(base.with_name(f"{base.stem}-{date_part}.log"))
32
+
33
+
34
+ def install_module_name_factory(project_root: str | Path | None = None) -> None:
35
+ """
36
+ Replaces the logger name 'root' with the module path (like app.bot),
37
+ so logging.info(...) works without logger = logging.getLogger(__name__).
38
+ """
39
+ root_dir = Path(project_root or Path.cwd()).resolve()
40
+
41
+ @lru_cache(maxsize=None)
42
+ def module_name(pathname: str) -> str:
43
+ try:
44
+ relative = Path(pathname).resolve().relative_to(root_dir)
45
+ return ".".join(relative.with_suffix("").parts)
46
+ except ValueError:
47
+ return Path(pathname).stem # file is outside the project
48
+
49
+ old_factory = logging.getLogRecordFactory()
50
+
51
+ def factory(*args, **kwargs):
52
+ record = old_factory(*args, **kwargs)
53
+ if record.name == "root":
54
+ record.name = module_name(record.pathname)
55
+ return record
56
+
57
+ logging.setLogRecordFactory(factory)
58
+
59
+
60
+ file_fmt = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s"
61
+ date_fmt = "%Y-%m-%d %H:%M:%S"
@@ -0,0 +1,121 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import threading
5
+ from datetime import datetime, timezone
6
+
7
+ from apscheduler.schedulers.background import BackgroundScheduler
8
+ from apscheduler.triggers.interval import IntervalTrigger
9
+ from colorama import init
10
+
11
+ from logging_setup.cleanup_old_logs import cleanup_old_logs_by_filename
12
+ from logging_setup.filters import max_level_filter, handle_exception, handle_thread_exception
13
+ from logging_setup.formats import ColorFormatter, file_fmt, date_fmt, DatedTimedRotatingFileHandler, \
14
+ install_module_name_factory
15
+
16
+ _initialized = False
17
+
18
+ NOISY_LOGGERS = (
19
+ "urllib3", "aiohttp", "selenium", "telethon",
20
+ "pyrogram", "asyncio", "apscheduler",
21
+ )
22
+
23
+
24
+ def init_logging(
25
+ directory: str = "logs",
26
+ enable_console: bool = True,
27
+ delete_old_logs: bool = True,
28
+ days_to_delete_old_logs: int = 30,
29
+ ):
30
+ """
31
+ Initialization of logging of the entire project and configuration.
32
+
33
+ :param directory: The directory where the logs (app/error) from the root of the project will be stored.
34
+ :param enable_console: Enable or Disable logging to the console.
35
+ :param delete_old_logs: Delete or Not Delete old logs.
36
+ :param days_to_delete_old_logs: Delete logs older than these days.
37
+ """
38
+ global _initialized
39
+ if _initialized:
40
+ return
41
+ _initialized = True
42
+
43
+ # Automatically reset the color
44
+ init(autoreset=True)
45
+
46
+ os.makedirs(directory, exist_ok=True)
47
+
48
+ color_formatter = ColorFormatter(fmt=file_fmt, datefmt=date_fmt)
49
+ file_formatter = logging.Formatter(fmt=file_fmt, datefmt=date_fmt)
50
+ handlers: list[logging.Handler] = []
51
+
52
+ # Console handlers
53
+ if enable_console:
54
+ stdout_handler = logging.StreamHandler(sys.stdout)
55
+ stdout_handler.setLevel(logging.DEBUG)
56
+ stdout_handler.setFormatter(color_formatter)
57
+ stdout_handler.addFilter(max_level_filter("WARNING"))
58
+
59
+ stderr_handler = logging.StreamHandler(sys.stderr)
60
+ stderr_handler.setLevel(logging.ERROR)
61
+ stderr_handler.setFormatter(color_formatter)
62
+
63
+ handlers.extend((stdout_handler, stderr_handler))
64
+
65
+ # File handlers with date rotation
66
+ # backupCount=0: old files are deleted only by cleanup_old_logs_by_filename
67
+ rotating_file = DatedTimedRotatingFileHandler(
68
+ filename=f"{directory}/app.log",
69
+ when="midnight",
70
+ interval=1,
71
+ backupCount=0,
72
+ encoding="utf-8",
73
+ utc=True,
74
+ )
75
+ rotating_file.setLevel(logging.DEBUG)
76
+ rotating_file.setFormatter(file_formatter)
77
+ rotating_file.addFilter(max_level_filter("WARNING"))
78
+
79
+ rotating_error = DatedTimedRotatingFileHandler(
80
+ filename=f"{directory}/error.log",
81
+ when="midnight",
82
+ interval=1,
83
+ backupCount=0,
84
+ encoding="utf-8",
85
+ utc=True,
86
+ )
87
+ rotating_error.setLevel(logging.ERROR)
88
+ rotating_error.setFormatter(file_formatter)
89
+
90
+ handlers.extend((rotating_file, rotating_error))
91
+
92
+ # Replace logger name "root" with the module name
93
+ install_module_name_factory()
94
+
95
+ # Root logger (force=True replaces handlers that were set before)
96
+ logging.basicConfig(level=logging.DEBUG, handlers=handlers, force=True)
97
+
98
+ # Make noisy libraries quiet
99
+ for name in NOISY_LOGGERS:
100
+ logging.getLogger(name).setLevel(logging.WARNING)
101
+
102
+ # Global error interception
103
+ sys.excepthook = handle_exception
104
+ threading.excepthook = handle_thread_exception
105
+
106
+ # Background cleanup of old logs
107
+ scheduler = BackgroundScheduler()
108
+ scheduler.start()
109
+
110
+ scheduler.add_job(
111
+ cleanup_old_logs_by_filename,
112
+ IntervalTrigger(days=1),
113
+ id="cleanup_old_logs",
114
+ replace_existing=True,
115
+ next_run_time=datetime.now(timezone.utc),
116
+ kwargs={
117
+ "delete_old_logs": delete_old_logs,
118
+ "days": days_to_delete_old_logs,
119
+ "logs_dir": directory,
120
+ },
121
+ )
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: loggingsetup
3
+ Version: 0.0.1
4
+ Summary: This is an improved and customized version of logging for a quick start.
5
+ Home-page: https://github.com/XEQU4/logging-setup
6
+ Author: xequ4
7
+ Author-email: b.m.04.11reg@gmail.com
8
+ Project-URL: GitHub, https://github.com/XEQU4/logging-setup
9
+ Keywords: logging colorama files logs shceduler archive
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: apscheduler>=3.11.3
16
+ Requires-Dist: colorama>=0.4.6
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: keywords
24
+ Dynamic: project-url
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # logging-setup
30
+
31
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
32
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
33
+ [![Code style](https://img.shields.io/badge/code%20style-PEP8-lightgrey.svg)](https://peps.python.org/pep-0008/)
34
+ [![Built on](https://img.shields.io/badge/built%20on-logging-yellow.svg)](https://docs.python.org/3/library/logging.html)
35
+
36
+ A ready-made configuration for Python's standard `logging` module.
37
+ Call `init_logging()` once, and the whole project gets colored console output, daily log files, and automatic cleanup.
38
+
39
+ ## Features
40
+
41
+ - Colored console output (DEBUG white, INFO cyan, WARNING yellow, ERROR red, CRITICAL bright red)
42
+ - `STDOUT` gets messages up to WARNING, `STDERR` gets ERROR and above
43
+ - Daily log files (UTC):
44
+ - `logs/app-YYYY-MM-DD.log` for DEBUG to WARNING
45
+ - `logs/error-YYYY-MM-DD.log` for ERROR and CRITICAL
46
+ - Old logs are deleted automatically (default: older than 30 days), once at startup and then every day
47
+ - No need for `logging.getLogger(__name__)`: `logging.info(...)` shows the real module name (for example `app.bot`) instead of `root`
48
+ - Uncaught exceptions in the main thread and in other threads are logged as errors
49
+ - Noisy libraries (`urllib3`, `aiohttp`, `selenium`, `telethon`, `pyrogram`, `asyncio`, `apscheduler`) are limited to WARNING
50
+ - Calling `init_logging()` twice is safe: the second call is ignored
51
+
52
+ ## Requirements
53
+
54
+ - Python 3.10+
55
+ - [colorama](https://pypi.org/project/colorama/)
56
+ - [APScheduler](https://pypi.org/project/APScheduler/)
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ pip install git+https://github.com/XEQU4/logging-setup.git
62
+ ```
63
+
64
+ Or copy the `logging_setup/` folder into your project.
65
+
66
+ ## Quick start
67
+
68
+ In your main script, call it once:
69
+
70
+ ```python
71
+ import logging
72
+ from logging_setup import init_logging
73
+
74
+ init_logging()
75
+
76
+ logging.info("Hello World!")
77
+ ```
78
+
79
+ In every other module, just use the standard `logging`:
80
+
81
+ ```python
82
+ import logging
83
+
84
+ def run():
85
+ logging.info("Bot started")
86
+ logging.error("Something went wrong")
87
+ ```
88
+
89
+ Example output:
90
+
91
+ ```
92
+ 2026-10-20 09:42:16 | INFO | app.bot | Bot started
93
+ 2026-10-20 09:42:16 | ERROR | app.bot | Something went wrong
94
+ ```
95
+
96
+ > `logger = logging.getLogger(__name__)` still works if you prefer it.
97
+
98
+ ## Parameters
99
+
100
+ | Parameter | Type | Default | Description |
101
+ |---------------------------|--------|----------|------------------------------------------|
102
+ | `directory` | `str` | `"logs"` | Folder for log files |
103
+ | `enable_console` | `bool` | `True` | Turn console output on or off |
104
+ | `delete_old_logs` | `bool` | `True` | Turn automatic cleanup on or off |
105
+ | `days_to_delete_old_logs` | `int` | `30` | Delete logs older than this many days |
106
+
107
+ ```python
108
+ init_logging(directory="my_logs", enable_console=False, days_to_delete_old_logs=14)
109
+ ```
110
+
111
+ ## Project structure
112
+
113
+ ```
114
+ logging_setup/
115
+ в”њв”Ђв”Ђ __init__.py # exports init_logging
116
+ в”њв”Ђв”Ђ my_setup.py # main setup function
117
+ в”њв”Ђв”Ђ formats.py # color formatter, rotating handler, module name factory
118
+ в”њв”Ђв”Ђ filters.py # level filters, exception hooks
119
+ в””в”Ђв”Ђ cleanup_old_logs.py # background cleanup of old log files
120
+ ```
121
+
122
+ ## Notes
123
+
124
+ - The module name is calculated from the file path relative to the current working directory. If you start the project from another folder, pass the project path to `install_module_name_factory(...)`.
125
+ - The entry script is shown as `main`, not `__main__`.
126
+ - Old files are deleted only by the cleanup job, not by `backupCount`.
@@ -0,0 +1,9 @@
1
+ logging_setup/__init__.py,sha256=snr6txw34CTLq1wrHGFMBm9Ja252mFxwtW9phg4HcjM,1950
2
+ logging_setup/cleanup_old_logs.py,sha256=9CJEu7SRX7BfD7_YDaSNNp0kvdvQlcD7Owy22pf5l8Y,1259
3
+ logging_setup/filters.py,sha256=U3mQJAdVd5vttoXwAvdn4tPIxRwXly2GzuXDJadg_vA,837
4
+ logging_setup/formats.py,sha256=vPWgA_vqMWjL8Gw8w33Nq8aupCDDWJxeBD2yTt2rOYc,1979
5
+ logging_setup/my_setup.py,sha256=Yk_71jLC1CiV9oxE3THHX8Zy_V5eGdXA-7gEUFKN9AE,3891
6
+ loggingsetup-0.0.1.dist-info/METADATA,sha256=LPS4uHyJfisiEZVKpC9-ea8LPsXAYOE5FE5eHyj7mVQ,4719
7
+ loggingsetup-0.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
8
+ loggingsetup-0.0.1.dist-info/top_level.txt,sha256=Eh3V7lEybRvsh8hA5WYub0Yrfllm6qZLK50nUfRTUl0,14
9
+ loggingsetup-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ logging_setup