title: How does Sentry aggregate errors?
body: <p>I am using Sentry (in a django project), and I'd like to know how I can get the errors to aggregate properly. I am logging certain user actions as errors, so there is no underlying system exception, and am using the <code>culprit</code> attribute to set a friendly error name. The message is templated, and contains a common message ("User 'x' was unable to perform action because 'y'"), but is never exactly the same (different users, different conditions).</p>
  
  <p>Sentry clearly uses some set of attributes under the hood to determine whether to aggregate errors as the same exception, but despite having looked through the code, I can't work out how.</p>
  
  <p>Can anyone short-cut my having to dig further into the code and tell me what properties I need to set in order to manage aggregation as I would like?</p>
  
  <p>[UPDATE 1: event grouping]</p>
  
  <p>This line appears in sentry.models.Group:</p>
  
  <pre><code>class Group(MessageBase):
      """
      Aggregated message which summarizes a set of Events.
      """
      ...
  
      class Meta:
          unique_together = (('project', 'logger', 'culprit', 'checksum'),)
      ...
  </code></pre>
  
  <p>Which makes sense - project, logger and culprit I am setting at the moment - the problem is <code>checksum</code>. I will investigate further, however 'checksum' suggests that binary equivalence, which is never going to work - it must be possible to group instances of the same exception, with differenct attributes?</p>
  
  <p>[UPDATE 2: event checksums]</p>
  
  <p>The event checksum comes from the <code>sentry.manager.get_checksum_from_event</code> method:</p>
  
  <pre><code>def get_checksum_from_event(event):
      for interface in event.interfaces.itervalues():
          result = interface.get_hash()
          if result:
              hash = hashlib.md5()
              for r in result:
                  hash.update(to_string(r))
              return hash.hexdigest()
      return hashlib.md5(to_string(event.message)).hexdigest()
  </code></pre>
  
  <p>Next stop - where do the event <code>interfaces</code> come from?</p>
  
  <p>[UPDATE 3: event interfaces]</p>
  
  <p>I have worked out that <a href="http://sentry.readthedocs.org/en/latest/developer/interfaces/index.html">interfaces</a> refer to the standard mechanism for describing data passed into sentry events, and that I am using the standard <code>sentry.interfaces.Message</code> and <code>sentry.interfaces.User</code> interfaces.</p>
  
  <p>Both of these will contain different data depending on the exception instance - and so a checksum will never match. Is there any way that I can exclude these from the checksum calculation? (Or at least the <code>User</code> interface value, as that has to be different - the <code>Message</code> interface value I could standardise.)</p>
  
  <p>[UPDATE 4: solution]</p>
  
  <p>Here are the two <code>get_hash</code> functions for the <code>Message</code> and <code>User</code> interfaces respectively:</p>
  
  <pre><code># sentry.interfaces.Message
  def get_hash(self):
      return [self.message]
  
  # sentry.interfaces.User
  def get_hash(self):
      return []
  </code></pre>
  
  <p>Looking at these two, only the <code>Message.get_hash</code> interface will return a value that is picked up by the <code>get_checksum_for_event</code> method, and so this is the one that will be returned (hashed etc.) The net effect of this is that the the checksum is evaluated on the message alone - which in theory means that I can standardise the message and keep the user definition unique.</p>
  
  <p>I've answered my own question here, but hopefully my investigation is of use to others having the same problem. (As an aside, I've also submitted a pull request against the Sentry documentation as part of this ;-))</p>
  
  <p><em>(Note to anyone using / extending Sentry with custom interfaces - if you want to avoid your interface being use to group exceptions, return an empty list.)</em></p>