Skip to content

Commit 4a0c282

Browse files
fix: Return SentryNoOpSpan when starting a child on a finished transaction (#2239)
* fix: Starting a new span on a finished span results in a SentryNoOpSpan Closes #1624 * Add a test * Changelog * Fix test * Fix test * Move changelog (this is such an annoying manual thing to keep fixing) * Whoops * Fix tests (why does this work locally?) * Clean up * Use self.tracer.isFinished * Add testStartGrandChildOnFinishedSpan * Move logic to SentryTracer
1 parent 0fdf0b2 commit 4a0c282

File tree

10 files changed

+62
-21
lines changed

10 files changed

+62
-21
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Fixes
6+
7+
- Return SentryNoOpSpan when starting a child on a finished transaction (#2239)
8+
39
## 7.27.0
410

511
### Features

Sentry.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
03F84D3727DD4191008FE43F /* SentrySamplingProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 03F84D3027DD4191008FE43F /* SentrySamplingProfiler.cpp */; };
3838
03F84D3827DD4191008FE43F /* SentryBacktrace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 03F84D3127DD4191008FE43F /* SentryBacktrace.cpp */; };
3939
03F9D37C2819A65C00602916 /* SentryProfilerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 03F9D37B2819A65C00602916 /* SentryProfilerTests.mm */; };
40+
0A1B497328E597DD00D7BFA3 /* TestLogOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1B497228E597DD00D7BFA3 /* TestLogOutput.swift */; };
4041
0A1C3592287D7107007D01E3 /* SentryMetaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1C3591287D7107007D01E3 /* SentryMetaTests.swift */; };
4142
0A2690B72885C2E000E4432D /* TestSentryPermissionsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AABE2EF2885C2120057ED69 /* TestSentryPermissionsObserver.swift */; };
4243
0A2D8D5B289815C0008720F6 /* SentryBaseIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D8D5A289815C0008720F6 /* SentryBaseIntegration.m */; };
@@ -743,6 +744,7 @@
743744
03F84D3027DD4191008FE43F /* SentrySamplingProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SentrySamplingProfiler.cpp; path = Sources/Sentry/SentrySamplingProfiler.cpp; sourceTree = SOURCE_ROOT; };
744745
03F84D3127DD4191008FE43F /* SentryBacktrace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SentryBacktrace.cpp; path = Sources/Sentry/SentryBacktrace.cpp; sourceTree = SOURCE_ROOT; };
745746
03F9D37B2819A65C00602916 /* SentryProfilerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerTests.mm; sourceTree = "<group>"; };
747+
0A1B497228E597DD00D7BFA3 /* TestLogOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestLogOutput.swift; sourceTree = "<group>"; };
746748
0A1C3591287D7107007D01E3 /* SentryMetaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryMetaTests.swift; sourceTree = "<group>"; };
747749
0A2D8D5A289815C0008720F6 /* SentryBaseIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBaseIntegration.m; sourceTree = "<group>"; };
748750
0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryBaseIntegration.h; path = include/SentryBaseIntegration.h; sourceTree = "<group>"; };
@@ -1885,6 +1887,7 @@
18851887
7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */,
18861888
8ED3D305264DFE700049393B /* SentryUIViewControllerSanitizerTests.swift */,
18871889
D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */,
1890+
0A1B497228E597DD00D7BFA3 /* TestLogOutput.swift */,
18881891
);
18891892
path = SentryTests;
18901893
sourceTree = "<group>";
@@ -3653,6 +3656,7 @@
36533656
7B68D93625FF5F1A0082D139 /* SentryAppState+Equality.m in Sources */,
36543657
7B5CAF7E27F5AD3500ED0DB6 /* TestNSURLRequestBuilder.m in Sources */,
36553658
8EAC7FF8265C8910005B44E5 /* SentryTracerTests.swift in Sources */,
3659+
0A1B497328E597DD00D7BFA3 /* TestLogOutput.swift in Sources */,
36563660
7BA61CBD247BC6B900C130A8 /* TestSentryCrashBinaryImageProvider.swift in Sources */,
36573661
7BFA69F627E0840400233199 /* SentryANRTrackingIntegrationTests.swift in Sources */,
36583662
7BBD18B62451807600427C76 /* SentryDefaultRateLimitsTests.swift in Sources */,

Sources/Sentry/SentrySpan.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#import "NSDate+SentryExtras.h"
33
#import "NSDictionary+SentrySanitize.h"
44
#import "SentryCurrentDate.h"
5+
#import "SentryLog.h"
56
#import "SentryNoOpSpan.h"
67
#import "SentryTraceHeader.h"
78
#import "SentryTracer.h"

Sources/Sentry/SentryTracer.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#import "SentryFramesTracker.h"
88
#import "SentryHub+Private.h"
99
#import "SentryLog.h"
10+
#import "SentryNoOpSpan.h"
1011
#import "SentryProfiler.h"
1112
#import "SentryProfilesSampler.h"
1213
#import "SentryProfilingConditionals.h"
@@ -261,6 +262,12 @@ - (void)cancelIdleTimeout
261262
{
262263
[self cancelIdleTimeout];
263264

265+
if (self.isFinished) {
266+
SENTRY_LOG_WARN(
267+
@"Starting a child on a finished span is not supported; it won't be sent to Sentry.");
268+
return [SentryNoOpSpan shared];
269+
}
270+
264271
SentrySpanContext *context =
265272
[[SentrySpanContext alloc] initWithTraceId:_rootSpan.context.traceId
266273
spanId:[[SentrySpanId alloc] init]

Tests/SentryTests/Helper/SentryLogTests.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,4 @@ class SentryLogTests: XCTestCase {
6868
"Sentry - info:: 3",
6969
"Sentry - debug:: 4"], logOutput.loggedMessages)
7070
}
71-
72-
class TestLogOutput: SentryLogOutput {
73-
74-
var loggedMessages: [String] = []
75-
override func log(_ message: String) {
76-
loggedMessages.append(message)
77-
}
78-
}
79-
8071
}

Tests/SentryTests/Integrations/SentryBaseIntegrationTests.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,4 @@ class SentryBaseIntegrationTests: XCTestCase {
4848
XCTAssertFalse(result)
4949
XCTAssertEqual(["Sentry - debug:: Not going to enable SentryTests.MyTestIntegration because enableAutoSessionTracking is disabled."], logOutput.loggedMessages)
5050
}
51-
52-
class TestLogOutput: SentryLogOutput {
53-
var loggedMessages: [String] = []
54-
override func log(_ message: String) {
55-
loggedMessages.append(message)
56-
}
57-
}
5851
}

Tests/SentryTests/Performance/SentryTracerObjCTests.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ - (void)testSpanFinishesAfterTracerReleased_NoCrash_TracerIsNil
3131
}
3232

3333
XCTAssertNotNil(child);
34-
XCTAssertNil(child.tracer);
3534
[child finish];
3635
}
3736

Tests/SentryTests/Performance/SentryTracerTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ class SentryTracerTests: XCTestCase {
262262

263263
child.finish()
264264
XCTAssertEqual(2, fixture.dispatchQueue.dispatchAfterInvocations.count)
265-
265+
266+
// The grandchild is a NoOp span
266267
let grandChild = child.startChild(operation: fixture.transactionOperation)
267268
XCTAssertEqual(3, fixture.dispatchQueue.dispatchCancelInvocations.count)
268269

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Foundation
2+
3+
class TestLogOutput: SentryLogOutput {
4+
var loggedMessages: [String] = []
5+
override func log(_ message: String) {
6+
loggedMessages.append(message)
7+
}
8+
}

Tests/SentryTests/Transaction/SentrySpanTests.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import Sentry
22
import XCTest
33

44
class SentrySpanTests: XCTestCase {
5-
5+
private var logOutput: TestLogOutput!
6+
private var fixture: Fixture!
7+
68
private class Fixture {
79
let someTransaction = "Some Transaction"
810
let someOperation = "Some Operation"
@@ -12,7 +14,7 @@ class SentrySpanTests: XCTestCase {
1214
let options: Options
1315
let currentDateProvider = TestCurrentDateProvider()
1416
let tracer = SentryTracer()
15-
17+
1618
init() {
1719
options = Options()
1820
options.tracesSampleRate = 1
@@ -32,9 +34,13 @@ class SentrySpanTests: XCTestCase {
3234

3335
}
3436

35-
private var fixture: Fixture!
3637
override func setUp() {
3738
super.setUp()
39+
40+
logOutput = TestLogOutput()
41+
SentryLog.configure(true, diagnosticLevel: SentryLevel.debug)
42+
SentryLog.setLogOutput(logOutput)
43+
3844
fixture = Fixture()
3945
CurrentDate.setCurrentDateProvider(fixture.currentDateProvider)
4046
}
@@ -153,6 +159,31 @@ class SentrySpanTests: XCTestCase {
153159
XCTAssertEqual(childSpan.context.operation, fixture.someOperation)
154160
XCTAssertEqual(childSpan.context.spanDescription, fixture.someDescription)
155161
}
162+
163+
func testStartChildOnFinishedSpan() {
164+
let span = fixture.getSut()
165+
span.finish()
166+
167+
let childSpan = span.startChild(operation: fixture.someOperation, description: fixture.someDescription)
168+
169+
XCTAssertNil(childSpan.context.parentSpanId)
170+
XCTAssertEqual(childSpan.context.operation, "")
171+
XCTAssertNil(childSpan.context.spanDescription)
172+
XCTAssertTrue(logOutput.loggedMessages.contains("Sentry - warning:: Starting a child on a finished span is not supported; it won\'t be sent to Sentry."))
173+
}
174+
175+
func testStartGrandChildOnFinishedSpan() {
176+
let span = fixture.getSut()
177+
let childSpan = span.startChild(operation: fixture.someOperation)
178+
childSpan.finish()
179+
span.finish()
180+
181+
let grandChild = childSpan.startChild(operation: fixture.someOperation, description: fixture.someDescription)
182+
XCTAssertNil(grandChild.context.parentSpanId)
183+
XCTAssertEqual(grandChild.context.operation, "")
184+
XCTAssertNil(grandChild.context.spanDescription)
185+
XCTAssertTrue(logOutput.loggedMessages.contains("Sentry - warning:: Starting a child on a finished span is not supported; it won\'t be sent to Sentry."))
186+
}
156187

157188
func testAddAndRemoveExtras() {
158189
let span = fixture.getSut()

0 commit comments

Comments
 (0)