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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Improvements

- Flush Logs on `WillTerminate` or `WillResignActive` App State (#6909)

## 9.0.0-rc.1

### Breaking Changes
Expand Down
44 changes: 42 additions & 2 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,15 @@
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 */; };
9246A2322ED5CDA7002FA318 /* SentryLogFlushIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9246A2312ED5CDA7002FA318 /* SentryLogFlushIntegration.h */; settings = {ATTRIBUTES = (Private, ); }; };
9246A2342ED5CDB5002FA318 /* SentryLogFlushIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 9246A2332ED5CDB5002FA318 /* SentryLogFlushIntegration.m */; };
9246A2372ED5D008002FA318 /* SentryAppStateListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9246A2362ED5D008002FA318 /* SentryAppStateListener.swift */; };
925824C22CB5897700C9B20B /* SentrySessionReplayIntegration-Hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = D80382BE2C09C6FD0090E048 /* SentrySessionReplayIntegration-Hybrid.h */; settings = {ATTRIBUTES = (Private, ); }; };
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 /* SentryLogFlushIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927D21FA2ED5DE7F00916D31 /* SentryLogFlushIntegrationTests.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 @@ -2074,10 +2078,14 @@
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>"; };
9246A2312ED5CDA7002FA318 /* SentryLogFlushIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryLogFlushIntegration.h; path = include/SentryLogFlushIntegration.h; sourceTree = "<group>"; };
9246A2332ED5CDB5002FA318 /* SentryLogFlushIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryLogFlushIntegration.m; sourceTree = "<group>"; };
9246A2362ED5D008002FA318 /* SentryAppStateListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAppStateListener.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 /* SentryLogFlushIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogFlushIntegrationTests.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 @@ -2939,6 +2947,7 @@
D85596EF280580BE0041FF8B /* Screenshot */,
0A9BF4E028A114690068D266 /* ViewHierarchy */,
D80CD8D52B752FD9002F710B /* SessionReplay */,
9246A22E2ED5CD59002FA318 /* Log */,
FA034AC72DD3DB4900FE3107 /* SentryIntegrationProtocol.h */,
7BA235622600B61200E12865 /* SentryInternalNotificationNames.h */,
0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */,
Expand Down Expand Up @@ -3516,6 +3525,7 @@
7B944FA924697E9700A10721 /* Integrations */ = {
isa = PBXGroup;
children = (
927D21F42ED5DE7800916D31 /* Log */,
843FB3422D156B9900558F18 /* Feedback */,
7BF6505D292B77D100BBA5A8 /* MetricKit */,
D808FB85281AB2EF009A2A33 /* UIEvents */,
Expand Down Expand Up @@ -4196,6 +4206,33 @@
name = Transaction;
sourceTree = "<group>";
};
9246A22E2ED5CD59002FA318 /* Log */ = {
isa = PBXGroup;
children = (
9246A2312ED5CDA7002FA318 /* SentryLogFlushIntegration.h */,
9246A2332ED5CDB5002FA318 /* SentryLogFlushIntegration.m */,
);
name = Log;
sourceTree = "<group>";
};
9246A2352ED5CFDC002FA318 /* AppState */ = {
isa = PBXGroup;
children = (
FA4C32972DF7513F001D7B01 /* SentryAppState.swift */,
9246A2362ED5D008002FA318 /* SentryAppStateListener.swift */,
FA560F5A2E8C876A00F2AF7F /* SentryAppStateManager.swift */,
);
path = AppState;
sourceTree = "<group>";
};
927D21F42ED5DE7800916D31 /* Log */ = {
isa = PBXGroup;
children = (
927D21FA2ED5DE7F00916D31 /* SentryLogFlushIntegrationTests.swift */,
);
path = Log;
sourceTree = "<group>";
};
D4009EA02D77196F0007AF30 /* ViewCapture */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4464,9 +4501,9 @@
D800942328F82E8D005D3943 /* Swift */ = {
isa = PBXGroup;
children = (
9246A2352ED5CFDC002FA318 /* AppState */,
FAAB95CC2EA18B260030A2DB /* SentryDependencyContainer.swift */,
FAAB95B92EA1633E0030A2DB /* State */,
FA560F5A2E8C876A00F2AF7F /* SentryAppStateManager.swift */,
F429D37E2E8532A300DBF387 /* Networking */,
F4FE9E062E6248BB0014FED5 /* SentryCrash */,
FABB48B22E59310D0071397E /* Transaction */,
Expand All @@ -4479,7 +4516,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 @@ -5299,6 +5335,7 @@
7BD86EC5264A63F6005439DB /* SentrySysctlObjC.h in Headers */,
63BE85701ECEC6DE00DC44F5 /* SentryDateUtils.h in Headers */,
63FE709520DA4C1000CDBAE8 /* SentryCrashReportFilterBasic.h in Headers */,
9246A2322ED5CDA7002FA318 /* SentryLogFlushIntegration.h in Headers */,
D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */,
7B63459D280EBA6300CFA05A /* SentryUIEventTracker.h in Headers */,
7B7D873424864C6600D2ECFF /* SentryCrashDefaultMachineContextWrapper.h in Headers */,
Expand Down Expand Up @@ -5959,6 +5996,7 @@
FA67DD042DDBD4EA00896B02 /* SwiftDescriptor.swift in Sources */,
FA67DD052DDBD4EA00896B02 /* SentrySDKLog.swift in Sources */,
FA67DD062DDBD4EA00896B02 /* SentryRedactOptions.swift in Sources */,
9246A2372ED5D008002FA318 /* SentryAppStateListener.swift in Sources */,
FA67DD072DDBD4EA00896B02 /* SentryLevel.swift in Sources */,
FA67DD082DDBD4EA00896B02 /* SentryDefaultViewRenderer.swift in Sources */,
FA67DD092DDBD4EA00896B02 /* URLSessionTaskHelper.swift in Sources */,
Expand Down Expand Up @@ -6109,6 +6147,7 @@
621F61F12BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift in Sources */,
FAB007362E9EF8D3001C806A /* SentryUIViewControllerPerformanceTracker.swift in Sources */,
D88817D826D7149100BF2251 /* SentryTraceContext.m in Sources */,
9246A2342ED5CDB5002FA318 /* SentryLogFlushIntegration.m in Sources */,
D8F67B1B2BE9728600C9197B /* SentrySRDefaultBreadcrumbConverter.swift in Sources */,
8EBF870926140D37001A6853 /* SentryPerformanceTracker.m in Sources */,
D865893029D6ECA7000BE151 /* SentryCrashBinaryImageCache.c in Sources */,
Expand Down Expand Up @@ -6324,6 +6363,7 @@
7B26BBFB24C0A66D00A79CCC /* SentrySdkInfoNilTests.m in Sources */,
D4E3F35D2D4A864600F79E2B /* SentryNSDictionarySanitizeTests.swift in Sources */,
7B984A9F28E572AF001F4BEE /* CrashReport.swift in Sources */,
927D21FB2ED5DE8A00916D31 /* SentryLogFlushIntegrationTests.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 flushLogsInvocations = Invocations<Void>()
public override func flushLogs() {
flushLogsInvocations.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
5 changes: 5 additions & 0 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,11 @@ - (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope
}
}

- (void)flushLogs
{
[self.logBatcher captureLogs];
Copy link
Member

Choose a reason for hiding this comment

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

m: If the logBatcher then actually flushes the logs, shouldn't we rename the method to flush?

Suggested change
[self.logBatcher captureLogs];
[self.logBatcher flush];

}

- (void)captureLogsData:(NSData *)data with:(NSNumber *)itemCount
{
SentryEnvelopeItem *envelopeItem =
Expand Down
61 changes: 61 additions & 0 deletions Sources/Sentry/SentryLogFlushIntegration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#import "SentryLogFlushIntegration.h"
#import "SentryClient+Private.h"
#import "SentryHub.h"
#import "SentryLogC.h"
#import "SentrySDK+Private.h"
#import "SentrySwift.h"

#if SENTRY_HAS_UIKIT
Copy link

Choose a reason for hiding this comment

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

Bug: Platform mismatch between integration and manager

The SentryLogFlushIntegration is conditionally compiled only for SENTRY_HAS_UIKIT platforms (iOS, tvOS, visionOS), but macOS also has equivalent notifications (NSApplication.willTerminateNotification and NSApplication.willResignActiveNotification). The feature won't work on macOS despite the platform having the necessary app lifecycle notifications, creating an inconsistent behavior across platforms. The SentryAppStateManager and its listener infrastructure also exclude macOS, preventing log flushing on app termination or resign on that platform.

Additional Locations (1)

Fix in Cursor Fix in Web


NS_ASSUME_NONNULL_BEGIN

@interface SentryLogFlushIntegration () <SentryAppStateListener>
Copy link
Member

Choose a reason for hiding this comment

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

h: We can write integrations in Swift after merging #6862. I think we should already convert this integration to Swift then.


@end

@implementation SentryLogFlushIntegration

- (BOOL)installWithOptions:(SentryOptions *)options
{
if (![super installWithOptions:options]) {
return NO;
}

[[[SentryDependencyContainer sharedInstance] appStateManager] addListener:self];
Copy link
Member

Choose a reason for hiding this comment

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

h: What drove you to use the app state manager here? I would rather subscribe and also unsubscribe to the notifications willResignActiveNotification and willTerminateNotification as the SentrySessionTracker does. Then we don't need to modify the app state manager for this.


return YES;
}

- (SentryIntegrationOption)integrationOptions
{
return kIntegrationOptionEnableLogs;
}

- (void)uninstall
{
[[[SentryDependencyContainer sharedInstance] appStateManager] removeListener:self];
}

# pragma mark - SentryAppStateListener

- (void)appStateManagerWillResignActive
{
SentryClientInternal *client = [SentrySDKInternal.currentHub getClient];
if (client != nil) {
[client flushLogs];
}
}

- (void)appStateManagerWillTerminate
{
SentryClientInternal *client = [SentrySDKInternal.currentHub getClient];
if (client != nil) {
[client flushLogs];
}
}

@end

NS_ASSUME_NONNULL_END

#endif // SENTRY_HAS_UIKIT
3 changes: 2 additions & 1 deletion Sources/Sentry/SentrySDKInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#if SENTRY_HAS_UIKIT
# import "SentryAppStartTrackingIntegration.h"
# import "SentryFramesTrackingIntegration.h"
# import "SentryLogFlushIntegration.h"
# import "SentryPerformanceTrackingIntegration.h"
# import "SentryScreenshotIntegration.h"
# import "SentryUIEventTrackingIntegration.h"
Expand Down Expand Up @@ -525,7 +526,7 @@ + (void)endSession
[SentryAppStartTrackingIntegration class], [SentryFramesTrackingIntegration class],
[SentryPerformanceTrackingIntegration class], [SentryUIEventTrackingIntegration class],
[SentryViewHierarchyIntegration class],
[SentryWatchdogTerminationTrackingIntegration class],
[SentryWatchdogTerminationTrackingIntegration class], [SentryLogFlushIntegration class],
#endif // SENTRY_HAS_UIKIT
#if SENTRY_TARGET_REPLAY_SUPPORTED
[SentryScreenshotIntegration class],
Expand Down
18 changes: 18 additions & 0 deletions Sources/Sentry/_SentryDispatchQueueWrapperInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ - (instancetype)initWithName:(const char *)name
{
if (self = [super init]) {
_queue = dispatch_queue_create(name, attributes);
void *key = (__bridge void *)self;
dispatch_queue_set_specific(_queue, key, key, NULL);
}
return self;
}
Expand All @@ -32,6 +34,8 @@ - (instancetype)initWithName:(const char *)name relativePriority:(int)relativePr
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, relativePriority);
_queue = dispatch_queue_create(name, attributes);
void *key = (__bridge void *)self;
dispatch_queue_set_specific(_queue, key, key, NULL);
}
return self;
}
Expand Down Expand Up @@ -107,6 +111,20 @@ - (void)dispatchOnce:(dispatch_once_t *)predicate block:(void (^)(void))block
dispatch_once(predicate, block);
}

- (BOOL)isCurrentQueue
{
void *key = (__bridge void *)self;
return dispatch_get_specific(key) == key;
}

- (void)dealloc
{
if (_queue != NULL) {
void *key = (__bridge void *)self;
dispatch_queue_set_specific(_queue, key, NULL, NULL);
}
}

@end

NS_ASSUME_NONNULL_END
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)flushLogs;

@end

NS_ASSUME_NONNULL_END
10 changes: 10 additions & 0 deletions Sources/Sentry/include/SentryLogFlushIntegration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#import "SentryBaseIntegration.h"
#import "SentryDefines.h"

NS_ASSUME_NONNULL_BEGIN

@interface SentryLogFlushIntegration : SentryBaseIntegration

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN
- (void)dispatchAsyncOnMainQueueIfNotMainThread:(void (^)(void))block
NS_SWIFT_NAME(dispatchAsyncOnMainQueueIfNotMainThread(block:));

- (BOOL)isCurrentQueue;

@end

NS_ASSUME_NONNULL_END
7 changes: 7 additions & 0 deletions Sources/Swift/AppState/SentryAppStateListener.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@_implementationOnly import _SentryPrivate
import Foundation

@_spi(Private) @objc public protocol SentryAppStateListener: NSObjectProtocol {
@objc optional func appStateManagerWillResignActive()
@objc optional func appStateManagerWillTerminate()
}
Loading
Loading