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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {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 add_stream_handler(
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 add StreamHandler to logger.
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
- # Adding the handler to the main logger
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
- while True:
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
- # 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
106
+ while True:
107
+ current_time = datetime.now()
108
+ next_rollover = datetime.fromtimestamp(handler.rolloverAt)
97
109
 
98
- # Perform manual rollover if needed
99
- if datetime.now() >= next_rollover:
100
- _rotate_log()
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
- def _calculate_next_rollover():
103
- return datetime.fromtimestamp(handler.rolloverAt)
115
+ # Update last_rollover_at to the new rolloverAt
116
+ last_rollover_at = handler.rolloverAt
104
117
 
105
- # Function to rotate logs
106
- def _rotate_log():
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 add_timedfilehandler_with_queuehandler(
128
- logger: logging.Logger,
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
- Function to add TimedRotatingFileHandler and QueueHandler to logger.
152
- TimedRotatingFileHandler will output messages to the file through QueueHandler.
153
- This is needed, since TimedRotatingFileHandler is not thread-safe, though official docs say it is.
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
- queue_handler = start_queue_listener_for_file_handler_and_get_queue_handler(file_handler)
201
- loggers.set_logging_level(queue_handler, logging_level)
202
-
203
- # Add the QueueHandler to the logger.
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
- return TimedRotatingFileHandler(
264
- filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding)
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 start_queue_listener_for_file_handler(
268
- file_handler: logging.FileHandler, queue_object) -> logging.handlers.QueueListener:
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 file_handler: FileHandler to get the messages from.
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, file_handler)
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.