在线WSGI应用的动态调试

初衷

开始像做这个的时候是由于要查看公司服务器上的数据库的数据. 本地不好连接. 而且使用SSH连接也很卡. http服务还可以.

Django vs Flask

Django和Flask在Debug模式下都有好的错误报告.

Django的错误报告如下:

在线WSGI应用的动态调试

错误原因以及,每个堆栈上的信息都给出来了.

但是相比Flask的错误, 这个还是不太牛.

Flask的错误如下:

在线WSGI应用的动态调试直接能够在每个堆栈上输入代码!!, 而且在线可以看到错误的代码的上下错误位置(inspect.getsourcefile之类的找到的)

源码发现是`werkzeug.debug`的功能之一.

`werkzeug`是wsgi的工具箱, 只要是wsgi程序`werkzeug`都可以使用, 修改django工程.

在`settings.py`文件结尾加入如下代码

{% highlight python linenos %}
if DEBUG:
WSGI_APPLICATION = 'yourproject.wsgi_debug.application'
DEBUG_PROPAGATE_EXCEPTIONS = True #防止被Django默认的异常处理吞掉
{% endhighlight %}
在project的`wsgi.py`目录下加入文件'wsgi_debug.py'.
文件中加入代码.

{% highlight python linenos %}
if DEBUG:
from .wsgi import application
from werkzeug.debug import DebuggedApplication

application = DebuggedApplication(application, True)

{% endhighlight %}

测试如下.

在线WSGI应用的动态调试

现在Django也有了动态调试的功能.

动态异常

看上面的图知道Flask只有在异常的时候才能蹦出调试功能.

如果只是返回的结果不正确,就只能修改源码, 加入log, print或者加入raise Exception("xx") (上图所示).

但是能不能不修改代码的.

不修改代码, 而加入异常, 可以在代码运行前期加入(AST方式)运行时加入(set_trace)方式.

这里使用 set_trace方式.

为了代码可以重用, 包装成 WSGI的Middleware模式, 使用的时候在地址中加入参数即可.如下

{% highlight python linenos %}
if DEBUG:
from werkzeug.wrappers import Request
import sys
import os

class LiveBpMiddleware(object):
def __init__(self, app, use_exception=Exception("Break Here")):
self.app = app
self.tracing = False
self.org_trace = sys.gettrace()
self.use_exception = use_exception

def __call__(self, environ, start_response):
request = Request(environ)
livebp = request.args.get('livebp', u'')
if livebp and livebp.count(u':') == 1:
model, line = livebp.split(u':')
filename = self.lookupmodule(model)
if filename:
self.trace_filename = filename
self.trace_line = int(line)
self.tracing = True
sys.settrace(self.trace_fun)
res = self.app(environ, start_response)
if self.tracing:
self.remove_trace()
return res

def remove_trace(self):
sys.settrace(self.org_trace)
self.tracing = False

def trace_fun(self, frame, event, arg):
if event == 'line':
line_no = frame.f_lineno
filename = frame.f_code.co_filename
if line_no == self.trace_line and os.path.realpath(filename) == os.path.realpath(self.trace_filename):
print line_no, filename
sys.settrace(self.org_trace)
self.tracing = False
raise self.use_exception
return self.trace_fun

def lookupmodule(self, filename):
'copy from pdb model'
if os.path.isabs(filename) and os.path.exists(filename):
return filename
f = os.path.join(sys.path[0], filename)
if os.path.exists(f):
return f
root, ext = os.path.splitext(filename)
if ext == '':
filename = filename + '.py'
if os.path.isabs(filename):
return filename
for dirname in sys.path:
while os.path.islink(dirname):
dirname = os.readlink(dirname)
fullname = os.path.join(dirname, filename)
if os.path.exists(fullname):
return fullname
return None
{% endhighlight %}

测试结果(加入参数livebp=livebp:76):

在线WSGI应用的动态调试

可以看到第76行没有特殊代码但是异常发生了.

注意(不要放在生产环境下)

S
Published on

已星,求星。

B
Published on

已星,求星

Sign in or Sign up Leave Comment