Skip to content

Commit 1d5617f

Browse files
feat: Add file_mode and file_encoding parameters to get_logger for finer control over the way a simple logger handles files.
1 parent 8d27931 commit 1d5617f

4 files changed

Lines changed: 49 additions & 133 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ Help this project by [Donation](DONATE.md)
66
Changes
77
-------
88

9+
### v3.2.0
10+
11+
Add `file_mode` and `file_encoding` parameters to `get_logger` for finer control over
12+
the way a simple logger handles files.
13+
14+
For even more control you can still define Logger, Handlers, and Formatters manually.
15+
16+
#### Example
17+
18+
```python
19+
import log21
20+
21+
logger = log21.get_logger(
22+
"My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
23+
file_encoding="utf-8"
24+
)
25+
26+
logger.info("Hello World!")
27+
```
28+
929
### v3.1.0
1030

1131
Change the way `argumentify` handles function parameters to argument-parser arguments

README.md

Lines changed: 11 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -69,140 +69,26 @@ pip install git+https://github.com/MPCodeWriter21/log21
6969
Changelog
7070
---------
7171

72-
### v3.1.0
72+
### v3.2.0
7373

74-
Change the way `argumentify` handles function parameters to argument-parser arguments
75-
conversion.
74+
Add `file_mode` and `file_encoding` parameters to `get_logger` for finer control over
75+
the way a simple logger handles files.
7676

77-
+ `POSITIONAL_ONLY` and `VAR_POSITIONAL` parameters will be positional arguments.
78-
+ `POSITIONAL_OR_KEYWORD` and `KEYWORD_ONLY` parameters have flags assigned to them.
79-
+ `POSITIONAL_OR_KEYWORD` parameters will be required if at least one `KEYWORD_ONLY`
80-
parameter is there, otherwise they are optional.
81-
+ `VAR_KEYWORD` parameters are still not supported.
77+
For even more control you can still define Logger, Handlers, and Formatters manually.
8278

83-
#### Example 1
79+
#### Example
8480

8581
```python
86-
def main(path: Path, /, output: Path, *, verbose: bool = False):
87-
"""Process a file.
82+
import log21
8883

89-
:param path: The input file path
90-
:param output: The output file
91-
:param verbose: Write more logs to the standard output.
92-
"""
93-
...
94-
95-
96-
if __name__ == "__main__":
97-
argumentify(main)
98-
```
99-
100-
The help looks like this:
101-
102-
```help
103-
usage: test.py [-h] --output OUTPUT [--verbose] path
104-
105-
Process a file.
106-
107-
positional arguments:
108-
path The input file path
109-
110-
options:
111-
-h, --help
112-
show this help message and exit
113-
--output OUTPUT, -o OUTPUT
114-
The output file
115-
--verbose, -v
116-
Write more logs to the standard output.
117-
118-
```
119-
120-
_Note that `path` and `output` are required._
121-
122-
#### Example 2
123-
124-
```python
125-
def main(output: Path, /, *inputs: Path):
126-
"""Process multiple files into one.
127-
128-
:param output: The output file
129-
:param inputs: The path to the input files
130-
"""
131-
# Since `inputs` is a VAR_POSITIONAL, while being a positional argument, it can have
132-
# zero length which is in many cases not intended.
133-
# You might want to add a check for its length and raise an ArgumentError if it does
134-
# not match your needs
135-
136-
# Check if at least one input has been passed and mark the argument as required
137-
# if len(inputs) < 1:
138-
# raise RequiredArgumentError("inputs")
139-
140-
# Raise an error unless at least two inputs are present
141-
if len(inputs) < 2:
142-
raise ArgumentError(message="You need to pass at least two files as input.")
143-
...
144-
```
145-
146-
The help looks like this:
147-
148-
```help
149-
usage: test.py [-h] output [inputs ...]
150-
151-
Process multiple files into one.
152-
153-
positional arguments:
154-
output The output file
155-
inputs The path to the input files
156-
157-
options:
158-
-h, --help
159-
show this help message and exit
84+
logger = log21.get_logger(
85+
"My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
86+
file_encoding="utf-8"
87+
)
16088

89+
logger.info("Hello World!")
16190
```
16291

163-
#### Example 3
164-
165-
```python
166-
def main(first_name: str, last_name: str, output: Path, verbose: bool = False):
167-
"""Write a greeting message.
168-
169-
:param first_name: The first name of the user to greet (optional)
170-
:param last_name: The last name of the user to greet (optional)
171-
:param output: The output file (stdout if none is provided)
172-
:param verbose: If provided, will write the debug logs to stdout
173-
"""
174-
...
175-
176-
177-
if __name__ == "__main__":
178-
argumentify(main)
179-
```
180-
181-
The help looks like this:
182-
183-
```help
184-
usage: test.py [-h] [--first-name FIRST_NAME] [--last-name LAST_NAME] [--output OUTPUT]
185-
[--verbose]
186-
187-
Write a greeting message.
188-
189-
options:
190-
-h, --help
191-
show this help message and exit
192-
--first-name FIRST_NAME, -f FIRST_NAME
193-
The first name of the user to greet (optional)
194-
--last-name LAST_NAME, -l LAST_NAME
195-
The last name of the user to greet (optional)
196-
--output OUTPUT, -o OUTPUT
197-
The output file (stdout if none is provided)
198-
--verbose, -v
199-
If provided, will write the debug logs to stdout
200-
201-
```
202-
203-
_Note that all the options are optional and default to None. `verbose` is False by
204-
default since a default value is provided for it in function definition._
205-
20692
[Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
20793

20894
Usage Examples

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies = [
2323
"webcolors",
2424
"docstring-parser"
2525
]
26-
version = "3.1.0"
26+
version = "3.2.0"
2727

2828
[build-system]
2929
requires = ["uv_build>=0.8.15,<0.9.0"]

src/log21/__init__.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
# yapf: enable
3232

3333
__author__ = 'CodeWriter21 (Mehrad Pooryoussof)'
34-
__version__ = '3.1.0'
34+
__version__ = '3.2.0'
3535
__github__ = 'https://GitHub.com/MPCodeWriter21/log21'
3636
__all__ = [
3737
'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
@@ -60,7 +60,8 @@ def _prepare_formatter(
6060
colorize_time_and_level: bool = True,
6161
level_names: _Optional[_Mapping[int, str]] = None,
6262
level_colors: _Optional[_Mapping[int, tuple[str, ...]]] = None,
63-
formatter_class: _Type[_logging.Formatter] = ColorizingFormatter
63+
formatter_class: _Type[_logging.Formatter] = ColorizingFormatter,
64+
prefix_carriage_return: bool = True,
6465
) -> _logging.Formatter:
6566
# Prepares a formatting if the fmt was None
6667
if not fmt:
@@ -70,7 +71,8 @@ def _prepare_formatter(
7071
fmt = '[%(levelname)s] ' + fmt
7172
if show_time:
7273
fmt = '[%(asctime)s] ' + fmt
73-
fmt = '\r' + fmt
74+
if prefix_carriage_return:
75+
fmt = '\r' + fmt
7476

7577
if level_colors and not issubclass(formatter_class, ColorizingFormatter):
7678
warning(
@@ -116,7 +118,10 @@ def get_logger(
116118
override: bool = False,
117119
level_names: _Optional[_Mapping[int, str]] = None,
118120
level_colors: _Optional[_Mapping[int, tuple[str, ...]]] = None,
119-
file: _Optional[_Union[_os.PathLike, str]] = None
121+
# TODO: Rename file to file_path in one future update
122+
file: _Optional[_Union[_os.PathLike, str]] = None,
123+
file_mode: _Optional[str] = None,
124+
file_encoding: _Optional[str] = None,
120125
) -> Logger:
121126
"""Returns a logging.Logger with colorizing support.
122127
@@ -182,7 +187,9 @@ def get_logger(
182187
:param level_names: Mapping[int, str] = None: You can specify custom level names.
183188
:param level_colors: Mapping[int, Tuple[str, ...]] = None: You can specify custom
184189
level colors.
185-
:param file: Union[os.PathLike, str] = None: The file to log to
190+
:param file: Union[os.PathLike, str] = None: The file path to log to
191+
:param file_mode: str = None: The mode to open file at (Defaults to 'a')
192+
:param file_encoding: str = None: The file encoding
186193
:return: log21.Logger
187194
"""
188195
if not isinstance(name, str):
@@ -209,7 +216,9 @@ def get_logger(
209216
_manager.addLogger(name, logger)
210217

211218
if file:
212-
file_handler = FileHandler(file)
219+
file_handler = DecolorizingFileHandler(
220+
file, mode=file_mode or 'a', encoding=file_encoding
221+
)
213222
file_formatter = _prepare_formatter(
214223
fmt,
215224
style,
@@ -218,7 +227,8 @@ def get_logger(
218227
show_time,
219228
False,
220229
level_names,
221-
formatter_class=DecolorizingFormatter
230+
formatter_class=DecolorizingFormatter,
231+
prefix_carriage_return=False,
222232
)
223233
file_handler.setFormatter(file_formatter)
224234
logger.addHandler(file_handler)

0 commit comments

Comments
 (0)