Passed
Push — master ( b9ba1c...723864 )
by Ian
06:13
created

build.rsudp.start_logging()   A

Complexity

Conditions 2

Size

Total Lines 27
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 27
ccs 11
cts 11
cp 1
rs 9.8
c 0
b 0
f 0
cc 2
nop 2
crap 2
1 1
import os, sys
2 1
import logging
3 1
import warnings
4 1
from time import gmtime
5 1
import pkg_resources
6 1
from . import _version
7
8
# saved in case needed in the future
9
# warnings.filterwarnings('ignore', category=UserWarning, module='rsudp')
10
# warnings.filterwarnings('ignore', category=FutureWarning, module='obspy')
11
12
13
'''
14
Contains logging and formatting resources for command line and logfile output of rsudp.
15
'''
16
17 1
name = 'rsudp'
18 1
__version__ = _version.version
19
20 1
default_loc = '%s/.config/rsudp' % os.path.expanduser('~').replace('\\', '/')
21 1
os.makedirs(default_loc, exist_ok=True)
22 1
log_dir = os.path.abspath('/tmp/rsudp')
23 1
log_name = 'rsudp.log'
24 1
os.makedirs(log_dir, exist_ok=True)
25
26
# formatter settings
27 1
logging.Formatter.converter = gmtime
28 1
LOG = logging.getLogger('main')
29 1
LOGFORMAT = '%(asctime)-15s %(msg)s'
30 1
TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
31
32 1
output_dir = False
33 1
data_dir = False
34 1
scap_dir = False
35 1
ms_path = False
36
37 1
COLOR = {
38
	'purple': '\033[95m',
39
	'blue': '\033[94m',
40
	'green': '\033[92m',
41
	'yellow': '\033[93m',
42
	'red': '\033[91m',
43
	'white': '\033[0m',
44
	'bold': "\033[1m"
45
}
46
47
48 1
class LevelFormatter(logging.Formatter):
49
	'''
50
	.. |so_lf| raw:: html
51
52
		<a href="https://stackoverflow.com/a/28636024" target="_blank">this stackoverflow answer</a>
53
54
	A class that formats messages differently depending on their level.
55
	Adapted from |so_lf|.
56
57
	:param fmt: Format of message strings (see :py:mod:`logging`; example: ``'%(asctime)-15s %(msg)s'``)
58
	:type fmt: str or None
59
	:param datefmt: Date strings in strftime format (see :py:mod:`logging` example: ``'%Y-%m-%d %H:%M:%S'``)
60
	:type datefmt: str or None
61
	:param level_fmts: Dictionary of log levels and associated formats ``{logging.INFO: 'infoformat', logging.WARNING: 'warnformat', logging.ERROR: 'errformat'}``
62
	:type level_fmts: dict
63
64
	'''
65
66 1
	def __init__(self, fmt=None, datefmt=None, level_fmts={}):
67 1
		self._level_formatters = {}
68 1
		for level, format in level_fmts.items():
69
			# Could optionally support level names too
70 1
			self._level_formatters[level] = logging.Formatter(fmt=format, datefmt=datefmt)
71
		# self._fmt will be the default format
72 1
		super(LevelFormatter, self).__init__(fmt=fmt, datefmt=datefmt)
73
	# format records
74 1
	def format(self, record):
75 1
		if record.levelno in self._level_formatters:
76 1
			return self._level_formatters[record.levelno].format(record)
77
		return super(LevelFormatter, self).format(record)
78
79
80 1
def init_dirs(odir):
81
	'''
82
	Initialize the write directories if they do not already exist.
83
84
	:param str odir: output directory
85
	:return: ``True``
86
	:rtype: bool
87
	'''
88
	global output_dir, data_dir, scap_dir
89 1
	output_dir = odir
90 1
	data_dir = os.path.join(odir, 'data')
91 1
	scap_dir = os.path.join(odir, 'screenshots')
92 1
	try:
93 1
		os.makedirs(odir, exist_ok=True)
94 1
		os.makedirs(data_dir, exist_ok=True)
95 1
		os.makedirs(scap_dir, exist_ok=True)
96
	except OSError as e:
97
		print(COLOR['red'] + 'Error creating output directory structure. Are you sure you have permission to write to the output folder?' + COLOR['white'])
98
		print(COLOR['red'] + 'More info: %s' + COLOR['white'] % e)
99
		exit(2)
100
101 1
	return True
102
103
104 1
def start_logging(logname=log_name, testing=False):
105
	'''
106
	Creates a handler for logging info and warnings to file.
107
108
	:param bool testing: whether or not testing is active (adds a "TESTING" label to messages)
109
	:return: ``True``
110
	:rtype: bool
111
	'''
112
113
	global LOG, LOGFORMAT
114 1
	LOG.setLevel('INFO')
115
	# logging formatters
116
	
117 1
	if testing:
118 1
		LOGFORMAT = '%(asctime)-15s TESTING %(msg)s'
119
120 1
	formatter = logging.Formatter(fmt=LOGFORMAT, datefmt=TIME_FORMAT)
121
122
	# this initializes logging to file
123 1
	f = logging.FileHandler(os.path.join(log_dir, logname))
124 1
	f.setLevel('INFO')
125 1
	f.setFormatter(formatter)
126
	# warnings also go to file
127
	# initialize logging
128 1
	LOG.addHandler(f)
129 1
	printM('Logging initialized successfully.', sender='Init')
130 1
	return True
131
132
133 1
def add_debug_handler(testing=False):
134
	'''
135
	Creates an additional handler for logging info and warnings to the command line.
136
137
	:param bool testing: whether or not testing is active (adds a "TESTING" label to messages)
138
	:return: ``True``
139
	:rtype: bool
140
	'''
141
	global LOGFORMAT
142
143 1
	if testing:
144 1
		LOGFORMAT = '%(asctime)-15s TESTING %(msg)s'
145
146
	# terminal formats
147 1
	termformat = '\x1b[2K\r' + LOGFORMAT		# note '\x1b[2K' erases current line and \r returns to home
148
	# warning format
149 1
	warnformat = '\x1b[2K\r' + COLOR['yellow'] + LOGFORMAT + COLOR['white']
150
	# error format
151 1
	failformat = '\x1b[2K\r' + COLOR['red'] + LOGFORMAT + COLOR['white']
152 1
	termformatter = LevelFormatter(fmt=LOGFORMAT,
153
								   datefmt=TIME_FORMAT,
154
								   level_fmts={logging.INFO: termformat,
155
											   logging.WARNING: warnformat,
156
											   logging.ERROR: failformat},)
157 1
	s = logging.StreamHandler(sys.stdout)
158 1
	s.setLevel('INFO')
159 1
	s.setFormatter(termformatter)
160 1
	logging.getLogger('main').addHandler(s)
161 1
	return True
162
163
164 1
def printM(msg, sender='', announce=False):
165
	'''
166
	Prints messages with datetime stamp and sends their output to the logging handlers.
167
168
	:param str msg: message to log
169
	:param str sender: the name of the class or function sending the message
170
	'''
171 1
	msg = u'[%s] %s' % (sender, msg) if sender != '' else msg
172
	# strip emoji from unicode by converting to ascii
173 1
	msg = msg.encode('ascii', 'ignore').decode('ascii')
174 1
	LOG.info(msg)
175
176
177 1
def printW(msg, sender='', announce=True, spaces=False):
178
	'''
179
	Prints warnings with datetime stamp and sends their output to the logging handlers.
180
181
	:param str msg: message to log
182
	:param str sender: the name of the class or function sending the message
183
	:param bool announce: whether or not to display "WARNING" before the message
184
	:param bool spaces: whether or not to display formatting spaces before the message
185
	'''
186 1
	if spaces:
187
		announce = False
188
189 1
	if announce:
190
		msg = u'[%s] WARNING: %s' % (sender, msg) if sender != '' else msg
191
	else:
192 1
		if spaces:
193
			msg = u'[%s]          %s' % (sender, msg) if sender != '' else msg
194
		else:
195 1
			msg = u'[%s] %s' % (sender, msg) if sender != '' else msg
196
	# strip emoji from unicode by converting to ascii
197 1
	msg = msg.encode('ascii', 'ignore').decode('ascii')
198 1
	LOG.warning(msg)
199
200
201 1
def printE(msg, sender='', announce=True, spaces=False):
202
	'''
203
	Prints errors with datetime stamp and sends their output to the logging handlers.
204
205
	:param str msg: message to log
206
	:param str sender: the name of the class or function sending the message
207
	:param bool announce: whether or not to display "WARNING" before the message
208
	:param bool spaces: whether or not to display formatting spaces before the message
209
	'''
210
	if spaces:
211
		announce = False
212
213
	if announce:
214
		msg = u'[%s] ERROR: %s' % (sender, msg) if sender != '' else msg
215
	else:
216
		if spaces:
217
			msg = u'[%s]        %s' % (sender, msg) if sender != '' else msg
218
		else:
219
			msg = u'[%s] %s' % (sender, msg) if sender != '' else msg
220
	# strip emoji from unicode by converting to ascii
221
	msg = msg.encode('ascii', 'ignore').decode('ascii')
222
	LOG.error(msg)
223