Skip to content

Commit 8c8868c

Browse files
authored
Merge branch 'master' into fix/trace-indicator-label-visibility-at-edges
2 parents 7696a87 + c3f9249 commit 8c8868c

237 files changed

Lines changed: 6703 additions & 2105 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/analytics/SKILL.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,10 @@ Every `trackAnalytics` call flows through the GetSentry override in `static/gsAp
115115

116116
## Non-Negotiable Constraints
117117

118-
1. **All events must be type-safe.** Every event key must exist in a `*EventParameters` type and be registered in the domain's event map.
119-
2. **All events flow through `trackAnalytics()`.** Never call `window.analytics`, `Amplitude.track()`, or any other analytics SDK directly.
120-
3. **Organization context is automatic.** Pass `organization` to `trackAnalytics` — the override system handles the rest.
121-
4. **Reuse over create.** Always search for existing events before defining new ones.
122-
5. **One event per interaction.** Do not fire multiple events for the same user action.
123-
6. **No PII in event parameters.** Never include user emails, IP addresses, full names, or other personally identifiable information. Use opaque IDs (org ID, user ID) when identity context is needed.
118+
1. **`trackAnalytics()` calls must be type-safe.** Every event key passed to `trackAnalytics()` must exist in a `*EventParameters` type and be registered in the domain's event map. This enforces that `organization` is always passed and that call sites sharing the same event key use consistent parameters. Declarative helpers — button `analyticsEventKey`/`analyticsParams` props and `useRouteAnalyticsParams` — are exempt because each instance is a one-off: two buttons labeled "Save" are inherently different (different forms, different contexts), so there are no shared call sites and less value in centralized types.
119+
2. **Prefer declarative helpers.** Use button analytics props and route analytics hooks when they fit. Fall back to `trackAnalytics()` only for interactions those helpers don't cover.
120+
3. **All events flow through `trackAnalytics()` or built-in helpers.** Never call `window.analytics`, `Amplitude.track()`, or any other analytics SDK directly.
121+
4. **Organization context is automatic.** Pass `organization` to `trackAnalytics` — the override system handles the rest.
122+
5. **Reuse over create.** Always search for existing events before defining new ones.
123+
6. **One event per interaction.** Do not fire multiple events for the same user action.
124+
7. **No PII in event parameters.** Never include user emails, IP addresses, full names, or other personally identifiable information. Use opaque IDs (org ID, user ID) when identity context is needed.

‎.agents/skills/analytics/references/tracking-patterns.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ useRouteAnalyticsParams({
4949

5050
## Button Click Tracking
5151

52-
Use built-in button analytics props for simple click tracking. No event definition required for Reload-only tracking.
52+
Use built-in button analytics props for simple click tracking. These are the preferred approach when a `<Button>` already exists — no event definition or type registration required.
5353

5454
```tsx
5555
<Button
@@ -72,7 +72,7 @@ Use built-in button analytics props for simple click tracking. No event definiti
7272
**Rules:**
7373

7474
- Prefer button props over a manual `trackAnalytics` call in an `onClick` handler.
75-
- If you need the event to be type-safe and appear in the typed event registry, also define it in the domain event file. Button props work without registration but lose type safety.
75+
- Button props do not require registration in the typed event registry. Each button instance is inherently a one-off — even two "Save" buttons are different buttons tied to different forms — so there are no shared call sites that would benefit from centralized types.
7676
- The tracking fires via `TrackingContext` — the GetSentry override wires it to Reload/Amplitude.
7777

7878
## Manual `trackAnalytics()` Calls

‎devservices/config.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ x-sentry-service-config:
199199
[snuba, postgres, relay, taskbroker, spotlight, taskworker, taskworker-scheduler]
200200
backend-ci:
201201
[snuba, postgres, redis, bigtable, redis-cluster, symbolicator, objectstore]
202-
symbolicator: [postgres, snuba, symbolicator, spotlight]
202+
symbolicator: [postgres, snuba, symbolicator, spotlight, objectstore]
203203
memcached: [postgres, snuba, memcached, spotlight]
204204
tracing:
205205
[

‎fixtures/seer/webhooks.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from sentry.sentry_apps.metrics import SentryAppEventType
1+
from sentry.sentry_apps.event_types import SentryAppEventType
22

33
MOCK_RUN_ID = 123
44
MOCK_GROUP_ID = 456

‎pnpm-lock.yaml‎

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ dependencies = [
9191
"sentry-kafka-schemas>=2.1.37",
9292
"sentry-ophio>=1.1.3",
9393
# sentry-options is only used in getsentry for now
94-
"sentry-options>=1.1.1",
95-
"sentry-protos>=0.34.2",
94+
"sentry-options>=1.1.2",
95+
"sentry-protos>=0.37.1",
9696
"sentry-redis-tools>=0.5.0",
9797
"sentry-relay>=0.9.27",
9898
"sentry-scm==0.30.0",
@@ -107,7 +107,7 @@ dependencies = [
107107
"statsd>=3.3.0",
108108
"structlog>=22.1.0",
109109
"symbolic>=13.1.1",
110-
"taskbroker-client>=0.20.7",
110+
"taskbroker-client>=0.20.8",
111111
"tiktoken>=0.8.0",
112112
"tokenizers>=0.22.0",
113113
"tldextract>=5.1.2",
@@ -974,7 +974,6 @@ module = [
974974
"sentry.issues.endpoints.group_attachments",
975975
"sentry.issues.endpoints.group_current_release",
976976
"sentry.issues.endpoints.group_first_last_release",
977-
"sentry.issues.endpoints.group_integration_details",
978977
"sentry.issues.endpoints.group_integrations",
979978
"sentry.issues.endpoints.group_reprocessing",
980979
"sentry.issues.endpoints.group_stats",

‎rspack.config.ts‎

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,17 @@ for (const locale of supportedLocales) {
184184
};
185185
}
186186

187+
const DEFINED_ENV_VARS = {
188+
'process.env.IS_ACCEPTANCE_TEST': JSON.stringify(IS_ACCEPTANCE_TEST),
189+
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV),
190+
'process.env.DEPLOY_PREVIEW_CONFIG': JSON.stringify(DEPLOY_PREVIEW_CONFIG),
191+
'process.env.EXPERIMENTAL_SPA': JSON.stringify(SENTRY_EXPERIMENTAL_SPA),
192+
'process.env.SPA_DSN': JSON.stringify(SENTRY_SPA_DSN),
193+
'process.env.SENTRY_RELEASE_VERSION': JSON.stringify(SENTRY_RELEASE_VERSION),
194+
'process.env.USE_TANSTACK_DEVTOOL': JSON.stringify(USE_TANSTACK_DEVTOOL),
195+
'process.env.ENABLE_SENTRY_TOOLBAR': JSON.stringify(ENABLE_SENTRY_TOOLBAR),
196+
};
197+
187198
const swcReactLoaderConfig: SwcLoaderOptions = {
188199
env: {
189200
mode: 'usage',
@@ -246,6 +257,7 @@ const swcReactLoaderConfig: SwcLoaderOptions = {
246257
*/
247258

248259
const appConfig: Configuration = {
260+
name: 'app',
249261
mode: WEBPACK_MODE,
250262
target: 'browserslist',
251263
// Fail on first error instead of continuing to build
@@ -433,16 +445,7 @@ const appConfig: Configuration = {
433445
/**
434446
* Defines environment specific flags.
435447
*/
436-
new rspack.DefinePlugin({
437-
'process.env.IS_ACCEPTANCE_TEST': JSON.stringify(IS_ACCEPTANCE_TEST),
438-
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV),
439-
'process.env.DEPLOY_PREVIEW_CONFIG': JSON.stringify(DEPLOY_PREVIEW_CONFIG),
440-
'process.env.EXPERIMENTAL_SPA': JSON.stringify(SENTRY_EXPERIMENTAL_SPA),
441-
'process.env.SPA_DSN': JSON.stringify(SENTRY_SPA_DSN),
442-
'process.env.SENTRY_RELEASE_VERSION': JSON.stringify(SENTRY_RELEASE_VERSION),
443-
'process.env.USE_TANSTACK_DEVTOOL': JSON.stringify(USE_TANSTACK_DEVTOOL),
444-
'process.env.ENABLE_SENTRY_TOOLBAR': JSON.stringify(ENABLE_SENTRY_TOOLBAR),
445-
}),
448+
new rspack.DefinePlugin(DEFINED_ENV_VARS),
446449

447450
...(SHOULD_FORK_TS
448451
? [
@@ -461,6 +464,7 @@ const appConfig: Configuration = {
461464
'**/*.spec.*',
462465
'**/*.snapshots.*',
463466
'static/eslint/**/*',
467+
'static/app/serviceWorker/worker/**/*',
464468
'scripts/**/*',
465469
],
466470
},
@@ -554,7 +558,11 @@ const appConfig: Configuration = {
554558
},
555559
output: {
556560
crossOriginLoading: 'anonymous',
557-
clean: true, // Clean the output directory before emit.
561+
// Clean the output dir before emit, but keep the service-worker assets
562+
// emitted by the separate `workerConfig` compiler below. Both compilers
563+
// write to this same `dist` path and run in parallel, so without `keep`
564+
// app's clean would race and delete the worker's output.
565+
clean: {keep: /(entrypoints|sourcemaps)\/service-worker/},
558566
path: distPath,
559567
publicPath: '',
560568
filename: 'entrypoints/[name].js',
@@ -584,6 +592,44 @@ const appConfig: Configuration = {
584592
devtool: IS_PRODUCTION ? 'source-map' : 'eval-cheap-module-source-map',
585593
};
586594

595+
/**
596+
* Separate config for the service-worker entry point.
597+
*/
598+
const workerConfig: Configuration = {
599+
name: 'service-worker',
600+
mode: appConfig.mode,
601+
target: 'webworker',
602+
bail: appConfig.bail,
603+
entry: {
604+
'service-worker': 'sentry/serviceWorker/worker/worker',
605+
},
606+
context: staticPrefix,
607+
experiments: appConfig.experiments,
608+
lazyCompilation: appConfig.lazyCompilation,
609+
module: appConfig.module,
610+
plugins: [
611+
/**
612+
* Without this, webpack will chunk the locales but attempt to load them all
613+
* eagerly.
614+
*/
615+
new rspack.IgnorePlugin({
616+
contextRegExp: /moment$/,
617+
resourceRegExp: /^\.\/locale$/,
618+
}),
619+
620+
/**
621+
* Defines environment specific flags.
622+
*/
623+
new rspack.DefinePlugin(DEFINED_ENV_VARS),
624+
],
625+
resolveLoader: {},
626+
resolve: appConfig.resolve,
627+
// Don't clean: app's compiler owns cleaning `dist` (see its `clean.keep`).
628+
output: {...appConfig.output, clean: false},
629+
optimization: appConfig.optimization,
630+
devtool: appConfig.devtool,
631+
};
632+
587633
if (IS_TEST) {
588634
(appConfig.resolve!.alias! as Record<string, string>)['sentry-fixture'] = path.join(
589635
import.meta.dirname,
@@ -594,6 +640,7 @@ if (IS_TEST) {
594640

595641
if (IS_ACCEPTANCE_TEST) {
596642
appConfig.plugins?.push(new LastBuiltPlugin({basePath: import.meta.dirname}));
643+
workerConfig.plugins?.push(new LastBuiltPlugin({basePath: import.meta.dirname}));
597644
}
598645

599646
// Dev only! Hot module reloading
@@ -882,6 +929,12 @@ if (IS_PRODUCTION) {
882929
test: /\.(js|map|css|svg|html|txt|ico|eot|ttf)$/,
883930
})
884931
);
932+
workerConfig.plugins?.push(
933+
new CompressionPlugin({
934+
algorithm: 'gzip',
935+
test: /\.(js|map|css|svg|html|txt|ico|eot|ttf)$/,
936+
})
937+
);
885938
}
886939

887940
// Enable sentry-webpack-plugin for production builds
@@ -922,4 +975,5 @@ if (env.WEBPACK_CACHE_PATH) {
922975
};
923976
}
924977

925-
export default appConfig;
978+
const configs = [appConfig, workerConfig];
979+
export default configs;

‎src/AGENTS.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,34 @@ analytics.record(
247247
)
248248
```
249249

250+
### Tracing / Spans
251+
252+
Use the wrappers in `sentry.utils.tracing` instead of calling the SDK directly. This is required while we dogfood the streaming trace lifecycle (Span First rollout).
253+
254+
| Instead of | Use |
255+
| -------------------------------- | ------------------------------------------------ |
256+
| `sentry_sdk.start_span()` | `start_span(name=..., op=...)` |
257+
| `sentry_sdk.start_transaction()` | `start_span(name=..., op=..., transaction=True)` |
258+
| `span.set_tag(key, value)` | `set_span_tag(span, key, value)` |
259+
| `span.set_data(key, value)` | `set_span_data(span, key, value)` |
260+
261+
```python
262+
from sentry.utils.tracing import start_span, set_span_tag, set_span_data
263+
264+
# Child span — no need to capture the span when you don't set tags/data
265+
with start_span(name="event_manager.save", op="save"):
266+
do_work()
267+
268+
# Child span with tags/data — capture via `as span`
269+
with start_span(name="event_manager.save", op="save") as span:
270+
set_span_tag(span, "platform", platform)
271+
set_span_data(span, "rows_count", len(rows))
272+
273+
# Transaction root (replaces sentry_sdk.start_transaction)
274+
with start_span(name="monitors.consumer", op="process", transaction=True):
275+
process_batch()
276+
```
277+
250278
### Metrics Tags
251279

252280
Every distinct tag-value combination is a separate time series, so keep tags **low-cardinality, meaningful, and minimal**:

‎src/sentry/api/endpoints/organization_trace_item_attributes.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,8 @@ def get(
534534
config=SearchResolverConfig(),
535535
definitions=column_definitions,
536536
)
537-
query_filter, _, _ = resolver.resolve_query(query_string)
537+
with handle_query_errors():
538+
query_filter, _, _ = resolver.resolve_query(query_string)
538539
meta = resolver.resolve_meta(referrer=referrer.value)
539540
meta.trace_item_type = constants.SUPPORTED_TRACE_ITEM_TYPE_MAP.get(
540541
trace_item_type, ProtoTraceItemType.TRACE_ITEM_TYPE_SPAN

‎src/sentry/api/endpoints/organization_trace_item_attributes_ranked.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,9 @@ def query_attribute_distributions(
259259
else f"({query_1}) AND {function_parameter}:<={function_value}"
260260
)
261261

262-
cohort_1, _, _ = resolver.resolve_query(query_1)
263-
cohort_2, _, _ = resolver.resolve_query(query_2)
262+
with handle_query_errors():
263+
cohort_1, _, _ = resolver.resolve_query(query_1)
264+
cohort_2, _, _ = resolver.resolve_query(query_2)
264265

265266
# Fetch attribute names for parallelization
266267
adjusted_start_date, adjusted_end_date = adjust_start_end_window(

0 commit comments

Comments
 (0)