sentry sdk custom performance integration for python app - python

Sentry can track performance for celery tasks and API endpoints
https://docs.sentry.io/product/performance/
I have custom script that are lunching by crone and do set of similar tasks
I want to incorporated sentry_sdk into my script to get performance tracing of my tasks
Any advise how to do it with
https://getsentry.github.io/sentry-python/api.html#sentry_sdk.capture_event

You don't need use capture_event
I would suggest to use sentry_sdk.start_transaction instead. It also allows track your function performance.
Look at my example
from time import sleep
from sentry_sdk import Hub, init, start_transaction
init(
dsn="dsn",
traces_sample_rate=1.0,
)
def sentry_trace(func):
def wrapper(*args, **kwargs):
transaction = Hub.current.scope.transaction
if transaction:
with transaction.start_child(op=func.__name__):
return func(*args, **kwargs)
else:
with start_transaction(op=func.__name__, name=func.__name__):
return func(*args, **kwargs)
return wrapper
#sentry_trace
def b():
for i in range(1000):
print(i)
#sentry_trace
def c():
sleep(2)
print(1)
#sentry_trace
def a():
sleep(1)
b()
c()
if __name__ == '__main__':
a()
After starting this code you can see basic info of transaction a with childs b and c

Related

Is there any way to get the result of an async method without blocking?

I'm providing some services through REST API that will occurs DB operation while performing a request.
So i'm trying to create a class that performs queries using oracledb(cx_Oracle).
A problem arises here. when that class executes a time-consuming query, i don't want other operations to block. So I looked for a lot of snippets a snippet that executes a method asynchronously. However, blocking occurred when there was a return value in all snippets.
asynchronously method without result works perfectly
such as (reference : Python Threading inside a class):
def threaded(fn):
def wrapper(*args, **kwargs):
Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class MyClass:
somevar = 'someval'
#threaded
def func_to_be_threaded(self):
time.sleep(3)
self.finished()
def finished(self):
print(datetime.datetime.now(), end=' ')
test = MyClass()
test.func_to_be_threaded()
test.func_to_be_threaded()
The result is exactly what I want.
2022-07-06 16:08:54.177499 2022-07-06 16:08:54.177499
but asynchronously method with result makes blocking.
Example from the same reference
def call_with_future(fn, future, args, kwargs):
try:
result = fn(*args, **kwargs)
future.set_result(result)
except Exception as exc:
future.set_exception(exc)
def threaded(fn):
def wrapper(*args, **kwargs):
future = Future()
Thread(target=call_with_future, args=(fn, future, args, kwargs)).start()
return future
return wrapper
class Test:
#threaded
def run_something(self):
time.sleep(5)
return datetime.datetime.now()
test = Test()
print(test.run_something().result(), test.run_something().result())
The result is
2022-07-06 16:08:12.159146 2022-07-06 16:08:17.167825
Is there any way to wait asynchronously for the result?
i don't want to hang while get query result.

Automatic debug logs when control goes inside/outside of a function

I am trying one project, that has many functions. I am using standard logging module The requirement is to log DEBUG logs which says:
<timestamp> DEBUG entered foo()
<timestamp> DEBUG exited foo()
<timestamp> DEBUG entered bar()
<timestamp> DEBUG exited bar()
But I don't want to write the DEBUG logs inside every function. Is there a way in Python which takes care of automatic log containing entry and exit of functions?
I don't want to use any decorator to all functions, unless it is the only solution in Python.
Any reason you don't want to use a decorator? It's pretty simple:
from functools import wraps
import logging
logging.basicConfig(filename='some_logfile.log', level=logging.DEBUG)
def tracelog(func):
#wraps(func) # to preserve docstring
def inner(*args, **kwargs):
logging.debug('entered {0}, called with args={1}, kwargs={2}'.format(func.func_name, *args, **kwargs))
func(*args, **kwargs)
logging.debug('exited {0}'.format(func.func_name))
return inner
If you get that, then passing in an independent logger is just another layer deep:
def tracelog(log):
def real_decorator(func):
#wraps(func)
def inner(*args, **kwargs):
log.debug('entered {0} called with args={1}, kwargs={2}'.format(func.func_name, *args, **kwargs))
func(*args, **kwargs)
log.debug('exited {0}'.format(func.func_name))
return inner
return real_decorator
Cool thing, is that this works for functions and methods
Usage example:
#tracelog(logger)
def somefunc():
print('running somefunc')
You want to have a look at sys.settrace.
There is a nice explanation with code examples for call tracing here: https://pymotw.com/2/sys/tracing.html
A very primitive way to do it, look at the link for more worked examples:
import sys
def trace_calls(frame, event, arg):
if event not in ('call', 'return'):
return
co = frame.f_code
func_name = co.co_name
if func_name == 'write':
# Ignore write() calls from print statements
return
if event == 'call':
print "ENTER: %s" % func_name
else:
print "EXIT: %s" % func_name
sys.settrace(trace_calls)

ThreadPoolExecutor logging? (python)

I have some code that looks like
with futures.ThreadPoolExecutor(max_workers=2) as executor:
for function in functions:
executor.submit(function)
How would I log which function is currently being handled by the executor? I may or may not have the capability to log from within the functions - would want the executor itself to log something like
print "handling process {i}".format(i=current_process)
Any thoughts on how to approach this?
I guess this is a little old but I stumbled across the questions and thought I would put an answer in. I just used a wrapper that can reference an instance of a logger prior to calling the function:
import logging
import os
import concurrent.futures
logging.basicConfig(filename=os.path.expanduser('~/Desktop/log.txt'), level=logging.INFO)
logger = logging.getLogger("MyLogger")
def logging_wrapper(func):
def wrapped(*args, **kwargs):
logger.info("Func name: {0}".format(func.__name__))
func(*args, **kwargs)
return wrapped
def a():
print('a ran')
def b():
print('b ran')
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
for func in [a, b]:
executor.submit(logging_wrapper(func))

GAE: Unit testing DeadlineExceededError

I've been using testbed, webtest, and nose to test my Python GAE app, and it is a great setup. I'm now implementing something similar to Nick's great example of using the deferred library, but I can't figure out a good way to test the parts of the code triggered by DeadlineExceededError.
Since this is in the context of a taskqueue, it would be painful to construct a test that took more than 10 minutes to run. Is there a way to temporarily set the taskqueue time limit to a few seconds for the purpose of testing? Or perhaps some other way to elegantly test the execution of code in the except DeadlineExceededError block?
Abstract the "GAE context" for your code. in production provide real "GAE implementation" for testing provide a mock own that will raise the DeadlineExceededError. The test should not depend on any timeout, should be fast.
Sample abstraction (just glue):
class AbstractGAETaskContext(object):
def task_spired(): pass # this will throw exception in mock impl
# here you define any method that you call into GAE, to be mocked
def defered(...): pass
If you don't like abstraction, you can do monkey patching for testing only, also you need to define the task_expired function to be your hook for testing.
task_expired should be called during your task implementation function.
*UPDATED*This the 3rd solution:
First I want to mention that the Nick's sample implementation is not so great, the Mapper class has to many responsabilities(deferring, query data, update in batch); and this make the test hard to made, a lot of mocks need to be defined. So I extract the deferring responsabilities in a separate class. You only want to test that deferring mechanism, what actually is happen(the update, query, etc) should be handled in other test.
Here is deffering class, also this no more depends on GAE:
class DeferredCall(object):
def __init__(self, deferred):
self.deferred = deferred
def run(self, long_execution_call, context, *args, **kwargs):
''' long_execution_call should return a tuple that tell us how was terminate operation, with timeout and the context where was abandoned '''
next_context, timeouted = long_execution_call(context, *args, **kwargs)
if timeouted:
self.deferred(self.run, next_context, *args, **kwargs)
Here is the test module:
class Test(unittest.TestCase):
def test_defer(self):
calls = []
def mock_deferrer(callback, *args, **kwargs):
calls.append((callback, args, kwargs))
def interrupted(self, context):
return "new_context", True
d = DeferredCall()
d.run(interrupted, "init_context")
self.assertEquals(1, len(calls), 'a deferred call should be')
def test_no_defer(self):
calls = []
def mock_deferrer(callback, *args, **kwargs):
calls.append((callback, args, kwargs))
def completed(self, context):
return None, False
d = DeferredCall()
d.run(completed, "init_context")
self.assertEquals(0, len(calls), 'no deferred call should be')
How will look the Nick's Mapper implementation:
class Mapper:
...
def _continue(self, start_key, batch_size):
... # here is same code, nothing was changed
except DeadlineExceededError:
# Write any unfinished updates to the datastore.
self._batch_write()
# Queue a new task to pick up where we left off.
##deferred.defer(self._continue, start_key, batch_size)
return start_key, True ## make compatible with DeferredCall
self.finish()
return None, False ## make it comaptible with DeferredCall
runner = _continue
Code where you register the long running task; this only depend on the GAE deferred lib.
import DeferredCall
import PersonMapper # this inherits the Mapper
from google.appengine.ext import deferred
mapper = PersonMapper()
DeferredCall(deferred).run(mapper.run)

celery task and customize decorator

I'm working on a project using django and celery(django-celery). Our team decided to wrap all data access code within (app-name)/manager.py(NOT wrap into Managers like the django way), and let code in (app-name)/task.py only dealing with assemble and perform tasks with celery(so we don't have django ORM dependency in this layer).
In my manager.py, I have something like this:
def get_tag(tag_name):
ctype = ContentType.objects.get_for_model(Photo)
try:
tag = Tag.objects.get(name=tag_name)
except ObjectDoesNotExist:
return Tag.objects.none()
return tag
def get_tagged_photos(tag):
ctype = ContentType.objects.get_for_model(Photo)
return TaggedItem.objects.filter(content_type__pk=ctype.pk, tag__pk=tag.pk)
def get_tagged_photos_count(tag):
return get_tagged_photos(tag).count()
In my task.py, I like to wrap them into tasks (then maybe use these tasks to do more complicated tasks), so I write this decorator:
import manager #the module within same app containing data access functions
class mfunc_to_task(object):
def __init__(mfunc_type='get'):
self.mfunc_type = mfunc_type
def __call__(self, f):
def wrapper_f(*args, **kwargs):
callback = kwargs.pop('callback', None)
mfunc = getattr(manager, f.__name__)
result = mfunc(*args, **kwargs)
if callback:
if self.mfunc_type == 'get':
subtask(callback).delay(result)
elif self.mfunc_type == 'get_or_create':
subtask(callback).delay(result[0])
else:
subtask(callback).delay()
return result
return wrapper_f
then (still in task.py):
##task
#mfunc_to_task()
def get_tag():
pass
##task
#mfunc_to_task()
def get_tagged_photos():
pass
##task
#mfunc_to_task()
def get_tagged_photos_count():
pass
Things work fine without #task.
But, after applying that #task decorator(to the top as celery documentation instructed), things just start to fall apart. Apparently, every time the mfunc_to_task.__call__ gets called, the same task.get_tag function gets passed as f. So I ended up with the same wrapper_f every time, and now the only thing I cat do is to get a single tag.
I'm new to decorators. Any one can help me understand what went wrong here, or point out other ways to achieve the task? I really hate to write the same task wrap code for every of my data access functions.
Not quite sure why passing arguments won't work?
if you use this example:
#task()
def add(x, y):
return x + y
lets add some logging to the MyCoolTask:
from celery import task
from celery.registry import tasks
import logging
import celery
logger = logging.getLogger(__name__)
class MyCoolTask(celery.Task):
def __call__(self, *args, **kwargs):
"""In celery task this function call the run method, here you can
set some environment variable before the run of the task"""
logger.info("Starting to run")
return self.run(*args, **kwargs)
def after_return(self, status, retval, task_id, args, kwargs, einfo):
#exit point of the task whatever is the state
logger.info("Ending run")
pass
and create an extended class (extending MyCoolTask, but now with arguments):
class AddTask(MyCoolTask):
def run(self,x,y):
if x and y:
result=add(x,y)
logger.info('result = %d' % result)
return result
else:
logger.error('No x or y in arguments')
tasks.register(AddTask)
and make sure you pass the kwargs as json data:
{"x":8,"y":9}
I get the result:
[2013-03-05 17:30:25,853: INFO/MainProcess] Starting to run
[2013-03-05 17:30:25,855: INFO/MainProcess] result = 17
[2013-03-05 17:30:26,739: INFO/MainProcess] Ending run
[2013-03-05 17:30:26,741: INFO/MainProcess] Task iamscheduler.tasks.AddTask[6a62641d-16a6-44b6-a1cf-7d4bdc8ea9e0] succeeded in 0.888684988022s: 17
Instead of use decorator why you don't create a base class that extend celery.Task ?
In this way all your tasks can extend your customized task class, where you can implement your personal behavior by using methods __call__ and after_return
.
You can also define common methods and object for all your task.
class MyCoolTask(celery.Task):
def __call__(self, *args, **kwargs):
"""In celery task this function call the run method, here you can
set some environment variable before the run of the task"""
return self.run(*args, **kwargs)
def after_return(self, status, retval, task_id, args, kwargs, einfo):
#exit point of the task whatever is the state
pass

Categories