Skip to content

Commit 9746559

Browse files
authored
feat(js-loader): Support logs+metrics bundle (#106959)
Adds backend support for using the logs+metrics bundle in the JS loader ref https://linear.app/getsentry/issue/JS-1465/support-logsmetrics-in-js-loader
1 parent bd966db commit 9746559

File tree

5 files changed

+317
-22
lines changed

5 files changed

+317
-22
lines changed

src/sentry/api/serializers/rest_framework/project_key.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ class DynamicSdkLoaderOptionSerializer(serializers.Serializer):
3232
- `Debug Bundles & Logging`
3333
- `Session Replay` - Note that the loader will load the ES6 bundle instead of the ES5 bundle.
3434
- `User Feedback` - Note that the loader will load the ES6 bundle instead of the ES5 bundle.
35+
- `Logs and Metrics` - Note that the loader will load the ES6 bundle instead of the ES5 bundle. Requires SDK >= 10.0.0.
3536
```json
3637
{
3738
"dynamicSdkLoaderOptions": {
3839
"hasReplay": true,
3940
"hasPerformance": true,
4041
"hasDebug": true,
41-
"hasFeedback": true
42+
"hasFeedback": true,
43+
"hasLogsAndMetrics": true
4244
}
4345
}
4446
```
@@ -48,6 +50,7 @@ class DynamicSdkLoaderOptionSerializer(serializers.Serializer):
4850
hasPerformance = serializers.BooleanField(required=False)
4951
hasDebug = serializers.BooleanField(required=False)
5052
hasFeedback = serializers.BooleanField(required=False)
53+
hasLogsAndMetrics = serializers.BooleanField(required=False)
5154

5255
def to_internal_value(self, data):
5356
# Drop any fields that are not specified as a `DynamicSdkLoaderOption`.

src/sentry/loader/dynamic_sdk_options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class DynamicSdkLoaderOption(str, Enum):
66
HAS_PERFORMANCE = "hasPerformance"
77
HAS_DEBUG = "hasDebug"
88
HAS_FEEDBACK = "hasFeedback"
9+
HAS_LOGS_AND_METRICS = "hasLogsAndMetrics"
910

1011

1112
def get_dynamic_sdk_loader_option(project_key, option: DynamicSdkLoaderOption, default=False):

src/sentry/web/frontend/analytics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class JsSdkLoaderRendered(analytics.Event):
1010
has_replay: bool
1111
has_debug: bool
1212
has_feedback: bool
13+
has_logs_and_metrics: bool
1314
sdk_version: str | None
1415
tmpl: str
1516

src/sentry/web/frontend/js_sdk_loader.py

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class SdkConfig(TypedDict):
3131
replaysOnErrorSampleRate: NotRequired[float]
3232
debug: NotRequired[bool]
3333
autoInjectFeedback: NotRequired[bool]
34+
enableLogs: NotRequired[bool]
3435

3536

3637
class LoaderInternalConfig(TypedDict):
@@ -40,6 +41,11 @@ class LoaderInternalConfig(TypedDict):
4041
hasReplay: bool
4142
hasDebug: bool
4243
hasFeedback: bool
44+
hasLogsAndMetrics: bool
45+
userEnabledPerformance: bool
46+
userEnabledReplay: bool
47+
userEnabledFeedback: bool
48+
userEnabledLogsAndMetrics: bool
4349

4450

4551
class LoaderContext(TypedDict):
@@ -64,34 +70,73 @@ def _get_loader_config(
6470
"hasReplay": False,
6571
"hasDebug": False,
6672
"hasFeedback": False,
73+
"hasLogsAndMetrics": False,
74+
"userEnabledPerformance": False,
75+
"userEnabledReplay": False,
76+
"userEnabledFeedback": False,
77+
"userEnabledLogsAndMetrics": False,
6778
}
6879

6980
is_v7_sdk = sdk_version >= Version("7.0.0") and sdk_version < Version("8.0.0")
7081
is_greater_or_equal_v7_sdk = sdk_version >= Version("7.0.0")
82+
is_greater_or_equal_v10_sdk = sdk_version >= Version("10.0.0")
7183

7284
is_lazy = True
7385
bundle_kind_modifier = ""
7486
has_replay = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_REPLAY)
7587
has_performance = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_PERFORMANCE)
7688
has_debug = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_DEBUG)
7789
has_feedback = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_FEEDBACK)
90+
has_logs_and_metrics = get_dynamic_sdk_loader_option(
91+
key, DynamicSdkLoaderOption.HAS_LOGS_AND_METRICS
92+
)
93+
94+
# Store the user's original preferences before we modify them for bundle selection.
95+
# We only want to enable features that the user explicitly requested.
96+
user_enabled_performance = has_performance
97+
user_enabled_replay = has_replay
98+
user_enabled_feedback = has_feedback
99+
user_enabled_logs_and_metrics = has_logs_and_metrics
78100

79101
# The order in which these modifiers are added is important, as the
80102
# bundle name is built up from left to right.
81103
# https://docs.sentry.io/platforms/javascript/install/cdn/
82104

83-
# Available bundles: bundle, bundle.tracing, bundle.replay, bundle.feedback,
84-
# bundle.tracing.replay, bundle.tracing.replay.feedback
85-
# Note: There is NO bundle.tracing.feedback or bundle.replay.feedback.
86-
# If feedback is combined with tracing or replay, we must use the full bundle.
105+
# Available bundles:
106+
# - bundle (base)
107+
# - bundle.feedback
108+
# - bundle.logs.metrics
109+
# - bundle.replay
110+
# - bundle.replay.feedback
111+
# - bundle.replay.logs.metrics
112+
# - bundle.tracing
113+
# - bundle.tracing.logs.metrics
114+
# - bundle.tracing.replay
115+
# - bundle.tracing.replay.feedback
116+
# - bundle.tracing.replay.feedback.logs.metrics
117+
# - bundle.tracing.replay.logs.metrics
118+
#
119+
# Note: There is NO bundle.tracing.feedback (tracing + feedback without replay).
120+
# If feedback is combined with tracing (without replay), we must use the full bundle.
121+
#
122+
# Note: There is NO bundle.feedback.logs.metrics, bundle.tracing.feedback.logs.metrics,
123+
# or bundle.replay.feedback.logs.metrics. If feedback is combined with logs+metrics,
124+
# we must use the full bundle (tracing.replay.feedback.logs.metrics).
87125

88126
# Feedback bundles require SDK >= 7.85.0, but the frontend only allows selecting
89127
# major versions (7.x, 8.x), which resolve to versions that support feedback.
90-
feedback_with_other_features = has_feedback and (has_performance or has_replay)
91128

92-
# When feedback is combined with tracing or replay, we must serve the full bundle
93-
# which includes all three features. Update the flags accordingly.
94-
if is_greater_or_equal_v7_sdk and feedback_with_other_features:
129+
# When feedback is combined with tracing (but not replay), we must serve the full bundle
130+
# which includes tracing, replay, and feedback. Update the flags accordingly.
131+
feedback_with_tracing_no_replay = has_feedback and has_performance and not has_replay
132+
if is_greater_or_equal_v7_sdk and feedback_with_tracing_no_replay:
133+
has_replay = True
134+
135+
# Logs and metrics bundles require SDK >= 10.0.0.
136+
# When logs+metrics is combined with feedback, we must serve the full bundle
137+
# (tracing.replay.feedback.logs.metrics) because there's no feedback.logs.metrics bundle.
138+
logs_metrics_with_feedback = has_logs_and_metrics and has_feedback
139+
if is_greater_or_equal_v10_sdk and logs_metrics_with_feedback:
95140
has_performance = True
96141
has_replay = True
97142

@@ -109,11 +154,19 @@ def _get_loader_config(
109154
bundle_kind_modifier += ".feedback"
110155
is_lazy = False
111156

157+
if is_greater_or_equal_v10_sdk and has_logs_and_metrics:
158+
bundle_kind_modifier += ".logs.metrics"
159+
is_lazy = False
160+
else:
161+
# If SDK < 10.0.0, disable logs+metrics feature even if user requested it
162+
has_logs_and_metrics = False
163+
user_enabled_logs_and_metrics = False
164+
112165
# In JavaScript SDK version 7, the default bundle code is ES6, however, in the loader we
113166
# want to provide the ES5 version. This is why we need to modify the requested bundle name here.
114167
#
115-
# If we are loading replay or feedback, do not add the es5 modifier, as those bundles are
116-
# ES6 only.
168+
# If we are loading replay or feedback, do not add the es5 modifier, as those bundles are ES6 only.
169+
# Note: logs+metrics bundles don't exist for v7 (they require v10+)
117170
if is_v7_sdk and not has_replay and not has_feedback:
118171
bundle_kind_modifier += ".es5"
119172

@@ -127,6 +180,11 @@ def _get_loader_config(
127180
"hasReplay": has_replay,
128181
"hasDebug": has_debug,
129182
"hasFeedback": has_feedback,
183+
"hasLogsAndMetrics": has_logs_and_metrics,
184+
"userEnabledPerformance": user_enabled_performance,
185+
"userEnabledReplay": user_enabled_replay,
186+
"userEnabledFeedback": user_enabled_feedback,
187+
"userEnabledLogsAndMetrics": user_enabled_logs_and_metrics,
130188
}
131189

132190
def _get_context(
@@ -166,17 +224,21 @@ def _get_context(
166224
if loader_config["hasDebug"]:
167225
config["debug"] = True
168226

169-
if loader_config["hasPerformance"]:
227+
# Only enable feature configs if the user explicitly enabled them, not just because
228+
# we're loading a bundle that includes those features for compatibility reasons.
229+
if loader_config["userEnabledPerformance"]:
170230
config["tracesSampleRate"] = 1
171231

172-
if loader_config["hasReplay"]:
232+
if loader_config["userEnabledReplay"]:
173233
config["replaysSessionSampleRate"] = 0.1
174234
config["replaysOnErrorSampleRate"] = 1
175235

176-
# Although this is not a top-level SDK option we pass this flag so we can auto-add the integration in the loader template later
177-
if loader_config["hasFeedback"]:
236+
if loader_config["userEnabledFeedback"]:
178237
config["autoInjectFeedback"] = True
179238

239+
if loader_config["userEnabledLogsAndMetrics"]:
240+
config["enableLogs"] = True
241+
180242
return (
181243
{
182244
"config": config,
@@ -227,6 +289,7 @@ def get(
227289
has_replay=loader_config["hasReplay"],
228290
has_debug=loader_config["hasDebug"],
229291
has_feedback=loader_config["hasFeedback"],
292+
has_logs_and_metrics=loader_config["hasLogsAndMetrics"],
230293
sdk_version=str(sdk_version) if sdk_version else None,
231294
tmpl=tmpl,
232295
)

0 commit comments

Comments
 (0)