atomicshop 2.14.7__py3-none-any.whl → 2.14.9__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.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +79 -0
- atomicshop/wrappers/loggingw/handlers.py +123 -133
- atomicshop/wrappers/loggingw/loggingw.py +23 -1
- {atomicshop-2.14.7.dist-info → atomicshop-2.14.9.dist-info}/METADATA +1 -1
- {atomicshop-2.14.7.dist-info → atomicshop-2.14.9.dist-info}/RECORD +9 -8
- {atomicshop-2.14.7.dist-info → atomicshop-2.14.9.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.14.7.dist-info → atomicshop-2.14.9.dist-info}/WHEEL +0 -0
- {atomicshop-2.14.7.dist-info → atomicshop-2.14.9.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HeaderFilter(logging.Filter):
|
|
6
|
+
"""
|
|
7
|
+
A logging.Filter that writes a header to a log file if the file is empty (
|
|
8
|
+
i.e., no log records have been written, i.e.2, on file rotation).
|
|
9
|
+
"""
|
|
10
|
+
def __init__(self, header, baseFilename):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self.header = header
|
|
13
|
+
self.baseFilename = baseFilename
|
|
14
|
+
self._write_header_if_needed()
|
|
15
|
+
|
|
16
|
+
def _write_header_if_needed(self):
|
|
17
|
+
if not os.path.exists(self.baseFilename) or os.path.getsize(self.baseFilename) == 0:
|
|
18
|
+
self._write_header()
|
|
19
|
+
|
|
20
|
+
def _write_header(self):
|
|
21
|
+
if self.header:
|
|
22
|
+
with open(self.baseFilename, 'a') as f:
|
|
23
|
+
f.write(self.header + '\n')
|
|
24
|
+
|
|
25
|
+
def filter(self, record):
|
|
26
|
+
self._write_header_if_needed()
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
A logging.Filter in Python's logging module is an object that provides a way to perform fine-grained
|
|
32
|
+
filtering of log records.
|
|
33
|
+
It allows you to control which log records are passed through and which are filtered out,
|
|
34
|
+
based on specific criteria you define.
|
|
35
|
+
|
|
36
|
+
Basic Concepts of logging.Filter
|
|
37
|
+
Purpose: Filters are used to allow or deny log records from being processed further.
|
|
38
|
+
This can be based on various criteria, such as the level of the log record, the source logger, or custom attributes.
|
|
39
|
+
Implementation: Filters are typically subclasses of logging.Filter,
|
|
40
|
+
but they can also be any callable that accepts a log record and returns a boolean value.
|
|
41
|
+
|
|
42
|
+
How logging.Filter Works
|
|
43
|
+
When a log record is emitted, it is passed through any filters attached to the logger or the handler.
|
|
44
|
+
If the filter returns True, the log record is processed. If the filter returns False, the log record is ignored.
|
|
45
|
+
|
|
46
|
+
Example of logging.Filter
|
|
47
|
+
Here’s a simple example to demonstrate the use of logging.Filter:
|
|
48
|
+
|
|
49
|
+
Create a Filter: Subclass logging.Filter and override the filter method.
|
|
50
|
+
import logging
|
|
51
|
+
|
|
52
|
+
class MyFilter(logging.Filter):
|
|
53
|
+
def filter(self, record):
|
|
54
|
+
# Example: Allow only log records with a level of WARNING or higher
|
|
55
|
+
return record.levelno >= logging.WARNING
|
|
56
|
+
|
|
57
|
+
Attach the Filter to a Handler or Logger:
|
|
58
|
+
# Create a logger
|
|
59
|
+
logger = logging.getLogger('my_logger')
|
|
60
|
+
logger.setLevel(logging.DEBUG)
|
|
61
|
+
|
|
62
|
+
# Create a console handler
|
|
63
|
+
console_handler = logging.StreamHandler()
|
|
64
|
+
|
|
65
|
+
# Create an instance of the custom filter
|
|
66
|
+
my_filter = MyFilter()
|
|
67
|
+
|
|
68
|
+
# Add the filter to the handler
|
|
69
|
+
console_handler.addFilter(my_filter)
|
|
70
|
+
|
|
71
|
+
# Add the handler to the logger
|
|
72
|
+
logger.addHandler(console_handler)
|
|
73
|
+
|
|
74
|
+
# Log some messages
|
|
75
|
+
logger.debug('This is a debug message') # Will be filtered out
|
|
76
|
+
logger.info('This is an info message') # Will be filtered out
|
|
77
|
+
logger.warning('This is a warning message') # Will be displayed
|
|
78
|
+
logger.error('This is an error message') # Will be displayed
|
|
79
|
+
"""
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from logging.handlers import TimedRotatingFileHandler, QueueListener, QueueHandler
|
|
3
|
-
from logging import FileHandler
|
|
4
3
|
import time
|
|
5
4
|
import re
|
|
6
5
|
import os
|
|
@@ -10,65 +9,15 @@ from typing import Literal, Union
|
|
|
10
9
|
import threading
|
|
11
10
|
from datetime import datetime
|
|
12
11
|
|
|
13
|
-
from . import loggers, formatters
|
|
12
|
+
from . import loggers, formatters, filters
|
|
14
13
|
from ... import datetimes, filesystem
|
|
15
14
|
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
"""
|
|
18
17
|
# Not used, only for the reference:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ForceAtTimeRotationTimedRotatingFileHandler(TimedRotatingFileHandler):
|
|
23
|
-
def __init__(self, *args, **kwargs):
|
|
24
|
-
super().__init__(*args, **kwargs)
|
|
25
|
-
self._last_rotated_date = None
|
|
26
|
-
self._start_rotation_check()
|
|
27
|
-
|
|
28
|
-
def _start_rotation_check(self):
|
|
29
|
-
self._rotation_thread = threading.Thread(target=self._check_for_rotation)
|
|
30
|
-
self._rotation_thread.daemon = True
|
|
31
|
-
self._rotation_thread.start()
|
|
32
|
-
|
|
33
|
-
def _check_for_rotation(self):
|
|
34
|
-
while True:
|
|
35
|
-
now = datetime.now()
|
|
36
|
-
current_date = now.date()
|
|
37
|
-
# Check if it's midnight and the logs haven't been rotated today
|
|
38
|
-
if now.hour == 0 and now.minute == 0 and current_date != self._last_rotated_date:
|
|
39
|
-
self._last_rotated_date = current_date
|
|
40
|
-
self.doRollover()
|
|
41
|
-
time.sleep(0.1)
|
|
42
|
-
|
|
43
|
-
def doRollover(self):
|
|
44
|
-
super().doRollover()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class TimedRotatingFileHandlerWithHeader(ForceAtTimeRotationTimedRotatingFileHandler):
|
|
48
|
-
"""
|
|
49
|
-
Custom TimedRotatingFileHandler that writes a header to the log file each time there is a file rotation.
|
|
50
|
-
Useful for writing CSV files.
|
|
51
|
-
|
|
52
|
-
:param header: string, Header to write to the log file.
|
|
53
|
-
Example: "time,host,error"
|
|
54
|
-
"""
|
|
55
|
-
def __init__(self, *args, **kwargs):
|
|
56
|
-
self.header = kwargs.pop('header', None)
|
|
57
|
-
super().__init__(*args, **kwargs)
|
|
58
|
-
|
|
59
|
-
def doRollover(self):
|
|
60
|
-
super().doRollover()
|
|
61
|
-
self._write_header()
|
|
62
|
-
|
|
63
|
-
def _write_header(self):
|
|
64
|
-
if self.header:
|
|
65
|
-
with open(self.baseFilename, 'a') as f:
|
|
66
|
-
f.write(self.header + '\n')
|
|
67
|
-
|
|
68
|
-
def emit(self, record):
|
|
69
|
-
if not os.path.exists(self.baseFilename) or os.path.getsize(self.baseFilename) == 0:
|
|
70
|
-
self._write_header()
|
|
71
|
-
super().emit(record)
|
|
18
|
+
DEFAULT_DATE_STRING_FORMAT: str = "%Y_%m_%d"
|
|
19
|
+
DEFAULT_DATE_REGEX_PATTERN: str = r"^\d{4}_\d{2}_\d{2}$"
|
|
20
|
+
"""
|
|
72
21
|
|
|
73
22
|
|
|
74
23
|
def _process_formatter_attribute(
|
|
@@ -133,6 +82,48 @@ def add_stream_handler(
|
|
|
133
82
|
loggers.set_propagation(logger)
|
|
134
83
|
|
|
135
84
|
|
|
85
|
+
# Function to start the interval-based rotation check
|
|
86
|
+
def _start_interval_rotation(handler):
|
|
87
|
+
def check_rotation():
|
|
88
|
+
while True:
|
|
89
|
+
next_rollover = _calculate_next_rollover()
|
|
90
|
+
while datetime.now() < next_rollover:
|
|
91
|
+
time.sleep(0.1)
|
|
92
|
+
|
|
93
|
+
# Check if the next_rollover has changed (indicating a rollover by an event)
|
|
94
|
+
if _calculate_next_rollover() != next_rollover:
|
|
95
|
+
next_rollover = _calculate_next_rollover()
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
# Perform manual rollover if needed
|
|
99
|
+
if datetime.now() >= next_rollover:
|
|
100
|
+
_rotate_log()
|
|
101
|
+
|
|
102
|
+
def _calculate_next_rollover():
|
|
103
|
+
return datetime.fromtimestamp(handler.rolloverAt)
|
|
104
|
+
|
|
105
|
+
# Function to rotate logs
|
|
106
|
+
def _rotate_log():
|
|
107
|
+
handler.doRollover()
|
|
108
|
+
|
|
109
|
+
rotation_thread = threading.Thread(target=check_rotation)
|
|
110
|
+
rotation_thread.daemon = True
|
|
111
|
+
rotation_thread.start()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _wrap_do_rollover(handler, header):
|
|
115
|
+
original_do_rollover = handler.doRollover
|
|
116
|
+
|
|
117
|
+
def new_do_rollover():
|
|
118
|
+
original_do_rollover()
|
|
119
|
+
# After rollover, write the header
|
|
120
|
+
if header:
|
|
121
|
+
with open(handler.baseFilename, 'a') as f:
|
|
122
|
+
f.write(header + '\n')
|
|
123
|
+
|
|
124
|
+
handler.doRollover = new_do_rollover
|
|
125
|
+
|
|
126
|
+
|
|
136
127
|
def add_timedfilehandler_with_queuehandler(
|
|
137
128
|
logger: logging.Logger,
|
|
138
129
|
file_path: str,
|
|
@@ -146,6 +137,10 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
146
137
|
str,
|
|
147
138
|
None] = None,
|
|
148
139
|
formatter_use_nanoseconds: bool = False,
|
|
140
|
+
rotate_at_rollover_time: bool = True,
|
|
141
|
+
rotation_date_format: str = None,
|
|
142
|
+
rotation_callback_namer_function: callable = None,
|
|
143
|
+
rotation_use_default_callback_namer_function: bool = True,
|
|
149
144
|
when: str = 'midnight',
|
|
150
145
|
interval: int = 1,
|
|
151
146
|
delay: bool = True,
|
|
@@ -158,10 +153,6 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
158
153
|
This is needed, since TimedRotatingFileHandler is not thread-safe, though official docs say it is.
|
|
159
154
|
"""
|
|
160
155
|
|
|
161
|
-
# If file name wasn't provided we will use the logger name instead.
|
|
162
|
-
# if not file_name_no_extension:
|
|
163
|
-
# file_name_no_extension = logger.name
|
|
164
|
-
|
|
165
156
|
# Setting the TimedRotatingFileHandler, without adding it to the logger.
|
|
166
157
|
# It will be added to the QueueListener, which will use the TimedRotatingFileHandler to write logs.
|
|
167
158
|
# This is needed since there's a bug in TimedRotatingFileHandler, which won't let it be used with
|
|
@@ -172,15 +163,8 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
172
163
|
|
|
173
164
|
filesystem.create_directory(os.path.dirname(file_path))
|
|
174
165
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
# This is needed since the CSV file will be rotated, and we'll need to set the header each time.
|
|
178
|
-
# We'll use the custom TimedRotatingFileHandlerWithHeader class.
|
|
179
|
-
file_handler = get_timed_rotating_file_handler_with_header(
|
|
180
|
-
file_path, when=when, interval=interval, delay=delay, encoding=encoding, header=header)
|
|
181
|
-
else:
|
|
182
|
-
file_handler = get_timed_rotating_file_handler(
|
|
183
|
-
file_path, when=when, interval=interval, delay=delay, encoding=encoding)
|
|
166
|
+
file_handler = get_timed_rotating_file_handler(
|
|
167
|
+
file_path, when=when, interval=interval, delay=delay, encoding=encoding)
|
|
184
168
|
|
|
185
169
|
loggers.set_logging_level(file_handler, logging_level)
|
|
186
170
|
|
|
@@ -195,7 +179,23 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
195
179
|
set_formatter(file_handler, logging_formatter)
|
|
196
180
|
|
|
197
181
|
# This function will change the suffix behavior of the rotated file name.
|
|
198
|
-
change_rotated_filename(
|
|
182
|
+
change_rotated_filename(
|
|
183
|
+
file_handler=file_handler, date_format_string=rotation_date_format,
|
|
184
|
+
callback_namer_function=rotation_callback_namer_function,
|
|
185
|
+
use_default_callback_namer_function=rotation_use_default_callback_namer_function
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# If header is set, we'll add the filter to the handler that will create the header on file rotation.
|
|
189
|
+
if header:
|
|
190
|
+
# Filter is added to write header on logger startup.
|
|
191
|
+
add_filter_to_handler(file_handler, filters.HeaderFilter(header, file_handler.baseFilename))
|
|
192
|
+
# Wrap the doRollover method to write the header after each rotation, since adding the filter
|
|
193
|
+
# will only write the header on log file creation.
|
|
194
|
+
_wrap_do_rollover(file_handler, header)
|
|
195
|
+
|
|
196
|
+
# Start the interval-based rotation forcing.
|
|
197
|
+
if rotate_at_rollover_time:
|
|
198
|
+
_start_interval_rotation(file_handler)
|
|
199
199
|
|
|
200
200
|
queue_handler = start_queue_listener_for_file_handler_and_get_queue_handler(file_handler)
|
|
201
201
|
loggers.set_logging_level(queue_handler, logging_level)
|
|
@@ -242,7 +242,7 @@ def get_stream_handler() -> logging.StreamHandler:
|
|
|
242
242
|
|
|
243
243
|
def get_timed_rotating_file_handler(
|
|
244
244
|
log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None
|
|
245
|
-
) ->
|
|
245
|
+
) -> TimedRotatingFileHandler:
|
|
246
246
|
"""
|
|
247
247
|
Function to get a TimedRotatingFileHandler.
|
|
248
248
|
This handler will output messages to a file, rotating the log file at certain timed intervals.
|
|
@@ -260,32 +260,10 @@ def get_timed_rotating_file_handler(
|
|
|
260
260
|
:return: TimedRotatingFileHandler.
|
|
261
261
|
"""
|
|
262
262
|
|
|
263
|
-
return
|
|
263
|
+
return TimedRotatingFileHandler(
|
|
264
264
|
filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding)
|
|
265
265
|
|
|
266
266
|
|
|
267
|
-
def get_timed_rotating_file_handler_with_header(
|
|
268
|
-
log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None,
|
|
269
|
-
header: str = None) -> TimedRotatingFileHandlerWithHeader:
|
|
270
|
-
"""
|
|
271
|
-
Function to get a TimedRotatingFileHandler with header.
|
|
272
|
-
This handler will output messages to a file, rotating the log file at certain timed intervals.
|
|
273
|
-
It will write a header to the log file each time there is a file rotation.
|
|
274
|
-
|
|
275
|
-
:param log_file_path: Path to the log file.
|
|
276
|
-
:param when: When to rotate the log file. Possible
|
|
277
|
-
:param interval: Interval to rotate the log file.
|
|
278
|
-
:param delay: bool, If set to True, the log file will be created only if there's something to write.
|
|
279
|
-
:param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
|
|
280
|
-
:param header: Header to write to the log file.
|
|
281
|
-
Example: "time,host,error"
|
|
282
|
-
:return: TimedRotatingFileHandlerWithHeader.
|
|
283
|
-
"""
|
|
284
|
-
|
|
285
|
-
return TimedRotatingFileHandlerWithHeader(
|
|
286
|
-
filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding, header=header)
|
|
287
|
-
|
|
288
|
-
|
|
289
267
|
def start_queue_listener_for_file_handler(
|
|
290
268
|
file_handler: logging.FileHandler, queue_object) -> logging.handlers.QueueListener:
|
|
291
269
|
"""
|
|
@@ -350,72 +328,74 @@ def get_handler_name(handler: logging.Handler) -> str:
|
|
|
350
328
|
|
|
351
329
|
def change_rotated_filename(
|
|
352
330
|
file_handler: logging.Handler,
|
|
353
|
-
date_format_string: str = None
|
|
331
|
+
date_format_string: str = None,
|
|
332
|
+
callback_namer_function: callable = None,
|
|
333
|
+
use_default_callback_namer_function: bool = True
|
|
354
334
|
):
|
|
355
335
|
"""
|
|
356
336
|
Function to change the way TimedRotatingFileHandler managing the rotating filename.
|
|
357
337
|
|
|
358
338
|
:param file_handler: FileHandler to change the rotating filename for.
|
|
359
|
-
:param date_format_string: Date format string to
|
|
360
|
-
|
|
339
|
+
:param date_format_string: Date format string to set to the handler's suffix.
|
|
340
|
+
:param callback_namer_function: Callback function to change the filename on rotation.
|
|
341
|
+
:param use_default_callback_namer_function: If set to True, the default callback namer function will be used
|
|
342
|
+
and the filename will be changed on rotation instead of using the default like this:
|
|
343
|
+
'file.log.2021-12-24' -> 'file_2021-12-24.log'.
|
|
344
|
+
|
|
345
|
+
---------------------
|
|
346
|
+
|
|
347
|
+
At this point, 'file_handler.suffix' is already '%Y-%m-%d' if 'when' is set to 'midnight'.
|
|
348
|
+
You can change it if you wish (example: '%Y_%m_%d'), the method is described below.
|
|
361
349
|
"""
|
|
362
|
-
# Changing the way TimedRotatingFileHandler managing the rotating filename
|
|
363
|
-
# Default file suffix is only "Year_Month_Day" with addition of the dot (".") character to the
|
|
364
|
-
# "file name + extension" that you provide it. Example: log file name:
|
|
365
|
-
# test.log
|
|
366
|
-
# After file is rotated at midnight, by default the old filename will be:
|
|
367
|
-
# test.log.2021_12_24
|
|
368
|
-
# And the log file of 25th, now will be "test.log".
|
|
369
|
-
# So, Changing the file suffix to include the extension to the suffix, so it will be:
|
|
370
|
-
# test.log.2021_12_24.log
|
|
371
|
-
# file_handler.suffix = logfile_suffix
|
|
372
|
-
# file_handler.suffix = "_%Y_%m_%d.txt"
|
|
373
|
-
# This step will remove the created ".log." above before the suffix and the filename will look like:
|
|
374
|
-
# test.2021_12_24.log
|
|
375
|
-
# file_handler.namer = lambda name: name.replace(log_file_extension + ".", "") + log_file_extension
|
|
376
|
-
# file_handler.namer = lambda name: name.replace(".txt.", "") + log_file_extension
|
|
377
|
-
# This will recompile the string to tell the handler the length of the suffix parts
|
|
378
|
-
# file_handler.extMatch = re.compile(r"^\d{4}_\d{2}_\d{2}" + re.escape(log_file_extension) + r"$")
|
|
379
|
-
# file_handler.extMatch = re.compile(r"^\d{4}_\d{2}_\d{2}.txt$")
|
|
380
350
|
|
|
381
351
|
def callback_namer(name):
|
|
382
352
|
"""
|
|
383
353
|
Callback function to change the filename of the rotated log file on file rotation.
|
|
384
354
|
"""
|
|
385
355
|
# Currently the 'name' is full file path + '.' + logfile_suffix.
|
|
386
|
-
# Example: 'C:\\path\\to\\file.log.
|
|
356
|
+
# Example: 'C:\\path\\to\\file.log.2021-12-24'
|
|
387
357
|
# Get the parent directory of the file: C:\path\to
|
|
388
358
|
parent_dir: str = str(Path(name).parent)
|
|
389
359
|
# Get the base filename without the extension: file.log
|
|
390
360
|
filename: str = Path(name).stem
|
|
391
|
-
# Get the date part of the filename:
|
|
361
|
+
# Get the date part of the filename: 2021-12-24
|
|
392
362
|
date_part: str = str(Path(name).suffix).replace(".", "")
|
|
393
363
|
# Get the file extension: log
|
|
394
364
|
file_extension: str = Path(filename).suffix
|
|
395
365
|
# Get the file name without the extension: file
|
|
396
366
|
file_stem: str = Path(filename).stem
|
|
397
367
|
|
|
398
|
-
return f"{parent_dir}{os.sep}{file_stem}{date_part}{file_extension}"
|
|
368
|
+
return f"{parent_dir}{os.sep}{file_stem}_{date_part}{file_extension}"
|
|
369
|
+
|
|
370
|
+
def change_file_handler_suffix():
|
|
371
|
+
# Get regex pattern from string format.
|
|
372
|
+
# Example: '%Y_%m_%d' -> r'\d{4}_\d{2}_\d{2}'
|
|
373
|
+
date_regex_pattern = datetimes.datetime_format_to_regex(date_format_string)
|
|
399
374
|
|
|
400
|
-
|
|
401
|
-
|
|
375
|
+
# Regex pattern to match the rotated log filenames
|
|
376
|
+
logfile_regex_suffix = re.compile(date_regex_pattern)
|
|
402
377
|
|
|
403
|
-
|
|
404
|
-
|
|
378
|
+
# Update the handler's suffix to include the date format
|
|
379
|
+
file_handler.suffix = date_format_string
|
|
405
380
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
date_regex_pattern = datetimes.datetime_format_to_regex(date_format_string)
|
|
381
|
+
# Update the handler's extMatch regex to match the new filename format
|
|
382
|
+
file_handler.extMatch = logfile_regex_suffix
|
|
409
383
|
|
|
410
|
-
|
|
411
|
-
|
|
384
|
+
if use_default_callback_namer_function and callback_namer_function:
|
|
385
|
+
raise ValueError("You can't use both default and custom callback namer function.")
|
|
386
|
+
elif not use_default_callback_namer_function and not callback_namer_function:
|
|
387
|
+
raise ValueError(
|
|
388
|
+
"You need to provide a 'callback_namer_function' or our 'use_default_callback_namer_function'.")
|
|
412
389
|
|
|
413
|
-
|
|
414
|
-
|
|
390
|
+
if date_format_string:
|
|
391
|
+
change_file_handler_suffix()
|
|
415
392
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
393
|
+
# Set the callback function to change the filename on rotation.
|
|
394
|
+
if use_default_callback_namer_function:
|
|
395
|
+
file_handler.namer = callback_namer
|
|
396
|
+
|
|
397
|
+
if callback_namer_function:
|
|
398
|
+
file_handler.namer = callback_namer_function
|
|
419
399
|
|
|
420
400
|
|
|
421
401
|
def has_handlers(logger: logging.Logger) -> bool:
|
|
@@ -437,7 +417,7 @@ def has_handlers(logger: logging.Logger) -> bool:
|
|
|
437
417
|
return True
|
|
438
418
|
|
|
439
419
|
|
|
440
|
-
def extract_datetime_format_from_file_handler(file_handler: FileHandler) -> Union[str, None]:
|
|
420
|
+
def extract_datetime_format_from_file_handler(file_handler: logging.FileHandler) -> Union[str, None]:
|
|
441
421
|
"""
|
|
442
422
|
Extract the datetime string formats from all TimedRotatingFileHandlers in the logger.
|
|
443
423
|
|
|
@@ -455,3 +435,13 @@ def extract_datetime_format_from_file_handler(file_handler: FileHandler) -> Unio
|
|
|
455
435
|
return datetime_format
|
|
456
436
|
|
|
457
437
|
return None
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def add_filter_to_handler(handler: logging.Handler, filter_object: logging.Filter):
|
|
441
|
+
"""
|
|
442
|
+
Function to add a filter to the handler.
|
|
443
|
+
:param handler: Handler to add the filter to.
|
|
444
|
+
:param filter_object: Filter object to add to the handler.
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
handler.addFilter(filter_object)
|
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
from typing import Literal, Union
|
|
4
4
|
|
|
5
|
-
from . import loggers, handlers
|
|
5
|
+
from . import loggers, handlers
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def create_logger(
|
|
@@ -26,6 +26,10 @@ def create_logger(
|
|
|
26
26
|
None] = None,
|
|
27
27
|
formatter_streamhandler_use_nanoseconds: bool = True,
|
|
28
28
|
formatter_filehandler_use_nanoseconds: bool = True,
|
|
29
|
+
filehandler_rotate_at_rollover_time: bool = True,
|
|
30
|
+
filehandler_rotation_date_format: str = None,
|
|
31
|
+
filehandler_rotation_callback_namer_function: callable = None,
|
|
32
|
+
filehandler_rotation_use_default_namer_function: bool = True,
|
|
29
33
|
when: str = "midnight",
|
|
30
34
|
interval: int = 1,
|
|
31
35
|
delay: bool = False,
|
|
@@ -67,6 +71,20 @@ def create_logger(
|
|
|
67
71
|
in the formatter in case you provide 'asctime' element.
|
|
68
72
|
:param formatter_filehandler_use_nanoseconds: bool, If set to True, the nanoseconds will be used
|
|
69
73
|
in the formatter in case you provide 'asctime' element.
|
|
74
|
+
:param filehandler_rotate_at_rollover_time: bool,
|
|
75
|
+
If set to True, the log file will be rotated at the rollover time, even if there's nothing to write.
|
|
76
|
+
If set to False, the log file will be rotated after 'when' time, but only when event occurs.
|
|
77
|
+
:param filehandler_rotation_date_format: string, Date format to use for the log file rotation.
|
|
78
|
+
Example for 'when="midnight"': the default date format is '%Y-%m-%d', resulting in filename on rotation like:
|
|
79
|
+
"test.log.2021-11-25"
|
|
80
|
+
If you want to change the date format to '%Y_%m_%d', the filename will be:
|
|
81
|
+
"test.log.2021_11_25"
|
|
82
|
+
:param filehandler_rotation_callback_namer_function: callable, Callback function to use for the log file naming
|
|
83
|
+
on rotation. If set to None, logging module default function will be used. With "when='midnight'",
|
|
84
|
+
and filename: "test.log" this will name the file on rotation similar to: "test.log.2021-11-25".
|
|
85
|
+
:param filehandler_rotation_use_default_namer_function: bool, If set to True, the default namer function will be
|
|
86
|
+
used for the log file naming on rotation. With "when='midnight'" and filename: "test.log",
|
|
87
|
+
this will name the file on rotation similar to: "test_2021-11-25.log".
|
|
70
88
|
:param when: string, When to rotate the log file. Default is 'midnight'.
|
|
71
89
|
[when="midnight"] is set to rotate the filename at midnight. This means that the current file name will be
|
|
72
90
|
added Yesterday's date to the end of the file and today's file will continue to write at the same
|
|
@@ -159,6 +177,10 @@ def create_logger(
|
|
|
159
177
|
handlers.add_timedfilehandler_with_queuehandler(
|
|
160
178
|
logger=logger, file_path=file_path, logging_level=logging_level, formatter=formatter_filehandler,
|
|
161
179
|
formatter_use_nanoseconds=formatter_filehandler_use_nanoseconds, file_type=file_type,
|
|
180
|
+
rotate_at_rollover_time=filehandler_rotate_at_rollover_time,
|
|
181
|
+
rotation_date_format=filehandler_rotation_date_format,
|
|
182
|
+
rotation_callback_namer_function=filehandler_rotation_callback_namer_function,
|
|
183
|
+
rotation_use_default_callback_namer_function=filehandler_rotation_use_default_namer_function,
|
|
162
184
|
when=when, interval=interval, delay=delay, encoding=encoding, header=header)
|
|
163
185
|
|
|
164
186
|
return logger
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=DBF3vx08oq-VNJ9rHPgRbBVXDm9QZq_mn99gKX4ERy0,123
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
|
|
4
4
|
atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
|
|
@@ -234,10 +234,11 @@ atomicshop/wrappers/factw/rest/statistics.py,sha256=vznwzKP1gEF7uXz3HsuV66BU9wrp
|
|
|
234
234
|
atomicshop/wrappers/factw/rest/status.py,sha256=4O3xS1poafwyUiLDkhyx4oMMe4PBwABuRPpOMnMKgIU,641
|
|
235
235
|
atomicshop/wrappers/fibratusw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
236
236
|
atomicshop/wrappers/fibratusw/install.py,sha256=PLVymDe0HuOvU0r2lje8BkQAgtiOWEeRO7n-1zKuL7A,3287
|
|
237
|
+
atomicshop/wrappers/loggingw/filters.py,sha256=CMs5PAMb68zxJgBcQobaOFDG5kLJBOVYnoBHjDgksO8,2859
|
|
237
238
|
atomicshop/wrappers/loggingw/formatters.py,sha256=808R7K3e3ZJD2BXfqI6UMOyXGrCgt9SYh2Uv7sL_1KQ,7432
|
|
238
|
-
atomicshop/wrappers/loggingw/handlers.py,sha256=
|
|
239
|
+
atomicshop/wrappers/loggingw/handlers.py,sha256=bv3oCm_P0JdXaJKYjhyfFNMNong6Nc9LE4JGFxLN2As,16940
|
|
239
240
|
atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
|
|
240
|
-
atomicshop/wrappers/loggingw/loggingw.py,sha256=
|
|
241
|
+
atomicshop/wrappers/loggingw/loggingw.py,sha256=lo4OZPXCbYZi3GqpaaJSs9SOGFfqD2EgHzzTK7f5IR4,11275
|
|
241
242
|
atomicshop/wrappers/loggingw/reading.py,sha256=yh7uNPxEdn6KsxSKrYny2C57XdI25F5gaByz77CO_pw,17038
|
|
242
243
|
atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
243
244
|
atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=QZg-R2iTQt7kFb8wNtnTmwraSGwvUs34JIasdbNa7ZU,5154
|
|
@@ -283,8 +284,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
283
284
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
284
285
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
285
286
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
286
|
-
atomicshop-2.14.
|
|
287
|
-
atomicshop-2.14.
|
|
288
|
-
atomicshop-2.14.
|
|
289
|
-
atomicshop-2.14.
|
|
290
|
-
atomicshop-2.14.
|
|
287
|
+
atomicshop-2.14.9.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
288
|
+
atomicshop-2.14.9.dist-info/METADATA,sha256=TL4CY4-kaxRLWK4vD8N1GxSUsFCHwBesZSZB3nAz7OU,10478
|
|
289
|
+
atomicshop-2.14.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
290
|
+
atomicshop-2.14.9.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
291
|
+
atomicshop-2.14.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|