atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/strings.py +72 -3
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -3
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +11 -6
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +1 -1
- atomicshop/diff_check.py +3 -3
- atomicshop/dns.py +128 -3
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/trace.py +40 -42
- atomicshop/etws/traces/trace_dns.py +56 -44
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +27 -5
- atomicshop/file_io/docxs.py +34 -17
- atomicshop/file_io/file_io.py +31 -17
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +616 -291
- atomicshop/get_process_list.py +3 -3
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -80
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +136 -40
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +39 -9
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -42
- atomicshop/process.py +24 -6
- atomicshop/process_poller/process_pool.py +0 -1
- atomicshop/process_poller/simple_process_pool.py +204 -5
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/system_resource_monitor.py +61 -47
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/urls.py +6 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +98 -27
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/githubw.py +537 -54
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +214 -107
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +860 -22
- atomicshop/wrappers/loggingw/reading.py +134 -112
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +37 -1
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +491 -182
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +1 -1
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/pycharm.py +0 -9
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
- atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/config_editor.py +0 -37
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -268
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -151
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/install.py +0 -81
- atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
- atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
- atomicshop/wrappers/msiw.py +0 -149
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/psutilw/networks.py +0 -45
- atomicshop/wrappers/pycharmw.py +0 -81
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.15.11.dist-info/RECORD +0 -302
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- /atomicshop/{archiver → permissions}/__init__.py +0 -0
- /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
- /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ DEFAULT_ROTATING_SUFFIXES_FROM_WHEN: dict = {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
DEFAULT_STREAM_FORMATTER: str = "%(levelname)s | %(threadName)s | %(name)s | %(message)s"
|
|
17
|
+
DEFAULT_STREAM_FORMATTER: str = "%(asctime)s | %(levelname)s | %(threadName)s | %(name)s | %(message)s"
|
|
18
18
|
DEFAULT_MESSAGE_FORMATTER: str = "%(message)s"
|
|
19
19
|
|
|
20
20
|
FORMAT_ELEMENT_TO_HEADER: dict = {
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import threading
|
|
2
3
|
import os
|
|
3
4
|
|
|
4
5
|
|
|
6
|
+
from ...basics import ansi_escape_codes
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
class HeaderFilter(logging.Filter):
|
|
6
10
|
"""
|
|
7
11
|
A logging.Filter that writes a header to a log file if the file is empty (
|
|
8
12
|
i.e., no log records have been written, i.e.2, on file rotation).
|
|
9
13
|
"""
|
|
14
|
+
|
|
15
|
+
# noinspection PyPep8Naming
|
|
10
16
|
def __init__(self, header, baseFilename):
|
|
11
17
|
super().__init__()
|
|
12
18
|
self.header = header
|
|
@@ -27,6 +33,23 @@ class HeaderFilter(logging.Filter):
|
|
|
27
33
|
return True
|
|
28
34
|
|
|
29
35
|
|
|
36
|
+
class ThreadColorLogFilter(logging.Filter):
|
|
37
|
+
"""
|
|
38
|
+
A logging.Filter that adds color to log records based on the thread that emitted the log record.
|
|
39
|
+
"""
|
|
40
|
+
def __init__(self, color: str, thread_id):
|
|
41
|
+
super().__init__()
|
|
42
|
+
self.color = color
|
|
43
|
+
self.thread_id = thread_id
|
|
44
|
+
|
|
45
|
+
def filter(self, record):
|
|
46
|
+
if threading.get_ident() == self.thread_id:
|
|
47
|
+
record.msg = (
|
|
48
|
+
ansi_escape_codes.get_colors_basic_dict(self.color) + record.msg +
|
|
49
|
+
ansi_escape_codes.ColorsBasic.END)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
|
|
30
53
|
"""
|
|
31
54
|
A logging.Filter in Python's logging module is an object that provides a way to perform fine-grained
|
|
32
55
|
filtering of log records.
|
|
@@ -155,3 +155,15 @@ def get_logging_formatter_from_string(
|
|
|
155
155
|
return NanosecondsFormatter(formatter, style=style, datefmt=datefmt, use_nanoseconds=use_nanoseconds)
|
|
156
156
|
else:
|
|
157
157
|
return logging.Formatter(formatter, style=style, datefmt=datefmt)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_formatter_string(formatter) -> str:
|
|
161
|
+
"""
|
|
162
|
+
Function to get the formatter string from the 'logging.Formatter'.
|
|
163
|
+
|
|
164
|
+
:param formatter: logging.Formatter, formatter to convert to string.
|
|
165
|
+
:return: str, formatter string.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
# noinspection PyProtectedMember
|
|
169
|
+
return formatter._fmt
|
|
@@ -8,12 +8,14 @@ import queue
|
|
|
8
8
|
from typing import Literal, Union
|
|
9
9
|
import threading
|
|
10
10
|
from datetime import datetime
|
|
11
|
+
import contextlib
|
|
12
|
+
import multiprocessing
|
|
11
13
|
|
|
12
14
|
from . import loggers, formatters, filters, consts
|
|
13
15
|
from ... import datetimes, filesystem
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
"""
|
|
18
|
+
r"""
|
|
17
19
|
# Not used, only for the reference:
|
|
18
20
|
DEFAULT_DATE_STRING_FORMAT: str = "%Y_%m_%d"
|
|
19
21
|
DEFAULT_DATE_REGEX_PATTERN: str = r"^\d{4}_\d{2}_\d{2}$"
|
|
@@ -47,17 +49,16 @@ def _process_formatter_attribute(
|
|
|
47
49
|
return formatter
|
|
48
50
|
|
|
49
51
|
|
|
50
|
-
def
|
|
51
|
-
logger: logging.Logger,
|
|
52
|
+
def get_stream_handler_extended(
|
|
52
53
|
logging_level: str = "DEBUG",
|
|
53
54
|
formatter: Union[
|
|
54
55
|
Literal['DEFAULT', 'MESSAGE'],
|
|
55
56
|
str,
|
|
56
57
|
None] = None,
|
|
57
58
|
formatter_use_nanoseconds: bool = False
|
|
58
|
-
):
|
|
59
|
+
) -> logging.StreamHandler:
|
|
59
60
|
"""
|
|
60
|
-
Function to
|
|
61
|
+
Function to get StreamHandler with extended configuration.
|
|
61
62
|
Stream formatter will output messages to the console.
|
|
62
63
|
"""
|
|
63
64
|
|
|
@@ -75,36 +76,47 @@ def add_stream_handler(
|
|
|
75
76
|
formatter=formatter, use_nanoseconds=formatter_use_nanoseconds)
|
|
76
77
|
set_formatter(stream_handler, logging_formatter)
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
loggers.add_handler(logger, stream_handler)
|
|
80
|
-
|
|
81
|
-
# Disable propagation from the 'root' logger, so we will not see the messages twice.
|
|
82
|
-
loggers.set_propagation(logger)
|
|
79
|
+
return stream_handler
|
|
83
80
|
|
|
84
81
|
|
|
85
82
|
# Function to start the interval-based rotation check
|
|
86
83
|
def _start_interval_rotation(handler):
|
|
84
|
+
# def check_rotation():
|
|
85
|
+
# while True:
|
|
86
|
+
# next_rollover = _calculate_next_rollover()
|
|
87
|
+
# while datetime.now() < next_rollover:
|
|
88
|
+
# time.sleep(0.1)
|
|
89
|
+
#
|
|
90
|
+
# # Check if the next_rollover has changed (indicating a rollover by an event)
|
|
91
|
+
# if _calculate_next_rollover() != next_rollover:
|
|
92
|
+
# next_rollover = _calculate_next_rollover()
|
|
93
|
+
# break
|
|
94
|
+
#
|
|
95
|
+
# # Perform manual rollover if needed
|
|
96
|
+
# if datetime.now() >= next_rollover:
|
|
97
|
+
# handler.doRollover()
|
|
98
|
+
#
|
|
99
|
+
# # handler.doRollover()
|
|
100
|
+
#
|
|
101
|
+
# def _calculate_next_rollover():
|
|
102
|
+
# return datetime.fromtimestamp(handler.rolloverAt)
|
|
87
103
|
def check_rotation():
|
|
88
|
-
|
|
89
|
-
next_rollover = _calculate_next_rollover()
|
|
90
|
-
while datetime.now() < next_rollover:
|
|
91
|
-
time.sleep(0.1)
|
|
104
|
+
last_rollover_at = handler.rolloverAt # Initial rollover time
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
break
|
|
106
|
+
while True:
|
|
107
|
+
current_time = datetime.now()
|
|
108
|
+
next_rollover = datetime.fromtimestamp(handler.rolloverAt)
|
|
97
109
|
|
|
98
|
-
#
|
|
99
|
-
if
|
|
100
|
-
|
|
110
|
+
# Check if the rollover time has passed and it hasn't been handled yet
|
|
111
|
+
if current_time >= next_rollover and handler.rolloverAt == last_rollover_at:
|
|
112
|
+
# Perform manual rollover
|
|
113
|
+
handler.doRollover()
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
# Update last_rollover_at to the new rolloverAt
|
|
116
|
+
last_rollover_at = handler.rolloverAt
|
|
104
117
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
handler.doRollover()
|
|
118
|
+
# Sleep for a short interval before checking again
|
|
119
|
+
time.sleep(0.1)
|
|
108
120
|
|
|
109
121
|
rotation_thread = threading.Thread(target=check_rotation)
|
|
110
122
|
rotation_thread.daemon = True
|
|
@@ -124,8 +136,80 @@ def _wrap_do_rollover(handler, header):
|
|
|
124
136
|
handler.doRollover = new_do_rollover
|
|
125
137
|
|
|
126
138
|
|
|
127
|
-
def
|
|
128
|
-
|
|
139
|
+
def get_queue_handler_and_start_queue_listener_for_file_handler(file_handler):
|
|
140
|
+
"""
|
|
141
|
+
Function to create QueueHandler and start QueueListener for the FileHandler.
|
|
142
|
+
The QueueListener, which will get the logs from the queue and use the FileHandler to write them to the
|
|
143
|
+
file.
|
|
144
|
+
The QueueHandler will put the logs to the queue.
|
|
145
|
+
|
|
146
|
+
:param file_handler: FileHandler object.
|
|
147
|
+
:return: QueueHandler object.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
# Create the Queue between threads. "-1" means that there can infinite number of items that can be
|
|
151
|
+
# put in the Queue. if integer is bigger than 0, it means that this will be the maximum
|
|
152
|
+
# number of items.
|
|
153
|
+
queue_object = queue.Queue(-1)
|
|
154
|
+
|
|
155
|
+
# Create QueueListener, which will get the logs from the queue and use the FileHandler to write them to the file.
|
|
156
|
+
start_queue_listener_for_handlers((file_handler,), queue_object)
|
|
157
|
+
|
|
158
|
+
# Get the QueueHandler, which will put the logs to the queue.
|
|
159
|
+
queue_handler = get_queue_handler(queue_object)
|
|
160
|
+
|
|
161
|
+
return queue_handler
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# BASE FUNCTIONS =======================================================================================================
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def get_stream_handler() -> logging.StreamHandler:
|
|
168
|
+
"""
|
|
169
|
+
Function to get a StreamHandler.
|
|
170
|
+
This handler that will output messages to the console.
|
|
171
|
+
|
|
172
|
+
:return: StreamHandler.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
return logging.StreamHandler()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# noinspection PyPep8Naming
|
|
179
|
+
def get_timed_rotating_file_handler(
|
|
180
|
+
log_file_path: str,
|
|
181
|
+
when: str = "midnight",
|
|
182
|
+
interval: int = 1,
|
|
183
|
+
backupCount: int = 0,
|
|
184
|
+
delay: bool = False,
|
|
185
|
+
encoding=None
|
|
186
|
+
) -> TimedRotatingFileHandler:
|
|
187
|
+
"""
|
|
188
|
+
Function to get a TimedRotatingFileHandler.
|
|
189
|
+
This handler will output messages to a file, rotating the log file at certain timed intervals.
|
|
190
|
+
|
|
191
|
+
:param log_file_path: Path to the log file.
|
|
192
|
+
:param when: When to rotate the log file. Possible values:
|
|
193
|
+
"S" - Seconds
|
|
194
|
+
"M" - Minutes
|
|
195
|
+
"H" - Hours
|
|
196
|
+
"D" - Days
|
|
197
|
+
"midnight" - Roll over at midnight
|
|
198
|
+
:param interval: Interval to rotate the log file.
|
|
199
|
+
:param backupCount: int, Number of backup files to keep. Default is 0.
|
|
200
|
+
If backupCount is > 0, when rollover is done, no more than backupCount files are kept, the oldest are deleted.
|
|
201
|
+
If backupCount is == 0, all the backup files will be kept.
|
|
202
|
+
:param delay: bool, If set to True, the log file will be created only if there's something to write.
|
|
203
|
+
:param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
|
|
204
|
+
:return: TimedRotatingFileHandler.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
return TimedRotatingFileHandler(
|
|
208
|
+
filename=log_file_path, when=when, interval=interval, backupCount=backupCount, delay=delay, encoding=encoding)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# noinspection PyPep8Naming
|
|
212
|
+
def get_timed_rotating_file_handler_extended(
|
|
129
213
|
file_path: str,
|
|
130
214
|
file_type: Literal[
|
|
131
215
|
'txt',
|
|
@@ -141,38 +225,63 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
141
225
|
rotation_date_format: str = None,
|
|
142
226
|
rotation_callback_namer_function: callable = None,
|
|
143
227
|
rotation_use_default_callback_namer_function: bool = True,
|
|
228
|
+
use_internal_queue_listener: bool = False,
|
|
144
229
|
when: str = 'midnight',
|
|
145
230
|
interval: int = 1,
|
|
146
231
|
delay: bool = True,
|
|
232
|
+
backupCount: int = 0,
|
|
147
233
|
encoding=None,
|
|
148
234
|
header: str = None
|
|
149
|
-
):
|
|
235
|
+
) -> Union[TimedRotatingFileHandler, logging.handlers.QueueHandler]:
|
|
150
236
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
237
|
+
:param file_path: Path to the log file.
|
|
238
|
+
:param file_type: Type of the file. Possible values: 'txt', 'csv', 'json'.
|
|
239
|
+
:param logging_level: Logging level for the handler.
|
|
240
|
+
:param formatter: Formatter for the handler.
|
|
241
|
+
:param formatter_use_nanoseconds: If set to True, the formatter will use nanoseconds.
|
|
242
|
+
:param rotate_at_rollover_time: If set to True, the handler will rotate the log file at the rollover time.
|
|
243
|
+
:param rotation_date_format: Date format string to set to the handler's suffix.
|
|
244
|
+
:param rotation_callback_namer_function: Callback function to change the filename on rotation.
|
|
245
|
+
:param rotation_use_default_callback_namer_function: If set to True, the default callback namer function will be used
|
|
246
|
+
and the filename will be changed on rotation instead of using the default like this:
|
|
247
|
+
'file.log.2021-12-24' -> 'file_2021-12-24.log'.
|
|
248
|
+
:param use_internal_queue_listener: If set to True, the handler will use internal QueueListener to write logs.
|
|
249
|
+
:param when: When to rotate the log file. Possible values:
|
|
250
|
+
"S" - Seconds
|
|
251
|
+
"M" - Minutes
|
|
252
|
+
"H" - Hours
|
|
253
|
+
"D" - Days
|
|
254
|
+
"midnight" - Roll over at midnight
|
|
255
|
+
:param use_internal_queue_listener: If set to True, the handler will use internal QueueListener to write logs.
|
|
256
|
+
Function to add TimedRotatingFileHandler and QueueHandler to logger.
|
|
257
|
+
TimedRotatingFileHandler will output messages to the file through QueueHandler.
|
|
258
|
+
This is needed, since TimedRotatingFileHandler is not thread-safe, though official docs say it is.
|
|
259
|
+
:param interval: Interval to rotate the log file.
|
|
260
|
+
:param delay: If set to True, the log file will be created only if there's something to write.
|
|
261
|
+
:param backupCount: Number of backup files to keep. Default is 0.
|
|
262
|
+
If backupCount is > 0, when rollover is done, no more than backupCount files are kept, the oldest are deleted.
|
|
263
|
+
If backupCount is == 0, all the backup files will be kept.
|
|
264
|
+
:param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
|
|
265
|
+
:param header: Header to write to the log file.
|
|
266
|
+
:return: TimedRotatingFileHandler or QueueHandler (if, use_internal_queue_listener is set to True).
|
|
154
267
|
"""
|
|
155
268
|
|
|
156
|
-
# Setting the TimedRotatingFileHandler, without adding it to the logger.
|
|
157
|
-
# It will be added to the QueueListener, which will use the TimedRotatingFileHandler to write logs.
|
|
158
|
-
# This is needed since there's a bug in TimedRotatingFileHandler, which won't let it be used with
|
|
159
|
-
# threads the same way it would be used for multiprocess.
|
|
160
|
-
|
|
161
269
|
# Creating file handler with log filename. At this stage the log file is created and locked by the handler,
|
|
162
270
|
# Unless we use "delay=True" to tell the class to write the file only if there's something to write.
|
|
163
271
|
|
|
164
272
|
filesystem.create_directory(os.path.dirname(file_path))
|
|
165
273
|
|
|
166
274
|
file_handler = get_timed_rotating_file_handler(
|
|
167
|
-
file_path, when=when, interval=interval, delay=delay, encoding=encoding)
|
|
275
|
+
file_path, when=when, interval=interval, delay=delay, backupCount=backupCount, encoding=encoding)
|
|
168
276
|
|
|
169
277
|
loggers.set_logging_level(file_handler, logging_level)
|
|
170
278
|
|
|
171
279
|
formatter = _process_formatter_attribute(formatter, file_type=file_type)
|
|
172
280
|
|
|
173
281
|
# If formatter was passed to the function we'll add it to handler.
|
|
282
|
+
# noinspection GrazieInspection
|
|
174
283
|
if formatter:
|
|
175
|
-
# Convert string to Formatter object. Moved to newer styling of python 3: style='{'
|
|
284
|
+
# Convert string to Formatter object. Moved to newer styling of python 3: style='{'.
|
|
176
285
|
logging_formatter = formatters.get_logging_formatter_from_string(
|
|
177
286
|
formatter=formatter, use_nanoseconds=formatter_use_nanoseconds)
|
|
178
287
|
# Setting the formatter in file handler.
|
|
@@ -197,86 +306,36 @@ def add_timedfilehandler_with_queuehandler(
|
|
|
197
306
|
if rotate_at_rollover_time:
|
|
198
307
|
_start_interval_rotation(file_handler)
|
|
199
308
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
#
|
|
204
|
-
loggers.add_handler(logger, queue_handler)
|
|
205
|
-
|
|
206
|
-
# Disable propagation from the 'root' logger, so we will not see the messages twice.
|
|
207
|
-
loggers.set_propagation(logger)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def start_queue_listener_for_file_handler_and_get_queue_handler(file_handler):
|
|
211
|
-
"""
|
|
212
|
-
Function to start QueueListener, which will put the logs from FileHandler to the Queue.
|
|
213
|
-
QueueHandler will get the logs from the Queue and put them to the file that was set in the FileHandler.
|
|
214
|
-
|
|
215
|
-
:param file_handler: FileHandler object.
|
|
216
|
-
:return: QueueHandler object.
|
|
217
|
-
"""
|
|
218
|
-
|
|
219
|
-
# Create the Queue between threads. "-1" means that there can infinite number of items that can be
|
|
220
|
-
# put in the Queue. if integer is bigger than 0, it means that this will be the maximum
|
|
221
|
-
# number of items.
|
|
222
|
-
queue_object = queue.Queue(-1)
|
|
223
|
-
# Create QueueListener, which will put the logs from FileHandler to the Queue and put the logs to the queue.
|
|
224
|
-
start_queue_listener_for_file_handler(file_handler, queue_object)
|
|
225
|
-
|
|
226
|
-
return get_queue_handler(queue_object)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
# BASE FUNCTIONS =======================================================================================================
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
def get_stream_handler() -> logging.StreamHandler:
|
|
233
|
-
"""
|
|
234
|
-
Function to get a StreamHandler.
|
|
235
|
-
This handler that will output messages to the console.
|
|
236
|
-
|
|
237
|
-
:return: StreamHandler.
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
return logging.StreamHandler()
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def get_timed_rotating_file_handler(
|
|
244
|
-
log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None
|
|
245
|
-
) -> TimedRotatingFileHandler:
|
|
246
|
-
"""
|
|
247
|
-
Function to get a TimedRotatingFileHandler.
|
|
248
|
-
This handler will output messages to a file, rotating the log file at certain timed intervals.
|
|
249
|
-
|
|
250
|
-
:param log_file_path: Path to the log file.
|
|
251
|
-
:param when: When to rotate the log file. Possible values:
|
|
252
|
-
"S" - Seconds
|
|
253
|
-
"M" - Minutes
|
|
254
|
-
"H" - Hours
|
|
255
|
-
"D" - Days
|
|
256
|
-
"midnight" - Roll over at midnight
|
|
257
|
-
:param interval: Interval to rotate the log file.
|
|
258
|
-
:param delay: bool, If set to True, the log file will be created only if there's something to write.
|
|
259
|
-
:param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
|
|
260
|
-
:return: TimedRotatingFileHandler.
|
|
261
|
-
"""
|
|
309
|
+
# Setting the TimedRotatingFileHandler, without adding it to the logger.
|
|
310
|
+
# It will be added to the QueueListener, which will use the TimedRotatingFileHandler to write logs.
|
|
311
|
+
# This is needed since there's a bug in TimedRotatingFileHandler, which won't let it be used with
|
|
312
|
+
# threads the same way it would be used for multiprocess.
|
|
262
313
|
|
|
263
|
-
|
|
264
|
-
|
|
314
|
+
# If internal queue listener is set to True, we'll start the QueueListener for the FileHandler.
|
|
315
|
+
if use_internal_queue_listener:
|
|
316
|
+
queue_handler = get_queue_handler_and_start_queue_listener_for_file_handler(file_handler)
|
|
317
|
+
loggers.set_logging_level(queue_handler, logging_level)
|
|
318
|
+
return queue_handler
|
|
319
|
+
else:
|
|
320
|
+
return file_handler
|
|
265
321
|
|
|
266
322
|
|
|
267
|
-
def
|
|
268
|
-
|
|
323
|
+
def start_queue_listener_for_handlers(
|
|
324
|
+
handlers: tuple[logging.Handler],
|
|
325
|
+
queue_object: Union[queue.Queue, multiprocessing.Queue]
|
|
326
|
+
) -> logging.handlers.QueueListener:
|
|
269
327
|
"""
|
|
270
328
|
Function to get a QueueListener for the FileHandler.
|
|
271
329
|
This handler get the messages from the FileHandler and put them in the Queue.
|
|
272
330
|
|
|
273
|
-
:param
|
|
331
|
+
:param handlers: Tuple of handlers to put in the QueueListener.
|
|
332
|
+
For example, it can be (stream_handler, file_handler).
|
|
274
333
|
:param queue_object: Queue object to put the messages in.
|
|
275
334
|
:return: QueueListener.
|
|
276
335
|
"""
|
|
277
336
|
|
|
278
337
|
# Create the QueueListener based on TimedRotatingFileHandler
|
|
279
|
-
queue_listener = QueueListener(queue_object,
|
|
338
|
+
queue_listener: logging.handlers.QueueListener = QueueListener(queue_object, *handlers)
|
|
280
339
|
# Start the QueueListener. Each logger will have its own instance of the Queue
|
|
281
340
|
queue_listener.start()
|
|
282
341
|
|
|
@@ -296,6 +355,25 @@ def get_queue_handler(queue_object) -> logging.handlers.QueueHandler:
|
|
|
296
355
|
return QueueHandler(queue_object)
|
|
297
356
|
|
|
298
357
|
|
|
358
|
+
def get_queue_handler_extended(
|
|
359
|
+
queue_object: Union[
|
|
360
|
+
queue.Queue,
|
|
361
|
+
multiprocessing.Queue],
|
|
362
|
+
logging_level: str = "DEBUG"):
|
|
363
|
+
"""
|
|
364
|
+
Function to get the QueueHandler.
|
|
365
|
+
QueueHandler of the logger will pass the logs to the Queue and the opposite QueueListener will write them
|
|
366
|
+
from the Queue to the file that was set in the FileHandler.
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
# Getting the QueueHandler.
|
|
370
|
+
queue_handler = get_queue_handler(queue_object)
|
|
371
|
+
# Setting log level for the handler, that will use the logger while initiated.
|
|
372
|
+
loggers.set_logging_level(queue_handler, logging_level)
|
|
373
|
+
|
|
374
|
+
return queue_handler
|
|
375
|
+
|
|
376
|
+
|
|
299
377
|
def set_formatter(handler: logging.Handler, logging_formatter: logging.Formatter):
|
|
300
378
|
"""
|
|
301
379
|
Function to set the formatter for the handler.
|
|
@@ -445,3 +523,32 @@ def add_filter_to_handler(handler: logging.Handler, filter_object: logging.Filte
|
|
|
445
523
|
"""
|
|
446
524
|
|
|
447
525
|
handler.addFilter(filter_object)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def get_formatter_string(handler: logging.Handler) -> str:
|
|
529
|
+
"""
|
|
530
|
+
Function to get the formatter string from the handler.
|
|
531
|
+
:param handler: Handler to get the formatter from.
|
|
532
|
+
:return: Formatter string.
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
return formatters.get_formatter_string(handler.formatter)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@contextlib.contextmanager
|
|
539
|
+
def temporary_change_formatter(handler: logging.Handler, formatter_string: str):
|
|
540
|
+
"""
|
|
541
|
+
Context manager to temporarily change the formatter of the handler.
|
|
542
|
+
|
|
543
|
+
Example:
|
|
544
|
+
with temporary_change_formatter(handler, formatter_string):
|
|
545
|
+
# Do something with the temporary formatter.
|
|
546
|
+
pass
|
|
547
|
+
"""
|
|
548
|
+
original_formatter = handler.formatter
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
handler.setFormatter(logging.Formatter(formatter_string))
|
|
552
|
+
yield
|
|
553
|
+
finally:
|
|
554
|
+
handler.setFormatter(original_formatter)
|
|
@@ -14,6 +14,25 @@ import logging
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def is_logger_exists(
|
|
18
|
+
logger_name: str
|
|
19
|
+
) -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Function to check if the logger exists.
|
|
22
|
+
:param logger_name: str, Name of the logger.
|
|
23
|
+
:return: bool, True if the logger exists, False otherwise.
|
|
24
|
+
"""
|
|
25
|
+
return logger_name in logging.Logger.manager.loggerDict
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_all_existing_loggers_names() -> list:
|
|
29
|
+
"""
|
|
30
|
+
Function to get all existing loggers names.
|
|
31
|
+
:return: list, List of all existing loggers names.
|
|
32
|
+
"""
|
|
33
|
+
return list(logging.Logger.manager.loggerDict.keys())
|
|
34
|
+
|
|
35
|
+
|
|
17
36
|
def get_logger(logger_name: str) -> logging.Logger:
|
|
18
37
|
"""
|
|
19
38
|
Function to get a logger.
|