Skip to content

Commit 884b224

Browse files
authored
feat: Add support for extras, fingerprint, and level watchdog termination events (#5569)
* feat: Add support for extras, fingerprint, and level watchdog termination events * Update changelog * Fix rebase changes * Ensure `deserializeArrayFromJsonData` returns an array + tests * extract SentryScopePersistentStore extensions for extras, fingerprint, and level encoding/decoding * Skip setLevel in `SentryWatchdogTerminationTrackingIntegration.m` * Remove setLevel assertions * Log class found on serialization fail * Remove level persistance * Sanitize extras * Add comment * Fix rebase issues * Use generic decoder * Move fixes in Changelog * Refactor fingerprint deserialization to return only string elements and add test for non-string elements
1 parent 2964fe9 commit 884b224

22 files changed

+812
-32
lines changed

CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
- User feedback widget can now be displayed in SwiftUI apps (#5223)
99
- Fix crash when SentryFileManger is nil (#5535)
1010
- Fix crash when capturing events at the same time `bindClient:` is called from a different thread (#5523)
11+
- Record user for watchdog termination events (#5558)
12+
- Add support for dist and environment fields for termination watch (#5560)
13+
- Add support for tags and context fields for termination watch (#5561)
14+
- Add support for extras, fingerprint, and level watchdog termination events (#5569)
1115

1216
### Improvements
1317

1418
- Removed `APPLICATION_EXTENSION_API_ONLY` requirement (#5524)
1519
- Improve launch profile configuration management (#5318)
16-
- Record user for watchdog termination events (#5558)
17-
- Add support for dist and environment fields for termination watch (#5560)
18-
- Record user for watchdog termination events (#5558)
19-
- Add support for tags and context fields for termination watch (#5561)
2020
- Deprecate getStoreEndpoint (#5591)
2121

2222
## 8.53.1

Sentry.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,8 @@
10321032
F458D1132E180BB00028273E /* SentryFileManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F458D1122E180BB00028273E /* SentryFileManagerProtocol.swift */; };
10331033
F458D1152E1869AD0028273E /* SentryScopePersistentStore+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F458D1142E1869AD0028273E /* SentryScopePersistentStore+String.swift */; };
10341034
F46DA6C32E1DBCA000DF6E3B /* SentryScopePersistentStore+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46DA6C22E1DBCA000DF6E3B /* SentryScopePersistentStore+Helper.swift */; };
1035+
F458D1172E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F458D1162E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift */; };
1036+
F458D1192E186E000028273E /* SentryScopePersistentStore+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = F458D1182E186E000028273E /* SentryScopePersistentStore+Extras.swift */; };
10351037
F49D41982DEA27AF00D9244E /* SentryUseNSExceptionCallstackWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49D41972DEA27AF00D9244E /* SentryUseNSExceptionCallstackWrapperTests.swift */; };
10361038
F49D419A2DEA2FB000D9244E /* SentryCrashExceptionApplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49D41992DEA2FB000D9244E /* SentryCrashExceptionApplicationTests.swift */; };
10371039
F49D419C2DEA30C300D9244E /* SentryCrashExceptionApplicationHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = F49D419B2DEA30B800D9244E /* SentryCrashExceptionApplicationHelper.h */; };
@@ -2301,6 +2303,8 @@
23012303
F458D1122E180BB00028273E /* SentryFileManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFileManagerProtocol.swift; sourceTree = "<group>"; };
23022304
F458D1142E1869AD0028273E /* SentryScopePersistentStore+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+String.swift"; sourceTree = "<group>"; };
23032305
F46DA6C22E1DBCA000DF6E3B /* SentryScopePersistentStore+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Helper.swift"; sourceTree = "<group>"; };
2306+
F458D1162E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Fingerprint.swift"; sourceTree = "<group>"; };
2307+
F458D1182E186E000028273E /* SentryScopePersistentStore+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Extras.swift"; sourceTree = "<group>"; };
23042308
F49D41972DEA27AF00D9244E /* SentryUseNSExceptionCallstackWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUseNSExceptionCallstackWrapperTests.swift; sourceTree = "<group>"; };
23052309
F49D41992DEA2FB000D9244E /* SentryCrashExceptionApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCrashExceptionApplicationTests.swift; sourceTree = "<group>"; };
23062310
F49D419B2DEA30B800D9244E /* SentryCrashExceptionApplicationHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryCrashExceptionApplicationHelper.h; path = include/SentryCrashExceptionApplicationHelper.h; sourceTree = "<group>"; };
@@ -4057,6 +4061,9 @@
40574061
F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */,
40584062
F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */,
40594063
F46DA6C22E1DBCA000DF6E3B /* SentryScopePersistentStore+Helper.swift */,
4064+
F458D11A2E186E0D0028273E /* SentryScopePersistentStore+Level.swift */,
4065+
F458D1182E186E000028273E /* SentryScopePersistentStore+Extras.swift */,
4066+
F458D1162E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift */,
40604067
);
40614068
path = Persistence;
40624069
sourceTree = "<group>";
@@ -5394,6 +5401,7 @@
53945401
D8CB741B2947286500A5F964 /* SentryEnvelopeItemHeader.m in Sources */,
53955402
92ECD73C2E05ACE00063EC10 /* SentryLog.swift in Sources */,
53965403
F458D1152E1869AD0028273E /* SentryScopePersistentStore+String.swift in Sources */,
5404+
F458D1172E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift in Sources */,
53975405
D8CB7417294724CC00A5F964 /* SentryEnvelopeAttachmentHeader.m in Sources */,
53985406
D84793262788737D00BE8E99 /* SentryByteCountFormatter.m in Sources */,
53995407
63AA769E1EB9C57A00D153DE /* SentryError.mm in Sources */,
@@ -5464,6 +5472,7 @@
54645472
848A45192BBF8D33006AAAEC /* SentryContinuousProfiler.mm in Sources */,
54655473
D81988C32BEC189C0020E36C /* SentryRRWebMetaEvent.swift in Sources */,
54665474
15E0A8E5240C457D00F044E3 /* SentryEnvelope.m in Sources */,
5475+
F458D1192E186E000028273E /* SentryScopePersistentStore+Extras.swift in Sources */,
54675476
8EC3AE7A25CA23B600E7591A /* SentrySpan.m in Sources */,
54685477
6360850E1ED2AFE100E8599E /* SentryBreadcrumb.m in Sources */,
54695478
D82859432C3E753C009A28AA /* SentrySessionReplaySyncC.c in Sources */,
@@ -5659,6 +5668,7 @@
56595668
D85852BA27EDDC5900C6D8AE /* SentryUIApplication.m in Sources */,
56605669
FAB3599A2E05D8080083D5E3 /* SentryEventSwiftHelper.m in Sources */,
56615670
F41362112E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift in Sources */,
5671+
F458D11B2E186E0D0028273E /* SentryScopePersistentStore+Level.swift in Sources */,
56625672
7B4E375F258231FC00059C93 /* SentryAttachment.m in Sources */,
56635673
636085141ED47BE600E8599E /* SentryFileManager.m in Sources */,
56645674
63FE710B20DA4C1000CDBAE8 /* SentryCrashMach.c in Sources */,

Sources/Sentry/SentryScope.m

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,6 @@
1919

2020
@interface SentryScope ()
2121

22-
/**
23-
* Set global extra -> these will be sent with every event
24-
*/
25-
@property (atomic, strong) NSMutableDictionary<NSString *, id> *extraDictionary;
26-
27-
/**
28-
* Set the fingerprint of an event to determine the grouping
29-
*/
30-
@property (atomic, strong) NSMutableArray<NSString *> *fingerprintArray;
31-
32-
/**
33-
* SentryLevel of the event
34-
*/
35-
@property (atomic) enum SentryLevel levelEnum;
36-
3722
@property (atomic) NSUInteger maxBreadcrumbs;
3823
@property (atomic) NSUInteger currentBreadcrumbIndex;
3924

Sources/Sentry/SentrySerialization.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,22 @@ + (SentryLevel)levelFromData:(NSData *)eventEnvelopeItemData
310310
return sentryLevelForString(eventDictionary[@"level"]);
311311
}
312312

313+
+ (NSArray *_Nullable)deserializeArrayFromJsonData:(NSData *)data
314+
{
315+
NSError *error = nil;
316+
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
317+
if (nil != error) {
318+
SENTRY_LOG_ERROR(@"Failed to deserialize json item array: %@", error);
319+
return nil;
320+
}
321+
if (![json isKindOfClass:[NSArray class]]) {
322+
SENTRY_LOG_ERROR(
323+
@"Deserialized json is not an NSArray, found %@", NSStringFromClass([json class]));
324+
return nil;
325+
}
326+
return (NSArray *)json;
327+
}
328+
313329
@end
314330

315331
NS_ASSUME_NONNULL_END

Sources/Sentry/SentryWatchdogTerminationScopeObserver.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,17 @@ - (void)setEnvironment:(nullable NSString *)environment
6464

6565
- (void)setExtras:(nullable NSDictionary<NSString *, id> *)extras
6666
{
67-
// Left blank on purpose
67+
[self.attributesProcessor setExtras:extras];
6868
}
6969

7070
- (void)setFingerprint:(nullable NSArray<NSString *> *)fingerprint
7171
{
72-
// Left blank on purpose
72+
[self.attributesProcessor setFingerprint:fingerprint];
7373
}
7474

7575
- (void)setLevel:(enum SentryLevel)level
7676
{
77-
// Left blank on purpose
77+
// Nothing to do here, watchdog termination events are always Fatal
7878
}
7979

8080
- (void)setTags:(nullable NSDictionary<NSString *, NSString *> *)tags

Sources/Sentry/SentryWatchdogTerminationTracker.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ - (void)start
6161
event.dist = [self.scopePersistentStore readPreviousDistFromDisk];
6262
event.environment = [self.scopePersistentStore readPreviousEnvironmentFromDisk];
6363
event.tags = [self.scopePersistentStore readPreviousTagsFromDisk];
64+
event.extra = [self.scopePersistentStore readPreviousExtrasFromDisk];
65+
event.fingerprint = [self.scopePersistentStore readPreviousFingerprintFromDisk];
66+
// Termination events always have fatal level, so we are not reading from disk
6467

6568
SentryException *exception =
6669
[[SentryException alloc] initWithValue:SentryWatchdogTerminationExceptionValue

Sources/Sentry/SentryWatchdogTerminationTrackingIntegration.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ - (BOOL)installWithOptions:(SentryOptions *)options
101101
[scopeObserver setEnvironment:outerScope.environmentString];
102102
[scopeObserver setDist:outerScope.distString];
103103
[scopeObserver setTags:outerScope.tags];
104+
[scopeObserver setExtras:outerScope.extraDictionary];
105+
[scopeObserver setFingerprint:outerScope.fingerprintArray];
104106
// We intentionally skip calling `setTraceContext:` since traces are not stored for watchdog
105107
// termination events
108+
// We intentionally skip calling `setLevel:` since all termination events have fatal level
106109
}];
107110

108111
return YES;

Sources/Sentry/include/SentryScope+Private.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ NS_ASSUME_NONNULL_BEGIN
3030
*/
3131
@property (atomic, copy) NSString *_Nullable distString;
3232

33+
/**
34+
* Set global extra -> these will be sent with every event
35+
*/
36+
@property (atomic, strong) NSMutableDictionary<NSString *, id> *extraDictionary;
37+
38+
/**
39+
* Set the fingerprint of an event to determine the grouping
40+
*/
41+
@property (atomic, strong) NSMutableArray<NSString *> *fingerprintArray;
42+
43+
/**
44+
* SentryLevel of the event
45+
*/
46+
@property (atomic) enum SentryLevel levelEnum;
47+
3348
@property (nonatomic, nullable, copy) NSString *currentScreen;
3449

3550
- (NSArray<SentryBreadcrumb *> *)breadcrumbs;

Sources/Sentry/include/SentrySerialization.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ NS_ASSUME_NONNULL_BEGIN
3434
*/
3535
+ (SentryLevel)levelFromData:(NSData *)eventEnvelopeItemData;
3636

37+
/**
38+
* Retrieves the json object from an event envelope item data.
39+
*/
40+
+ (NSArray *_Nullable)deserializeArrayFromJsonData:(NSData *)data;
41+
3742
@end
3843

3944
NS_ASSUME_NONNULL_END

Sources/Swift/Integrations/WatchdogTerminations/Processors/SentryWatchdogTerminationAttributesProcessor.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ import Foundation
5454
}
5555
}
5656

57+
public func setExtras(_ extras: [String: Any]?) {
58+
setData(data: extras, field: .extras) { [weak self] data in
59+
self?.scopePersistentStore.writeExtrasToDisk(extras: data)
60+
}
61+
}
62+
63+
public func setFingerprint(_ fingerprint: [String]?) {
64+
setData(data: fingerprint, field: .fingerprint) { [weak self] data in
65+
self?.scopePersistentStore.writeFingerprintToDisk(fingerprint: data)
66+
}
67+
}
68+
5769
// MARK: - Private
5870
private func setData<T>(data: T?, field: SentryScopeField, save: @escaping (T) -> Void) {
5971
SentrySDKLog.debug("Setting \(field.name) in background queue: \(String(describing: data))")

0 commit comments

Comments
 (0)