Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/sentry/api/serializers/rest_framework/project_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ class DynamicSdkLoaderOptionSerializer(serializers.Serializer):
- `Debug Bundles & Logging`
- `Session Replay` - Note that the loader will load the ES6 bundle instead of the ES5 bundle.
- `User Feedback` - Note that the loader will load the ES6 bundle instead of the ES5 bundle.
- `Logs and Metrics` - Note that the loader will load the ES6 bundle instead of the ES5 bundle. Requires SDK >= 10.0.0.
```json
{
"dynamicSdkLoaderOptions": {
"hasReplay": true,
"hasPerformance": true,
"hasDebug": true,
"hasFeedback": true
"hasFeedback": true,
"hasLogsAndMetrics": true
}
}
```
Expand All @@ -48,6 +50,7 @@ class DynamicSdkLoaderOptionSerializer(serializers.Serializer):
hasPerformance = serializers.BooleanField(required=False)
hasDebug = serializers.BooleanField(required=False)
hasFeedback = serializers.BooleanField(required=False)
hasLogsAndMetrics = serializers.BooleanField(required=False)

def to_internal_value(self, data):
# Drop any fields that are not specified as a `DynamicSdkLoaderOption`.
Expand Down
1 change: 1 addition & 0 deletions src/sentry/loader/dynamic_sdk_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class DynamicSdkLoaderOption(str, Enum):
HAS_PERFORMANCE = "hasPerformance"
HAS_DEBUG = "hasDebug"
HAS_FEEDBACK = "hasFeedback"
HAS_LOGS_AND_METRICS = "hasLogsAndMetrics"


def get_dynamic_sdk_loader_option(project_key, option: DynamicSdkLoaderOption, default=False):
Expand Down
1 change: 1 addition & 0 deletions src/sentry/web/frontend/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class JsSdkLoaderRendered(analytics.Event):
has_replay: bool
has_debug: bool
has_feedback: bool
has_logs_and_metrics: bool
sdk_version: str | None
tmpl: str

Expand Down
91 changes: 77 additions & 14 deletions src/sentry/web/frontend/js_sdk_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SdkConfig(TypedDict):
replaysOnErrorSampleRate: NotRequired[float]
debug: NotRequired[bool]
autoInjectFeedback: NotRequired[bool]
enableLogs: NotRequired[bool]


class LoaderInternalConfig(TypedDict):
Expand All @@ -40,6 +41,11 @@ class LoaderInternalConfig(TypedDict):
hasReplay: bool
hasDebug: bool
hasFeedback: bool
hasLogsAndMetrics: bool
userEnabledPerformance: bool
userEnabledReplay: bool
userEnabledFeedback: bool
userEnabledLogsAndMetrics: bool


class LoaderContext(TypedDict):
Expand All @@ -64,34 +70,73 @@ def _get_loader_config(
"hasReplay": False,
"hasDebug": False,
"hasFeedback": False,
"hasLogsAndMetrics": False,
"userEnabledPerformance": False,
"userEnabledReplay": False,
"userEnabledFeedback": False,
"userEnabledLogsAndMetrics": False,
}

is_v7_sdk = sdk_version >= Version("7.0.0") and sdk_version < Version("8.0.0")
is_greater_or_equal_v7_sdk = sdk_version >= Version("7.0.0")
is_greater_or_equal_v10_sdk = sdk_version >= Version("10.0.0")

is_lazy = True
bundle_kind_modifier = ""
has_replay = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_REPLAY)
has_performance = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_PERFORMANCE)
has_debug = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_DEBUG)
has_feedback = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_FEEDBACK)
has_logs_and_metrics = get_dynamic_sdk_loader_option(
key, DynamicSdkLoaderOption.HAS_LOGS_AND_METRICS
)

# Store the user's original preferences before we modify them for bundle selection.
# We only want to enable features that the user explicitly requested.
user_enabled_performance = has_performance
user_enabled_replay = has_replay
user_enabled_feedback = has_feedback
user_enabled_logs_and_metrics = has_logs_and_metrics

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

# Available bundles: bundle, bundle.tracing, bundle.replay, bundle.feedback,
# bundle.tracing.replay, bundle.tracing.replay.feedback
# Note: There is NO bundle.tracing.feedback or bundle.replay.feedback.
# If feedback is combined with tracing or replay, we must use the full bundle.
# Available bundles:
# - bundle (base)
# - bundle.feedback
# - bundle.logs.metrics
# - bundle.replay
# - bundle.replay.feedback
# - bundle.replay.logs.metrics
# - bundle.tracing
# - bundle.tracing.logs.metrics
# - bundle.tracing.replay
# - bundle.tracing.replay.feedback
# - bundle.tracing.replay.feedback.logs.metrics
# - bundle.tracing.replay.logs.metrics
#
# Note: There is NO bundle.tracing.feedback (tracing + feedback without replay).
# If feedback is combined with tracing (without replay), we must use the full bundle.
#
# Note: There is NO bundle.feedback.logs.metrics, bundle.tracing.feedback.logs.metrics,
# or bundle.replay.feedback.logs.metrics. If feedback is combined with logs+metrics,
# we must use the full bundle (tracing.replay.feedback.logs.metrics).

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

# When feedback is combined with tracing or replay, we must serve the full bundle
# which includes all three features. Update the flags accordingly.
if is_greater_or_equal_v7_sdk and feedback_with_other_features:
# When feedback is combined with tracing (but not replay), we must serve the full bundle
# which includes tracing, replay, and feedback. Update the flags accordingly.
feedback_with_tracing_no_replay = has_feedback and has_performance and not has_replay
if is_greater_or_equal_v7_sdk and feedback_with_tracing_no_replay:
has_replay = True

# Logs and metrics bundles require SDK >= 10.0.0.
# When logs+metrics is combined with feedback, we must serve the full bundle
# (tracing.replay.feedback.logs.metrics) because there's no feedback.logs.metrics bundle.
logs_metrics_with_feedback = has_logs_and_metrics and has_feedback
if is_greater_or_equal_v10_sdk and logs_metrics_with_feedback:
has_performance = True
has_replay = True

Expand All @@ -109,11 +154,19 @@ def _get_loader_config(
bundle_kind_modifier += ".feedback"
is_lazy = False

if is_greater_or_equal_v10_sdk and has_logs_and_metrics:
bundle_kind_modifier += ".logs.metrics"
is_lazy = False
else:
# If SDK < 10.0.0, disable logs+metrics feature even if user requested it
has_logs_and_metrics = False
user_enabled_logs_and_metrics = False

# In JavaScript SDK version 7, the default bundle code is ES6, however, in the loader we
# want to provide the ES5 version. This is why we need to modify the requested bundle name here.
#
# If we are loading replay or feedback, do not add the es5 modifier, as those bundles are
# ES6 only.
# If we are loading replay or feedback, do not add the es5 modifier, as those bundles are ES6 only.
# Note: logs+metrics bundles don't exist for v7 (they require v10+)
if is_v7_sdk and not has_replay and not has_feedback:
bundle_kind_modifier += ".es5"

Expand All @@ -127,6 +180,11 @@ def _get_loader_config(
"hasReplay": has_replay,
"hasDebug": has_debug,
"hasFeedback": has_feedback,
"hasLogsAndMetrics": has_logs_and_metrics,
"userEnabledPerformance": user_enabled_performance,
"userEnabledReplay": user_enabled_replay,
"userEnabledFeedback": user_enabled_feedback,
"userEnabledLogsAndMetrics": user_enabled_logs_and_metrics,
}

def _get_context(
Expand Down Expand Up @@ -166,17 +224,21 @@ def _get_context(
if loader_config["hasDebug"]:
config["debug"] = True

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

if loader_config["hasReplay"]:
if loader_config["userEnabledReplay"]:
config["replaysSessionSampleRate"] = 0.1
config["replaysOnErrorSampleRate"] = 1

# 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
if loader_config["hasFeedback"]:
if loader_config["userEnabledFeedback"]:
config["autoInjectFeedback"] = True

if loader_config["userEnabledLogsAndMetrics"]:
config["enableLogs"] = True

return (
{
"config": config,
Expand Down Expand Up @@ -227,6 +289,7 @@ def get(
has_replay=loader_config["hasReplay"],
has_debug=loader_config["hasDebug"],
has_feedback=loader_config["hasFeedback"],
has_logs_and_metrics=loader_config["hasLogsAndMetrics"],
sdk_version=str(sdk_version) if sdk_version else None,
tmpl=tmpl,
)
Expand Down
Loading
Loading