diff --git a/events/ua_stuff.py b/events/ua_stuff.py new file mode 100644 index 0000000..02e8a96 --- /dev/null +++ b/events/ua_stuff.py @@ -0,0 +1,45 @@ +from user_agents import parse as ua_parse + + +def enrich_contexts_with_ua(parsed_data): + # GlitchTip has some mechanism to get "synthetic" (i.e. not present in the original, UA-header derived) info into + # first the contexts, which is then propagated (with a whole bunch of other info from contexts) to the tags. Both + # these steps happen on-digest. + # + # I'm not sure what I want myself... on the one hand I'm not a fan of this kind of magic. On the other: if the data + # usually lives in the "context" (e.g. in the JS world), you might as well synthesize it into that location when iit + # is not in that location in the data (but is available through other means). + # + # my set of samples has very little data for contexts, so it's hard to get a feel for it. I imagine that having this + # info available in tags can be useful for searching, or for getting a quick feel of the data (tags show up in the + # RHS of various screens). But again: too little data to tell yet. Add to that that I'm much less inclined than my + # competitors to give OS/browser info the main stage (icons? yuck!). So we'll just parse it, put it "somewhere", and + # look at it again "later". + contexts = parsed_data.get("contexts", {}) + + ua_string = (parsed_data.get("request", {}).get("headers", {})).get("User-Agent") + if ua_string is None: + return contexts + + user_agent = ua_parse(ua_string) + + if "browser" not in contexts: + contexts["browser"] = { + "name": user_agent.browser.family, + "version": user_agent.browser.version_string, + } + + if "os" not in contexts: + contexts["os"] = { + "name": user_agent.os.family, + "version": user_agent.os.version_string, + } + + if "device" not in contexts: + contexts["device"] = { + "family": user_agent.device.family, + "model": user_agent.device.model, + "brand": user_agent.device.brand, + } + + return contexts diff --git a/issues/templates/issues/event_details.html b/issues/templates/issues/event_details.html index 0e445ce..ad1c70f 100644 --- a/issues/templates/issues/event_details.html +++ b/issues/templates/issues/event_details.html @@ -73,7 +73,6 @@ {% endif %} {% if parsed_data.request %} -

Request

@@ -91,15 +90,12 @@ {% if parsed_data.request.headers %}

REQUEST HEADERS

- {# TODO the user's browser and OS can be deduced from the request headers. Perhaps that info should go near the headers #} - {% for key, value in parsed_data.request.headers.items %}
{{ key }}
{{ value }}
{% endfor %} -
{% endif %} {# end if parsed_data.request.headers #} @@ -121,6 +117,34 @@ {% endif %} +{% if contexts %} +

Contexts

+ +
+ {% for context_key, context in contexts|items %} +

{{ context_key|upper }}

+ {% for key, value in context|items %} +
+
{{ key }}
+
{{ value }}
+
+ {% endfor %} + {% endfor %} +
+{% endif %} + +{% comment %} +earlier I said about "tracing": I don't believe much in this whole business of tracing, so I'm not going to display the associated data either + +now that we "just display all contexts" this is no longer true... some of the feeling persists, but I don't think +that I'm so much anti-tracing that I want specifically exclude it from a generic loop. The data's there, let's just +show it (in a non-special way) +{% endcomment %} + +{% comment %} +commented-out like it's 1999. +this is now part of the more general "contexts" handling right above this section. +the fact that we commented-out rather than clobbered reveals a small amount of doubt about whether this is the way. {% if parsed_data.contexts.runtime %} {# sentry gives this prime location (even a picture)... but why... it's kinda obvious what you're working in right? Maybe I could put it at the top of the modules list instead. And check if there's any other relevant info in that runtime context (RTFM) #} @@ -135,6 +159,7 @@ {% endfor %}
{% endif %} +{% endcomment %} {% if parsed_data.modules %}

Modules

@@ -177,21 +202,5 @@ {% endif %} -{% comment %} -{# I don't believe much in this whole business of tracing, so I'm not going to display the associated data either #} -{% if parsed_data.contexts.trace %} -

Trace

- -
- {% for key, value in parsed_data.contexts.trace|items %} -
-
{{ key }}
-
{{ value }}
-
- {% endfor %} -
-{% endif %} -{% endcomment %} - {% endblock %} diff --git a/issues/views.py b/issues/views.py index 1335db8..9e43747 100644 --- a/issues/views.py +++ b/issues/views.py @@ -16,6 +16,8 @@ from bugsink.transaction import durable_atomic from bugsink.period_utils import add_periods_to_datetime from events.models import Event +from events.ua_stuff import enrich_contexts_with_ua + from compat.timestamp import format_timestamp from projects.models import ProjectMembership @@ -409,6 +411,8 @@ def issue_event_details(request, issue, event_pk=None, digest_order=None, nav=No ([("environment", parsed_data["environment"])] if "environment" in parsed_data else []) + \ ([("server_name", parsed_data["server_name"])] if "server_name" in parsed_data else []) + contexts = enrich_contexts_with_ua(parsed_data) + return render(request, "issues/event_details.html", { "tab": "event-details", "this_view": "event_details", @@ -419,6 +423,7 @@ def issue_event_details(request, issue, event_pk=None, digest_order=None, nav=No "parsed_data": parsed_data, "key_info": key_info, "deployment_info": deployment_info, + "contexts": contexts, "mute_options": GLOBAL_MUTE_OPTIONS, }) diff --git a/requirements.txt b/requirements.txt index 7f29e24..cbffbc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ python-dateutil whitenoise requests # for sentry-sdk-extensions, which is loaded in non-dev setup too monofy +user_agents