在使用Sentry时,你会发现有两种颜色的柱形图,一个是紫色的,在上面;一个是灰色的,在下面。这两类柱形图分别代表error和transaction,而在Python脚本环境下,不会自动进行transaction的记录,也就是说只会在出现异常时进行记录,而正常情况不会。
这时就要了解一下Sentry的transaction创建逻辑
首先是自动
在使用一些框架的情况下会自动创建,官方给出了这些框架的列表
spans就是在一个transaction中对下列类型操作的记录
以下面的一个爬虫transaction为例,可以看到spans记录的有http请求以及db操作,而数据处理的部分则是没有记录,显示为‘Missing instrumentation’
如何将这些‘Missing instrumentation’变为自定义的呢?
from sentry_sdk import Hub
def process_item(item):
transaction = Hub.current.scope.transaction
# omitted code...
with transaction.start_child(op="http", description="GET /") as span:
response = my_custom_http_library.request("GET", "/")
span.set_tag("http.status_code", response.status_code)
span.set_data("http.foobarsessionid", get_foobar_sessionid())
参考:https://sentry-docs-git-sentry-ruby-40.sentry.dev/platforms/python/performance/#:~:text=op%20and%20description.-,Python,-Copied
如果想在span中创建span
from sentry_sdk import Hub
def process_item(item):
parent_span = Hub.current.scope.span
# omitted code...
with parent_span.start_child(op="http", description="GET /") as span:
response = my_custom_http_library.request("GET", "/")
span.set_tag("http.status_code", response.status_code)
span.set_data("http.foobarsessionid", get_foobar_sessionid())
参考:https://sentry-docs-git-sentry-ruby-40.sentry.dev/platforms/python/performance/#:~:text=tree%20of%20spans%3A-,Python,-Copied
那么如何手动创建transaction呢?
其实,上图中的transaction就是通过手动创建的
from sentry_sdk import start_transaction
while True:
item = get_from_queue()
with start_transaction(op="task", name=item.get_transaction_name()):
# process_item may create more spans internally (see next examples)
process_item(item)
这种就适合于那种没有使用框架的Python脚本使用
参考:
https://docs.sentry.io/platforms/python/performance/instrumentation/automatic-instrumentation/
https://docs.sentry.io/platforms/python/performance/instrumentation/custom-instrumentation/
根据官方说明:
status用来计算failure_rate,而failure_rate表示不成功事务的百分比。 Sentry 将状态不是“ok”、“cancelled”和“unknown”的事务视为失败。
我们看到,所有自定义transaction的状态都是unknown
如果想将其改为’ok’,可以将status参数设置为’ok’
with start_transaction(op='xxxx', name='xxxx', status='ok') as transaction:
...
...
参考:https://develop.sentry.dev/sdk/event-payloads/span/
首先是希望在多进程中使用,根据官方说明写出了如下代码:
# main.py
from sentry_sdk import start_transaction
from multiprocessing import Process
import zhuo
import long
with start_transaction(op='xxxx', name='xxxx', status='ok') as transaction:
p1 = Process(target=zhuo.main)
p2 = Process(target=long.main)
p1.start()
p2.start()
p1.join()
p2.join()
# zhuo.py
import sentry_sdk
transaction = sentry_sdk.Hub.current.scope.transaction
def main():
with transaction.start_child(op="zhuo"):
...
...
# long.py
import sentry_sdk
transaction = sentry_sdk.Hub.current.scope.transaction
def main():
with transaction.start_child(op="long"):
...
...
但是报错
AttributeError: 'NoneType' object has no attribute 'start_child'
也就是说
transaction = sentry_sdk.Hub.current.scope.transaction
总是为None
所以就将代码改为如下:
# main.py
from sentry_sdk import start_transaction
from multiprocessing import Process
import zhuo
import long
with start_transaction(op='xxxx', name='xxxx', status='ok') as transaction:
p1 = Process(target=zhuo.main, args=(transaction, ))
p2 = Process(target=long.main, args=(transaction, ))
p1.start()
p2.start()
p1.join()
p2.join()
# zhuo.py
import sentry_sdk
def main(transaction: sentry_sdk.tracing.Transaction):
with transaction.start_child(op="zhuo"):
...
...
# long.py
import sentry_sdk
transaction = sentry_sdk.Hub.current.scope.transaction
def main(transaction: sentry_sdk.tracing.Transaction):
with transaction.start_child(op="long"):
...
...
但这时又报错
Traceback (most recent call last):
File "main.py", line 6, in
process.start()
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py", line 121, in start
self._popen = self._Popen(self)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/context.py", line 284, in _Popen
return Popen(process_obj)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/popen_spawn_posix.py", line 32, in __init__
super().__init__(process_obj)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/popen_fork.py", line 19, in __init__
self._launch(process_obj)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/popen_spawn_posix.py", line 47, in _launch
reduction.dump(process_obj, fp)
File "/usr/local/Cellar/[email protected]/3.8.12_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_thread.RLock' object
原因是transaction无法被pickle序列化,解决思路是将transaction的构建放在目标函数中,这里只传Python自身的数据类型。但问题是transaction无法重新构建,每个进程必须要传入同一个transaction对象,故暂时无法解决。
参考:
https://blog.csdn.net/weixin_43064185/article/details/103213823
https://stackoverflow.com/questions/44144584/typeerror-cant-pickle-thread-lock-objects
多进程不行就改为多线程试试
# main.py
from sentry_sdk import start_transaction
from threading import Thread
import zhuo
import long
with start_transaction(op='xxxx', name='xxxx', status='ok') as transaction:
p1 = Thread(target=zhuo.main, args=(transaction, ))
p2 = Thread(target=long.main, args=(transaction, ))
p1.start()
p2.start()
p1.join()
p2.join()
# zhuo.py
import sentry_sdk
def main(transaction: sentry_sdk.tracing.Transaction):
with transaction.start_child(op="zhuo"):
...
...
# long.py
import sentry_sdk
transaction = sentry_sdk.Hub.current.scope.transaction
def main(transaction: sentry_sdk.tracing.Transaction):
with transaction.start_child(op="long"):
...
...
一切正常了
但
transaction = sentry_sdk.Hub.current.scope.transaction
依旧为None
可以看到zhuo与long中的所有http请求以及数据库操作均被记录下来
所以这种方式仅适用于调试阶段,如果数据量过大,可能造成sentry服务器崩溃