Logging functionality (#193)
* Add files via upload * Add files via upload * Logging functionality (Resolves #146) * Created customized logger and setup file * Start replacing prints * Custom StreamHandler to allow '\r' as line terminator and updated more prints * Remove setup.py * Logger functionality to write red lines and records without format * Possibility to set logging level when logging without format and usage of debug level instead of verboseOutput * Replace utils logger function calls * Fixes * Import missing info color * Move xsstrike.py imports to properly initialize loggers and add logger method to debug data using json * Minor fix
This commit is contained in:
194
core/log.py
Normal file
194
core/log.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import logging
|
||||
from .colors import *
|
||||
|
||||
__all__ = ['setup_logger', 'console_log_level', 'file_log_level', 'log_file']
|
||||
|
||||
console_log_level = 'INFO'
|
||||
file_log_level = None
|
||||
log_file = 'xsstrike.log'
|
||||
|
||||
"""
|
||||
Default Logging Levels
|
||||
CRITICAL = 50
|
||||
ERROR = 40
|
||||
WARNING = 30
|
||||
INFO = 20
|
||||
DEBUG = 10
|
||||
"""
|
||||
|
||||
VULN_LEVEL_NUM = 60
|
||||
RUN_LEVEL_NUM = 22
|
||||
GOOD_LEVEL_NUM = 25
|
||||
|
||||
|
||||
logging.addLevelName(VULN_LEVEL_NUM, 'VULN')
|
||||
logging.addLevelName(RUN_LEVEL_NUM, 'RUN')
|
||||
logging.addLevelName(GOOD_LEVEL_NUM, 'GOOD')
|
||||
|
||||
|
||||
def _vuln(self, msg, *args, **kwargs):
|
||||
if self.isEnabledFor(VULN_LEVEL_NUM):
|
||||
self._log(VULN_LEVEL_NUM, msg, args, **kwargs)
|
||||
|
||||
|
||||
def _run(self, msg, *args, **kwargs):
|
||||
if self.isEnabledFor(RUN_LEVEL_NUM):
|
||||
self._log(RUN_LEVEL_NUM, msg, args, **kwargs)
|
||||
|
||||
|
||||
def _good(self, msg, *args, **kwargs):
|
||||
if self.isEnabledFor(GOOD_LEVEL_NUM):
|
||||
self._log(GOOD_LEVEL_NUM, msg, args, **kwargs)
|
||||
|
||||
|
||||
logging.Logger.vuln = _vuln
|
||||
logging.Logger.run = _run
|
||||
logging.Logger.good = _good
|
||||
|
||||
|
||||
log_config = {
|
||||
'DEBUG': {
|
||||
'value': logging.DEBUG,
|
||||
'prefix': '{}[*]{}'.format(yellow, end),
|
||||
},
|
||||
'INFO': {
|
||||
'value': logging.INFO,
|
||||
'prefix': info,
|
||||
},
|
||||
'RUN': {
|
||||
'value': RUN_LEVEL_NUM,
|
||||
'prefix': run,
|
||||
},
|
||||
'GOOD': {
|
||||
'value': GOOD_LEVEL_NUM,
|
||||
'prefix': good,
|
||||
},
|
||||
'WARNING': {
|
||||
'value': logging.WARNING,
|
||||
'prefix': '[!!]'.format(yellow, end),
|
||||
},
|
||||
'ERROR': {
|
||||
'value': logging.ERROR,
|
||||
'prefix': bad,
|
||||
},
|
||||
'CRITICAL': {
|
||||
'value': logging.CRITICAL,
|
||||
'prefix': '{}[--]{}'.format(red, end),
|
||||
},
|
||||
'VULN': {
|
||||
'value': VULN_LEVEL_NUM,
|
||||
'prefix': '{}[++]{}'.format(green, red),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
msg = super().format(record)
|
||||
if record.levelname in log_config.keys():
|
||||
msg = '%s %s %s' % (log_config[record.levelname]['prefix'], msg, end)
|
||||
return msg
|
||||
|
||||
|
||||
class CustomStreamHandler(logging.StreamHandler):
|
||||
default_terminator = '\n'
|
||||
|
||||
def emit(self, record):
|
||||
"""
|
||||
Overrides emit method to temporally update terminator character in case last log record character is '\r'
|
||||
:param record:
|
||||
:return:
|
||||
"""
|
||||
if record.msg.endswith('\r'):
|
||||
self.terminator = '\r'
|
||||
super().emit(record)
|
||||
self.terminator = self.default_terminator
|
||||
else:
|
||||
super().emit(record)
|
||||
|
||||
|
||||
def _switch_to_no_format_loggers(self):
|
||||
self.removeHandler(self.console_handler)
|
||||
self.addHandler(self.no_format_console_handler)
|
||||
if hasattr(self, 'file_handler') and hasattr(self, 'no_format_file_handler'):
|
||||
self.removeHandler(self.file_handler)
|
||||
self.addHandler(self.no_format_file_handler)
|
||||
|
||||
|
||||
def _switch_to_default_loggers(self):
|
||||
self.removeHandler(self.no_format_console_handler)
|
||||
self.addHandler(self.console_handler)
|
||||
if hasattr(self, 'file_handler') and hasattr(self, 'no_format_file_handler'):
|
||||
self.removeHandler(self.no_format_file_handler)
|
||||
self.addHandler(self.file_handler)
|
||||
|
||||
|
||||
def _get_level_and_log(self, msg, level):
|
||||
if level.upper() in log_config.keys():
|
||||
log_method = getattr(self, level.lower())
|
||||
log_method(msg)
|
||||
else:
|
||||
self.info(msg)
|
||||
|
||||
|
||||
def log_red_line(self, amount=60, level='INFO'):
|
||||
_switch_to_no_format_loggers(self)
|
||||
_get_level_and_log(self, red + ('-' * amount) + end, level)
|
||||
_switch_to_default_loggers(self)
|
||||
|
||||
|
||||
def log_no_format(self, msg='', level='INFO'):
|
||||
_switch_to_no_format_loggers(self)
|
||||
_get_level_and_log(self, msg, level)
|
||||
_switch_to_default_loggers(self)
|
||||
|
||||
|
||||
def log_debug_json(self, msg='', data={}):
|
||||
if self.isEnabledFor(logging.DEBUG):
|
||||
if isinstance(data, dict):
|
||||
import json
|
||||
try:
|
||||
self.debug('{} {}'.format(msg, json.dumps(data, indent=2)))
|
||||
except TypeError:
|
||||
self.debug('{} {}'.format(msg, data))
|
||||
else:
|
||||
self.debug('{} {}'.format(msg, data))
|
||||
|
||||
|
||||
def setup_logger(name='xsstrike'):
|
||||
from types import MethodType
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
console_handler = CustomStreamHandler(sys.stdout)
|
||||
console_handler.setLevel(log_config[console_log_level]['value'])
|
||||
console_handler.setFormatter(CustomFormatter('%(message)s'))
|
||||
logger.addHandler(console_handler)
|
||||
# Setup blank handler to temporally use to log without format
|
||||
no_format_console_handler = CustomStreamHandler(sys.stdout)
|
||||
no_format_console_handler.setLevel((log_config[console_log_level]['value']))
|
||||
no_format_console_handler.setFormatter(logging.Formatter(fmt=''))
|
||||
# Store current handlers
|
||||
logger.console_handler = console_handler
|
||||
logger.no_format_console_handler = no_format_console_handler
|
||||
|
||||
if file_log_level:
|
||||
detailed_formatter = logging.Formatter('%(asctime)s %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler = logging.FileHandler(log_file)
|
||||
file_handler.setLevel(log_config[file_log_level]['value'])
|
||||
file_handler.setFormatter(detailed_formatter)
|
||||
logger.addHandler(file_handler)
|
||||
# Setup blank handler to temporally use to log without format
|
||||
no_format_file_handler = logging.FileHandler(log_file)
|
||||
no_format_file_handler.setLevel(log_config[file_log_level]['value'])
|
||||
no_format_file_handler.setFormatter(logging.Formatter(fmt=''))
|
||||
# Store file handlers
|
||||
logger.file_handler = file_handler
|
||||
logger.no_format_file_handler = no_format_file_handler
|
||||
|
||||
# Create logger method to only log a red line
|
||||
logger.red_line = MethodType(log_red_line, logger)
|
||||
# Create logger method to log without format
|
||||
logger.no_format = MethodType(log_no_format, logger)
|
||||
# Create logger method to convert data to json and log with debug level
|
||||
logger.debug_json = MethodType(log_debug_json, logger)
|
||||
return logger
|
||||
Reference in New Issue
Block a user