Skip to content

Commit 679ba02

Browse files
authored
add transaction name to error objects (elastic#1441)
* add transaction name to error objects also, move determination of transaction name in Django to earliest possible moment. * update changelog
1 parent 3c5ed55 commit 679ba02

File tree

5 files changed

+77
-20
lines changed

5 files changed

+77
-20
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ endif::[]
2929
[float]
3030
===== Features
3131
* use "unknown-python-service" as default service name if no service name is configured {pull}1438[#1438]
32-
32+
* add transaction name to error objects {pull}1441[#1441]
3333
3434
[[release-notes-6.x]]
3535
=== Python Agent version 6.x

elasticapm/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,11 @@ def _build_msg_for_logging(
547547
# parent id might already be set in the handler
548548
event_data.setdefault("parent_id", span.id if span else transaction.id)
549549
event_data["transaction_id"] = transaction.id
550-
event_data["transaction"] = {"sampled": transaction.is_sampled, "type": transaction.transaction_type}
550+
event_data["transaction"] = {
551+
"sampled": transaction.is_sampled,
552+
"type": transaction.transaction_type,
553+
"name": transaction.name,
554+
}
551555

552556
return event_data
553557

elasticapm/contrib/django/middleware/__init__.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333

3434
import logging
3535
import threading
36+
from types import FunctionType
37+
from typing import Optional
3638

3739
from django.apps import apps
3840
from django.conf import settings as django_settings
41+
from django.http import HttpRequest, HttpResponse
3942

4043
import elasticapm
4144
from elasticapm.conf import constants
@@ -114,10 +117,10 @@ def process_response_wrapper(wrapped, instance, args, kwargs):
114117
response = wrapped(*args, **kwargs)
115118
try:
116119
request, original_response = args
117-
# if there's no view_func on the request, and this middleware created
120+
# if we haven't set the name in a view, and this middleware created
118121
# a new response object, it's logged as the responsible transaction
119122
# name
120-
if not hasattr(request, "_elasticapm_view_func") and response is not original_response:
123+
if not getattr(request, "_elasticapm_name_set", False) and response is not original_response:
121124
elasticapm.set_transaction_name(
122125
build_name_with_http_method_prefix(get_name_from_middleware(wrapped, instance), request)
123126
)
@@ -159,25 +162,17 @@ def instrument_middlewares(self):
159162
except ImportError:
160163
client.logger.warning("Can't instrument middleware %s", middleware_path)
161164

162-
def process_view(self, request, view_func, view_args, view_kwargs):
163-
request._elasticapm_view_func = view_func
165+
def process_view(self, request: HttpRequest, view_func: FunctionType, view_args: list, view_kwargs: dict):
166+
elasticapm.set_transaction_name(self.get_transaction_name(request, view_func), override=False)
167+
request._elasticapm_name_set = True
164168

165-
def process_response(self, request, response):
169+
def process_response(self, request: HttpRequest, response: HttpResponse):
166170
if django_settings.DEBUG and not self.client.config.debug:
167171
return response
168172
try:
169173
if hasattr(response, "status_code"):
170-
transaction_name = None
171-
if self.client.config.django_transaction_name_from_route and hasattr(request.resolver_match, "route"):
172-
r = request.resolver_match
173-
# if no route is defined (e.g. for the root URL), fall back on url_name and then function name
174-
transaction_name = r.route or r.url_name or get_name_from_func(r.func)
175-
elif getattr(request, "_elasticapm_view_func", False):
176-
transaction_name = get_name_from_func(request._elasticapm_view_func)
177-
if transaction_name:
178-
transaction_name = build_name_with_http_method_prefix(transaction_name, request)
179-
elasticapm.set_transaction_name(transaction_name, override=False)
180-
174+
if not getattr(request, "_elasticapm_name_set", False):
175+
elasticapm.set_transaction_name(self.get_transaction_name(request), override=False)
181176
elasticapm.set_context(
182177
lambda: self.client.get_data_from_request(request, constants.TRANSACTION), "request"
183178
)
@@ -191,6 +186,18 @@ def process_response(self, request, response):
191186
self.client.error_logger.error("Exception during timing of request", exc_info=True)
192187
return response
193188

189+
def get_transaction_name(self, request: HttpRequest, view_func: Optional[FunctionType] = None) -> str:
190+
transaction_name = ""
191+
if self.client.config.django_transaction_name_from_route and hasattr(request.resolver_match, "route"):
192+
r = request.resolver_match
193+
# if no route is defined (e.g. for the root URL), fall back on url_name and then function name
194+
transaction_name = r.route or r.url_name or get_name_from_func(r.func)
195+
elif view_func:
196+
transaction_name = get_name_from_func(view_func)
197+
if transaction_name:
198+
transaction_name = build_name_with_http_method_prefix(transaction_name, request)
199+
return transaction_name
200+
194201

195202
class ErrorIdMiddleware(MiddlewareMixin):
196203
"""

tests/contrib/django/django_tests.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,52 @@ def test_transaction_metrics_error(django_elasticapm_client, client):
770770
assert transaction["outcome"] == "failure"
771771

772772

773+
def test_transaction_metrics_exception(django_elasticapm_client, client):
774+
with override_settings(
775+
**middleware_setting(django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])
776+
):
777+
assert len(django_elasticapm_client.events[TRANSACTION]) == 0
778+
try:
779+
client.get(reverse("elasticapm-raise-exc"))
780+
except Exception:
781+
pass
782+
assert len(django_elasticapm_client.events[TRANSACTION]) == 1
783+
784+
transactions = django_elasticapm_client.events[TRANSACTION]
785+
errors = django_elasticapm_client.events[ERROR]
786+
787+
assert len(transactions) == 1
788+
assert len(errors) == 1
789+
transaction = transactions[0]
790+
error = errors[0]
791+
assert transaction["duration"] > 0
792+
assert transaction["result"] == "HTTP 5xx"
793+
assert transaction["name"] == "GET tests.contrib.django.testapp.views.raise_exc"
794+
assert transaction["outcome"] == "failure"
795+
assert transaction["name"] == error["transaction"]["name"]
796+
797+
798+
def test_transaction_metrics_404(django_elasticapm_client, client):
799+
with override_settings(
800+
**middleware_setting(django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])
801+
):
802+
assert len(django_elasticapm_client.events[TRANSACTION]) == 0
803+
try:
804+
r = client.get("/non-existant-url")
805+
except Exception as e:
806+
pass
807+
assert len(django_elasticapm_client.events[TRANSACTION]) == 1
808+
809+
transactions = django_elasticapm_client.events[TRANSACTION]
810+
811+
assert len(transactions) == 1
812+
transaction = transactions[0]
813+
assert transaction["duration"] > 0
814+
assert transaction["result"] == "HTTP 4xx"
815+
assert transaction["name"] == ""
816+
assert transaction["outcome"] == "success"
817+
818+
773819
def test_transaction_metrics_debug(django_elasticapm_client, client):
774820
with override_settings(
775821
DEBUG=True, **middleware_setting(django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])

tests/contrib/django/testapp/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import django
3434
from django.conf import settings
35-
from django.http import HttpResponse
35+
from django.http import HttpResponseServerError
3636

3737
from tests.contrib.django.testapp import views
3838

@@ -46,7 +46,7 @@
4646
def handler500(request):
4747
if getattr(settings, "BREAK_THAT_500", False):
4848
raise ValueError("handler500")
49-
return HttpResponse("")
49+
return HttpResponseServerError("")
5050

5151

5252
urlpatterns = (

0 commit comments

Comments
 (0)