Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ This changelog lists every breaking change. For a high-level overview and upgrad
- Expose attachment type on `SentryAttachment` for downstream SDKs (like sentry-godot) (#6521)
- Increase attachment max size to 100MB (#6537)
- Increase maximum attachment size to 200MB (#6726)
- Flush Logs on `WillTerminate` or `WillResignActive` App State (#6909)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 🚫 The changelog entry seems to be part of an already released section ## 9.0.0.
    Consider moving the entry to the ## Unreleased section, please.


## 9.0.0-rc.1

Expand Down
36 changes: 34 additions & 2 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,12 @@
92235CAC2E15369900865983 /* SentryLogBatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAB2E15369900865983 /* SentryLogBatcher.swift */; };
92235CAE2E15549C00865983 /* SentryLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAD2E15549C00865983 /* SentryLogger.swift */; };
92235CB02E155B2600865983 /* SentryLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAF2E155B2600865983 /* SentryLoggerTests.swift */; };
925189AC2EDDA6A300557BD1 /* FlushLogsIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925189AA2EDDA6A300557BD1 /* FlushLogsIntegration.swift */; };
9264E1EB2E2E385E00B077CF /* SentryLogMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */; };
9264E1ED2E2E397C00B077CF /* SentryLogMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9264E1EC2E2E397400B077CF /* SentryLogMessageTests.swift */; };
92672BB629C9A2A9006B021C /* SentryBreadcrumb+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
927A5CC42DD7626B00B82404 /* SentryEnvelopeItemHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */; };
927D21FB2ED5DE8A00916D31 /* FlushLogsIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927D21FA2ED5DE7F00916D31 /* FlushLogsIntegrationTests.swift */; };
928207C42E251B8F009285A4 /* SentryScope+PrivateSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */; };
9286059529A5096600F96038 /* SentryGeo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9286059429A5096600F96038 /* SentryGeo.h */; settings = {ATTRIBUTES = (Public, ); }; };
9286059729A5098900F96038 /* SentryGeo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9286059629A5098900F96038 /* SentryGeo.m */; };
Expand Down Expand Up @@ -2066,10 +2068,12 @@
92235CAB2E15369900865983 /* SentryLogBatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogBatcher.swift; sourceTree = "<group>"; };
92235CAD2E15549C00865983 /* SentryLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogger.swift; sourceTree = "<group>"; };
92235CAF2E155B2600865983 /* SentryLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLoggerTests.swift; sourceTree = "<group>"; };
925189AA2EDDA6A300557BD1 /* FlushLogsIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlushLogsIntegration.swift; sourceTree = "<group>"; };
9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogMessage.swift; sourceTree = "<group>"; };
9264E1EC2E2E397400B077CF /* SentryLogMessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogMessageTests.swift; sourceTree = "<group>"; };
92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryBreadcrumb+Private.h"; path = "include/HybridPublic/SentryBreadcrumb+Private.h"; sourceTree = "<group>"; };
927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeItemHeaderTests.swift; sourceTree = "<group>"; };
927D21FA2ED5DE7F00916D31 /* FlushLogsIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlushLogsIntegrationTests.swift; sourceTree = "<group>"; };
928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryScope+PrivateSwift.h"; path = "include/SentryScope+PrivateSwift.h"; sourceTree = "<group>"; };
9286059429A5096600F96038 /* SentryGeo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryGeo.h; path = Public/SentryGeo.h; sourceTree = "<group>"; };
9286059629A5098900F96038 /* SentryGeo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryGeo.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3509,6 +3513,7 @@
7B944FA924697E9700A10721 /* Integrations */ = {
isa = PBXGroup;
children = (
927D21F42ED5DE7800916D31 /* Log */,
843FB3422D156B9900558F18 /* Feedback */,
7BF6505D292B77D100BBA5A8 /* MetricKit */,
D808FB85281AB2EF009A2A33 /* UIEvents */,
Expand Down Expand Up @@ -4187,6 +4192,31 @@
name = Transaction;
sourceTree = "<group>";
};
9246A2352ED5CFDC002FA318 /* AppState */ = {
isa = PBXGroup;
children = (
FA4C32972DF7513F001D7B01 /* SentryAppState.swift */,
FA560F5A2E8C876A00F2AF7F /* SentryAppStateManager.swift */,
);
path = AppState;
sourceTree = "<group>";
};
925189AB2EDDA6A300557BD1 /* Log */ = {
isa = PBXGroup;
children = (
925189AA2EDDA6A300557BD1 /* FlushLogsIntegration.swift */,
);
path = Log;
sourceTree = "<group>";
};
927D21F42ED5DE7800916D31 /* Log */ = {
isa = PBXGroup;
children = (
927D21FA2ED5DE7F00916D31 /* FlushLogsIntegrationTests.swift */,
);
path = Log;
sourceTree = "<group>";
};
D4009EA02D77196F0007AF30 /* ViewCapture */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4455,9 +4485,9 @@
D800942328F82E8D005D3943 /* Swift */ = {
isa = PBXGroup;
children = (
9246A2352ED5CFDC002FA318 /* AppState */,
FAAB95CC2EA18B260030A2DB /* SentryDependencyContainer.swift */,
FAAB95B92EA1633E0030A2DB /* State */,
FA560F5A2E8C876A00F2AF7F /* SentryAppStateManager.swift */,
F429D37E2E8532A300DBF387 /* Networking */,
F4FE9E062E6248BB0014FED5 /* SentryCrash */,
FABB48B22E59310D0071397E /* Transaction */,
Expand All @@ -4470,7 +4500,6 @@
D856272A2A374A6800FB8062 /* Tools */,
D8B665BB2B95F5A100BD0E7B /* module.modulemap */,
FA4C32962DF7513F001D7B00 /* SentryExperimentalOptions.swift */,
FA4C32972DF7513F001D7B01 /* SentryAppState.swift */,
FA6251FE2EB52DD700BFC967 /* SentryHub.swift */,
FA6252052EB5489B00BFC967 /* SentryClient.swift */,
FA27EC152EB9236000F2ECF7 /* Options.swift */,
Expand Down Expand Up @@ -4831,6 +4860,7 @@
D8CAC02D2BA0663E00E38F34 /* Integrations */ = {
isa = PBXGroup;
children = (
925189AB2EDDA6A300557BD1 /* Log */,
FAB0073C2E9F47DE001C806A /* Session */,
FAE579B42E7DBE9400B710F9 /* SentryGlobalEventProcessor.swift */,
D49064862DFAE1B700555785 /* Screenshot */,
Expand Down Expand Up @@ -5772,6 +5802,7 @@
63AA75EF1EB8B3C400D153DE /* SentryClient.m in Sources */,
D4B0DC7F2DA9257A00DE61B6 /* SentryRenderVideoResult.swift in Sources */,
FAAB964E2EA698730030A2DB /* SentryDebugImageProvider.swift in Sources */,
925189AC2EDDA6A300557BD1 /* FlushLogsIntegration.swift in Sources */,
7B7D873624864C9D00D2ECFF /* SentryCrashDefaultMachineContextWrapper.m in Sources */,
63FE712F20DA4C1100CDBAE8 /* SentryCrashSysCtl.c in Sources */,
62212B872D520CB00062C2FA /* SentryEventCodable.swift in Sources */,
Expand Down Expand Up @@ -6315,6 +6346,7 @@
D88817DD26D72BA500BF2251 /* SentryTraceContextTests.swift in Sources */,
D4E3F35D2D4A864600F79E2B /* SentryNSDictionarySanitizeTests.swift in Sources */,
7B984A9F28E572AF001F4BEE /* CrashReport.swift in Sources */,
927D21FB2ED5DE8A00916D31 /* FlushLogsIntegrationTests.swift in Sources */,
D4AF00252D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m in Sources */,
D46712622DCD059900D4074A /* SentryRedactDefaultOptionsTests.swift in Sources */,
D480F9DB2DE47AF2009A0594 /* SentryScopePersistentStoreTests.swift in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions SentryTestUtils/Sources/TestClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,9 @@ public class TestClient: SentryClientInternal {
captureLogInvocations.record((castLog, scope))
}
}

public var captureLogsInvocations = Invocations<Void>()
public override func captureLogs() {
captureLogsInvocations.record(())
}
}
5 changes: 5 additions & 0 deletions Sources/Sentry/SentryBaseIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ - (BOOL)shouldBeEnabledWithOptions:(SentryOptions *)options
#endif // SENTRY_HAS_UIKIT
}

if ((integrationOptions & kIntegrationOptionEnableLogs) && !options.enableLogs) {
[self logWithOptionName:@"enableLogs"];
return NO;
}

return YES;
}

Expand Down
21 changes: 17 additions & 4 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,18 @@ - (instancetype)initWithOptions:(SentryOptions *)options
self.locale = locale;
self.timezone = timezone;
self.attachmentProcessors = [[NSMutableArray alloc] init];
self.logBatcher = [[SentryLogBatcher alloc]
initWithOptions:options
dispatchQueue:SentryDependencyContainer.sharedInstance.dispatchQueueWrapper
delegate:self];

// Uses DEFAULT priority (not LOW) because captureLogs() is called synchronously during
// app lifecycle events (willResignActive, willTerminate) and needs to complete quickly.
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
SentryDispatchQueueWrapper *logBatcherQueue =
[[SentryDispatchQueueWrapper alloc] initWithName:"io.sentry.log-batcher"
attributes:attributes];

self.logBatcher = [[SentryLogBatcher alloc] initWithOptions:options
dispatchQueue:logBatcherQueue
delegate:self];

// The SDK stores the installationID in a file. The first call requires file IO. To avoid
// executing this on the main thread, we cache the installationID async here.
Expand Down Expand Up @@ -1105,6 +1113,11 @@ - (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope
}
}

- (void)captureLogs
{
[self.logBatcher captureLogs];
}

- (void)captureLogsData:(NSData *)data with:(NSNumber *)itemCount
{
SentryEnvelopeItem *envelopeItem =
Expand Down
1 change: 1 addition & 0 deletions Sources/Sentry/include/SentryBaseIntegration.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef NS_OPTIONS(NSUInteger, SentryIntegrationOption) {
kIntegrationOptionEnableMetricKit = 1 << 17,
kIntegrationOptionEnableReplay = 1 << 18,
kIntegrationOptionStartFramesTracker = 1 << 19,
kIntegrationOptionEnableLogs = 1 << 20,
};

@class SentryOptions;
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryClient+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ NS_ASSUME_NONNULL_BEGIN

- (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope;

- (void)captureLogs;

@end

NS_ASSUME_NONNULL_END
11 changes: 8 additions & 3 deletions Sources/Swift/Core/Integrations/Integrations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ private struct AnyIntegration {
@_spi(Private) @objc public final class SentrySwiftIntegrationInstaller: NSObject {
@objc public class func install(with options: Options) {
let dependencies = SentryDependencyContainer.sharedInstance()
var integrations: [AnyIntegration] = []

#if os(iOS) && !SENTRY_NO_UIKIT
let integrations: [AnyIntegration] = [.init(UserFeedbackIntegration<SentryDependencyContainer>.self)]
#else
let integrations: [AnyIntegration] = []
integrations.append(.init(UserFeedbackIntegration<SentryDependencyContainer>.self))
#endif

#if ((os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))) && !SENTRY_NO_UIKIT) || ((os(macOS) || targetEnvironment(macCatalyst)) && !SENTRY_NO_UIKIT)
integrations.append(.init(FlushLogsIntegration<SentryDependencyContainer>.self))
#endif

integrations.forEach { anyIntegration in
guard let integration = anyIntegration.install(options, dependencies) else { return }

Expand Down
82 changes: 82 additions & 0 deletions Sources/Swift/Integrations/Log/FlushLogsIntegration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@_implementationOnly import _SentryPrivate

#if (os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))) && !SENTRY_NO_UIKIT
import UIKit
private typealias CrossPlatformApplication = UIApplication
#elseif (os(macOS) || targetEnvironment(macCatalyst)) && !SENTRY_NO_UIKIT
import AppKit
private typealias CrossPlatformApplication = NSApplication
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Mac Catalyst incorrectly imports AppKit instead of UIKit

The conditional compilation directives incorrectly import AppKit for Mac Catalyst apps. Mac Catalyst uses UIKit and UIApplication, not AppKit and NSApplication. The targetEnvironment(macCatalyst) condition should be included in the first #if block with iOS/tvOS (line 3), not in the #elseif block with macOS (line 6). This causes the wrong framework to be imported and the wrong application type to be used for Mac Catalyst builds.

Fix in Cursor Fix in Web


#if ((os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))) && !SENTRY_NO_UIKIT) || ((os(macOS) || targetEnvironment(macCatalyst)) && !SENTRY_NO_UIKIT)

protocol NotificationCenterProvider {
var notificationCenterWrapper: SentryNSNotificationCenterWrapper { get }
}

final class FlushLogsIntegration<Dependencies: NotificationCenterProvider>: NSObject, SwiftIntegration {

private let notificationCenter: SentryNSNotificationCenterWrapper

init?(with options: Options, dependencies: Dependencies) {
guard options.enableLogs else {
return nil
}

self.notificationCenter = dependencies.notificationCenterWrapper

super.init()

notificationCenter.addObserver(
self,
selector: #selector(willResignActive),
name: CrossPlatformApplication.willResignActiveNotification,
object: nil
)

notificationCenter.addObserver(
self,
selector: #selector(willTerminate),
name: CrossPlatformApplication.willTerminateNotification,
object: nil
)
}

func uninstall() {
notificationCenter.removeObserver(
self,
name: CrossPlatformApplication.willResignActiveNotification,
object: nil
)

notificationCenter.removeObserver(
self,
name: CrossPlatformApplication.willTerminateNotification,
object: nil
)
}

deinit {
uninstall()
}

@objc private func willResignActive() {
guard let client = SentrySDKInternal.currentHub().getClient() else {
return
}
client.captureLogs()
}

@objc private func willTerminate() {
guard let client = SentrySDKInternal.currentHub().getClient() else {
return
}
client.captureLogs()
}

static var name: String {
"FlushLogsIntegration"
}
}

#endif
4 changes: 4 additions & 0 deletions Sources/Swift/SentryDependencyContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,7 @@ extension SentryFileManager: SentryFileManagerProtocol { }
#if os(iOS) && !SENTRY_NO_UIKIT
extension SentryDependencyContainer: ScreenshotSourceProvider { }
#endif

#if ((os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))) && !SENTRY_NO_UIKIT) || ((os(macOS) || targetEnvironment(macCatalyst)) && !SENTRY_NO_UIKIT)
extension SentryDependencyContainer: NotificationCenterProvider { }
#endif
5 changes: 3 additions & 2 deletions Tests/SentryTests/Helper/SentryAppStateManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import XCTest

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
class SentryAppStateManagerTests: XCTestCase {
private static let dsnAsString = TestConstants.dsnAsString(username: "SentryOutOfMemoryTrackerTests")
final class SentryAppStateManagerTests: XCTestCase {
private static let dsnAsString = TestConstants.dsnAsString(username: "SentryAppStateManagerTests")

private class Fixture {

Expand Down Expand Up @@ -51,6 +51,7 @@ class SentryAppStateManagerTests: XCTestCase {

override func tearDown() {
super.tearDown()
sut.stop(withForce: true)
fixture.fileManager.deleteAppState()
clearTestState()
}
Expand Down
Loading
Loading