apm-agent-python
apm-agent-python copied to clipboard
Transaction: Context Manager & Decorator
Is your feature request related to a problem? Please describe. No, it is not.
Describe the solution you'd like
Just for increasing the convenience of using Transactions, it would be nice to have an option for using them as a context manager and/or decorator, instead of calling start_transaction
and end_transaction
every time. It would be something like this:
with transaction('my_transaction_type'):
calling_my_transaction()
as a decorator it would look like this
@elasticapm.transaction('my_transaction_type', 'my_transaction_name')
def this_is_my_view_method():
pass
Describe alternatives you've considered
The alternative is use what is already provided (start_transaction
and end_transaction
)
Additional context Just to illustrate, the context manager implementation would be similar to the following code
def transaction(self, transaction_type, transaction_name=None, trace_parent=None, start=None):
transaction = self.begin_transaction(
transaction_type,
transaction_name=transaction_name,
trace_parent=trace_parent,
start=start
)
yield
self.tracer.end_transaction(transaction.name)
Hey @romulorosa! We considered to implement this in the past, as it would indeed provide a very nice way to create transactions. The problem is that as far as we know, there is no good way to get a reference to the agent in the decorator (the self
in your example).
One option would be to store it in a threadlocal variable, similar to what we do with the current transaction and current span, but that might introduce other problems... hmm.
@beniwohli Thanks for the quick feedback! You are right, it is not so that easy to get the agent reference (specially in the decorator). I will try to find some time to dive deeper in the code and see if I can get some insights here.
But at first glance, maybe it is possible to create both the decorator and the context manager in different files (decorators.py
and contextmanagers.py
) as stand alone functions which receives the agent as a dependency which can be injected during the bootstrap. The final user, then, needs to import these structures separately like from elasticapm.decorators import transaction
and from elasticapm.contextmanagers import transaction
. Not sure if it is feasible but maybe it is an idea.
@beniwohli As I briefly described before here is my idea for having convenient contextmanager and decorators for transactions. I drafted some code to share with you and see if it makes any sense.
You can check it here https://github.com/romulorosa/apm-agent-python/pull/1
The basic idea here is to have a bootstrap method/class and use dependency injection in order to inject the client into the methods that makes usage of that, guaranteeing that we always have the client reference.
What do you think?
In [1]: from elasticapm.base import bootstrap
In [2]: cli = bootstrap({'SERVICE_NAME': 'foo'})
In [3]: from elasticapm.contextmanagers import transaction
In [4]: with transaction('TRANSACTION_TYPE', 'TRANSACTION TEST'):
...: print('hi there')
...:
Begin transaction
hi there
End transaction