loggez 0.5.0__tar.gz

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.
loggez-0.5.0/PKG-INFO ADDED
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: loggez
3
+ Version: 0.5.0
4
+ Summary: Python Easy Logging (LOGG EZ)
5
+ Home-page: https://gitlab.com/meehai/loggez
6
+ License: WTFPL
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: colorama>=0.4.6
10
+ Dynamic: description
11
+ Dynamic: description-content-type
12
+ Dynamic: home-page
13
+ Dynamic: license
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
17
+
18
+ # loggez: Python EZ logging
19
+
20
+ Control logging levels with env vars and have a unique logger per name, for library purposes...
21
+
22
+ Only writes to stderr, no files or stuff. Use something else for file logging though you can hack this library too.
23
+
24
+ ## Installation
25
+
26
+ ```
27
+ pip install loggez
28
+ ```
29
+
30
+ ## Usage:
31
+ ```python
32
+ # run.py
33
+ from loggez import make_logger, loggez_logger
34
+ from loguru import logger
35
+
36
+ logger.info("loguru hi")
37
+ my_logger = make_logger("my_logger")
38
+ my_logger.add_file_handler("/path/to/logs.txt") # optional
39
+ my_logger.info("my_logger hi")
40
+ my_logger.debug("my_logger hi")
41
+ my_logger.debug2("my_logger hi")
42
+ my_logger.debug4("my_logger hi")
43
+
44
+ my_logger2 = make_logger("my_logger2")
45
+ my_logger2.info("my_logger2 hi")
46
+ my_logger2.debug("my_logger2 hi")
47
+ my_logger2.debug2("my_logger2 hi")
48
+ my_logger2.debug4("my_logger hi")
49
+
50
+ loggez_logger.info("loggez_logger hi")
51
+ loggez_logger.debug("loggez_logger hi")
52
+ loggez_logger.debug2("loggez_logger hi")
53
+ loggez_logger.debug4("loggez_logger hi")
54
+
55
+ ```
56
+
57
+ Run with:
58
+ ```
59
+ my_logger_LOGLEVEL=0 run.py
60
+ my_logger_LOGLEVEL=1 run.py
61
+ my_logger_LOGLEVEL=2 run.py
62
+ LOGGEZ_LOGLEVEL=4 run.py
63
+ ```
64
+
65
+ Additional env vars:
66
+ - `my_logger_MESSAGE=...`: see the default in `loggez/loggez.py` to control colors and stuff.
67
+ - `my_logger_INFO_MESSAGE=...`, `my_logger_DEBUG_MESSAGE=...` etc.
68
+
69
+ Note: You can use also use the global predefined logger: `from loggez import loggez_logger as logger; logger.info(...)`.
70
+ Env variables are: `LOGGEZ_LOGLEVEL=...`, `LOGGEZ_INFO_MESSAGE=....` etc.
71
+
72
+ That's all.
loggez-0.5.0/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # loggez: Python EZ logging
2
+
3
+ Control logging levels with env vars and have a unique logger per name, for library purposes...
4
+
5
+ Only writes to stderr, no files or stuff. Use something else for file logging though you can hack this library too.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ pip install loggez
11
+ ```
12
+
13
+ ## Usage:
14
+ ```python
15
+ # run.py
16
+ from loggez import make_logger, loggez_logger
17
+ from loguru import logger
18
+
19
+ logger.info("loguru hi")
20
+ my_logger = make_logger("my_logger")
21
+ my_logger.add_file_handler("/path/to/logs.txt") # optional
22
+ my_logger.info("my_logger hi")
23
+ my_logger.debug("my_logger hi")
24
+ my_logger.debug2("my_logger hi")
25
+ my_logger.debug4("my_logger hi")
26
+
27
+ my_logger2 = make_logger("my_logger2")
28
+ my_logger2.info("my_logger2 hi")
29
+ my_logger2.debug("my_logger2 hi")
30
+ my_logger2.debug2("my_logger2 hi")
31
+ my_logger2.debug4("my_logger hi")
32
+
33
+ loggez_logger.info("loggez_logger hi")
34
+ loggez_logger.debug("loggez_logger hi")
35
+ loggez_logger.debug2("loggez_logger hi")
36
+ loggez_logger.debug4("loggez_logger hi")
37
+
38
+ ```
39
+
40
+ Run with:
41
+ ```
42
+ my_logger_LOGLEVEL=0 run.py
43
+ my_logger_LOGLEVEL=1 run.py
44
+ my_logger_LOGLEVEL=2 run.py
45
+ LOGGEZ_LOGLEVEL=4 run.py
46
+ ```
47
+
48
+ Additional env vars:
49
+ - `my_logger_MESSAGE=...`: see the default in `loggez/loggez.py` to control colors and stuff.
50
+ - `my_logger_INFO_MESSAGE=...`, `my_logger_DEBUG_MESSAGE=...` etc.
51
+
52
+ Note: You can use also use the global predefined logger: `from loggez import loggez_logger as logger; logger.info(...)`.
53
+ Env variables are: `LOGGEZ_LOGLEVEL=...`, `LOGGEZ_INFO_MESSAGE=....` etc.
54
+
55
+ That's all.
@@ -0,0 +1,3 @@
1
+ """init file"""
2
+ from .loggez import make_logger, loggez_logger
3
+
@@ -0,0 +1,181 @@
1
+ """
2
+ Python logger settings.
3
+ Uses ENV variables to control the log level:
4
+ from simple_logging import make_logger
5
+ my_logger = make_logger("MY_KEY")
6
+ my_logger.debug4("message")
7
+ run with:
8
+ MY_KEY=4 python blabla.py
9
+
10
+ """
11
+ from __future__ import annotations
12
+ import os
13
+ import sys
14
+ import logging
15
+ from pathlib import Path
16
+ from logging.handlers import RotatingFileHandler
17
+ from colorama import Fore, Back, Style
18
+
19
+ D2 = logging.DEBUG2 = logging.DEBUG - 1
20
+ D3 = logging.DEBUG3 = logging.DEBUG - 2
21
+ D4 = logging.DEBUG4 = logging.DEBUG - 3
22
+ logging.addLevelName(D2, "DGB2")
23
+ logging.addLevelName(D3, "DGB3")
24
+ logging.addLevelName(D4, "DGB4")
25
+
26
+ STR_LEVELS = ["DEBUG", "DEBUG2", "DEBUG3", "DEBUG4", "INFO", "WARNING", "ERROR", "CRITICAL"]
27
+ _PRE_LOGGEZ = "%(asctime)s %(levelname)s"
28
+ _PRE = "%(asctime)s %(name)s-%(levelname)s"
29
+
30
+ def _colorize(msg: str) -> str:
31
+ _colors = {
32
+ "cyan": Fore.CYAN,
33
+ "magenta": Fore.MAGENTA,
34
+ "red": Fore.RED,
35
+ "green": Fore.GREEN,
36
+ "yellow": Fore.YELLOW,
37
+ "back_red": Back.RED,
38
+ "back_cyan": Back.CYAN,
39
+ }
40
+ def _get_next(msg: str, replacements: dict[str, str]) -> str:
41
+ for replacement in replacements.keys():
42
+ if msg[0: len(replacement)] == replacement:
43
+ return replacement
44
+ raise RuntimeError(f"Found no next color in {msg} out of {list(replacements)}")
45
+
46
+ active_color = None
47
+ new_message = []
48
+ i = 0
49
+ while i < len(msg):
50
+ if msg[i] == "<":
51
+ assert active_color is None or msg[i + 1] == "/", f"Use </color> before starting a new color: {msg}"
52
+ _color = _get_next(msg[i + 2:], _colors) if active_color else _get_next(msg[i + 1:], _colors)
53
+ assert active_color is None or _color == active_color, f"Active color: {active_color}. Got: {_color}"
54
+ skip = len(_color) + 1 + (active_color is not None)
55
+ assert msg[i + skip] == ">", f"Expected <color>(ch {i}), got: {msg}"
56
+ new_message.append((_colors[_color] if active_color is None else Style.RESET_ALL))
57
+ active_color = None if active_color is not None else _color
58
+ i += skip + 1
59
+ else:
60
+ new_message.append(msg[i])
61
+ i += 1
62
+ return "".join(new_message)
63
+
64
+ def _get_default_formats(pre: str):
65
+ return {
66
+ "DEBUG": _colorize(f"<cyan>[{pre}]</cyan> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
67
+ "DEBUG2": _colorize(f"<back_cyan>[{pre}]</back_cyan> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
68
+ "DEBUG3": _colorize(f"<magenta>[{pre}]</magenta> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
69
+ "DEBUG4": _colorize(f"<back_red>[{pre}]</back_red> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
70
+ "INFO": _colorize(f"<green>[{pre}]</green> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
71
+ "WARNING": _colorize(f"<yellow>[{pre}]</yellow> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
72
+ "ERROR": _colorize(f"<red>[{pre}]</red> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
73
+ "CRITICAL": _colorize(f"<back_red>[{pre}]</back_red> %(message)s <yellow>(%(filename)s:%(funcName)s:%(lineno)d)</yellow>"),
74
+ }
75
+
76
+ class LoggezLogger(logging.Logger):
77
+ """small interface-like class on top of the default logger for the extra methods"""
78
+ def add_file_handler(self, path: str):
79
+ """adds file handler"""
80
+ def remove_file_handler(self):
81
+ """Removes file handler"""
82
+ def get_file_handler(self) -> logging.FileHandler:
83
+ """Gets the file handler. Must be called after add_file_handler"""
84
+
85
+ class _FileHandler(logging.FileHandler):
86
+ """same as filehandler but create the file on emit so we don't end up with empty files"""
87
+ def emit(self, record):
88
+ Path(self.baseFilename).parent.mkdir(exist_ok=True, parents=True)
89
+ return super().emit(record)
90
+
91
+ def _add_file_handler(_logger: logging.Logger, path: str):
92
+ if any(isinstance(handler, logging.FileHandler) for handler in _logger.handlers):
93
+ _logger.debug2("File handler exists already. Removing and replacing.")
94
+ _remove_file_handler(_logger)
95
+ _logger.debug2(f"Adding file handler to this logger ({_logger.name}) to '{path}'")
96
+ _logger.addHandler(logging.FileHandler(path))
97
+
98
+ def _remove_file_handler(_logger: logging.Logger):
99
+ fh = [handler for handler in _logger.handlers if isinstance(handler, logging.FileHandler)]
100
+ assert len(fh) == 1, _logger.handlers
101
+ _logger.debug2(f"Removing FileHandler: {fh[0]}")
102
+ _logger.removeHandler(fh[0])
103
+
104
+ def _get_file_handler(_logger: logging.LoggerAdapter) -> logging.FileHandler:
105
+ fh = [handler for handler in _logger.handlers if isinstance(handler, logging.FileHandler)]
106
+ assert len(fh) == 1, _logger.handlers
107
+ return fh[0]
108
+
109
+ class CustomFormatter(logging.Formatter):
110
+ """Custom formatting for logger."""
111
+ def __init__(self, formats, *args, **kwargs):
112
+ self.formats = formats
113
+ super().__init__(*args, **kwargs)
114
+
115
+ def format(self, record):
116
+ log_fmt = self.formats[record.levelno]
117
+ formatter = logging.Formatter(log_fmt)
118
+ formatter.formatTime = self.formatTime
119
+ return formatter.format(record)
120
+
121
+ # here we define the time format.
122
+ def formatTime(self, record, datefmt=None):
123
+ return super().formatTime(record, "%Y-%m-%dT%H:%M:%S")
124
+
125
+ def make_logger(key: str, exists_ok: bool=False, log_file: Path | str | None=None) -> LoggezLogger:
126
+ ENV_KEY = f"{key}_LOGLEVEL"
127
+ # defaults to -1 (no logger!).
128
+ env_var = int(os.environ[ENV_KEY]) if ENV_KEY in os.environ else 0
129
+
130
+ # we need numbers below 5 (last logging module used number)
131
+ try:
132
+ log_levels = {
133
+ -1: logging.NOTSET,
134
+ 0: logging.INFO,
135
+ 1: logging.DEBUG,
136
+ 2: logging.DEBUG2,
137
+ 3: logging.DEBUG3,
138
+ 4: logging.DEBUG4,
139
+ }
140
+ loglvl = log_levels[env_var]
141
+ except KeyError:
142
+ sys.stderr.write(f"You tried to use {key}_LOGLEVEL={env_var}. You need to set it between -1 and 4\n")
143
+ sys.exit(1)
144
+ # add the custom ones in the logger
145
+
146
+ if key in (X := logging.Logger.manager.loggerDict):
147
+ if not exists_ok:
148
+ raise ValueError(f"'{key}' exists in {list(X.keys())} already.\n")
149
+ else:
150
+ del logging.Logger.manager.loggerDict[key]
151
+
152
+
153
+ # instantiate logger and set log level
154
+ new_logger: logging.Logger = logging.getLogger(key)
155
+ new_logger.setLevel(loglvl)
156
+ new_logger.debug2 = lambda msg, *args: (new_logger._log(D2, msg, args=args) if loglvl > 0 and loglvl <= D2 else "")
157
+ new_logger.debug3 = lambda msg, *args: (new_logger._log(D3, msg, args=args) if loglvl > 0 and loglvl <= D3 else "")
158
+ new_logger.debug4 = lambda msg, *args: (new_logger._log(D4, msg, args=args) if loglvl > 0 and loglvl <= D4 else "")
159
+ # add custom formatter to logger
160
+ handler = logging.StreamHandler()
161
+
162
+ # Example [TIME:LEVEL:NAME] Message [FILE:FUNC:LINE]. We can update some other format here easily
163
+ default_formats = _get_default_formats(pre := (_PRE if key != "LOGGEZ" else _PRE_LOGGEZ))
164
+ formats = {getattr(logging, k): os.getenv(f"{key}_{k}_MESSAGE", default_formats[k]) for k in STR_LEVELS}
165
+ handler.setFormatter(CustomFormatter(formats))
166
+ new_logger.addHandler(handler)
167
+ new_logger.add_file_handler = lambda path: _add_file_handler(new_logger, path)
168
+ new_logger.remove_file_handler = lambda: _remove_file_handler(new_logger)
169
+ new_logger.get_file_handler = lambda: _get_file_handler(new_logger)
170
+
171
+ if log_file is not None:
172
+ file_handler = _FileHandler(log_file, mode="a", delay=True) # delay=True doesn't open the file yet.
173
+ file_handler.setLevel(logging.INFO) # log everything to file if provided.
174
+ file_default_format = (f"[{pre}] %(message)s (%(filename)s:%(funcName)s:%(lineno)d)")
175
+ file_formats = {getattr(logging, k): file_default_format for k in STR_LEVELS}
176
+ file_handler.setFormatter(CustomFormatter(file_formats))
177
+ new_logger.addHandler(file_handler)
178
+
179
+ return new_logger
180
+
181
+ loggez_logger = make_logger("LOGGEZ")
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: loggez
3
+ Version: 0.5.0
4
+ Summary: Python Easy Logging (LOGG EZ)
5
+ Home-page: https://gitlab.com/meehai/loggez
6
+ License: WTFPL
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: colorama>=0.4.6
10
+ Dynamic: description
11
+ Dynamic: description-content-type
12
+ Dynamic: home-page
13
+ Dynamic: license
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
17
+
18
+ # loggez: Python EZ logging
19
+
20
+ Control logging levels with env vars and have a unique logger per name, for library purposes...
21
+
22
+ Only writes to stderr, no files or stuff. Use something else for file logging though you can hack this library too.
23
+
24
+ ## Installation
25
+
26
+ ```
27
+ pip install loggez
28
+ ```
29
+
30
+ ## Usage:
31
+ ```python
32
+ # run.py
33
+ from loggez import make_logger, loggez_logger
34
+ from loguru import logger
35
+
36
+ logger.info("loguru hi")
37
+ my_logger = make_logger("my_logger")
38
+ my_logger.add_file_handler("/path/to/logs.txt") # optional
39
+ my_logger.info("my_logger hi")
40
+ my_logger.debug("my_logger hi")
41
+ my_logger.debug2("my_logger hi")
42
+ my_logger.debug4("my_logger hi")
43
+
44
+ my_logger2 = make_logger("my_logger2")
45
+ my_logger2.info("my_logger2 hi")
46
+ my_logger2.debug("my_logger2 hi")
47
+ my_logger2.debug2("my_logger2 hi")
48
+ my_logger2.debug4("my_logger hi")
49
+
50
+ loggez_logger.info("loggez_logger hi")
51
+ loggez_logger.debug("loggez_logger hi")
52
+ loggez_logger.debug2("loggez_logger hi")
53
+ loggez_logger.debug4("loggez_logger hi")
54
+
55
+ ```
56
+
57
+ Run with:
58
+ ```
59
+ my_logger_LOGLEVEL=0 run.py
60
+ my_logger_LOGLEVEL=1 run.py
61
+ my_logger_LOGLEVEL=2 run.py
62
+ LOGGEZ_LOGLEVEL=4 run.py
63
+ ```
64
+
65
+ Additional env vars:
66
+ - `my_logger_MESSAGE=...`: see the default in `loggez/loggez.py` to control colors and stuff.
67
+ - `my_logger_INFO_MESSAGE=...`, `my_logger_DEBUG_MESSAGE=...` etc.
68
+
69
+ Note: You can use also use the global predefined logger: `from loggez import loggez_logger as logger; logger.info(...)`.
70
+ Env variables are: `LOGGEZ_LOGLEVEL=...`, `LOGGEZ_INFO_MESSAGE=....` etc.
71
+
72
+ That's all.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ setup.py
3
+ loggez/__init__.py
4
+ loggez/loggez.py
5
+ loggez.egg-info/PKG-INFO
6
+ loggez.egg-info/SOURCES.txt
7
+ loggez.egg-info/dependency_links.txt
8
+ loggez.egg-info/requires.txt
9
+ loggez.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ colorama>=0.4.6
@@ -0,0 +1 @@
1
+ loggez
loggez-0.5.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
loggez-0.5.0/setup.py ADDED
@@ -0,0 +1,27 @@
1
+ from setuptools import setup, find_packages
2
+ from os import path
3
+
4
+ name = "loggez"
5
+ version = "0.5.0"
6
+ description = "Python Easy Logging (LOGG EZ)"
7
+ url = "https://gitlab.com/meehai/loggez"
8
+
9
+ loc = path.abspath(path.dirname(__file__))
10
+ with open(f"{loc}/README.md", "r", encoding="utf-8") as fh:
11
+ long_description = fh.read()
12
+
13
+ required = ["colorama>=0.4.6"]
14
+
15
+ setup(
16
+ name=name,
17
+ version=version,
18
+ description=description,
19
+ long_description=long_description,
20
+ long_description_content_type="text/markdown",
21
+ url=url,
22
+ packages=find_packages(),
23
+ install_requires=required,
24
+ dependency_links=[],
25
+ license="WTFPL",
26
+ python_requires=">=3.8"
27
+ )