-stdout/stderr redirect to capture output and output in JSON results

-JSON result output cleanup
pull/266/head
David Pokora 6 years ago
parent 4fe2eb169e
commit 4efe4b2fb5
No known key found for this signature in database
GPG Key ID: 3CED48D1BB21BDD7
  1. 74
      slither/__main__.py
  2. 67
      slither/utils/output_redirect.py

@ -19,6 +19,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.printers import all_printers from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.output_redirect import StandardOutputRedirect
from slither.utils.colors import red, yellow, set_colorization_enabled from slither.utils.colors import red, yellow, set_colorization_enabled
from slither.utils.command_line import (output_detectors, output_results_to_markdown, from slither.utils.command_line import (output_detectors, output_results_to_markdown,
output_detectors_json, output_printers, output_detectors_json, output_printers,
@ -100,27 +101,16 @@ def process_files(filenames, args, detector_classes, printer_classes):
################################################################################### ###################################################################################
def wrap_json_detectors_results(success, error_message, results=None): def output_json(filename, error, results):
""" # Create our encapsulated JSON result.
Wrap the detector results. json_result = {
:param success: "success": error is None,
:param error_message: "error": error,
:param results: "results": results
:return:
"""
results_json = {}
if results:
results_json['detectors'] = results
return {
"success": success,
"error": error_message,
"results": results_json
} }
# Determine if our filename is referring to stdout
def output_json(results, filename): if filename == "-":
json_result = wrap_json_detectors_results(True, None, results)
if filename is None:
# Write json to console # Write json to console
print(json.dumps(json_result)) print(json.dumps(json_result))
else: else:
@ -350,13 +340,11 @@ def parse_args(detector_classes, printer_classes):
action='store_true', action='store_true',
default=defaults_flag_in_config['exclude_high']) default=defaults_flag_in_config['exclude_high'])
group_misc.add_argument('--json', group_misc.add_argument('--json',
help='Export the results as a JSON file ("--json -" to export to stdout)', help='Export the results as a JSON file ("--json -" to export to stdout)',
action='store', action='store',
default=defaults_flag_in_config['json']) default=defaults_flag_in_config['json'])
group_misc.add_argument('--disable-color', group_misc.add_argument('--disable-color',
help='Disable output colorization', help='Disable output colorization',
action='store_true', action='store_true',
@ -396,7 +384,6 @@ def parse_args(detector_classes, printer_classes):
action=OutputMarkdown, action=OutputMarkdown,
default=False) default=False)
group_misc.add_argument('--checklist', group_misc.add_argument('--checklist',
help=argparse.SUPPRESS, help=argparse.SUPPRESS,
action='store_true', action='store_true',
@ -524,10 +511,12 @@ def main_impl(all_detector_classes, all_printer_classes):
# Set colorization option # Set colorization option
set_colorization_enabled(not args.disable_color) set_colorization_enabled(not args.disable_color)
# If we are outputting json to stdout, we'll want to disable any logging. # If we are outputting json to stdout, we'll want to define some variables and redirect stdout
stdout_json = args.json == "-" output_error = None
if stdout_json: json_results = {}
logging.disable(logging.CRITICAL) outputting_json = args.json is not None
if outputting_json:
StandardOutputRedirect.enable()
printer_classes = choose_printers(args, all_printer_classes) printer_classes = choose_printers(args, all_printer_classes)
detector_classes = choose_detectors(args, all_detector_classes) detector_classes = choose_detectors(args, all_detector_classes)
@ -586,8 +575,8 @@ def main_impl(all_detector_classes, all_printer_classes):
else: else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename)) raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))
if args.json: if args.json and results:
output_json(results, None if stdout_json else args.json) json_results['detectors'] = results
if args.checklist: if args.checklist:
output_results_to_markdown(results) output_results_to_markdown(results)
# Dont print the number of result for printers # Dont print the number of result for printers
@ -599,27 +588,30 @@ def main_impl(all_detector_classes, all_printer_classes):
logger.info('%s analyzed (%d contracts), %d result(s) found', filename, number_contracts, len(results)) logger.info('%s analyzed (%d contracts), %d result(s) found', filename, number_contracts, len(results))
if args.ignore_return_value: if args.ignore_return_value:
return return
exit(results)
except SlitherException as se: except SlitherException as se:
# Output our error accordingly, via JSON or logging. output_error = str(se)
if stdout_json:
print(json.dumps(wrap_json_detectors_results(False, str(se), [])))
else:
logging.error(red('Error:')) logging.error(red('Error:'))
logging.error(red(se)) logging.error(red(output_error))
logging.error('Please report an issue to https://github.com/crytic/slither/issues') logging.error('Please report an issue to https://github.com/crytic/slither/issues')
sys.exit(-1)
except Exception: except Exception:
# Output our error accordingly, via JSON or logging. output_error = traceback.format_exc()
if stdout_json:
print(json.dumps(wrap_json_detectors_results(False, traceback.format_exc(), [])))
else:
logging.error('Error in %s' % args.filename) logging.error('Error in %s' % args.filename)
logging.error(traceback.format_exc()) logging.error(output_error)
sys.exit(-1)
# If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON.
if outputting_json:
json_results['stdout'] = StandardOutputRedirect.get_stdout_output()
json_results['stderr'] = StandardOutputRedirect.get_stderr_output()
StandardOutputRedirect.disable()
output_json(args.json, output_error, json_results)
# Exit with the appropriate status code
if output_error:
sys.exit(-1)
else:
exit(results)
if __name__ == '__main__': if __name__ == '__main__':

@ -0,0 +1,67 @@
import io
import logging
import sys
class StandardOutputRedirect:
"""
Redirects and captures standard output/errors.
"""
original_stdout = None
original_stderr = None
@staticmethod
def enable():
"""
Redirects stdout and/or stderr to a captureable StringIO.
:param redirect_stdout: True if redirection is desired for stdout.
:param redirect_stderr: True if redirection is desired for stderr.
:return: None
"""
# Redirect stdout
if StandardOutputRedirect.original_stdout is None:
StandardOutputRedirect.original_stdout = sys.stdout
sys.stdout = io.StringIO()
# Redirect stderr
if StandardOutputRedirect.original_stderr is None:
StandardOutputRedirect.original_stderr = sys.stderr
sys.stderr = io.StringIO()
root_logger = logging.getLogger()
root_logger.handlers = [logging.StreamHandler(sys.stderr)]
@staticmethod
def disable():
"""
Disables redirection of stdout/stderr, if previously enabled.
:return: None
"""
# If we have a stdout backup, restore it.
if StandardOutputRedirect.original_stdout is not None:
sys.stdout.close()
sys.stdout = StandardOutputRedirect.original_stdout
StandardOutputRedirect.original_stdout = None
# If we have an stderr backup, restore it.
if StandardOutputRedirect.original_stderr is not None:
sys.stderr.close()
sys.stderr = StandardOutputRedirect.original_stderr
StandardOutputRedirect.original_stderr = None
@staticmethod
def get_stdout_output():
"""
Obtains the output from stdout
:return: Returns stdout output as a string
"""
sys.stdout.seek(0)
return sys.stdout.read()
@staticmethod
def get_stderr_output():
"""
Obtains the output from stdout
:return: Returns stdout output as a string
"""
sys.stderr.seek(0)
return sys.stderr.read()
Loading…
Cancel
Save