Flask中使用Sentry来捕获异常,通常只需要在flask的init.py最后加入如下代码即可
if config.SENTRY_ENABLED:
from raven.contrib.flaskimport Sentry
sentry = Sentry(app, dsn='https://xxxx')
我想看下每个event的数据是怎么封装的,于是找到了build_msg这个函数
raven的base.py中函数build_msg
def build_msg(self, event_type, data=None, date=None,
time_spent=None, extra=None, stack=None, public_key=None,
tags=None, fingerprint=None,**kwargs):
"""
Captures, processes and serializes an event into a dict object
The result of ``build_msg`` should be a standardized dict, with
all default values available.
"""
# create ID client-side so that it can be passed to application
event_id= uuid.uuid4().hex
data= merge_dicts(self.context.data, data)
data.setdefault('tags', {})
data.setdefault('extra', {})
if '.' not in event_type:
# Assume it's a builtin
event_type= 'raven.events.%s' % event_type
handler= self.get_handler(event_type)
result= handler.capture(**kwargs)
# data (explicit) culprit takes over auto event detection
culprit= result.pop('culprit', None)
if data.get('culprit'):
culprit= data['culprit']
for k, vin iteritems(result):
if knot in data:
data[k]= v
# auto_log_stacks only applies to events that are not exceptions
# due to confusion about which stack is which and the automatic
# application of stacktrace to exception objects by Sentry
if stackis Noneand 'exception' not in data:
stack= self.auto_log_stacks
if stackand 'stacktrace' not in data:
if stackis True:
frames= iter_stack_frames()
else:
frames= stack
stack_info= get_stack_info(
frames,
transformer=self.transform,
capture_locals=self.capture_locals,
)
data.update({
'stacktrace': stack_info,
})
if self.include_paths:
for framein self._iter_frames(data):
if frame.get('in_app')is not None:
continue
path= frame.get('module')
if not path:
continue
if path.startswith('raven.'):
frame['in_app']= False
else:
frame['in_app']= (
any(path.startswith(x)for xin self.include_paths)and
not any(path.startswith(x)for xin self.exclude_paths)
)
if not culprit:
culprit= self.transaction.peek()
if not data.get('level'):
data['level']= kwargs.get('level')or logging.ERROR
if not data.get('server_name'):
data['server_name']= self.name
if not data.get('modules'):
data['modules']= self.get_module_versions()
if self.releaseis not None:
data['release']= self.release
if self.environmentis not None:
data['environment']= self.environment
data['tags']= merge_dicts(self.tags, data['tags'], tags)
data['extra']= merge_dicts(self.extra, data['extra'], extra)
# Legacy support for site attribute
site= data.pop('site', None)or self.site
if site:
data['tags'].setdefault('site', site)
if culprit:
data['culprit']= culprit
if fingerprint:
data['fingerprint']= fingerprint
# Run the data through processors
for processorin self.get_processors():
data.update(processor.process(data))
if 'message' not in data:
data['message']= kwargs.get('message', handler.to_string(data))
# tags should only be key=>u'value'
for key, valuein iteritems(data['tags']):
data['tags'][key]= to_unicode(value)
# print "KV", key, value, data['tags'][key]
# extra data can be any arbitrary value
for k, vin iteritems(data['extra']):
data['extra'][k]= self.transform(v)
# It's important date is added **after** we serialize
data.setdefault('project', self.remote.project)
data.setdefault('timestamp', dateor datetime.utcnow())
data.setdefault('time_spent', time_spent)
data.setdefault('event_id', event_id)
data.setdefault('platform', PLATFORM_NAME)
data.setdefault('sdk', SDK_VALUE)
data.setdefault('repos', self.repos)
print "[TAGS]", data['tags'], data.keys()
print "[VALUES]", data.values()
# insert breadcrumbs
if self.enable_breadcrumbs:
crumbs= self.context.breadcrumbs.get_buffer()
if crumbs:
# Make sure we send the crumbs here as "values" as we use the
# raven client internally in sentry and the alternative
# submission option of a list here is not supported by the
# internal sender.
data.setdefault('breadcrumbs', {'values': crumbs})
return data
在代码中随便写了一个print来打印一个不存在的key来引发异常,每个event的data.values()结构有点复杂,结果如下
[None, {'values': [{ 'stacktrace': {'frames': [ {'function': 'wsgi_app', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/.venv/lib/python2.7/site-packages/flask/app.py', 'pre_context': [' ctx = self.request_context(environ)', ' ctx.push()', ' error = None', ' try:', ' try:' ], 'post_context': [ ' except Exception as e:', ' error = e', ' response = self.handle_exception(e)', ' except:', ' error = sys.exc_info()[1]' ], 'vars': {'e': "KeyError('name',)", 'start_response': '', 'self': "", 'ctx': "", 'environ': {"'HTTP_CACHE_CONTROL'": "'max-age=0'", "'HTTP_UPGRADE_INSECURE_REQUESTS'": "'1'", "'wsgi.multiprocess'": True, "'HTTP_ACCEPT_ENCODING'": "'gzip, deflate, br'", "'HTTP_ACCEPT_LANGUAGE'": "'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7'", "'HTTP_ACCEPT'": "'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'", "'wsgi.multithread'": False, "'SCRIPT_NAME'": "''", "'QUERY_STRING'": "'code=2109d42687cc00d85129d795d5ad8ad905be5bc19533215be817f6b8c5783f31'", "'wsgi.input'": '<_io.BytesIO object at 0x10b4f20b0>', "'PATH_INFO'": "'/oauth/callback'", "'REQUEST_METHOD'": "'GET'", "'HTTP_USER_AGENT'": "'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'", "'SERVER_PORT'": "'5000'", "'wsgi.url_scheme'": "'http'", "'REMOTE_ADDR'": "'127.0.0.1'", "'SERVER_PROTOCOL'": "'HTTP/1.1'", "'wsgi.version'": [1, 0], "'wsgi.errors'": "', mode 'w' at 0x10787f1e0>", "'SERVER_NAME'": "'127.0.0.1'", "'werkzeug.request'": "", "'wsgi.run_once'": False, "'HTTP_HOST'": "'127.0.0.1:5000'", "'HTTP_CONNECTION'": "'keep-alive'", "'HTTP_COOKIE'": "'session=eyJPQVVUSF9BRlRFUl9MT0dJTl9TRVNTSU9OX1VSTCI6Ii8ifQ.DTDDRg.oW8B4bGCRz1RWWo36GwLKctcHlw'"}, 'error': "KeyError('name',)"}, 'module': 'flask.app', 'filename': 'flask/app.py', 'lineno': 1982, 'in_app': False, 'context_line': ' response = self.full_dispatch_request()' }, {'function': 'full_dispatch_request', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/.venv/lib/python2.7/site-packages/flask/app.py', 'pre_context': [' request_started.send(self)', ' rv = self.preprocess_request()', ' if rv is None:', ' rv = self.dispatch_request()', ' except Exception as e:'], 'post_context': [' return self.finalize_request(rv)', '', ' def finalize_request(self, rv, from_error_handler=False):', ' """Given the return value from a view function this finalizes', ' the request by converting it into a response and invoking the'], 'vars': {'rv': None, 'e': "KeyError('name',)", 'self': ""}, 'module': 'flask.app', 'filename': 'flask/app.py', 'lineno': 1614, 'in_app': False, 'context_line': ' rv = self.handle_user_exception(e)' }, {'function': 'handle_user_exception', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/.venv/lib/python2.7/site-packages/flask/app.py', 'pre_context': [' return self.handle_http_exception(e)', '', ' handler = self._find_error_handler(e)', '', ' if handler is None:'], 'post_context': [' return handler(e)', '', ' def handle_exception(self, e):', ' """Default exception handling that kicks in when an exception', ' occurs that is not caught. In debug mode the exception will'], 'vars': {'e': "KeyError('name',)", 'self': "", 'exc_type': "", 'handler': None, 'tb': '', 'exc_value': "KeyError('name',)"}, 'module': 'flask.app', 'filename': 'flask/app.py', 'lineno': 1517, 'in_app': False, 'context_line': ' reraise(exc_type, exc_value, tb)' }, {'function': 'full_dispatch_request', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/.venv/lib/python2.7/site-packages/flask/app.py', 'pre_context': [' self.try_trigger_before_first_request_functions()', ' try:', ' request_started.send(self)', ' rv = self.preprocess_request()', ' if rv is None:'], 'post_context': [' except Exception as e:', ' rv = self.handle_user_exception(e)', ' return self.finalize_request(rv)', '', ' def finalize_request(self, rv, from_error_handler=False):'], 'vars': {'rv': None, 'e': "KeyError('name',)", 'self': ""}, 'module': 'flask.app', 'filename': 'flask/app.py', 'lineno': 1612, 'in_app': False, 'context_line': ' rv = self.dispatch_request()' }, {'function': 'dispatch_request', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/.venv/lib/python2.7/site-packages/flask/app.py', 'pre_context': [' # request came with the OPTIONS method, reply automatically', " if getattr(rule, 'provide_automatic_options', False) \\", " and req.method == 'OPTIONS':", ' return self.make_default_options_response()', ' # otherwise dispatch to the handler for that endpoint'], 'post_context': ['', ' def full_dispatch_request(self):', ' """Dispatches the request and on top of that performs request', ' pre and postprocessing as well as HTTP exception catching and', ' error handling.'], 'vars': {'self': "", 'req': "", 'rule': "gitlab.authorized>"}, 'module': 'flask.app', 'filename': 'flask/app.py', 'lineno': 1598, 'in_app': False, 'context_line': ' return self.view_functions[rule.endpoint](**req.view_args)' }, {'function': 'authorized', 'abs_path': '/Users/charles/workspace/jiayincloud/titans/titans/lib/oauth/gitlab.py', 'pre_context': ['', ' # 1. using requests method', ' s = requests.session()', " res = s.request('POST', config.GITLAB_OAUTH_ACCESS_TOKEN_URL, **payload)", ' auth = res.json()'], 'post_context': [' """', ' For Gitlab 8.14.5', " res: {u'access_token': u'ed1471bf5f6e3b7caae9f33f7c73a36d5d20b392c483adab7ca0161a496f8dd9', ", " u'token_type': u'bearer', ", " u'created_at': 1512534693, "], 'vars': {'res': '', 's': '',
'payload': {"'data'": {"'client_id'": "'0e96e89b7e4c84fb066039e85aa59731e7a0dae72912f25a9af5935cdfd02d95'",
"'scope'": "'read_user'",
"'redirect_uri'": "'http://127.0.0.1:5000/oauth/callback'",
"'grant_type'": "'authorization_code'",
"'client_secret'": '********',
"'code'": u"u'2109d42687cc00d85129d795d5ad8ad905be5bc19533215be817f6b8c5783f31'"
}
},
'auth': {u"u'error'": u"u'invalid_grant'",
u"u'error_description'": u"u'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'"
}
},
'module': 'titans.lib.oauth.gitlab',
'filename': 'titans/lib/oauth/gitlab.py',
'lineno': 63,
'in_app': True,
'context_line': ' print auth["name"]'
}
]
},
'type': 'KeyError',
'module': u'exceptions',
'value': u"'name'"
}
]
},
'/oauth/callback',
u'nuaaysdeMacBook-Pro.local',
{'sys.argv': ("'.venv/bin/gunicorn'", "'--name=gcloud'", "'--preload'", "'--workers=4'", "'--worker-class=tornado'", "'--bind=0.0.0.0:5000'", "'--log-level=DEBUG'", "'--access-logfile=access.log'", "'--error-logfile=error.log'", "'titans:app'")
},
'd8516b6a7cff4261a904a816504dd0eb',
{'python': '2.7.10'},
{},
40,
{'url': u'http://127.0.0.1:5000/oauth/callback',
'headers': {'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7', 'Accept-Encoding': 'gzip, deflate, br', 'Host': '127.0.0.1:5000', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36', 'Connection': 'keep-alive', 'Cookie': 'session=eyJPQVVUSF9BRlRFUl9MT0dJTl9TRVNTSU9OX1VSTCI6Ii8ifQ.DTDDRg.oW8B4bGCRz1RWWo36GwLKctcHlw', 'Cache-Control': 'max-age=0', 'Upgrade-Insecure-Requests': '1'},
'env': {'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '5000', 'REMOTE_ADDR': '127.0.0.1'},
'query_string': u'code=2109d42687cc00d85129d795d5ad8ad905be5bc19533215be817f6b8c5783f31', 'data': {}, 'method': 'GET'
},
'2',
'python',
{'ip_address': '127.0.0.1'},
datetime.datetime(2018, 1, 5, 8, 5, 29, 750664),
u"KeyError: 'name'",
{},
{'version': '6.4.0', 'name': 'raven-python'}
]