Просмотр исходного кода

feat(grouping): Create audit log entry for grouping updates (#38798)

We are currently not creating a log entry when the automatic grouping
update kicks in which can be confusing to users. This creates such an
audit entry.

I'm not sure if the locking here is the optimal solution but that's the
main one I found.

Fixes INGEST-1614
Armin Ronacher 2 лет назад
Родитель
Сommit
de57183da5
2 измененных файлов с 35 добавлено и 8 удалено
  1. 27 7
      src/sentry/event_manager.py
  2. 8 1
      tests/sentry/event_manager/test_event_manager.py

+ 27 - 7
src/sentry/event_manager.py

@@ -53,6 +53,7 @@ from sentry.grouping.result import CalculatedHashes
 from sentry.ingest.inbound_filters import FilterStatKeys
 from sentry.killswitches import killswitch_matches_context
 from sentry.lang.native.utils import STORE_CRASH_REPORTS_ALL, convert_crashreport_count
+from sentry.locks import locks
 from sentry.models import (
     CRASH_REPORT_TYPES,
     Activity,
@@ -601,13 +602,32 @@ def _auto_update_grouping(project):
 
     # update to latest grouping config but not if a user is already on
     # beta.
-    if old_grouping != new_grouping and old_grouping != BETA_GROUPING_CONFIG:
-        project.update_option("sentry:secondary_grouping_config", old_grouping)
-        project.update_option(
-            "sentry:secondary_grouping_expiry",
-            int(time.time()) + settings.SENTRY_GROUPING_UPDATE_MIGRATION_PHASE,
-        )
-        project.update_option("sentry:grouping_config", new_grouping)
+    if old_grouping == new_grouping or old_grouping == BETA_GROUPING_CONFIG:
+        return
+
+    from sentry import audit_log
+    from sentry.utils.audit import create_system_audit_entry
+
+    expiry = int(time.time()) + settings.SENTRY_GROUPING_UPDATE_MIGRATION_PHASE
+    project.update_option("sentry:secondary_grouping_config", old_grouping)
+    project.update_option("sentry:secondary_grouping_expiry", expiry)
+    project.update_option("sentry:grouping_config", new_grouping)
+
+    # Write an audit log entry if we haven't yet.  Because the way the auto grouping upgrade
+    # happening is racy, we want to try to write the audit log entry only once.  For this a
+    # cache key is used.  That's not perfect, but should reduce the risk significantly.
+    cache_key = f"grouping-config-update:{project.id}:{old_grouping}"
+    lock = f"grouping-update-lock:{project.id}"
+    if cache.get(cache_key) is None:
+        with locks.get(lock, duration=60, name="grouping-update-lock").acquire():
+            if cache.get(cache_key) is None:
+                cache.set(cache_key, "1", 60 * 5)
+                create_system_audit_entry(
+                    organization=project.organization,
+                    target_object=project.id,
+                    event=audit_log.get_event_id("PROJECT_EDIT"),
+                    data={"sentry:grouping_config": new_grouping, **project.get_audit_log_data()},
+                )
 
 
 @metrics.wraps("event_manager.background_grouping")

+ 8 - 1
tests/sentry/event_manager/test_event_manager.py

@@ -19,7 +19,7 @@ from fixtures.github import (
     GET_PRIOR_COMMIT_EXAMPLE,
     LATER_COMMIT_SHA,
 )
-from sentry import nodestore, tsdb
+from sentry import audit_log, nodestore, tsdb
 from sentry.attachments import CachedAttachment, attachment_cache
 from sentry.constants import MAX_VERSION_LENGTH, DataCategory
 from sentry.event_manager import (
@@ -56,6 +56,7 @@ from sentry.models import (
     ReleaseProjectEnvironment,
     UserReport,
 )
+from sentry.models.auditlogentry import AuditLogEntry
 from sentry.projectoptions.defaults import DEFAULT_GROUPING_CONFIG, LEGACY_GROUPING_CONFIG
 from sentry.spans.grouping.utils import hash_values
 from sentry.testutils import TestCase, assert_mock_called_once_with_partial
@@ -2095,6 +2096,12 @@ class EventManagerTest(TestCase, EventManagerTestMixin):
             project = Project.objects.get(id=self.project.id)
             assert project.get_option("sentry:grouping_config") == DEFAULT_GROUPING_CONFIG
 
+            # and we should see an audit log record.
+            record = AuditLogEntry.objects.first()
+            assert record.event == audit_log.get_event_id("PROJECT_EDIT")
+            assert record.data["sentry:grouping_config"] == DEFAULT_GROUPING_CONFIG
+            assert record.data["slug"] == self.project.slug
+
     @override_options({"store.use-ingest-performance-detection-only": 1.0})
     @override_options({"performance.issues.all.problem-creation": 1.0})
     @override_options({"performance.issues.all.problem-detection": 1.0})