logging-handler 1.0.7__tar.gz → 1.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {logging_handler-1.0.7 → logging_handler-1.0.8}/PKG-INFO +16 -7
- {logging_handler-1.0.7 → logging_handler-1.0.8}/README.md +13 -5
- {logging_handler-1.0.7 → logging_handler-1.0.8}/pyproject.toml +2 -3
- logging_handler-1.0.8/src/logging_handler/__init__.py +213 -0
- {logging_handler-1.0.7 → logging_handler-1.0.8}/src/logging_handler.egg-info/PKG-INFO +17 -8
- logging_handler-1.0.7/src/logging_handler/__init__.py +0 -110
- {logging_handler-1.0.7 → logging_handler-1.0.8}/LICENSE +0 -0
- {logging_handler-1.0.7 → logging_handler-1.0.8}/setup.cfg +0 -0
- {logging_handler-1.0.7 → logging_handler-1.0.8}/src/logging_handler.egg-info/SOURCES.txt +0 -0
- {logging_handler-1.0.7 → logging_handler-1.0.8}/src/logging_handler.egg-info/dependency_links.txt +0 -0
- {logging_handler-1.0.7 → logging_handler-1.0.8}/src/logging_handler.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: logging_handler
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Python Library to quickly create logging handlers
|
|
5
5
|
Author-email: Thomas Dunteman <git@learningtopi.com>
|
|
6
6
|
Project-URL: Homepage, https://www.learningtopi.com/python-modules-applications/python_logging_handler/
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Requires-Python: >=3.6
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
17
18
|
|
|
18
19
|
# Python Logging Helper
|
|
19
20
|
Python library to quickly create logging handlers. Supports logging to the console as well as to file using a syslog style output format.
|
|
@@ -65,8 +66,16 @@ In the examples above, you can create multiple loggers with different names. If
|
|
|
65
66
|
|
|
66
67
|
## Release Notes
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
Release notes can now be found in the release notes folder.
|
|
70
|
+
|
|
71
|
+
## Changing Logging Level
|
|
72
|
+
|
|
73
|
+
As of v1.0.8, two functions, "update_console_level" and "update_file_level" have been added. To change the logging level on the fly:
|
|
74
|
+
|
|
75
|
+
>>> from logging_helper import create_logger, update_console_level
|
|
76
|
+
>>> logger = create_logger(console=True)
|
|
77
|
+
>>> update_console_level(logger, 'info')
|
|
78
|
+
|
|
79
|
+
## CustomLogger class
|
|
80
|
+
|
|
81
|
+
As of v1.0.8, a CustomLogger class was added. This performs the same function as using create_logger, however rather than requiring a separate function to update logging levels, the class includes a "console_level" and "file_level" function that can be used. The class also utilizes threading to enable old logfile cleanup.
|
|
@@ -48,8 +48,16 @@ In the examples above, you can create multiple loggers with different names. If
|
|
|
48
48
|
|
|
49
49
|
## Release Notes
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
Release notes can now be found in the release notes folder.
|
|
52
|
+
|
|
53
|
+
## Changing Logging Level
|
|
54
|
+
|
|
55
|
+
As of v1.0.8, two functions, "update_console_level" and "update_file_level" have been added. To change the logging level on the fly:
|
|
56
|
+
|
|
57
|
+
>>> from logging_helper import create_logger, update_console_level
|
|
58
|
+
>>> logger = create_logger(console=True)
|
|
59
|
+
>>> update_console_level(logger, 'info')
|
|
60
|
+
|
|
61
|
+
## CustomLogger class
|
|
62
|
+
|
|
63
|
+
As of v1.0.8, a CustomLogger class was added. This performs the same function as using create_logger, however rather than requiring a separate function to update logging levels, the class includes a "console_level" and "file_level" function that can be used. The class also utilizes threading to enable old logfile cleanup.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "logging_handler"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.8"
|
|
4
4
|
description = "Python Library to quickly create logging handlers"
|
|
5
5
|
authors = [{name = "Thomas Dunteman", email= "git@learningtopi.com"}]
|
|
6
6
|
keywords = ["logging", "syslog"]
|
|
@@ -10,8 +10,7 @@ classifiers = [
|
|
|
10
10
|
"Topic :: System :: Logging",
|
|
11
11
|
"License :: OSI Approved :: MIT License",
|
|
12
12
|
"Operating System :: OS Independent",
|
|
13
|
-
"Programming Language :: Python :: 3"
|
|
14
|
-
]
|
|
13
|
+
"Programming Language :: Python :: 3"]
|
|
15
14
|
dependencies = []
|
|
16
15
|
|
|
17
16
|
[project.urls]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'''
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2022 LearningToPi
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
|
24
|
+
'''
|
|
25
|
+
import logging
|
|
26
|
+
import logging.handlers
|
|
27
|
+
from glob import glob
|
|
28
|
+
from datetime import datetime, timedelta
|
|
29
|
+
import pathlib
|
|
30
|
+
import os
|
|
31
|
+
import threading
|
|
32
|
+
from time import sleep
|
|
33
|
+
|
|
34
|
+
VERSION = (1, 0, 8)
|
|
35
|
+
|
|
36
|
+
DEFAULT_LEVEL = logging.WARNING
|
|
37
|
+
|
|
38
|
+
DEBUG = 'DEBUG'
|
|
39
|
+
INFO = 'INFO'
|
|
40
|
+
WARNING = 'WARNING'
|
|
41
|
+
ERROR = 'ERROR'
|
|
42
|
+
CRITICAL = 'CRITICAL'
|
|
43
|
+
|
|
44
|
+
_log_level_number = {
|
|
45
|
+
'DEBUG': logging.DEBUG,
|
|
46
|
+
"INFO": logging.INFO,
|
|
47
|
+
"WARNING": logging.WARNING,
|
|
48
|
+
"ERROR": logging.ERROR,
|
|
49
|
+
"CRITICAL": logging.CRITICAL
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_logger(console_level=WARNING, log_file='', file_level=WARNING, name='', file_mode='a', console=True,
|
|
54
|
+
syslog=False, syslog_script_name='', log_file_vars=None, log_file_retention_days=0, propagate=False):
|
|
55
|
+
""" Creates a logger and returns the handle.
|
|
56
|
+
Log file vars should be sent as a list of dict -> [{"var": "{date}", "set": "%Y-%m-%d-%Y-%M"}]
|
|
57
|
+
|
|
58
|
+
Supported log file vars:
|
|
59
|
+
{date} - will be replaced with the current date using the provided strftime format
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
logger = logging.getLogger(name)
|
|
63
|
+
logger.handlers.clear() # Clear all existing handlers before creating new ones!
|
|
64
|
+
logger.setLevel(logging.DEBUG)
|
|
65
|
+
logger.propagate = propagate
|
|
66
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
67
|
+
|
|
68
|
+
# Console
|
|
69
|
+
if console:
|
|
70
|
+
console_handler = logging.StreamHandler()
|
|
71
|
+
console_handler.setLevel(console_level if isinstance(console_level, int) else _log_level_number.get(str(console_level).upper(), DEFAULT_LEVEL))
|
|
72
|
+
console_handler.setFormatter(formatter)
|
|
73
|
+
logger.addHandler(console_handler)
|
|
74
|
+
|
|
75
|
+
# syslog
|
|
76
|
+
if syslog:
|
|
77
|
+
syslog_formatter = logging.Formatter(syslog_script_name + '[%(process)d]: %(levelname)s: %(message)s')
|
|
78
|
+
syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')
|
|
79
|
+
syslog_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
80
|
+
syslog_handler.setFormatter(syslog_formatter)
|
|
81
|
+
logger.addHandler(syslog_handler)
|
|
82
|
+
|
|
83
|
+
# file
|
|
84
|
+
if log_file != '':
|
|
85
|
+
# replace variables in the log file name
|
|
86
|
+
if log_file_vars is not None:
|
|
87
|
+
for var in log_file_vars:
|
|
88
|
+
if isinstance(var, dict) and 'var' in var and 'set' in var and var['var'] == "{date}":
|
|
89
|
+
log_file = log_file.replace(var['var'], datetime.now().strftime(var['set']))
|
|
90
|
+
file_handler = logging.FileHandler(log_file, mode=file_mode, encoding='utf-8', delay=False)
|
|
91
|
+
file_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
92
|
+
file_handler.setFormatter(formatter)
|
|
93
|
+
logger.addHandler(file_handler)
|
|
94
|
+
|
|
95
|
+
# manage retention
|
|
96
|
+
if log_file_retention_days > 0:
|
|
97
|
+
# replace variables with a *
|
|
98
|
+
log_file_search_name = log_file
|
|
99
|
+
if log_file_vars is not None:
|
|
100
|
+
for var in log_file_vars:
|
|
101
|
+
log_file_search_name.replace(var['var'], '*')
|
|
102
|
+
old_log_files = glob(log_file_search_name)
|
|
103
|
+
for old_log_file in old_log_files:
|
|
104
|
+
# check the age and delete if needed
|
|
105
|
+
fname = pathlib.Path(old_log_file)
|
|
106
|
+
mtime = datetime.fromtimestamp(fname.stat().st_mtime)
|
|
107
|
+
if mtime < datetime.now() - timedelta(days=log_file_retention_days):
|
|
108
|
+
logger.info('Deleting old log file %s. Modified time %s, retention set to %i days.', old_log_file, mtime, log_file_retention_days)
|
|
109
|
+
os.remove(old_log_file)
|
|
110
|
+
|
|
111
|
+
# return the logger
|
|
112
|
+
return logger
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def update_console_level(logger, level):
|
|
116
|
+
""" Updates the console log level for the provided logger. """
|
|
117
|
+
for handler in logger.handlers:
|
|
118
|
+
if isinstance(handler, logging.StreamHandler):
|
|
119
|
+
handler.setLevel(level if isinstance(level, int) else _log_level_number.get(str(level).upper(), DEFAULT_LEVEL))
|
|
120
|
+
logger.info('Console log level updated to %s', level)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def update_file_level(logger, level):
|
|
124
|
+
""" Updates the file log level for the provided logger. """
|
|
125
|
+
for handler in logger.handlers:
|
|
126
|
+
if isinstance(handler, logging.handlers.SysLogHandler):
|
|
127
|
+
handler.setLevel(level if isinstance(level, int) else _log_level_number.get(str(level).upper(), DEFAULT_LEVEL))
|
|
128
|
+
logger.info('File log level updated to %s', level)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class CustomLogger(logging.Logger):
|
|
132
|
+
""" Custom logger class that extends the standard logging.Logger. """
|
|
133
|
+
def __init__(self, name, console_level:str=WARNING, log_file:str|None=None, file_level:str|None=None, file_mode:str='a', console:bool=True,
|
|
134
|
+
syslog:bool=False, syslog_script_name:str='', log_file_vars:dict|None=None, log_file_retention_days:int=0, propagate:bool=False):
|
|
135
|
+
super().__init__(name, console_level if isinstance(console_level, int) else _log_level_number.get(str(console_level).upper(), DEFAULT_LEVEL))
|
|
136
|
+
self.handlers.clear() # Clear all existing handlers before creating new ones!
|
|
137
|
+
self.setLevel(logging.DEBUG)
|
|
138
|
+
self.propagate = propagate
|
|
139
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
140
|
+
self.log_file_vars, self.log_file_retention_days = log_file_vars, log_file_retention_days
|
|
141
|
+
|
|
142
|
+
# Console
|
|
143
|
+
if console:
|
|
144
|
+
console_handler = logging.StreamHandler()
|
|
145
|
+
console_handler.setLevel(console_level if isinstance(console_level, int) else _log_level_number.get(str(console_level).upper(), DEFAULT_LEVEL))
|
|
146
|
+
console_handler.setFormatter(formatter)
|
|
147
|
+
self.addHandler(console_handler)
|
|
148
|
+
|
|
149
|
+
# syslog
|
|
150
|
+
if syslog:
|
|
151
|
+
syslog_formatter = logging.Formatter(syslog_script_name + '[%(process)d]: %(levelname)s: %(message)s')
|
|
152
|
+
syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')
|
|
153
|
+
syslog_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
154
|
+
syslog_handler.setFormatter(syslog_formatter)
|
|
155
|
+
self.addHandler(syslog_handler)
|
|
156
|
+
|
|
157
|
+
# file
|
|
158
|
+
if log_file not in (None, ''):
|
|
159
|
+
# replace variables in the log file name
|
|
160
|
+
if log_file_vars is not None:
|
|
161
|
+
for var in log_file_vars:
|
|
162
|
+
if isinstance(var, dict) and 'var' in var and 'set' in var and var['var'] == "{date}":
|
|
163
|
+
log_file = log_file.replace(var['var'], datetime.now().strftime(var['set']))
|
|
164
|
+
file_handler = logging.FileHandler(log_file, mode=file_mode, encoding='utf-8', delay=False)
|
|
165
|
+
file_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
166
|
+
file_handler.setFormatter(formatter)
|
|
167
|
+
self.addHandler(file_handler)
|
|
168
|
+
|
|
169
|
+
# start background thread for log file cleanup if retention is set
|
|
170
|
+
if log_file_retention_days > 0:
|
|
171
|
+
self._cleanup_thread = threading.Thread(target=self._logfile_cleanup_thread, daemon=True)
|
|
172
|
+
self._cleanup_thread.start()
|
|
173
|
+
|
|
174
|
+
def console_level(self, level):
|
|
175
|
+
""" Updates the console log level for this logger. """
|
|
176
|
+
for handler in self.handlers:
|
|
177
|
+
if isinstance(handler, logging.StreamHandler):
|
|
178
|
+
handler.setLevel(level if isinstance(level, int) else _log_level_number.get(str(level).upper(), DEFAULT_LEVEL))
|
|
179
|
+
self.info('Console log level updated to %s', level)
|
|
180
|
+
|
|
181
|
+
def file_level(self, level):
|
|
182
|
+
""" Updates the file log level for this logger. """
|
|
183
|
+
for handler in self.handlers:
|
|
184
|
+
if isinstance(handler, logging.handlers.SysLogHandler):
|
|
185
|
+
handler.setLevel(level if isinstance(level, int) else _log_level_number.get(str(level).upper(), DEFAULT_LEVEL))
|
|
186
|
+
self.info('File log level updated to %s', level)
|
|
187
|
+
|
|
188
|
+
def _logfile_cleanup_thread(self):
|
|
189
|
+
""" Thread function to clean up old log files based on retention settings. """
|
|
190
|
+
while True:
|
|
191
|
+
try:
|
|
192
|
+
for handler in self.handlers:
|
|
193
|
+
if isinstance(handler, logging.FileHandler):
|
|
194
|
+
log_file = handler.baseFilename
|
|
195
|
+
# manage retention
|
|
196
|
+
if hasattr(self, 'log_file_retention_days') and self.log_file_retention_days > 0:
|
|
197
|
+
# replace variables with a *
|
|
198
|
+
log_file_search_name = log_file
|
|
199
|
+
if hasattr(self, 'log_file_vars') and self.log_file_vars is not None:
|
|
200
|
+
for var in self.log_file_vars:
|
|
201
|
+
log_file_search_name.replace(var['var'], '*')
|
|
202
|
+
old_log_files = glob(log_file_search_name)
|
|
203
|
+
for old_log_file in old_log_files:
|
|
204
|
+
# check the age and delete if needed
|
|
205
|
+
fname = pathlib.Path(old_log_file)
|
|
206
|
+
mtime = datetime.fromtimestamp(fname.stat().st_mtime)
|
|
207
|
+
if mtime < datetime.now() - timedelta(days=self.log_file_retention_days):
|
|
208
|
+
self.info('Deleting old log file %s. Modified time %s, retention set to %i days.', old_log_file, mtime, self.log_file_retention_days)
|
|
209
|
+
os.remove(old_log_file)
|
|
210
|
+
except Exception as e: # pylint: disable=broad-except
|
|
211
|
+
self.error('Error in log file cleanup thread, %s: %s', e.__class__.__name__, str(e))
|
|
212
|
+
finally:
|
|
213
|
+
sleep(300) # Sleep for 5 minutes before checking again
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 1.0.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logging_handler
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Python Library to quickly create logging handlers
|
|
5
5
|
Author-email: Thomas Dunteman <git@learningtopi.com>
|
|
6
6
|
Project-URL: Homepage, https://www.learningtopi.com/python-modules-applications/python_logging_handler/
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Requires-Python: >=3.6
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
17
18
|
|
|
18
19
|
# Python Logging Helper
|
|
19
20
|
Python library to quickly create logging handlers. Supports logging to the console as well as to file using a syslog style output format.
|
|
@@ -65,8 +66,16 @@ In the examples above, you can create multiple loggers with different names. If
|
|
|
65
66
|
|
|
66
67
|
## Release Notes
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
Release notes can now be found in the release notes folder.
|
|
70
|
+
|
|
71
|
+
## Changing Logging Level
|
|
72
|
+
|
|
73
|
+
As of v1.0.8, two functions, "update_console_level" and "update_file_level" have been added. To change the logging level on the fly:
|
|
74
|
+
|
|
75
|
+
>>> from logging_helper import create_logger, update_console_level
|
|
76
|
+
>>> logger = create_logger(console=True)
|
|
77
|
+
>>> update_console_level(logger, 'info')
|
|
78
|
+
|
|
79
|
+
## CustomLogger class
|
|
80
|
+
|
|
81
|
+
As of v1.0.8, a CustomLogger class was added. This performs the same function as using create_logger, however rather than requiring a separate function to update logging levels, the class includes a "console_level" and "file_level" function that can be used. The class also utilizes threading to enable old logfile cleanup.
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
MIT License
|
|
3
|
-
|
|
4
|
-
Copyright (c) 2022 LearningToPi
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
23
|
-
|
|
24
|
-
'''
|
|
25
|
-
import logging
|
|
26
|
-
import logging.handlers
|
|
27
|
-
from glob import glob
|
|
28
|
-
from datetime import datetime, timedelta
|
|
29
|
-
import pathlib
|
|
30
|
-
import os
|
|
31
|
-
|
|
32
|
-
__VERSION__ = (1, 0, 7)
|
|
33
|
-
|
|
34
|
-
DEFAULT_LEVEL = logging.WARNING
|
|
35
|
-
|
|
36
|
-
DEBUG = 'DEBUG'
|
|
37
|
-
INFO = 'INFO'
|
|
38
|
-
WARNING = 'WARNING'
|
|
39
|
-
ERROR = 'ERROR'
|
|
40
|
-
CRITICAL = 'CRITICAL'
|
|
41
|
-
|
|
42
|
-
_log_level_number = {
|
|
43
|
-
'DEBUG': logging.DEBUG,
|
|
44
|
-
"INFO": logging.INFO,
|
|
45
|
-
"WARNING": logging.WARNING,
|
|
46
|
-
"ERROR": logging.ERROR,
|
|
47
|
-
"CRITICAL": logging.CRITICAL
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def create_logger(console_level=WARNING, log_file='', file_level=WARNING, name='', file_mode='a', console=True,
|
|
52
|
-
syslog=False, syslog_script_name='', log_file_vars=None, log_file_retention_days=0, propagate=False):
|
|
53
|
-
""" Creates a logger and returns the handle.
|
|
54
|
-
Log file vars should be sent as a list of dict -> [{"var": "{date}", "set": "%Y-%m-%d-%Y-%M"}]
|
|
55
|
-
|
|
56
|
-
Supported log file vars:
|
|
57
|
-
{date} - will be replaced with the current date using the provided strftime format
|
|
58
|
-
|
|
59
|
-
"""
|
|
60
|
-
logger = logging.getLogger(name)
|
|
61
|
-
logger.handlers.clear() # Clear all existing handlers before creating new ones!
|
|
62
|
-
logger.setLevel(logging.DEBUG)
|
|
63
|
-
logger.propagate = propagate
|
|
64
|
-
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
65
|
-
|
|
66
|
-
# Console
|
|
67
|
-
if console:
|
|
68
|
-
console_handler = logging.StreamHandler()
|
|
69
|
-
console_handler.setLevel(console_level if isinstance(console_level, int) else _log_level_number.get(str(console_level).upper(), DEFAULT_LEVEL))
|
|
70
|
-
console_handler.setFormatter(formatter)
|
|
71
|
-
logger.addHandler(console_handler)
|
|
72
|
-
|
|
73
|
-
# syslog
|
|
74
|
-
if syslog:
|
|
75
|
-
syslog_formatter = logging.Formatter(syslog_script_name + '[%(process)d]: %(levelname)s: %(message)s')
|
|
76
|
-
syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')
|
|
77
|
-
syslog_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
78
|
-
syslog_handler.setFormatter(syslog_formatter)
|
|
79
|
-
logger.addHandler(syslog_handler)
|
|
80
|
-
|
|
81
|
-
# file
|
|
82
|
-
if log_file != '':
|
|
83
|
-
# replace variables in the log file name
|
|
84
|
-
if log_file_vars is not None:
|
|
85
|
-
for var in log_file_vars:
|
|
86
|
-
if isinstance(var, dict) and 'var' in var and 'set' in var and var['var'] == "{date}":
|
|
87
|
-
log_file = log_file.replace(var['var'], datetime.now().strftime(var['set']))
|
|
88
|
-
file_handler = logging.FileHandler(log_file, mode=file_mode, encoding='utf-8', delay=False)
|
|
89
|
-
file_handler.setLevel(file_level if isinstance(file_level, int) else _log_level_number.get(str(file_level).upper(), DEFAULT_LEVEL))
|
|
90
|
-
file_handler.setFormatter(formatter)
|
|
91
|
-
logger.addHandler(file_handler)
|
|
92
|
-
|
|
93
|
-
# manage retention
|
|
94
|
-
if log_file_retention_days > 0:
|
|
95
|
-
# replace variables with a *
|
|
96
|
-
log_file_search_name = log_file
|
|
97
|
-
if log_file_vars is not None:
|
|
98
|
-
for var in log_file_vars:
|
|
99
|
-
log_file_search_name.replace(var['var'], '*')
|
|
100
|
-
old_log_files = glob(log_file_search_name)
|
|
101
|
-
for old_log_file in old_log_files:
|
|
102
|
-
# check the age and delete if needed
|
|
103
|
-
fname = pathlib.Path(old_log_file)
|
|
104
|
-
mtime = datetime.fromtimestamp(fname.stat().st_mtime)
|
|
105
|
-
if mtime < datetime.now() - timedelta(days=log_file_retention_days):
|
|
106
|
-
logger.info('Deleting old log file %s. Modified time %s, retention set to %i days.', old_log_file, mtime, log_file_retention_days)
|
|
107
|
-
os.remove(old_log_file)
|
|
108
|
-
|
|
109
|
-
# return the logger
|
|
110
|
-
return logger
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{logging_handler-1.0.7 → logging_handler-1.0.8}/src/logging_handler.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|