Skip to content

Commit bfd4dba

Browse files
authored
Merge fe454d1 into 070db34
2 parents 070db34 + fe454d1 commit bfd4dba

File tree

61 files changed

+2404
-52
lines changed

Some content is hidden

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

61 files changed

+2404
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- Add timing API for Metrics (#3812):
88
- Add [rate limiting](https://develop.sentry.dev/sdk/rate-limiting/) for Metrics (#3838)
99
- Data normalization for Metrics (#3843)
10+
- Add Session Replay (#3625)
11+
**Note:** This is an experimental feature.
1012

1113
## 8.23.0
1214

Samples/iOS-Swift/iOS-Swift/AppDelegate.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1717
let dsn = DSNStorage.shared.getDSN() ?? AppDelegate.defaultDSN
1818
DSNStorage.shared.saveDSN(dsn: dsn)
1919

20-
SentrySDK.start { options in
20+
SentrySDK.start(configureOptions: { options in
2121
options.dsn = dsn
2222
options.beforeSend = { event in
2323
return event
2424
}
2525
options.debug = true
2626

27+
if #available(iOS 16.0, *) {
28+
options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, errorSampleRate: 1, redactAllText: false, redactAllImages: true)
29+
}
30+
2731
if #available(iOS 15.0, *) {
2832
options.enableMetricKit = true
2933
}
@@ -60,7 +64,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
6064
options.sessionTrackingIntervalMillis = 5_000
6165
options.attachScreenshot = true
6266
options.attachViewHierarchy = true
63-
67+
6468
#if targetEnvironment(simulator)
6569
options.enableSpotlight = true
6670
options.environment = "test-app"
@@ -130,7 +134,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
130134
}
131135
return scope
132136
}
133-
}
137+
})
134138

135139
SentrySDK.metrics.increment(key: "app.start", value: 1.0, tags: ["view": "app-delegate"])
136140

Sentry.podspec

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ Pod::Spec.new do |s|
3232

3333
s.subspec 'Core' do |sp|
3434
sp.source_files = "Sources/Sentry/**/*.{h,hpp,m,mm,c,cpp}",
35-
"Sources/SentryCrash/**/*.{h,hpp,m,mm,c,cpp}", "Sources/Swift/**/*.{swift,h,hpp,m,mm,c,cpp}",
35+
"Sources/SentryCrash/**/*.{h,hpp,m,mm,c,cpp}", "Sources/Swift/**/*.{swift,h,hpp,m,mm,c,cpp}"
36+
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
3637
sp.public_header_files =
3738
"Sources/Sentry/Public/*.h"
3839

@@ -43,7 +44,8 @@ Pod::Spec.new do |s|
4344
s.subspec 'HybridSDK' do |sp|
4445
sp.source_files = "Sources/Sentry/**/*.{h,hpp,m,mm,c,cpp}",
4546
"Sources/SentryCrash/**/*.{h,hpp,m,mm,c,cpp}", "Sources/Swift/**/*.{swift,h,hpp,m,mm,c,cpp}"
46-
47+
48+
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
4749
sp.public_header_files =
4850
"Sources/Sentry/Public/*.h", "Sources/Sentry/include/HybridPublic/*.h"
4951

Sentry.xcodeproj/project.pbxproj

Lines changed: 146 additions & 22 deletions
Large diffs are not rendered by default.

SentryTestUtils/TestCurrentDateProvider.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public class TestCurrentDateProvider: SentryCurrentDateProvider {
4343
setDate(date: date().addingTimeInterval(TimeInterval(nanoseconds) / 1e9))
4444
internalSystemTime += nanoseconds
4545
}
46+
47+
public func advanceBy(interval: TimeInterval) {
48+
setDate(date: date().addingTimeInterval(interval))
49+
}
4650

4751
public var timezoneOffsetValue = 0
4852
public override func timezoneOffset() -> Int {

SentryTestUtils/TestTransport.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _SentryPrivate
12
import Foundation
23

34
@objc
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "Sentry.xcconfig"
2+
3+
OTHER_SWIFT_FLAGS = -DSENTRY_NO_UIKIT

Sources/Sentry/Public/SentryOptions.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
NS_ASSUME_NONNULL_BEGIN
55

6-
@class SentryDsn, SentryMeasurementValue, SentryHttpStatusCodeRange, SentryScope;
6+
@class SentryDsn, SentryMeasurementValue, SentryHttpStatusCodeRange, SentryScope,
7+
SentryReplayOptions;
8+
@class SentryExperimentalOptions;
79

810
NS_SWIFT_NAME(Options)
911
@interface SentryOptions : NSObject
@@ -269,6 +271,7 @@ NS_SWIFT_NAME(Options)
269271
* @note Default value is @c NO .
270272
*/
271273
@property (nonatomic, assign) BOOL enablePreWarmedAppStartTracing;
274+
272275
#endif // SENTRY_UIKIT_AVAILABLE
273276

274277
/**
@@ -604,6 +607,12 @@ NS_SWIFT_NAME(Options)
604607
*/
605608
@property (nullable, nonatomic, copy) SentryBeforeEmitMetricCallback beforeEmitMetric;
606609

610+
/**
611+
* This aggregates options for experimental features.
612+
* Be aware that the options available for experimental can change at any time.
613+
*/
614+
@property (nonatomic, readonly) SentryExperimentalOptions *experimental;
615+
607616
@end
608617

609618
NS_ASSUME_NONNULL_END

Sources/Sentry/SentryBaseIntegration.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import "SentryBaseIntegration.h"
22
#import "SentryCrashWrapper.h"
33
#import "SentryLog.h"
4+
#import "SentrySwift.h"
45
#import <Foundation/Foundation.h>
56
#import <SentryDependencyContainer.h>
67
#import <SentryOptions+Private.h>
@@ -140,6 +141,19 @@ - (BOOL)shouldBeEnabledWithOptions:(SentryOptions *)options
140141
[self logWithOptionName:@"attachViewHierarchy"];
141142
return NO;
142143
}
144+
145+
if (integrationOptions & kIntegrationOptionEnableReplay) {
146+
if (@available(iOS 16.0, tvOS 16.0, *)) {
147+
if (options.experimental.sessionReplay.errorSampleRate == 0
148+
&& options.experimental.sessionReplay.sessionSampleRate == 0) {
149+
[self logWithOptionName:@"sessionReplaySettings"];
150+
return NO;
151+
}
152+
} else {
153+
[self logWithReason:@"Session replay requires iOS 16 or above"];
154+
return NO;
155+
}
156+
}
143157
#endif
144158

145159
if ((integrationOptions & kIntegrationOptionEnableCrashHandler)

Sources/Sentry/SentryClient.m

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#import "SentryDependencyContainer.h"
1212
#import "SentryDispatchQueueWrapper.h"
1313
#import "SentryDsn.h"
14-
#import "SentryEnvelope.h"
14+
#import "SentryEnvelope+Private.h"
1515
#import "SentryEnvelopeItemType.h"
1616
#import "SentryEvent.h"
1717
#import "SentryException.h"
@@ -27,13 +27,16 @@
2727
#import "SentryMechanismMeta.h"
2828
#import "SentryMessage.h"
2929
#import "SentryMeta.h"
30+
#import "SentryMsgPackSerializer.h"
3031
#import "SentryNSDictionarySanitize.h"
3132
#import "SentryNSError.h"
3233
#import "SentryOptions+Private.h"
3334
#import "SentryPropagationContext.h"
3435
#import "SentryRandom.h"
36+
#import "SentryReplayEvent.h"
3537
#import "SentrySDK+Private.h"
3638
#import "SentryScope+Private.h"
39+
#import "SentrySerialization.h"
3740
#import "SentrySession.h"
3841
#import "SentryStacktraceBuilder.h"
3942
#import "SentrySwift.h"
@@ -472,13 +475,44 @@ - (void)captureSession:(SentrySession *)session
472475
}
473476

474477
SentryEnvelopeItem *item = [[SentryEnvelopeItem alloc] initWithSession:session];
475-
SentryEnvelopeHeader *envelopeHeader = [[SentryEnvelopeHeader alloc] initWithId:nil
476-
traceContext:nil];
477-
SentryEnvelope *envelope = [[SentryEnvelope alloc] initWithHeader:envelopeHeader
478+
SentryEnvelope *envelope = [[SentryEnvelope alloc] initWithHeader:[SentryEnvelopeHeader empty]
478479
singleItem:item];
479480
[self captureEnvelope:envelope];
480481
}
481482

483+
- (void)captureReplayEvent:(SentryReplayEvent *)replayEvent
484+
replayRecording:(SentryReplayRecording *)replayRecording
485+
video:(NSURL *)videoURL
486+
withScope:(SentryScope *)scope
487+
{
488+
replayEvent = (SentryReplayEvent *)[self prepareEvent:replayEvent
489+
withScope:scope
490+
alwaysAttachStacktrace:NO];
491+
492+
if (![replayEvent isKindOfClass:SentryReplayEvent.class]) {
493+
SENTRY_LOG_DEBUG(@"The event preprocessor didn't update the replay event in place. The "
494+
@"replay was discarded.");
495+
return;
496+
}
497+
498+
SentryEnvelopeItem *videoEnvelopeItem =
499+
[[SentryEnvelopeItem alloc] initWithReplayEvent:replayEvent
500+
replayRecording:replayRecording
501+
video:videoURL];
502+
503+
if (videoEnvelopeItem == nil) {
504+
SENTRY_LOG_DEBUG(@"The Session Replay segment will not be sent to Sentry because an "
505+
@"Envelope Item could not be created.");
506+
return;
507+
}
508+
509+
SentryEnvelope *envelope = [[SentryEnvelope alloc]
510+
initWithHeader:[[SentryEnvelopeHeader alloc] initWithId:replayEvent.eventId]
511+
items:@[ videoEnvelopeItem ]];
512+
513+
[self captureEnvelope:envelope];
514+
}
515+
482516
- (void)captureEnvelope:(SentryEnvelope *)envelope
483517
{
484518
if ([self isDisabled]) {
@@ -553,9 +587,11 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
553587

554588
BOOL eventIsNotATransaction
555589
= event.type == nil || ![event.type isEqualToString:SentryEnvelopeItemTypeTransaction];
590+
BOOL eventIsNotReplay
591+
= event.type == nil || ![event.type isEqualToString:SentryEnvelopeItemTypeReplayVideo];
556592

557-
// Transactions have their own sampleRate
558-
if (eventIsNotATransaction && [self isSampled:self.options.sampleRate]) {
593+
// Transactions and replays have their own sampleRate
594+
if (eventIsNotATransaction && eventIsNotReplay && [self isSampled:self.options.sampleRate]) {
559595
SENTRY_LOG_DEBUG(@"Event got sampled, will not send the event");
560596
[self recordLostEvent:kSentryDataCategoryError reason:kSentryDiscardReasonSampleRate];
561597
return nil;
@@ -582,8 +618,8 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
582618

583619
[self setSdk:event];
584620

585-
// We don't want to attach debug meta and stacktraces for transactions;
586-
if (eventIsNotATransaction) {
621+
// We don't want to attach debug meta and stacktraces for transactions and replays.
622+
if (eventIsNotATransaction && eventIsNotReplay) {
587623
BOOL shouldAttachStacktrace = alwaysAttachStacktrace || self.options.attachStacktrace
588624
|| (nil != event.exceptions && [event.exceptions count] > 0);
589625

@@ -623,6 +659,10 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
623659

624660
event = [scope applyToEvent:event maxBreadcrumb:self.options.maxBreadcrumbs];
625661

662+
if (!eventIsNotReplay) {
663+
event.breadcrumbs = nil;
664+
}
665+
626666
if ([self isWatchdogTermination:event isCrashEvent:isCrashEvent]) {
627667
// Remove some mutable properties from the device/app contexts which are no longer
628668
// applicable

0 commit comments

Comments
 (0)