Passed
Push — master ( 72413d...dc58f3 )
by Ian
04:38 queued 14s
created

build.rsudp.make_colors_friendly()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.576

Importance

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