Skip to content

Commit b8b13da

Browse files
authored
feat(logs): Return hasLogs in projects response (#96591)
1 parent 53b78f5 commit b8b13da

File tree

9 files changed

+47
-0
lines changed

9 files changed

+47
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from sentry import analytics
2+
3+
4+
@analytics.eventclass("first_log.sent")
5+
class FirstLogSentEvent(analytics.Event):
6+
organization_id: int
7+
project_id: int
8+
platform: str | None = None
9+
user_id: int | None = None
10+
11+
12+
analytics.register(FirstLogSentEvent)

src/sentry/api/serializers/models/project.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ class ProjectSerializerBaseResponse(_ProjectSerializerOptionalBaseResponse):
285285
hasInsightsLlmMonitoring: bool
286286
hasInsightsAgentMonitoring: bool
287287
hasInsightsMCP: bool
288+
hasLogs: bool
288289

289290

290291
class ProjectSerializerResponse(ProjectSerializerBaseResponse):
@@ -554,6 +555,7 @@ def serialize(
554555
"hasInsightsLlmMonitoring": bool(obj.flags.has_insights_llm_monitoring),
555556
"hasInsightsAgentMonitoring": bool(obj.flags.has_insights_agent_monitoring),
556557
"hasInsightsMCP": bool(obj.flags.has_insights_mcp),
558+
"hasLogs": bool(obj.flags.has_logs),
557559
"isInternal": obj.is_internal_project(),
558560
"isPublic": obj.public,
559561
# Projects don't have avatar uploads, but we need to maintain the payload shape for
@@ -790,6 +792,7 @@ def serialize( # type: ignore[override] # intentionally different data shape
790792
hasInsightsLlmMonitoring=bool(obj.flags.has_insights_llm_monitoring),
791793
hasInsightsAgentMonitoring=bool(obj.flags.has_insights_agent_monitoring),
792794
hasInsightsMCP=bool(obj.flags.has_insights_mcp),
795+
hasLogs=bool(obj.flags.has_logs),
793796
platform=obj.platform,
794797
platforms=attrs["platforms"],
795798
latestRelease=attrs["latest_release"],

src/sentry/apidocs/examples/organization_examples.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ class OrganizationExamples:
389389
"hasInsightsLlmMonitoring": False,
390390
"hasInsightsAgentMonitoring": False,
391391
"hasInsightsMCP": False,
392+
"hasLogs": False,
392393
"platform": "node",
393394
"platforms": [],
394395
"latestRelease": None,
@@ -453,6 +454,7 @@ class OrganizationExamples:
453454
"hasInsightsLlmMonitoring": False,
454455
"hasInsightsAgentMonitoring": False,
455456
"hasInsightsMCP": False,
457+
"hasLogs": False,
456458
"latestRelease": None,
457459
}
458460
],

src/sentry/apidocs/examples/project_examples.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"hasInsightsLlmMonitoring": False,
103103
"hasInsightsAgentMonitoring": False,
104104
"hasInsightsMCP": False,
105+
"hasLogs": False,
105106
"isInternal": False,
106107
"isPublic": False,
107108
"avatar": {"avatarType": "letter_avatar", "avatarUuid": None},
@@ -339,6 +340,7 @@
339340
"hasInsightsLlmMonitoring": False,
340341
"hasInsightsAgentMonitoring": False,
341342
"hasInsightsMCP": False,
343+
"hasLogs": False,
342344
"platform": "node-express",
343345
"platforms": [],
344346
"latestRelease": None,

src/sentry/apidocs/examples/team_examples.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ class TeamExamples:
250250
"hasInsightsLlmMonitoring": False,
251251
"hasInsightsAgentMonitoring": False,
252252
"hasInsightsMCP": False,
253+
"hasLogs": False,
253254
"isInternal": False,
254255
"isPublic": False,
255256
"avatar": {"avatarType": "letter_avatar", "avatarUuid": None},
@@ -309,6 +310,7 @@ class TeamExamples:
309310
"hasInsightsLlmMonitoring": False,
310311
"hasInsightsAgentMonitoring": False,
311312
"hasInsightsMCP": False,
313+
"hasLogs": False,
312314
"isInternal": False,
313315
"isPublic": False,
314316
"avatar": {"avatarType": "letter_avatar", "avatarUuid": None},

src/sentry/projects/services/project/model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class RpcProjectFlags(RpcModel):
5959
has_flags: bool
6060
has_insights_agent_monitoring: bool
6161
has_insights_mcp: bool
62+
has_logs: bool
6263

6364

6465
class RpcProject(RpcModel):

src/sentry/receivers/onboarding.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from sentry.analytics.events.first_feedback_sent import FirstFeedbackSentEvent
1616
from sentry.analytics.events.first_flag_sent import FirstFlagSentEvent
1717
from sentry.analytics.events.first_insight_span_sent import FirstInsightSpanSentEvent
18+
from sentry.analytics.events.first_log_sent import FirstLogSentEvent
1819
from sentry.analytics.events.first_profile_sent import FirstProfileSentEvent
1920
from sentry.analytics.events.first_replay_sent import FirstReplaySentEvent
2021
from sentry.analytics.events.first_transaction_sent import FirstTransactionSentEvent
@@ -43,6 +44,7 @@
4344
first_feedback_received,
4445
first_flag_received,
4546
first_insight_span_received,
47+
first_log_received,
4648
first_new_feedback_received,
4749
first_profile_received,
4850
first_replay_received,
@@ -319,6 +321,18 @@ def record_first_insight_span(project, module: InsightModules, **kwargs):
319321
)
320322

321323

324+
@first_log_received.connect(weak=False, dispatch_uid="onboarding.record_first_log")
325+
def record_first_log(project, **kwargs):
326+
analytics.record(
327+
FirstLogSentEvent(
328+
user_id=get_owner_id(project),
329+
organization_id=project.organization_id,
330+
project_id=project.id,
331+
platform=project.platform,
332+
)
333+
)
334+
335+
322336
# TODO (mifu67): update this to use the new org member invite model
323337
@member_invited.connect(weak=False, dispatch_uid="onboarding.record_member_invited")
324338
def record_member_invited(member, user, **kwargs):

src/sentry/signals.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def _log_robust_failure(self, receiver: object, err: Exception) -> None:
119119
cron_monitor_created = BetterSignal() # ["project", "user", "from_upsert"]
120120
first_cron_checkin_received = BetterSignal() # ["project", "monitor_id"]
121121
first_insight_span_received = BetterSignal() # ["project", "module"]
122+
first_log_received = BetterSignal() # ["project"]
122123
member_invited = BetterSignal() # ["member", "user"]
123124
member_joined = BetterSignal() # ["organization_member_id", "organization_id", "user_id"]
124125
plugin_enabled = BetterSignal() # ["plugin", "project", "user"]

tests/sentry/api/serializers/test_project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,16 @@ def test_has_flags_flag(self) -> None:
494494
result = serialize(self.project, self.user, ProjectSummarySerializer())
495495
assert result["hasFlags"] is True
496496

497+
def test_has_logs_flag(self) -> None:
498+
result = serialize(self.project, self.user, ProjectSummarySerializer())
499+
assert result["hasLogs"] is False
500+
501+
self.project.first_event = timezone.now()
502+
self.project.update(flags=F("flags").bitor(Project.flags.has_logs))
503+
504+
result = serialize(self.project, self.user, ProjectSummarySerializer())
505+
assert result["hasLogs"] is True
506+
497507
def test_no_environments(self) -> None:
498508
# remove environments and related models
499509
Deploy.objects.all().delete()

0 commit comments

Comments
 (0)