Analytics

This guide steps you through instrumenting your code with Sentry's 3rd-party analytics infrastructure.

Big Query

BigQuery is a Google data warehouse that a lot of our data calls home. This includes all our analytics data and some (not all) production data that might be of interest when joins are necessary for answering richer more complex questions. From sentry/getsentry, our data goes through reload, our ETL for BigQuery.

Adding It In Code

Step 1: Create your Analytics Events!

Conventionally, the analytics events are stored in a file named analytics within the folder of the code it tracks. i.e.: sentry/integrations/analytics.py for the Integration analytic events and sentry/api/analytics.py for the API analytic events.

The Event classes look like this:

Copied
from __future__ import absolute_import, print_function

from sentry import analytics

class ExampleTutorialCreatedEvent(analytics.Event):
  type = 'example_tutorial.created'

   attributes = (
      analytics.Attribute('id'),
      analytics.Attribute('user_id'),
  )

class ExampleTutorialDeletedEvent(analytics.Event):
  type = 'example_tutorial.deleted'

   attributes = (
      analytics.Attribute('id'),
      analytics.Attribute('user_id'),
  )

analytics.register(ExampleTutorialCreatedEvent)
analytics.register(ExampleTutorialDeletedEvent)

Your event classes will inherit from analytics.Event as shown above. All events have a type and attributes.

type

The type describes what the Event is, and this name should be unique across all analytics event classes.

attributes

The attributes will be what you would like to track, for example the user_id of the user performing the action. All attributes must be an Attribute object as shown above. Note that you cannot create an attribute named 'type'.

Finally, register your event classes so that the analytics event_manager will pick them up.

If you are creating the analytics.py file for the first time:

If you are creating a new analytics file for the first time, you will need to add an import to the package's __init__.py.

If the Event classes are defined in a file named: sentry/examples/analytics, then the class below would be defined at sentry/examples/__init__.py:

Copied
from __future__ import absolute_import

from .analytics import *  # NOQA

Here, you have your usual absolute_import but in addition you will import every class in your analytics.py add # NOQA to avoid linter complaints.

Step 2: Add it to the code you want to track

You'll be adding code in some user-facing area like an API endpoint.

Copied
from sentry import analytics

class ExampleEndpoint(Endpoint):

  def post(self, request):
    example = Example.objects.create(...)
    analytics.record(
        'example_tutorial.created',
        id=example.id,
        user_id=request.user.id,
    )
    return Response(serialize(example, request.user))

Do whatever you would normally with the endpoint, then use the analytics.record method to gather the information you want. Note that it takes input of the form:

Copied
analytics.record(
	'event_type_as_string',
	<attribute_name_0>=<value>,
		....
	<attribute_name_n>=<value>,
)

Run the tests that touch the endpoint to ensure everything is Gucci.

Step 3: Add it to ETL

To also send the analytics event in Amplitude, add the event to this file: https://github.com/getsentry/etl/blob/master/etl/operators/analytics_events_schema.py

Copied
'my_backend_event.sent': { // Needs to match the `type` field in the class you created in step 1
    'name': 'My Backend Event', // The event name in Amplitude
    'uid_field': 'target_user_id' // Optional. Field name that represents the user id of the event.
},

Note that in the future this will change so that by default, all events will go to Amplitude.

For Frontend events

All front end events should come from a function generated by makeAnalyticsFunction which will type events to just the ones passed in as a generic. In Sentry, you can use the trackAdvancedAnalyticsEvent and in Getsentry there is trackGetsentryAnalytics (but it is possible to make a custom one like trackIntegrationAnalytics). These functions always send events to reload and can be configured to send events to Amplitude as well. These tie into the hook analytics:track-event-v2.

Step 1: Add the Typescript Definition

First, add the Typescript definiition of the event to an analytics event file inside the analytics directory like issueAnalyticsEvents.tsx. There are two parts of this:

  1. Define the event parameters that get exported (ex: IssueEventParameters)
  2. Define the Reload to Amplitude name mapping (ex: 'issue_search.failed': 'Issue Search: Failed'). If the value is null, then the event will not be sent to Amplitude.

Step 2: Add the Event in Code

Now, you can use the event in code using a function generated by makeAnalyticsFunction (primarily trackAdvancedAnalyticsEvent in Sentry). The function takes the following arguments:

eventKey

This describes the key used in Sentry's own analytics system (Reload). It will map to an Amplitude name as determined in step 1.

analyticsParams

This object will hold all the information you're interested in tracking. Generally, you always pass in an Organization object into it unless the event is not tied to a specific organization. In getsentry, you should pass the Subscription as well. Certain fields like the role and plan will be pulled out of those entities and added to the event payloads.

options

This field allows passing the following optional fields:

  • mapValuesFn: An arbitrary function to map the parameters to new parameters
  • startSession: If true, starts an analytics session. This session can be used to construct funnels. The start of the funnel should have startSession set to true.
  • time: Optional unix timestamp.

Typing and Mapping

All events should be typed which specifies what the payload should be. We also define a mapping from the Reload event name to the Amplitude event name.

Naming Convention

Our current naming convention for Reload events is descriptor.action e.g. what we have above with example_tutorial.created and example_tutorial.deleted . You want these to be specific enough to capture the action of interest but not too specific that you have a million distinctly named events with information that could be captured in the data object. For example, if you can create your example tutorial from multiple places, fight the urge to have the source as part of your descriptor i.e. example_tutorial_onboarding.created and example_tutorial_settings.created. Your future self running analytics will thank you. Amplitude event names should be similar to the Reload event name except you should capitalize the words and use spaces instead of underscores.

getsentry

Copied
import PropTypes from "prop-types";
import React from "react";

import trackGetsentryAnalytics from "getsentry/utils/trackGetsentryAnalytics";

class ExampleComponent extends React.Component {
  static propTypes = {
    organization: PropTypes.object,
  };

  componentDidMount() {
    trackGetsentryAnalytics("example_tutorial.created", {
      organization,
      subscription,
      source: "wakanda",
    });
  }

  render() {
    return <h1> HI! </h1>;
  }
}

sentry

All you'll actually need is to import analytics from utils and call it wherever you need. Keep in mind the effect of React lifecycles on your data. In this case, we only want to send one event when the component mounts so we place it in componentDidMount .

Copied
import PropTypes from "prop-types";
import React from "react";

import trackAdvancedAnalyticsEvent from "sentry/utils/analytics/trackAdvancedAnalyticsEvent";

class ExampleComponent extends React.Component {
  static propTypes = {
    organization: PropTypes.object,
  };

  componentDidMount() {
    trackAdvancedAnalyticsEvent("example_tutorial.deleted", {
      organization,
      source: "wakanda",
    });
  }
  render() {
    return <h1> HI! </h1>;
  }
}

For Backend and Frontend

Add the events to Reload

We have a repo called https://github.com/getsentry/reload. The https://github.com/getsentry/reload/blob/master/reload_app/events.py is a legacy list that specifies an optional schema for events. Newer front-end analytics events don't need to be added to this reload Schema. file holds a list of all accepted events.

Here's an example of what that schema looks like:

Copied
'example_tutorial.created': {
	'org_id': int,
	'source': str,
	'plan': str,
},

'example_tutorial.deleted': {
	'org_id': int,
	'source': str,
},

Deploy your changes in Reload through freight.

Metrics

Track aggregrate stats with Metrics. For example, this can be used to track aggregate response codes for an endpoint.

Import the metrics library and use the metrics.inc function. The key needs to be unique.

Copied
from sentry.utils import metrics

metrics.incr(
    "codeowners.create.http_response", # needs to be unique
    sample_rate=1.0,
    tags={"status": status},
)

If you don't put a sample rate, you get 1 in 10 events. If the service is expected to have low traffic, we can start with a sample rate of 1.

You can edit this page on GitHub.