From 33b2f5140a89fa9ba5de0dd0d3a23a1c5a2645ce Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 5 Apr 2023 17:02:36 +0200 Subject: [PATCH] ref: Use SentryTracerConfiguration to initialize SentryTracer (#2821) Instead of having multiple functions to initialize SentryTracer with different configurations we should we one complex object that contains all the configurations. --- Sentry.xcodeproj/project.pbxproj | 8 + Sources/Sentry/SentryHub.m | 43 +---- Sources/Sentry/SentryPerformanceTracker.m | 14 +- Sources/Sentry/SentryTracer.m | 170 ++++++------------ Sources/Sentry/SentryTracerConfiguration.m | 28 +++ Sources/Sentry/SentryUIEventTracker.m | 17 +- Sources/Sentry/include/SentryHub+Private.h | 12 +- Sources/Sentry/include/SentryTracer.h | 49 +---- .../include/SentryTracerConfiguration.h | 50 ++++++ .../Network/SentryNetworkTrackerTests.swift | 5 +- .../SentryPerformanceTrackerTests.swift | 2 +- .../SentryTests/SentryTests-Bridging-Header.h | 1 + .../Transaction/SentryTracerObjCTests.m | 41 +++-- .../Transaction/SentryTracerTests.swift | 15 +- 14 files changed, 219 insertions(+), 236 deletions(-) create mode 100644 Sources/Sentry/SentryTracerConfiguration.m create mode 100644 Sources/Sentry/include/SentryTracerConfiguration.h diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index f77a3013ee3..10409b81ad1 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -765,6 +765,8 @@ D8ACE3CD2762187D00F5A213 /* SentryNSDataSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */; }; D8ACE3CE2762187D00F5A213 /* SentryNSDataTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */; }; D8ACE3CF2762187D00F5A213 /* SentryFileIOTrackingIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */; }; + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */; }; + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */; }; D8B76B062808066D000A58C4 /* SentryScreenshotIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */; }; D8B76B0828081461000A58C4 /* TestSentryScreenShot.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */; }; D8BBD32728FD9FC00011F850 /* SentrySwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D8BBD32628FD9FBF0011F850 /* SentrySwift.h */; }; @@ -1678,6 +1680,8 @@ D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataSwizzling.h; path = include/SentryNSDataSwizzling.h; sourceTree = ""; }; D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataTracker.h; path = include/SentryNSDataTracker.h; sourceTree = ""; }; D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryFileIOTrackingIntegration.h; path = include/SentryFileIOTrackingIntegration.h; sourceTree = ""; }; + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryTracerConfiguration.h; path = include/SentryTracerConfiguration.h; sourceTree = ""; }; + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTracerConfiguration.m; sourceTree = ""; }; D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryScreenshotIntegrationTests.swift; sourceTree = ""; }; D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentryScreenShot.swift; sourceTree = ""; }; D8BBD32628FD9FBF0011F850 /* SentrySwift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentrySwift.h; path = include/SentrySwift.h; sourceTree = ""; }; @@ -3140,6 +3144,8 @@ 8E133FA025E72DEF00ABD0BF /* SentrySamplingContext.m */, 8E4E7C7B25DAB287006AB9E2 /* SentryTracer.h */, 8E4E7C8125DAB2A5006AB9E2 /* SentryTracer.m */, + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */, + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */, 84AF45A429A7FFA500FBB177 /* SentryTracerConcurrency.h */, 84AF45A529A7FFA500FBB177 /* SentryTracerConcurrency.mm */, 8E8C57A525EEFC42001CEEFA /* SentryTracesSampler.h */, @@ -3490,6 +3496,7 @@ 63BE85701ECEC6DE00DC44F5 /* NSDate+SentryExtras.h in Headers */, 63FE709520DA4C1000CDBAE8 /* SentryCrashReportFilterBasic.h in Headers */, 844EDCE52947DC3100C86F34 /* SentryNSTimerWrapper.h in Headers */, + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */, 63FE70A120DA4C1000CDBAE8 /* SentryCrashCString.h in Headers */, 7B63459D280EBA6300CFA05A /* SentryUIEventTracker.h in Headers */, 7B7D873424864C6600D2ECFF /* SentryCrashDefaultMachineContextWrapper.h in Headers */, @@ -3843,6 +3850,7 @@ 7B3B473825D6CC7E00D01640 /* SentryNSError.m in Sources */, D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */, 7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */, + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */, 8ECC674A25C23A20000E2BF6 /* SentryTransactionContext.mm in Sources */, 03BCC38C27E1C01A003232C7 /* SentryTime.mm in Sources */, A8F17B342902870300990B25 /* SentryHttpStatusCodeRange.m in Sources */, diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index 92a1f389e99..de8f8de3b6a 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -369,9 +369,8 @@ - (SentryId *)captureEvent:(SentryEvent *)event { return [self startTransactionWithContext:transactionContext bindToScope:bindToScope - waitForChildren:NO customSamplingContext:customSamplingContext - timerWrapper:nil]; + configuration:[SentryTracerConfiguration defaultConfiguration]]; } - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)context @@ -388,41 +387,10 @@ - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)con parentSampled:context.parentSampled]; } -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - SentrySamplingContext *samplingContext = - [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext - customSamplingContext:customSamplingContext]; - - SentryTracesSamplerDecision *samplerDecision = [_tracesSampler sample:samplingContext]; - transactionContext = [self transactionContext:transactionContext - withSampled:samplerDecision.decision]; - transactionContext.sampleRate = samplerDecision.sampleRate; - - SentryProfilesSamplerDecision *profilesSamplerDecision = - [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; - - id tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext - hub:self - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - timerWrapper:timerWrapper]; - - if (bindToScope) - self.scope.span = tracer; - - return tracer; -} - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper + configuration:(SentryTracerConfiguration *)configuration { SentrySamplingContext *samplingContext = [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext @@ -436,11 +404,12 @@ - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transa SentryProfilesSamplerDecision *profilesSamplerDecision = [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; + configuration.profilesSamplerDecision = profilesSamplerDecision; + SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext hub:self - profilesSamplerDecision:profilesSamplerDecision - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper]; + configuration:configuration]; + if (bindToScope) self.scope.span = tracer; diff --git a/Sources/Sentry/SentryPerformanceTracker.m b/Sources/Sentry/SentryPerformanceTracker.m index 9751eb47aa8..671e91a3486 100644 --- a/Sources/Sentry/SentryPerformanceTracker.m +++ b/Sources/Sentry/SentryPerformanceTracker.m @@ -79,11 +79,15 @@ - (SentrySpanId *)startSpanWithName:(NSString *)name } SENTRY_LOG_DEBUG(@"Creating new transaction bound to scope: %d", bindToScope); - newSpan = [SentrySDK.currentHub startTransactionWithContext:context - bindToScope:bindToScope - waitForChildren:YES - customSamplingContext:@{} - timerWrapper:nil]; + + newSpan = [SentrySDK.currentHub + startTransactionWithContext:context + bindToScope:bindToScope + customSamplingContext:@{} + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; [(SentryTracer *)newSpan setDelegate:self]; }]; diff --git a/Sources/Sentry/SentryTracer.m b/Sources/Sentry/SentryTracer.m index ff73c082018..31da399df8f 100644 --- a/Sources/Sentry/SentryTracer.m +++ b/Sources/Sentry/SentryTracer.m @@ -54,10 +54,9 @@ * @c -[finish] doesn't necessarily lead to finishing the tracer, because it could still wait for * child spans to finish if @c waitForChildren is @c YES . */ @property (nonatomic) BOOL wasFinishCalled; -@property (nonatomic) NSTimeInterval idleTimeout; -@property (nonatomic, nullable, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper; -@property (nonatomic, nullable, strong) SentryNSTimerWrapper *timerWrapper; @property (nonatomic, nullable, strong) NSTimer *deadlineTimer; +@property (nonnull, strong) SentryTracerConfiguration *configuration; + #if SENTRY_TARGET_PROFILING_SUPPORTED @property (nonatomic) BOOL isProfiling; @property (nonatomic) uint64_t startSystemTime; @@ -67,7 +66,6 @@ @implementation SentryTracer { /** Wether the tracer should wait for child spans to finish before finishing itself. */ - BOOL _waitForChildren; SentryTraceContext *_traceContext; SentryAppStartMeasurement *appStartMeasurement; NSMutableDictionary *_measurements; @@ -77,7 +75,6 @@ @implementation SentryTracer { NSDate *_originalStartTimestamp; #if SENTRY_HAS_UIKIT - NSUInteger initTotalFrames; NSUInteger initSlowFrames; NSUInteger initFrozenFrames; @@ -100,113 +97,60 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti { return [self initWithTransactionContext:transactionContext hub:hub - profilesSamplerDecision:nil - waitForChildren:NO - timerWrapper:nil]; + configuration:SentryTracerConfiguration.defaultConfiguration]; } - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren + configuration:(SentryTracerConfiguration *)configuration; { - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:nil - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:nil]; -} + if (!(self = [super initWithContext:transactionContext])) { + return nil; + } -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:timerWrapper]; -} + _configuration = configuration; -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:YES - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper - timerWrapper:nil]; -} - -- (instancetype) - initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision:(nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(nullable SentryDispatchQueueWrapper *)dispatchQueueWrapper - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - if (self = [super initWithContext:transactionContext]) { - self.transactionContext = transactionContext; - _children = [[NSMutableArray alloc] init]; - self.hub = hub; - self.wasFinishCalled = NO; - _waitForChildren = waitForChildren; - _measurements = [[NSMutableDictionary alloc] init]; - self.finishStatus = kSentrySpanStatusUndefined; - self.idleTimeout = idleTimeout; - self.dispatchQueueWrapper = dispatchQueueWrapper; - - if (timerWrapper == nil) { - self.timerWrapper = [[SentryNSTimerWrapper alloc] init]; - } else { - self.timerWrapper = timerWrapper; - } + self.transactionContext = transactionContext; + _children = [[NSMutableArray alloc] init]; + self.hub = hub; + self.wasFinishCalled = NO; + _measurements = [[NSMutableDictionary alloc] init]; + self.finishStatus = kSentrySpanStatusUndefined; - appStartMeasurement = [self getAppStartMeasurement]; + if (_configuration.timerWrapper == nil) { + _configuration.timerWrapper = [[SentryNSTimerWrapper alloc] init]; + } - if ([self hasIdleTimeout]) { - [self dispatchIdleTimeout]; - } + appStartMeasurement = [self getAppStartMeasurement]; - if ([self isAutoGeneratedTransaction]) { - [self startDeadlineTimer]; - } + if ([self hasIdleTimeout]) { + [self dispatchIdleTimeout]; + } + + if ([self isAutoGeneratedTransaction]) { + [self startDeadlineTimer]; + } #if SENTRY_HAS_UIKIT - // Store current amount of frames at the beginning to be able to calculate the amount of - // frames at the end of the transaction. - SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; - if (framesTracker.isRunning) { - SentryScreenFrames *currentFrames = framesTracker.currentFrames; - initTotalFrames = currentFrames.total; - initSlowFrames = currentFrames.slow; - initFrozenFrames = currentFrames.frozen; - } + // Store current amount of frames at the beginning to be able to calculate the amount of + // frames at the end of the transaction. + SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; + if (framesTracker.isRunning) { + SentryScreenFrames *currentFrames = framesTracker.currentFrames; + initTotalFrames = currentFrames.total; + initSlowFrames = currentFrames.slow; + initFrozenFrames = currentFrames.frozen; + } #endif // SENTRY_HAS_UIKIT #if SENTRY_TARGET_PROFILING_SUPPORTED - if (profilesSamplerDecision.decision == kSentrySampleDecisionYes) { - _isProfiling = YES; - _startSystemTime = getAbsoluteTime(); - [SentryProfiler startWithHub:hub]; - trackTracerWithID(self.traceId); - } -#endif // SENTRY_TARGET_PROFILING_SUPPORTED + if (_configuration.profilesSamplerDecision.decision == kSentrySampleDecisionYes) { + _isProfiling = YES; + _startSystemTime = getAbsoluteTime(); + [SentryProfiler startWithHub:hub]; + trackTracerWithID(self.traceId); } +#endif // SENTRY_TARGET_PROFILING_SUPPORTED return self; } @@ -219,40 +163,42 @@ - (nullable SentryTracer *)tracer - (void)dispatchIdleTimeout { if (_idleTimeoutBlock != nil) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } __weak SentryTracer *weakSelf = self; - _idleTimeoutBlock = [self.dispatchQueueWrapper createDispatchBlock:^{ + _idleTimeoutBlock = [_configuration.dispatchQueueWrapper createDispatchBlock:^{ if (weakSelf == nil) { SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything."); return; } [weakSelf finishInternal]; }]; + if (_idleTimeoutBlock == NULL) { SENTRY_LOG_WARN(@"Couln't create idle time out block. Can't schedule idle timeout. " @"Finishing transaction"); // If the transaction has no children, the SDK will discard it. [self finishInternal]; } else { - [self.dispatchQueueWrapper dispatchAfter:self.idleTimeout block:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchAfter:_configuration.idleTimeout + block:_idleTimeoutBlock]; } } - (BOOL)hasIdleTimeout { - return self.idleTimeout > 0 && self.dispatchQueueWrapper != nil; + return _configuration.idleTimeout > 0 && _configuration.dispatchQueueWrapper != nil; } - (BOOL)isAutoGeneratedTransaction { - return self.waitForChildren || [self hasIdleTimeout]; + return _configuration.waitForChildren || [self hasIdleTimeout]; } - (void)cancelIdleTimeout { if ([self hasIdleTimeout]) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } } @@ -260,14 +206,14 @@ - (void)startDeadlineTimer { __weak SentryTracer *weakSelf = self; self.deadlineTimer = - [self.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE - repeats:NO - block:^(NSTimer *_Nonnull timer) { - if (weakSelf == nil) { - return; - } - [weakSelf deadlineTimerFired]; - }]; + [_configuration.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE + repeats:NO + block:^(NSTimer *_Nonnull timer) { + if (weakSelf == nil) { + return; + } + [weakSelf deadlineTimerFired]; + }]; } - (void)deadlineTimerFired @@ -456,7 +402,7 @@ - (void)canBeFinished - (BOOL)hasUnfinishedChildSpansToWaitFor { - if (!_waitForChildren) { + if (!self.configuration.waitForChildren) { return NO; } @@ -524,7 +470,7 @@ - (void)finishInternal }]; @synchronized(_children) { - if (self.idleTimeout > 0.0 && _children.count == 0) { + if (_configuration.idleTimeout > 0.0 && _children.count == 0) { SENTRY_LOG_DEBUG(@"Was waiting for timeout for UI event trace but it had no children, " @"will not keep transaction."); return; diff --git a/Sources/Sentry/SentryTracerConfiguration.m b/Sources/Sentry/SentryTracerConfiguration.m new file mode 100644 index 00000000000..c14ec504f4b --- /dev/null +++ b/Sources/Sentry/SentryTracerConfiguration.m @@ -0,0 +1,28 @@ +#import "SentryTracerConfiguration.h" + +@implementation SentryTracerConfiguration + ++ (SentryTracerConfiguration *)defaultConfiguration +{ + return [[SentryTracerConfiguration alloc] init]; +} + ++ (SentryTracerConfiguration *)configurationWithBlock:(void (^)(SentryTracerConfiguration *))block +{ + SentryTracerConfiguration *result = [[SentryTracerConfiguration alloc] init]; + + block(result); + + return result; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.idleTimeout = 0; + self.waitForChildren = NO; + } + return self; +} + +@end diff --git a/Sources/Sentry/SentryUIEventTracker.m b/Sources/Sentry/SentryUIEventTracker.m index f15cbf752eb..d99bdf06523 100644 --- a/Sources/Sentry/SentryUIEventTracker.m +++ b/Sources/Sentry/SentryUIEventTracker.m @@ -121,12 +121,17 @@ - (void)start && ![span.operation containsString:SentrySpanOperationUIAction]; BOOL bindToScope = !ongoingScreenLoadTransaction && !ongoingManualTransaction; - transaction = - [SentrySDK.currentHub startTransactionWithContext:context - bindToScope:bindToScope - customSamplingContext:@{} - idleTimeout:self.idleTimeout - dispatchQueueWrapper:self.dispatchQueueWrapper]; + + transaction = [SentrySDK.currentHub + startTransactionWithContext:context + bindToScope:bindToScope + customSamplingContext:@{} + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *config) { + config.idleTimeout = self.idleTimeout; + config.waitForChildren = YES; + config.dispatchQueueWrapper = self.dispatchQueueWrapper; + }]]; SENTRY_LOG_DEBUG(@"SentryUIEventTracker automatically started a new transaction " @"with name: %@, bindToScope: %@", diff --git a/Sources/Sentry/include/SentryHub+Private.h b/Sources/Sentry/include/SentryHub+Private.h index 21692e7d22c..57f9ecf6b8d 100644 --- a/Sources/Sentry/include/SentryHub+Private.h +++ b/Sources/Sentry/include/SentryHub+Private.h @@ -1,7 +1,8 @@ #import "SentryHub.h" +#import "SentryTracer.h" @class SentryEnvelopeItem, SentryId, SentryScope, SentryTransaction, SentryDispatchQueueWrapper, - SentryEnvelope, SentryTracer, SentryNSTimerWrapper; + SentryEnvelope, SentryNSTimerWrapper; NS_ASSUME_NONNULL_BEGIN @@ -33,17 +34,10 @@ SentryHub (Private) operation:(NSString *)operation bindToScope:(BOOL)bindToScope; -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope diff --git a/Sources/Sentry/include/SentryTracer.h b/Sources/Sentry/include/SentryTracer.h index 300ea72a447..1a6fab6a8f8 100644 --- a/Sources/Sentry/include/SentryTracer.h +++ b/Sources/Sentry/include/SentryTracer.h @@ -1,5 +1,6 @@ #import "SentrySpan.h" #import "SentrySpanProtocol.h" +#import "SentryTracerConfiguration.h" #import NS_ASSUME_NONNULL_BEGIN @@ -31,13 +32,6 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; @property (nullable, nonatomic, copy) void (^finishCallback)(SentryTracer *); -/** - * Indicates whether this tracer will be finished only if all children have been finished. - * If this property is @c YES and the finish function is called before all children are finished - * the tracer will automatically finish when the last child finishes. - */ -@property (readonly) BOOL waitForChildren; - /** * Retrieves a trace context from this tracer. */ @@ -72,46 +66,17 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; hub:(nullable SentryHub *)hub; /** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction - * @param waitForChildren Whether this tracer should wait all children to finish. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction. - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction. - * @param waitForChildren Whether this tracer should wait all children to finish. - * @param timerWrapper A wrapper around @c NSTimer, to make it testable. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. + * Init a SentryTracer with given transaction context and hub and set other fields by default + * * @param transactionContext Transaction context * @param hub A hub to bind this transaction - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction - * @param idleTimeout The idle time to wait until to finish the transaction. + * @param configuration Configuration on how SentryTracer will behave + * + * @return SentryTracer */ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (id)startChildWithParentId:(SentrySpanId *)parentId operation:(NSString *)operation diff --git a/Sources/Sentry/include/SentryTracerConfiguration.h b/Sources/Sentry/include/SentryTracerConfiguration.h new file mode 100644 index 00000000000..25070ff20d3 --- /dev/null +++ b/Sources/Sentry/include/SentryTracerConfiguration.h @@ -0,0 +1,50 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@class SentryNSTimerWrapper, SentryDispatchQueueWrapper, SentryProfilesSamplerDecision; + +@interface SentryTracerConfiguration : NSObject + +/** + * Return an instance of SentryTracerConfiguration with default values. + */ +@property (class, readonly) SentryTracerConfiguration *defaultConfiguration; + +/** + * Indicates whether the tracer will be finished only if all children have been finished. + * If this property is YES and the finish function is called before all children are finished + * the tracer will automatically finish when the last child finishes. + * + * Default is NO. + */ +@property (nonatomic) BOOL waitForChildren; + +/** + * A dispatch queue wrapper to intermediate between the tracer and dispatch calls. + */ +@property (nonatomic, strong, nullable) SentryDispatchQueueWrapper *dispatchQueueWrapper; + +/** + * Whether to sample a profile corresponding to this transaction + */ +@property (nonatomic, strong, nullable) SentryProfilesSamplerDecision *profilesSamplerDecision; + +/** + * The idle time to wait until to finish the transaction + * + * Default is 0 seconds + */ +@property (nonatomic) NSTimeInterval idleTimeout; + +/** + * A writer around NSTimer, to make it testable + */ +@property (nonatomic, strong, nullable) SentryNSTimerWrapper *timerWrapper; + ++ (SentryTracerConfiguration *)configurationWithBlock: + (void (^)(SentryTracerConfiguration *configuration))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift index 8b24de0ada8..7641face2c3 100644 --- a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift @@ -155,7 +155,7 @@ class SentryNetworkTrackerTests: XCTestCase { let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), hub: nil, - waitForChildren: true) + configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) tracer.finish() @@ -172,8 +172,7 @@ class SentryNetworkTrackerTests: XCTestCase { let task = createDataTask() let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), - hub: nil, - waitForChildren: true) + hub: nil, configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) fixture.scope.span = tracer sut.urlSessionTaskResume(task) diff --git a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift index b4bda6d7347..5df7d37db68 100644 --- a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift @@ -47,7 +47,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let scopeSpan = fixture.scope.span XCTAssert(scopeSpan === transaction) - XCTAssertTrue(transaction.waitForChildren) + XCTAssertTrue(Dynamic(transaction).configuration.waitForChildren.asBool ?? false) XCTAssertEqual(transaction.transactionContext.name, fixture.someTransaction) XCTAssertEqual(transaction.transactionContext.nameSource, .custom) } diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 45bea05f484..8e32aee1441 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -191,6 +191,7 @@ #import "SentryPerformanceTracker+Testing.h" #import "SentrySpanOperations.h" #import "SentryTimeToDisplayTracker.h" +#import "SentryTracerConfiguration.h" #import "TestSentryViewHierarchy.h" #if SENTRY_HAS_UIKIT # import "MockUIScene.h" diff --git a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m index 495fc004e9c..8378315cfb6 100644 --- a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m +++ b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m @@ -27,11 +27,14 @@ - (void)testSpanFinishesAfterTracerReleased_NoCrash_TracerIsNil SentryHub *hub = [[SentryHub alloc] initWithClient:nil andScope:nil]; SentryTransactionContext *context = [[SentryTransactionContext alloc] initWithOperation:@""]; - SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:context - hub:hub - profilesSamplerDecision:nil - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer = [[SentryTracer alloc] + initWithTransactionContext:context + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; + [tracer finish]; child = [tracer startChildWithOperation:@"child"]; } @@ -55,17 +58,23 @@ - (void)testConcurrentTracerProfiling [[SentryProfilesSamplerDecision alloc] initWithDecision:kSentrySampleDecisionYes forSampleRate:@1]; - SentryTracer *tracer1 = [[SentryTracer alloc] initWithTransactionContext:context1 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; - - SentryTracer *tracer2 = [[SentryTracer alloc] initWithTransactionContext:context2 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer1 = [[SentryTracer alloc] + initWithTransactionContext:context1 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; + + SentryTracer *tracer2 = [[SentryTracer alloc] + initWithTransactionContext:context2 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; // force some samples to be taken by the profiler NSMutableString *string = [NSMutableString string]; diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index 27ba0b07e99..c67dec01722 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -88,9 +88,11 @@ class SentryTracerTests: XCTestCase { let tracer = hub.startTransaction( with: transactionContext, bindToScope: false, - waitForChildren: waitForChildren, customSamplingContext: [:], - timerWrapper: timerWrapper) as! SentryTracer + configuration: SentryTracerConfiguration(block: { + $0.waitForChildren = waitForChildren + $0.timerWrapper = self.timerWrapper + })) return tracer } @@ -99,8 +101,11 @@ class SentryTracerTests: XCTestCase { with: transactionContext, bindToScope: false, customSamplingContext: [:], - idleTimeout: idleTimeout, - dispatchQueueWrapper: dispatchQueueWrapper + configuration: SentryTracerConfiguration(block: { + $0.idleTimeout = idleTimeout + $0.dispatchQueueWrapper = dispatchQueueWrapper + $0.waitForChildren = true + }) ) return tracer } @@ -276,7 +281,7 @@ class SentryTracerTests: XCTestCase { } func testFinish_WithoutHub_DoesntCaptureTransaction() { - let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil, waitForChildren: false) + let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil) sut.finish()