import inspect
import subprocess
import time
import logging
from threading import Timer
log = logging.getLogger("butterfly")
def kill_command(process):
"""
kill command
"""
process.terminate()
class Result(object):
"""
easyrun 返回结果封装
"""
def __init__(self, command="", retcode="", output="", cost="", reqid=""):
"""
command : (str) 执行命令
retcode : (int) 执行结果返回码
output : (str) 输出结果
cost : (str) 执行命令耗时
"""
self.command = command or ''
self.retcode = retcode
self._output = output
self._output_len = len(output)
self._success = False
self.cost = cost
self.reqid = reqid
if retcode == 0:
self._success = True
self.err_msg = "OK"
else:
self.err_msg = output
self._logger()
def __str__(self):
"""
object str format
"""
return "[command]:{command} [success]:{success} [output]:{output}".format(
command=self.command,
success=self._success,
output=self._output
)
def _logger(self):
"""
record log
"""
f = inspect.currentframe().f_back.f_back
file_name, lineno, func_name = self._get_backframe_info(f)
if self._output_len > 50:
output_log = self._output[:50].replace("\n", ">>>") + "... :("
else:
output_log = self._output.replace("\n", ">>>") + ":)"
log_msg = ("[file={file_name}:{func_name}:{lineno} "
"reqid={reqid} "
"type=shell "
"req_path={req_path} "
"req_data=None "
"cost={cost} "
"is_success={is_success} "
"err_no={err_no} "
"err_msg={err_msg} "
"res_len={res_len} "
"res_data={res_data} "
"res_attr=None]".format(
file_name=file_name, func_name=func_name, lineno=lineno,
reqid=self.reqid,
req_path=self.command,
cost=self.cost,
is_success=self._success,
err_no=self.retcode,
err_msg=self.err_msg,
res_len=self._output_len,
res_data=output_log,
))
if self._success:
log.info(log_msg)
else:
log.error(log_msg)
def _get_backframe_info(self, f):
"""
get backframe info
"""
return f.f_back.f_code.co_filename, f.f_back.f_lineno, f.f_back.f_code.co_name
def success(self):
"""
检查执行是否成功
"""
return self._success
def output(self):
"""
返回输出结果
"""
return self._output
def run(command, timeout=10, reqid=""):
"""
Args:
command : (str) 执行的命令
timeout : (int) 默认 10s
reqid : (str) 用于记录异步任务 reqid, 此 reqid 为请求发起时的 reqid
Returns:
Result
"""
timeout = int(timeout)
process = subprocess.Popen(
command,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
shell=True)
timer = None
if timeout > 0:
timer = Timer(timeout, kill_command, [process])
timer.start()
t_beginning = time.time()
try:
output, _ = process.communicate()
finally:
if timer is not None:
timer.cancel()
seconds_passed = time.time() - t_beginning
cost_str = "%.6f" % seconds_passed
if timeout and seconds_passed > timeout:
return Result(command=command, retcode=124, output="exe timeout", cost=cost_str, reqid=reqid)
output = output.strip('\n')
return Result(command=command, retcode=process.returncode, output=output, cost=cost_str, reqid=reqid)