We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Parse the email template contexts for identifiers and log them along with the mail.queued event.
We currently log types of mail sent but do not add any context along with that, so it's easy to see that someone got an email about an event, but since the `event_id` is not passed along, the log event is somewhat useless.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/sentry/plugins/bases/notify.py`
Content:
```
1 """
2 sentry.plugins.bases.notify
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 
5 :copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.
6 :license: BSD, see LICENSE for more details.
7 """
8 from __future__ import absolute_import, print_function
9 
10 import logging
11 import six
12 
13 from django import forms
14 
15 from sentry.app import (
16     digests,
17     ratelimiter,
18 )
19 from sentry.digests import get_option_key as get_digest_option_key
20 from sentry.digests.notifications import (
21     event_to_record,
22     unsplit_key,
23 )
24 from sentry.plugins import Notification, Plugin
25 from sentry.models import (
26     ProjectOption,
27     UserOption,
28 )
29 from sentry.tasks.digests import deliver_digest
30 
31 
32 class NotificationConfigurationForm(forms.Form):
33     pass
34 
35 
36 class BaseNotificationUserOptionsForm(forms.Form):
37     def __init__(self, plugin, user, *args, **kwargs):
38         self.plugin = plugin
39         self.user = user
40         super(BaseNotificationUserOptionsForm, self).__init__(*args, **kwargs)
41 
42     def get_title(self):
43         return self.plugin.get_conf_title()
44 
45     def get_description(self):
46         return ""
47 
48     def save(self):
49         raise NotImplementedError
50 
51 
52 class NotificationPlugin(Plugin):
53     description = ('Notify project members when a new event is seen for the first time, or when an '
54                    'already resolved event has changed back to unresolved.')
55     # site_conf_form = NotificationConfigurationForm
56     project_conf_form = NotificationConfigurationForm
57 
58     def get_plugin_type(self):
59         return 'notification'
60 
61     def notify(self, notification):
62         event = notification.event
63         return self.notify_users(event.group, event)
64 
65     def rule_notify(self, event, futures):
66         rules = []
67         for future in futures:
68             rules.append(future.rule)
69             if not future.kwargs:
70                 continue
71             raise NotImplementedError('The default behavior for notification de-duplication does not support args')
72 
73         project = event.group.project
74         if hasattr(self, 'notify_digest') and digests.enabled(project):
75             get_digest_option = lambda key: ProjectOption.objects.get_value(
76                 project,
77                 get_digest_option_key(self.get_conf_key(), key),
78             )
79             digest_key = unsplit_key(self, event.group.project)
80             immediate_delivery = digests.add(
81                 digest_key,
82                 event_to_record(event, rules),
83                 increment_delay=get_digest_option('increment_delay'),
84                 maximum_delay=get_digest_option('maximum_delay'),
85             )
86             if immediate_delivery:
87                 deliver_digest.delay(digest_key)
88 
89         else:
90             notification = Notification(
91                 event=event,
92                 rules=rules,
93             )
94             self.notify(notification)
95             self.logger.info('notification.dispatched', extra={
96                 'event_id': event.id,
97                 'plugin': self.slug,
98                 'rule_id': rules[0].id if rules else None,
99             })
100 
101     def notify_users(self, group, event, fail_silently=False):
102         raise NotImplementedError
103 
104     def notify_about_activity(self, activity):
105         pass
106 
107     def get_sendable_users(self, project):
108         """
109         Return a collection of user IDs that are eligible to receive
110         notifications for the provided project.
111         """
112         conf_key = self.get_conf_key()
113 
114         alert_settings = dict(
115             (o.user_id, int(o.value))
116             for o in UserOption.objects.filter(
117                 project=project,
118                 key='%s:alert' % conf_key,
119             )
120         )
121 
122         disabled = set(u for u, v in six.iteritems(alert_settings) if v == 0)
123 
124         member_set = set(project.member_set.exclude(
125             user__in=disabled,
126         ).values_list('user', flat=True))
127 
128         # determine members default settings
129         members_to_check = set(u for u in member_set if u not in alert_settings)
130         if members_to_check:
131             disabled = set(UserOption.objects.filter(
132                 key='subscribe_by_default',
133                 value='0',
134                 user__in=members_to_check,
135             ).values_list('user', flat=True))
136             member_set = [x for x in member_set if x not in disabled]
137 
138         return member_set
139 
140     def __is_rate_limited(self, group, event):
141         return ratelimiter.is_limited(
142             project=group.project,
143             key=self.get_conf_key(),
144             limit=10,
145         )
146 
147     def is_configured(self, project):
148         raise NotImplementedError
149 
150     def should_notify(self, group, event):
151         project = event.project
152         if not self.is_configured(project=project):
153             return False
154 
155         if group.is_muted():
156             return False
157 
158         # If the plugin doesn't support digests or they are not enabled,
159         # perform rate limit checks to support backwards compatibility with
160         # older plugins.
161         if not (hasattr(self, 'notify_digest') and digests.enabled(project)) and self.__is_rate_limited(group, event):
162             logger = logging.getLogger('sentry.plugins.{0}'.format(self.get_conf_key()))
163             logger.info('notification.rate_limited', extra={'project_id': project.id})
164             return False
165 
166         return True
167 
168     def test_configuration(self, project):
169         from sentry.utils.samples import create_sample_event
170         event = create_sample_event(project, platform='python')
171         notification = Notification(event=event)
172         return self.notify(notification)
173 
174     def get_notification_doc_html(self, **kwargs):
175         return ""
176 
177 
178 # Backwards-compatibility
179 NotifyConfigurationForm = NotificationConfigurationForm
180 NotifyPlugin = NotificationPlugin
181
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/sentry/plugins/bases/notify.py b/src/sentry/plugins/bases/notify.py
--- a/src/sentry/plugins/bases/notify.py
+++ b/src/sentry/plugins/bases/notify.py
@@ -64,19 +64,28 @@
 
     def rule_notify(self, event, futures):
         rules = []
+        extra = {
+            'event_id': event.id,
+            'group_id': event.group_id,
+            'plugin': self.slug,
+        }
+        log_event = 'dispatched'
         for future in futures:
             rules.append(future.rule)
+            extra['rule_id'] = future.rule.id
             if not future.kwargs:
                 continue
             raise NotImplementedError('The default behavior for notification de-duplication does not support args')
 
         project = event.group.project
+        extra['project_id'] = project.id
         if hasattr(self, 'notify_digest') and digests.enabled(project):
             get_digest_option = lambda key: ProjectOption.objects.get_value(
                 project,
                 get_digest_option_key(self.get_conf_key(), key),
             )
             digest_key = unsplit_key(self, event.group.project)
+            extra['digest_key'] = digest_key
             immediate_delivery = digests.add(
                 digest_key,
                 event_to_record(event, rules),
@@ -85,6 +94,8 @@
             )
             if immediate_delivery:
                 deliver_digest.delay(digest_key)
+            else:
+                log_event = 'digested'
 
         else:
             notification = Notification(
@@ -92,11 +103,8 @@
                 rules=rules,
             )
             self.notify(notification)
-            self.logger.info('notification.dispatched', extra={
-                'event_id': event.id,
-                'plugin': self.slug,
-                'rule_id': rules[0].id if rules else None,
-            })
+
+        self.logger.info('notification.%s' % log_event, extra=extra)
 
     def notify_users(self, group, event, fail_silently=False):
         raise NotImplementedError

{"golden_diff": "diff --git a/src/sentry/plugins/bases/notify.py b/src/sentry/plugins/bases/notify.py\n--- a/src/sentry/plugins/bases/notify.py\n+++ b/src/sentry/plugins/bases/notify.py\n@@ -64,19 +64,28 @@\n \n     def rule_notify(self, event, futures):\n         rules = []\n+        extra = {\n+            'event_id': event.id,\n+            'group_id': event.group_id,\n+            'plugin': self.slug,\n+        }\n+        log_event = 'dispatched'\n         for future in futures:\n             rules.append(future.rule)\n+            extra['rule_id'] = future.rule.id\n             if not future.kwargs:\n                 continue\n             raise NotImplementedError('The default behavior for notification de-duplication does not support args')\n \n         project = event.group.project\n+        extra['project_id'] = project.id\n         if hasattr(self, 'notify_digest') and digests.enabled(project):\n             get_digest_option = lambda key: ProjectOption.objects.get_value(\n                 project,\n                 get_digest_option_key(self.get_conf_key(), key),\n             )\n             digest_key = unsplit_key(self, event.group.project)\n+            extra['digest_key'] = digest_key\n             immediate_delivery = digests.add(\n                 digest_key,\n                 event_to_record(event, rules),\n@@ -85,6 +94,8 @@\n             )\n             if immediate_delivery:\n                 deliver_digest.delay(digest_key)\n+            else:\n+                log_event = 'digested'\n \n         else:\n             notification = Notification(\n@@ -92,11 +103,8 @@\n                 rules=rules,\n             )\n             self.notify(notification)\n-            self.logger.info('notification.dispatched', extra={\n-                'event_id': event.id,\n-                'plugin': self.slug,\n-                'rule_id': rules[0].id if rules else None,\n-            })\n+\n+        self.logger.info('notification.%s' % log_event, extra=extra)\n \n     def notify_users(self, group, event, fail_silently=False):\n         raise NotImplementedError\n", "issue": "Parse the email template contexts for identifiers and log them along with the mail.queued event.\nWe currently log types of mail sent but do not add any context along with that, so it's easy to see that someone got an email about an event, but since the `event_id` is not passed along, the log event is somewhat useless.\n\n", "before_files": [{"content": "\"\"\"\nsentry.plugins.bases.notify\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.\n:license: BSD, see LICENSE for more details.\n\"\"\"\nfrom __future__ import absolute_import, print_function\n\nimport logging\nimport six\n\nfrom django import forms\n\nfrom sentry.app import (\n    digests,\n    ratelimiter,\n)\nfrom sentry.digests import get_option_key as get_digest_option_key\nfrom sentry.digests.notifications import (\n    event_to_record,\n    unsplit_key,\n)\nfrom sentry.plugins import Notification, Plugin\nfrom sentry.models import (\n    ProjectOption,\n    UserOption,\n)\nfrom sentry.tasks.digests import deliver_digest\n\n\nclass NotificationConfigurationForm(forms.Form):\n    pass\n\n\nclass BaseNotificationUserOptionsForm(forms.Form):\n    def __init__(self, plugin, user, *args, **kwargs):\n        self.plugin = plugin\n        self.user = user\n        super(BaseNotificationUserOptionsForm, self).__init__(*args, **kwargs)\n\n    def get_title(self):\n        return self.plugin.get_conf_title()\n\n    def get_description(self):\n        return \"\"\n\n    def save(self):\n        raise NotImplementedError\n\n\nclass NotificationPlugin(Plugin):\n    description = ('Notify project members when a new event is seen for the first time, or when an '\n                   'already resolved event has changed back to unresolved.')\n    # site_conf_form = NotificationConfigurationForm\n    project_conf_form = NotificationConfigurationForm\n\n    def get_plugin_type(self):\n        return 'notification'\n\n    def notify(self, notification):\n        event = notification.event\n        return self.notify_users(event.group, event)\n\n    def rule_notify(self, event, futures):\n        rules = []\n        for future in futures:\n            rules.append(future.rule)\n            if not future.kwargs:\n                continue\n            raise NotImplementedError('The default behavior for notification de-duplication does not support args')\n\n        project = event.group.project\n        if hasattr(self, 'notify_digest') and digests.enabled(project):\n            get_digest_option = lambda key: ProjectOption.objects.get_value(\n                project,\n                get_digest_option_key(self.get_conf_key(), key),\n            )\n            digest_key = unsplit_key(self, event.group.project)\n            immediate_delivery = digests.add(\n                digest_key,\n                event_to_record(event, rules),\n                increment_delay=get_digest_option('increment_delay'),\n                maximum_delay=get_digest_option('maximum_delay'),\n            )\n            if immediate_delivery:\n                deliver_digest.delay(digest_key)\n\n        else:\n            notification = Notification(\n                event=event,\n                rules=rules,\n            )\n            self.notify(notification)\n            self.logger.info('notification.dispatched', extra={\n                'event_id': event.id,\n                'plugin': self.slug,\n                'rule_id': rules[0].id if rules else None,\n            })\n\n    def notify_users(self, group, event, fail_silently=False):\n        raise NotImplementedError\n\n    def notify_about_activity(self, activity):\n        pass\n\n    def get_sendable_users(self, project):\n        \"\"\"\n        Return a collection of user IDs that are eligible to receive\n        notifications for the provided project.\n        \"\"\"\n        conf_key = self.get_conf_key()\n\n        alert_settings = dict(\n            (o.user_id, int(o.value))\n            for o in UserOption.objects.filter(\n                project=project,\n                key='%s:alert' % conf_key,\n            )\n        )\n\n        disabled = set(u for u, v in six.iteritems(alert_settings) if v == 0)\n\n        member_set = set(project.member_set.exclude(\n            user__in=disabled,\n        ).values_list('user', flat=True))\n\n        # determine members default settings\n        members_to_check = set(u for u in member_set if u not in alert_settings)\n        if members_to_check:\n            disabled = set(UserOption.objects.filter(\n                key='subscribe_by_default',\n                value='0',\n                user__in=members_to_check,\n            ).values_list('user', flat=True))\n            member_set = [x for x in member_set if x not in disabled]\n\n        return member_set\n\n    def __is_rate_limited(self, group, event):\n        return ratelimiter.is_limited(\n            project=group.project,\n            key=self.get_conf_key(),\n            limit=10,\n        )\n\n    def is_configured(self, project):\n        raise NotImplementedError\n\n    def should_notify(self, group, event):\n        project = event.project\n        if not self.is_configured(project=project):\n            return False\n\n        if group.is_muted():\n            return False\n\n        # If the plugin doesn't support digests or they are not enabled,\n        # perform rate limit checks to support backwards compatibility with\n        # older plugins.\n        if not (hasattr(self, 'notify_digest') and digests.enabled(project)) and self.__is_rate_limited(group, event):\n            logger = logging.getLogger('sentry.plugins.{0}'.format(self.get_conf_key()))\n            logger.info('notification.rate_limited', extra={'project_id': project.id})\n            return False\n\n        return True\n\n    def test_configuration(self, project):\n        from sentry.utils.samples import create_sample_event\n        event = create_sample_event(project, platform='python')\n        notification = Notification(event=event)\n        return self.notify(notification)\n\n    def get_notification_doc_html(self, **kwargs):\n        return \"\"\n\n\n# Backwards-compatibility\nNotifyConfigurationForm = NotificationConfigurationForm\nNotifyPlugin = NotificationPlugin\n", "path": "src/sentry/plugins/bases/notify.py"}], "after_files": [{"content": "\"\"\"\nsentry.plugins.bases.notify\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.\n:license: BSD, see LICENSE for more details.\n\"\"\"\nfrom __future__ import absolute_import, print_function\n\nimport logging\nimport six\n\nfrom django import forms\n\nfrom sentry.app import (\n    digests,\n    ratelimiter,\n)\nfrom sentry.digests import get_option_key as get_digest_option_key\nfrom sentry.digests.notifications import (\n    event_to_record,\n    unsplit_key,\n)\nfrom sentry.plugins import Notification, Plugin\nfrom sentry.models import (\n    ProjectOption,\n    UserOption,\n)\nfrom sentry.tasks.digests import deliver_digest\n\n\nclass NotificationConfigurationForm(forms.Form):\n    pass\n\n\nclass BaseNotificationUserOptionsForm(forms.Form):\n    def __init__(self, plugin, user, *args, **kwargs):\n        self.plugin = plugin\n        self.user = user\n        super(BaseNotificationUserOptionsForm, self).__init__(*args, **kwargs)\n\n    def get_title(self):\n        return self.plugin.get_conf_title()\n\n    def get_description(self):\n        return \"\"\n\n    def save(self):\n        raise NotImplementedError\n\n\nclass NotificationPlugin(Plugin):\n    description = ('Notify project members when a new event is seen for the first time, or when an '\n                   'already resolved event has changed back to unresolved.')\n    # site_conf_form = NotificationConfigurationForm\n    project_conf_form = NotificationConfigurationForm\n\n    def get_plugin_type(self):\n        return 'notification'\n\n    def notify(self, notification):\n        event = notification.event\n        return self.notify_users(event.group, event)\n\n    def rule_notify(self, event, futures):\n        rules = []\n        extra = {\n            'event_id': event.id,\n            'group_id': event.group_id,\n            'plugin': self.slug,\n        }\n        log_event = 'dispatched'\n        for future in futures:\n            rules.append(future.rule)\n            extra['rule_id'] = future.rule.id\n            if not future.kwargs:\n                continue\n            raise NotImplementedError('The default behavior for notification de-duplication does not support args')\n\n        project = event.group.project\n        extra['project_id'] = project.id\n        if hasattr(self, 'notify_digest') and digests.enabled(project):\n            get_digest_option = lambda key: ProjectOption.objects.get_value(\n                project,\n                get_digest_option_key(self.get_conf_key(), key),\n            )\n            digest_key = unsplit_key(self, event.group.project)\n            extra['digest_key'] = digest_key\n            immediate_delivery = digests.add(\n                digest_key,\n                event_to_record(event, rules),\n                increment_delay=get_digest_option('increment_delay'),\n                maximum_delay=get_digest_option('maximum_delay'),\n            )\n            if immediate_delivery:\n                deliver_digest.delay(digest_key)\n            else:\n                log_event = 'digested'\n\n        else:\n            notification = Notification(\n                event=event,\n                rules=rules,\n            )\n            self.notify(notification)\n\n        self.logger.info('notification.%s' % log_event, extra=extra)\n\n    def notify_users(self, group, event, fail_silently=False):\n        raise NotImplementedError\n\n    def notify_about_activity(self, activity):\n        pass\n\n    def get_sendable_users(self, project):\n        \"\"\"\n        Return a collection of user IDs that are eligible to receive\n        notifications for the provided project.\n        \"\"\"\n        conf_key = self.get_conf_key()\n\n        alert_settings = dict(\n            (o.user_id, int(o.value))\n            for o in UserOption.objects.filter(\n                project=project,\n                key='%s:alert' % conf_key,\n            )\n        )\n\n        disabled = set(u for u, v in six.iteritems(alert_settings) if v == 0)\n\n        member_set = set(project.member_set.exclude(\n            user__in=disabled,\n        ).values_list('user', flat=True))\n\n        # determine members default settings\n        members_to_check = set(u for u in member_set if u not in alert_settings)\n        if members_to_check:\n            disabled = set(UserOption.objects.filter(\n                key='subscribe_by_default',\n                value='0',\n                user__in=members_to_check,\n            ).values_list('user', flat=True))\n            member_set = [x for x in member_set if x not in disabled]\n\n        return member_set\n\n    def __is_rate_limited(self, group, event):\n        return ratelimiter.is_limited(\n            project=group.project,\n            key=self.get_conf_key(),\n            limit=10,\n        )\n\n    def is_configured(self, project):\n        raise NotImplementedError\n\n    def should_notify(self, group, event):\n        project = event.project\n        if not self.is_configured(project=project):\n            return False\n\n        if group.is_muted():\n            return False\n\n        # If the plugin doesn't support digests or they are not enabled,\n        # perform rate limit checks to support backwards compatibility with\n        # older plugins.\n        if not (hasattr(self, 'notify_digest') and digests.enabled(project)) and self.__is_rate_limited(group, event):\n            logger = logging.getLogger('sentry.plugins.{0}'.format(self.get_conf_key()))\n            logger.info('notification.rate_limited', extra={'project_id': project.id})\n            return False\n\n        return True\n\n    def test_configuration(self, project):\n        from sentry.utils.samples import create_sample_event\n        event = create_sample_event(project, platform='python')\n        notification = Notification(event=event)\n        return self.notify(notification)\n\n    def get_notification_doc_html(self, **kwargs):\n        return \"\"\n\n\n# Backwards-compatibility\nNotifyConfigurationForm = NotificationConfigurationForm\nNotifyPlugin = NotificationPlugin\n", "path": "src/sentry/plugins/bases/notify.py"}]}