Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
455e640
Support capturing logs in hub and client
denrase Oct 23, 2025
fe496c6
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Oct 23, 2025
79f488f
fix typo
denrase Oct 23, 2025
0a4031e
update tests
denrase Oct 24, 2025
7a38ee2
add documentation
denrase Oct 24, 2025
6f84e05
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Oct 24, 2025
010294f
add cl entry
denrase Oct 24, 2025
f376254
update public api
denrase Oct 24, 2025
f1e0dc3
relax flush accuracy
denrase Oct 24, 2025
e9d6a1e
fix SPM circular dependency issue
denrase Oct 24, 2025
b8be304
make new hub/scope selectors available in SPM
denrase Oct 24, 2025
e060a58
provide selector protocols outside of swift pkg if/else
denrase Oct 24, 2025
ca30a2d
use correct scope parameter, add test
denrase Oct 24, 2025
585893a
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Oct 27, 2025
4d72a5d
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Oct 28, 2025
4533b76
introduce captureLog dispatcher, handle case where it’ll not work and…
denrase Oct 28, 2025
4446d9f
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Oct 28, 2025
f913b46
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 3, 2025
4bd9c2a
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 3, 2025
5220b71
add test for replayid
denrase Nov 3, 2025
2eafc00
remove NS_SWIFT_NAME
denrase Nov 3, 2025
4197873
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 3, 2025
b42d510
fix typo
denrase Nov 3, 2025
f00c069
add comment
denrase Nov 3, 2025
dee5276
rename
denrase Nov 3, 2025
11031c7
expose sentrylogger on Hub
denrase Nov 3, 2025
ebaac6b
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 3, 2025
0239c61
use cast to exose logger
denrase Nov 4, 2025
130e34d
update test
denrase Nov 4, 2025
dd00bcb
update access to fw decalered log APIs
denrase Nov 4, 2025
ca58453
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
df33907
cleanup
denrase Nov 4, 2025
11f2254
remove mention of logs in beta
denrase Nov 4, 2025
ddc8ad7
use _swiftLogger in hub swift
denrase Nov 4, 2025
1ee870f
cleanup
denrase Nov 4, 2025
1fe6bb7
remove redundant test
denrase Nov 4, 2025
d7e46ca
update public api
denrase Nov 4, 2025
33d4912
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
0dd1af0
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
0d24147
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
75d3996
Add possibility to mutate log attributes from ObjC, Add heleper metho…
denrase Nov 4, 2025
c662522
Move log abtcher delegate to ctor and make it private
denrase Nov 4, 2025
a8766e9
make teh delegate non-nil
denrase Nov 4, 2025
c6c9bb6
add comment
denrase Nov 4, 2025
26d91ce
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
d5e2104
Merge branch 'main' into denrase/logs-in-hub-and-client
denrase Nov 4, 2025
c811a52
update public api
denrase Nov 4, 2025
7510ff6
Merge branch 'denrase/logs-in-hub-and-client' of github.com:getsentry…
denrase Nov 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
This option is still disabled by default and will be enabled in a future major release.
- Move `enableDataSwizzling` from experimental options to top-level options (#6592). This option remains enabled by default.
- Add `sentry.replay_id` attribute to logs ([#6515](https://github.com/getsentry/sentry-cocoa/pull/6515))
- Structured Logs: Add log APIs to `Hub` and `Client` (#6518)

### Fixes

Expand Down
11 changes: 8 additions & 3 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,8 @@
92235CAE2E15549C00865983 /* SentryLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAD2E15549C00865983 /* SentryLogger.swift */; };
92235CB02E155B2600865983 /* SentryLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAF2E155B2600865983 /* SentryLoggerTests.swift */; };
925824C22CB5897700C9B20B /* SentrySessionReplayIntegration-Hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = D80382BE2C09C6FD0090E048 /* SentrySessionReplayIntegration-Hybrid.h */; settings = {ATTRIBUTES = (Private, ); }; };
92622E092EABB71000ABE7FF /* SentryLogSPMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92622E082EABB71000ABE7FF /* SentryLogSPMTests.swift */; };
92622E142EABBDA900ABE7FF /* SentryLog+SPM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92622E132EABBDA900ABE7FF /* SentryLog+SPM.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, ); }; };
Expand All @@ -732,7 +734,6 @@
92D957732E05A44600E20E66 /* SentryAsyncLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 92D957722E05A44600E20E66 /* SentryAsyncLog.m */; };
92D957772E05A4F300E20E66 /* SentryAsyncLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 92D957762E05A4F300E20E66 /* SentryAsyncLog.h */; };
92E5F3D62CDBB3BF00B7AD98 /* SentrySampling.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8C57A525EEFC42001CEEFA /* SentrySampling.h */; };
92EC54CE2E1EB54B00A10AC2 /* SentryClient+Logs.h in Headers */ = {isa = PBXBuildFile; fileRef = 92EC54CD2E1EB54B00A10AC2 /* SentryClient+Logs.h */; };
92ECD7202E05A7DF0063EC10 /* SentryLogC.h in Headers */ = {isa = PBXBuildFile; fileRef = D8AE48B12C5786AA0092A2A6 /* SentryLogC.h */; settings = {ATTRIBUTES = (Private, ); }; };
92ECD73C2E05ACE00063EC10 /* SentryLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */; };
92ECD73E2E05AD320063EC10 /* SentryLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */; };
Expand Down Expand Up @@ -2082,6 +2083,8 @@
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>"; };
92622E082EABB71000ABE7FF /* SentryLogSPMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogSPMTests.swift; sourceTree = "<group>"; };
92622E132EABBDA900ABE7FF /* SentryLog+SPM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryLog+SPM.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>"; };
Expand All @@ -2095,7 +2098,6 @@
92B6BDAC2E05B9F700D538B3 /* SentryLogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogTests.swift; sourceTree = "<group>"; };
92D957722E05A44600E20E66 /* SentryAsyncLog.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryAsyncLog.m; sourceTree = "<group>"; };
92D957762E05A4F300E20E66 /* SentryAsyncLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryAsyncLog.h; path = include/SentryAsyncLog.h; sourceTree = "<group>"; };
92EC54CD2E1EB54B00A10AC2 /* SentryClient+Logs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryClient+Logs.h"; path = "include/SentryClient+Logs.h"; sourceTree = "<group>"; };
92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLog.swift; sourceTree = "<group>"; };
92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogLevel.swift; sourceTree = "<group>"; };
92ECD73F2E05AD500063EC10 /* SentryLogAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogAttribute.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2694,6 +2696,7 @@
84B0E0062CD963F9007FB332 /* SentryIconography.swift */,
621F61F02BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift */,
F4FE9DFB2E622CD70014FED5 /* SentryDefaultObjCRuntimeWrapper.swift */,
92622E132EABBDA900ABE7FF /* SentryLog+SPM.swift */,
F4FE9DFC2E622CD70014FED5 /* SentryObjCRuntimeWrapper.swift */,
);
path = Helper;
Expand Down Expand Up @@ -3614,6 +3617,7 @@
F4A930242E661856006DA6EF /* SentryMobileProvisionParserTests.swift */,
D4F7BD7C2E4373BB004A2D77 /* SentryLevelMapperTests.swift */,
D8AE48BE2C578D540092A2A6 /* SentrySDKLog.swift */,
92622E082EABB71000ABE7FF /* SentryLogSPMTests.swift */,
849AC3FF29E0C1FF00889C16 /* SentryFormatterTests.swift */,
7B88F30324BC8E6500ADF90A /* SentrySerializationTests.swift */,
62F4DDA02C04CB9700588890 /* SentryBaggageSerializationTests.swift */,
Expand Down Expand Up @@ -5224,7 +5228,6 @@
8E7C98312693E1CC00E6336C /* SentryTraceHeader.h in Headers */,
62C316812B1F2E93000D7031 /* SentryDelayedFramesTracker.h in Headers */,
92D957772E05A4F300E20E66 /* SentryAsyncLog.h in Headers */,
92EC54CE2E1EB54B00A10AC2 /* SentryClient+Logs.h in Headers */,
7B8713AE26415ADF006D6004 /* SentryAppStartTrackingIntegration.h in Headers */,
7B7D873224864BB900D2ECFF /* SentryCrashMachineContextWrapper.h in Headers */,
861265F92404EC1500C4AFDE /* SentryArray.h in Headers */,
Expand Down Expand Up @@ -6101,6 +6104,7 @@
7B14089824878F950035403D /* SentryCrashStackEntryMapper.m in Sources */,
D8BC28C82BFF5EBB0054DA4D /* SentryTouchTracker.swift in Sources */,
63FE711720DA4C1000CDBAE8 /* SentryCrashStackCursor_Backtrace.c in Sources */,
92622E142EABBDA900ABE7FF /* SentryLog+SPM.swift in Sources */,
FA3A42722E1C5F9B00A08C39 /* SentryNSNotificationCenterWrapper.swift in Sources */,
63FE70CB20DA4C1000CDBAE8 /* SentryCrashReportFixer.c in Sources */,
F4A930232E65FDBF006DA6EF /* SentryMobileProvisionParser.swift in Sources */,
Expand Down Expand Up @@ -6210,6 +6214,7 @@
7BE3C78724472E9800A38442 /* TestRequestManager.swift in Sources */,
63FE722220DA66EC00CDBAE8 /* SentryCrashJSONCodec_Tests.m in Sources */,
7B0A5452252311CE00A71716 /* SentryBreadcrumbTests.swift in Sources */,
92622E092EABB71000ABE7FF /* SentryLogSPMTests.swift in Sources */,
7BE3C7752445C82300A38442 /* SentryCurrentDateTests.swift in Sources */,
7B3398672459C4AE00BD9C96 /* SentryEnvelopeRateLimitTests.swift in Sources */,
8EA9AF492665AC48002771B4 /* SentryPerformanceTrackerTests.swift in Sources */,
Expand Down
8 changes: 5 additions & 3 deletions SentryTestUtils/Sources/TestClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,10 @@ public class TestClient: SentryClientInternal {
flushInvocations.record(timeout)
}

public var captureLogsDataInvocations = Invocations<(data: Data, count: NSNumber)>()
public override func captureLogsData(_ data: Data, with count: NSNumber) {
captureLogsDataInvocations.record((data, count))
public var captureLogInvocations = Invocations<(log: SentryLog, scope: Scope)>()
public override func _swiftCaptureLog(_ log: NSObject, with scope: Scope) {
if let castLog = log as? SentryLog {
captureLogInvocations.record((castLog, scope))
}
}
}
22 changes: 19 additions & 3 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@

NS_ASSUME_NONNULL_BEGIN

@interface SentryClientInternal ()
@interface SentryClientInternal () <SentryLogBatcherDelegate>

@property (nonatomic, strong) SentryTransportAdapter *transportAdapter;
@property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider;
@property (nonatomic, strong) id<SentryRandomProtocol> random;
@property (nonatomic, strong) NSLocale *locale;
@property (nonatomic, strong) NSTimeZone *timezone;
@property (nonatomic, strong) SentryLogBatcher *logBatcher;

@end

Expand Down Expand Up @@ -113,6 +114,10 @@ - (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];

// 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 @@ -618,7 +623,11 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event

- (void)flush:(NSTimeInterval)timeout
{
[self.transportAdapter flush:timeout];
NSTimeInterval captureLogsDuration = [self.logBatcher captureLogs];
// Capturing batched logs should never take long, but we need to fall back to a sane value.
// This is a workaround for in-memory logs, until we'll write batched logs to disk,
// to avoid data loss due to crashes. This is a trade-off until then.
[self.transportAdapter flush:fmax(timeout / 2, timeout - captureLogsDuration)];
}

- (void)close
Expand Down Expand Up @@ -1088,7 +1097,14 @@ - (void)removeAttachmentProcessor:(id<SentryClientAttachmentProcessor>)attachmen
return processedAttachments;
}

- (void)captureLogsData:(NSData *)data with:(NSNumber *)itemCount;
- (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope
{
if ([log isKindOfClass:[SentryLog class]]) {
[self.logBatcher addLog:(SentryLog *)log scope:scope];
}
}
Comment on lines +1100 to +1105
Copy link
Member

Choose a reason for hiding this comment

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

m: We use SentryLog below. What drove you to use NSObject here. Can't we use SentryLog?

Suggested change
- (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope
{
if ([log isKindOfClass:[SentryLog class]]) {
[self.logBatcher addLog:(SentryLog *)log scope:scope];
}
}
- (void)_swiftCaptureLog:(SentryLog *)log withScope:(SentryScope *)scope
{
[self.logBatcher addLog:log scope:scope];
}

Copy link
Collaborator Author

@denrase denrase Nov 4, 2025

Choose a reason for hiding this comment

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

See above, cause this is a Swift Type used in ObjC, once we call the internal hub from Swift (when using SPM), it's not visible.


- (void)captureLogsData:(NSData *)data with:(NSNumber *)itemCount
{
SentryEnvelopeItemHeader *header =
[[SentryEnvelopeItemHeader alloc] initWithType:SentryEnvelopeItemTypes.log
Expand Down
34 changes: 33 additions & 1 deletion Sources/Sentry/SentryHub.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

NS_ASSUME_NONNULL_BEGIN

@interface SentryHubInternal ()
@interface SentryHubInternal () <SentryLoggerDelegate>

@property (nullable, atomic, strong) SentryClientInternal *client;
@property (nullable, nonatomic, strong) SentryScope *scope;
Expand Down Expand Up @@ -73,6 +73,10 @@ - (instancetype)initWithClient:(nullable SentryClientInternal *)client
if (_scope) {
[_crashWrapper enrichScope:SENTRY_UNWRAP_NULLABLE(SentryScope, _scope)];
}

__swiftLogger = [[SentryLogger alloc]
initWithDelegate:self
dateProvider:SentryDependencyContainer.sharedInstance.dateProvider];
}

return self;
Expand Down Expand Up @@ -833,6 +837,34 @@ - (void)unregisterSessionListener:(id<SentrySessionListener>)listener
}
}

// SentryLoggerDelegate

- (void)captureLog:(SentryLog *)log
{
SentryClientInternal *client = self.client;
if (client != nil) {
#if SENTRY_TARGET_REPLAY_SUPPORTED
NSString *scopeReplayId = self.scope.replayId;
if (scopeReplayId != nil) {
// Session mode: use scope replay ID
[log setAttribute:[[SentryStructuredLogAttribute alloc] initWithString:scopeReplayId]
forKey:@"sentry.replay_id"];
} else {
// Buffer mode: check if hub has a session replay ID
NSString *sessionReplayId = [self getSessionReplayId];
if (sessionReplayId != nil) {
[log setAttribute:[[SentryStructuredLogAttribute alloc]
initWithString:sessionReplayId]
forKey:@"sentry.replay_id"];
[log setAttribute:[[SentryStructuredLogAttribute alloc] initWithBoolean:YES]
forKey:@"sentry._internal.replay_is_buffering"];
}
}
#endif
Copy link

Choose a reason for hiding this comment

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

Bug: Swift-Objective-C mismatch crashes from log attributes

The captureLog method creates SentryStructuredLogAttribute objects and assigns them to log.attributes, which is a Swift property expecting SentryLog.Attribute values. This type mismatch across the Swift-Objective-C boundary can cause runtime errors or crashes when log attributes are accessed.

Fix in Cursor Fix in Web

[client _swiftCaptureLog:log withScope:self.scope];
}
}

#pragma mark - Protected

- (NSArray<NSString *> *)trimmedInstalledIntegrationNames
Expand Down
2 changes: 0 additions & 2 deletions Sources/Sentry/SentrySDKInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,6 @@ + (void)close

[SentrySDKInternal setCurrentHub:nil];

[SentrySDK clearLogger];

[SentryDependencyContainer.sharedInstance.crashWrapper stopBinaryImageCache];
[SentryDependencyContainer.sharedInstance.binaryImageCache stop];

Expand Down
4 changes: 4 additions & 0 deletions Sources/Sentry/include/SentryClient+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)addAttachmentProcessor:(id<SentryClientAttachmentProcessor>)attachmentProcessor;
- (void)removeAttachmentProcessor:(id<SentryClientAttachmentProcessor>)attachmentProcessor;

// Do not use this directly, instead use the non-underscored `captureLog` method that is
// defined through a SentryClient.swift file.
- (void)_swiftCaptureLog:(NSObject *)log withScope:(SentryScope *)scope;

@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions Sources/Sentry/include/SentryHub+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, strong) NSMutableArray<id<SentryIntegrationProtocol>> *installedIntegrations;

// Do not use this directly, instead use the non-underscored `logger` property that is
// defined through a SentryHub.swift file.
@property (nonatomic, readonly, strong) NSObject *_swiftLogger;

/**
* Every integration starts with "Sentry" and ends with "Integration". To keep the payload of the
* event small we remove both.
Expand Down
1 change: 0 additions & 1 deletion Sources/Sentry/include/SentryPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#import "SentryANRTrackerV1.h"
#import "SentryANRTrackerV2.h"
#import "SentryAsyncLog.h"
#import "SentryClient+Logs.h"
#import "SentryContinuousProfiler.h"
#import "SentryCrash.h"
#import "SentryCrashDebug.h"
Expand Down
31 changes: 31 additions & 0 deletions Sources/Swift/Helper/SentryLog+SPM.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@_implementationOnly import _SentryPrivate
import Foundation

// Swift extensions to provide properly typed log-related APIs for SPM builds.
// In SPM builds, SentryLog is only forward declared in the Objective-C headers,
// which causes Swift-to-Objective-C bridging issues. These extensions work around
// that limitation by providing Swift-native methods and properties that use dynamic
// dispatch internally.

#if SWIFT_PACKAGE
Copy link
Member

Choose a reason for hiding this comment

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

@noahsmartin, can you please double-check if this approach works?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is existing code, I just moved it to its own file so it stays on our radar. Circular dependency issue when installing through SPM, before that, the SPM target on CI would not compile.


/**
* Use this callback to drop or modify a log before the SDK sends it to Sentry. Return `nil` to
* drop the log.
*/
public typealias SentryBeforeSendLogCallback = (SentryLog) -> SentryLog?

@objc
public extension Options {
/**
* Use this callback to drop or modify a log before the SDK sends it to Sentry. Return `nil` to
* drop the log.
*/
@objc
var beforeSendLog: SentryBeforeSendLogCallback? {
get { return value(forKey: "beforeSendLogDynamic") as? SentryBeforeSendLogCallback }
set { setValue(newValue, forKey: "beforeSendLogDynamic") }
}
}

#endif // SWIFT_PACKAGE
61 changes: 7 additions & 54 deletions Sources/Swift/Helper/SentrySDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,13 @@ import Foundation

/// API to access Sentry logs
@objc public static var logger: SentryLogger {
return _loggerLock.synchronized {
let sdkEnabled = SentrySDKInternal.isEnabled
if !sdkEnabled {
SentrySDKLog.fatal("Logs called before SentrySDK.start() will be dropped.")
}
if let _logger, _loggerConfigured {
return _logger
}
let hub = SentrySDKInternal.currentHub()
var batcher: SentryLogBatcher?
if let client = hub.getClient(), client.options.enableLogs {
batcher = SentryLogBatcher(client: client, dispatchQueue: Dependencies.dispatchQueueWrapper)
}
let logger = SentryLogger(
hub: hub,
dateProvider: Dependencies.dateProvider,
batcher: batcher
)
_logger = logger
_loggerConfigured = sdkEnabled
return logger
if !SentrySDKInternal.isEnabled {
SentrySDKLog.fatal("Logs called before SentrySDK.start() will be dropped.")
}
// We know the type so it's fine to force cast.
// swiftlint:disable force_cast
return SentrySDKInternal.currentHub()._swiftLogger as! SentryLogger
// swiftlint:enable force_cast
Comment on lines +34 to +36
Copy link
Member

Choose a reason for hiding this comment

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

m: I would prefer getting rid of this force cast by trying to change the type of the _swiftLogger property to SentryLogger. SentryLogger is public so it should work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same issue as above, when using SPM, logger would not be visible here due tu circular dependency between Swift/Objc parts of the SDK.

}

/// Inits and configures Sentry (`SentryHub`, `SentryClient`) and sets up all integrations. Make sure to
Expand Down Expand Up @@ -360,18 +345,12 @@ import Foundation
/// - note: This might take slightly longer than the specified timeout if there are many batched logs to capture.
@objc(flush:)
public static func flush(timeout: TimeInterval) {
let captureLogsDuration = captureLogs()
// Capturing batched logs should never take long, but we need to fall back to a sane value.
// This is a workaround for experimental logs, until we'll write batched logs to disk,
// to avoid data loss due to crashes. This is a trade-off until then.
SentrySDKInternal.flush(timeout: max(timeout / 2, timeout - captureLogsDuration))
SentrySDKInternal.flush(timeout: timeout)
}

/// Closes the SDK, uninstalls all the integrations, and calls `flush` with
/// `SentryOptions.shutdownTimeInterval`.
@objc public static func close() {
// Capturing batched logs should never take long, ignore the duration here.
_ = captureLogs()
SentrySDKInternal.close()
}

Expand Down Expand Up @@ -412,32 +391,6 @@ import Foundation
SentrySDKInternal.stopProfiler()
}
#endif

// MARK: Internal

/// - note: Conceptually internal but needs to be marked public with SPI for ObjC visibility
@objc @_spi(Private) public static func clearLogger() {
_loggerLock.synchronized {
_logger = nil
_loggerConfigured = false
}
}

// MARK: Private

private static var _loggerLock = NSLock()
private static var _logger: SentryLogger?
// Flag to re-create instance if accessed before SDK init.
private static var _loggerConfigured = false

@discardableResult
private static func captureLogs() -> TimeInterval {
var duration: TimeInterval = 0.0
_loggerLock.synchronized {
duration = _logger?.captureLogs() ?? 0.0
}
return duration
}
}

// swiftlint:enable file_length
Loading
Loading