Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
be66378
Flush logs when app terminates or resigns active
denrase Nov 25, 2025
fd55fdf
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Nov 25, 2025
115a625
fix deadlock issue
denrase Nov 25, 2025
71f478e
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Nov 26, 2025
162273e
rename group name
denrase Nov 26, 2025
3f4813d
move into existing confitional block
denrase Nov 26, 2025
aa1fbc9
Use dispatch_queue_set_specific/dispatch_get_specific to get corrrect…
denrase Nov 26, 2025
6cdd1b0
cleanup queu specific key
denrase Nov 26, 2025
8e36044
check client for nil
denrase Nov 26, 2025
d502009
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Dec 1, 2025
d22d16e
Listen to notifications directly in integration
denrase Dec 1, 2025
539d652
move integration to swift class
denrase Dec 1, 2025
e214d5e
use dedicated queue for log batcher
denrase Dec 1, 2025
b493f18
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Dec 1, 2025
13de622
update
denrase Dec 1, 2025
e270fa1
cleanup
denrase Dec 1, 2025
19df878
cleanup
denrase Dec 1, 2025
0f74c74
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Dec 1, 2025
6daca9e
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Dec 3, 2025
e3f2df8
update cl
denrase Dec 3, 2025
dd00065
fix build issue :facepalm
denrase Dec 3, 2025
f2e5b8b
update changelog
denrase Dec 3, 2025
f9a7ed8
Merge branch 'main' into feat/flush-logs-on-app-state-change
philprime Dec 3, 2025
d828f69
remove kIntegrationOptionEnableLogs
denrase Dec 3, 2025
3b22387
use dsnForTestCase
denrase Dec 3, 2025
0682b5b
update test setup
denrase Dec 3, 2025
fda13c2
Call with QOS_CLASS_DEFAULT instead of DISPATCH_QUEUE_PRIORITY_DEFAULT
denrase Dec 3, 2025
2edc82f
Merge branch 'main' into feat/flush-logs-on-app-state-change
denrase Dec 4, 2025
7a57e7a
fix incorrect import of NSApplication in macCatalyst environments
denrase Dec 4, 2025
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];
}

- (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

NS_ASSUME_NONNULL_BEGIN

@interface SentryLogFlushIntegration () <SentryAppStateListener>

@end

@implementation SentryLogFlushIntegration

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

[[[SentryDependencyContainer sharedInstance] appStateManager] addListener:self];

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
2 changes: 2 additions & 0 deletions Sources/Sentry/include/_SentryDispatchQueueWrapperInternal.h
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