Skip to content

Commit 7f6a9a5

Browse files
authored
feat: Add attributes data to SentryScope (#6830)
* feat: Add attributes data to `SentryScope` * Update changelog * Update sdk_api.json * Implement PR feedback * Implement PR feedback * Fix build in macOS before macOS 12 * Manually remove attachments from crashReporter's userInfo * Add `setAttributes` to `SentryScopeObserver` * Update note on `SentryScope` * Update changelog * Add tests for SentryCrashIntegration * Add `setAttributes` to SentryCrashScopeObserver * Update changelog * Update changelog
1 parent 87a7e98 commit 7f6a9a5

File tree

14 files changed

+545
-0
lines changed

14 files changed

+545
-0
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- Add attributes data to `SentryScope` (#6830)
8+
39
## 9.0.0
410

511
This changelog lists every breaking change. For a high-level overview and upgrade guidance, see the [migration guide](https://docs.sentry.io/platforms/apple/migration/).

Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ public struct SentrySDKWrapper {
196196
}
197197
let data = Data("hello".utf8)
198198
scope.addAttachment(Attachment(data: data, filename: "log.txt"))
199+
200+
scope.setAttribute(value: "\(Bundle.main.bundleIdentifier ?? "")-custom-attribute", key: "custom-attribute-text")
201+
scope.setAttribute(value: Date().timeIntervalSince1970, key: "custom-attribute-numeric")
202+
scope.setAttribute(value: true, key: "custom-attribute-boolean")
199203

200204
return scope
201205
}

Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,16 @@
12561256
</button>
12571257
</subviews>
12581258
</stackView>
1259+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nv9-fs-cCH" userLabel="Show Scope VC">
1260+
<rect key="frame" x="0.0" y="486" width="160" height="28"/>
1261+
<accessibility key="accessibilityConfiguration" identifier="show-scope-vc"/>
1262+
<fontDescription key="fontDescription" type="system" pointSize="13"/>
1263+
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
1264+
<state key="normal" title="Scope"/>
1265+
<connections>
1266+
<segue destination="ARr-dx-Xze" kind="show" id="eAg-n6-RXj"/>
1267+
</connections>
1268+
</button>
12591269
</subviews>
12601270
</stackView>
12611271
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="ULj-Tl-kYs">
@@ -1830,6 +1840,116 @@
18301840
</objects>
18311841
<point key="canvasLocation" x="-201" y="3364"/>
18321842
</scene>
1843+
<!--Scope Debug-->
1844+
<scene sceneID="cos-sS-BSR">
1845+
<objects>
1846+
<viewController title="Scope Debug" id="ARr-dx-Xze" customClass="ScopeViewController" customModule="iOS_Swift" customModuleProvider="target" sceneMemberID="viewController">
1847+
<view key="view" contentMode="scaleToFill" id="IXm-Dl-P82">
1848+
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
1849+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1850+
<subviews>
1851+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Attribute Name:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xb7-S3-3s7">
1852+
<rect key="frame" x="16" y="80" width="120" height="21"/>
1853+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
1854+
<nil key="textColor"/>
1855+
<nil key="highlightedColor"/>
1856+
</label>
1857+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This VC only tests for string attributes, but other types are tested in unit tests" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QHu-ah-Fmv">
1858+
<rect key="frame" x="16" y="253" width="288" height="29"/>
1859+
<fontDescription key="fontDescription" type="system" pointSize="12"/>
1860+
<nil key="textColor"/>
1861+
<nil key="highlightedColor"/>
1862+
</label>
1863+
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="No attributes" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="ctB-hS-Uvr">
1864+
<rect key="frame" x="16" y="335" width="288" height="217"/>
1865+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
1866+
<constraints>
1867+
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="KV6-nJ-j9f"/>
1868+
</constraints>
1869+
<color key="textColor" systemColor="labelColor"/>
1870+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1871+
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
1872+
</textView>
1873+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fHY-KL-zoc">
1874+
<rect key="frame" x="63.5" y="292" width="193" height="35"/>
1875+
<state key="normal" title="Button"/>
1876+
<buttonConfiguration key="configuration" style="plain" title="Get Current Attributes"/>
1877+
<connections>
1878+
<action selector="updateAttributesTextView:" destination="ARr-dx-Xze" eventType="touchUpInside" id="Xsy-sH-iMB"/>
1879+
</connections>
1880+
</button>
1881+
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Cmk-s4-Os4">
1882+
<rect key="frame" x="144" y="73.5" width="160" height="34"/>
1883+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1884+
<textInputTraits key="textInputTraits"/>
1885+
</textField>
1886+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Attribute Value:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Eap-5e-Pfq">
1887+
<rect key="frame" x="16" y="174.5" width="117" height="21"/>
1888+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
1889+
<nil key="textColor"/>
1890+
<nil key="highlightedColor"/>
1891+
</label>
1892+
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="a8X-Mk-Wb8">
1893+
<rect key="frame" x="141" y="168" width="163" height="34"/>
1894+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1895+
<textInputTraits key="textInputTraits"/>
1896+
</textField>
1897+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ldh-mW-4FD">
1898+
<rect key="frame" x="82" y="123.5" width="156" height="35"/>
1899+
<state key="normal" title="Button"/>
1900+
<buttonConfiguration key="configuration" style="plain" title="Remove Attribute"/>
1901+
<connections>
1902+
<action selector="removeAttribute:" destination="ARr-dx-Xze" eventType="touchUpInside" id="w18-qb-kM9"/>
1903+
</connections>
1904+
</button>
1905+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Eyt-Da-PWz">
1906+
<rect key="frame" x="76.5" y="218" width="167" height="35"/>
1907+
<state key="normal" title="Button"/>
1908+
<buttonConfiguration key="configuration" style="plain" title="Set Attribute Value"/>
1909+
<connections>
1910+
<action selector="setAttribute:" destination="ARr-dx-Xze" eventType="touchUpInside" id="Cbt-i9-QEv"/>
1911+
</connections>
1912+
</button>
1913+
</subviews>
1914+
<viewLayoutGuide key="safeArea" id="UBd-ip-qaw"/>
1915+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
1916+
<constraints>
1917+
<constraint firstItem="Cmk-s4-Os4" firstAttribute="leading" secondItem="xb7-S3-3s7" secondAttribute="trailing" constant="8" symbolic="YES" id="0v4-D5-asd"/>
1918+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="a8X-Mk-Wb8" secondAttribute="trailing" constant="16" id="1PS-dq-wfp"/>
1919+
<constraint firstItem="Eyt-Da-PWz" firstAttribute="top" secondItem="a8X-Mk-Wb8" secondAttribute="bottom" constant="16" id="9w7-Ef-rQF"/>
1920+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="top" secondItem="fHY-KL-zoc" secondAttribute="bottom" constant="8" symbolic="YES" id="9zm-ED-VUa"/>
1921+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="bottom" secondItem="UBd-ip-qaw" secondAttribute="bottom" constant="-16" id="DDh-Lp-MhL"/>
1922+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="Cmk-s4-Os4" secondAttribute="trailing" constant="16" id="FKD-bi-OMi"/>
1923+
<constraint firstItem="QHu-ah-Fmv" firstAttribute="top" secondItem="Eyt-Da-PWz" secondAttribute="bottom" id="FjT-0R-bhy"/>
1924+
<constraint firstItem="fHY-KL-zoc" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="G4z-ue-zpZ"/>
1925+
<constraint firstItem="xb7-S3-3s7" firstAttribute="top" secondItem="UBd-ip-qaw" secondAttribute="top" constant="16" id="GwE-oo-jpO"/>
1926+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="Ntd-Yu-W6y"/>
1927+
<constraint firstItem="a8X-Mk-Wb8" firstAttribute="leading" secondItem="Eap-5e-Pfq" secondAttribute="trailing" constant="8" symbolic="YES" id="ana-uV-xp2"/>
1928+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="ctB-hS-Uvr" secondAttribute="trailing" constant="16" id="ewx-Aj-ceC"/>
1929+
<constraint firstItem="ldh-mW-4FD" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="fWX-1T-sKi"/>
1930+
<constraint firstItem="Eyt-Da-PWz" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="gOC-8q-KHd"/>
1931+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="pTT-cw-o5Y"/>
1932+
<constraint firstItem="fHY-KL-zoc" firstAttribute="top" secondItem="QHu-ah-Fmv" secondAttribute="bottom" constant="10" id="qA5-ib-uPE"/>
1933+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="centerY" secondItem="a8X-Mk-Wb8" secondAttribute="centerY" id="qvj-kR-bOa"/>
1934+
<constraint firstItem="ldh-mW-4FD" firstAttribute="top" secondItem="Cmk-s4-Os4" secondAttribute="bottom" constant="16" id="r0m-Bt-dtP"/>
1935+
<constraint firstItem="xb7-S3-3s7" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="rDH-7y-xfI"/>
1936+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="top" secondItem="ldh-mW-4FD" secondAttribute="bottom" constant="16" id="x5j-hs-JHs"/>
1937+
<constraint firstItem="xb7-S3-3s7" firstAttribute="centerY" secondItem="Cmk-s4-Os4" secondAttribute="centerY" id="xOj-UV-Kte"/>
1938+
<constraint firstAttribute="trailing" secondItem="QHu-ah-Fmv" secondAttribute="trailing" constant="16" id="y42-oq-LBV"/>
1939+
<constraint firstItem="QHu-ah-Fmv" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="yCc-ZU-OQv"/>
1940+
</constraints>
1941+
</view>
1942+
<navigationItem key="navigationItem" id="nX7-Ze-SG9"/>
1943+
<connections>
1944+
<outlet property="attributeNameField" destination="Cmk-s4-Os4" id="cUb-lc-TJx"/>
1945+
<outlet property="attributeValueField" destination="a8X-Mk-Wb8" id="vou-eg-nhn"/>
1946+
<outlet property="attributesTextView" destination="ctB-hS-Uvr" id="fr2-ut-urt"/>
1947+
</connections>
1948+
</viewController>
1949+
<placeholder placeholderIdentifier="IBFirstResponder" id="1Q9-z5-Hib" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
1950+
</objects>
1951+
<point key="canvasLocation" x="4362.686567164179" y="-774.37070938215095"/>
1952+
</scene>
18331953
</scenes>
18341954
<resources>
18351955
<image name="clock.fill" catalog="system" width="128" height="123"/>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import Sentry
2+
import UIKit
3+
4+
class ScopeViewController: UIViewController {
5+
6+
@IBOutlet var attributesTextView: UITextView!
7+
@IBOutlet var attributeNameField: UITextField!
8+
@IBOutlet var attributeValueField: UITextField!
9+
10+
override func viewDidLoad() {
11+
super.viewDidLoad()
12+
13+
updateAttributesTextView()
14+
}
15+
16+
@IBAction func setAttribute(_ sender: Any?) {
17+
guard let attributeName = attributeNameField.text, let attributeValue = attributeValueField.text else {
18+
return
19+
}
20+
21+
SentrySDK.configureScope { scope in
22+
scope.setAttribute(value: attributeValue, key: attributeName)
23+
}
24+
25+
updateAttributesTextView()
26+
}
27+
28+
@IBAction func removeAttribute(_ sender: Any?) {
29+
guard let attributeName = attributeNameField.text else {
30+
return
31+
}
32+
33+
SentrySDK.configureScope { scope in
34+
scope.removeAttribute(key: attributeName)
35+
}
36+
37+
updateAttributesTextView()
38+
}
39+
40+
@IBAction func updateAttributesTextView(_ sender: Any?) {
41+
updateAttributesTextView()
42+
}
43+
44+
private func updateAttributesTextView() {
45+
SentrySDK.configureScope { [weak self] scope in
46+
guard let self else { return }
47+
48+
guard let jsonData = try? JSONSerialization.data(withJSONObject: scope.attributes, options: [.prettyPrinted]) else {
49+
self.attributesTextView.text = "Error serializing attributes to JSON"
50+
return
51+
}
52+
53+
guard let jsonString = String(data: jsonData, encoding: .utf8) else {
54+
self.attributesTextView.text = "Error converting data to JSON text"
55+
return
56+
}
57+
58+
self.attributesTextView.text = jsonString
59+
}
60+
}
61+
}

Sources/Sentry/Public/SentryScope.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ NS_SWIFT_NAME(Scope)
4141
*/
4242
@property (nonatomic, readonly, copy) NSDictionary<NSString *, NSString *> *tags;
4343

44+
/**
45+
* Gets the dictionary of currently set attributes.
46+
*/
47+
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *attributes;
48+
4449
- (instancetype)initWithMaxBreadcrumbs:(NSInteger)maxBreadcrumbs NS_DESIGNATED_INITIALIZER;
4550
- (instancetype)init;
4651
- (instancetype)initWithScope:(SentryScope *)scope;
@@ -137,6 +142,24 @@ NS_SWIFT_NAME(Scope)
137142
*/
138143
- (void)addAttachment:(SentryAttachment *)attachment NS_SWIFT_NAME(addAttachment(_:));
139144

145+
/**
146+
* Set global attributes. Attributes are searchable key/value string pairs attached to every log
147+
* message.
148+
* @note The SDK only applies attributes to Logs. The SDK doesn't apply the attributes to
149+
* Events, Transactions, Spans, Profiles, Session Replay.
150+
* @param value Supported values are string, integers, boolean and double
151+
* @param key The key to store, cannot be an empty string
152+
*/
153+
- (void)setAttributeValue:(id)value forKey:(NSString *)key NS_SWIFT_NAME(setAttribute(value:key:));
154+
155+
/**
156+
* Remove the attribute for the specified key.
157+
* @note The SDK only applies attributes to Logs. The SDK doesn't apply the attributes to
158+
* Events, Transactions, Spans, Profiles, Session Replay.
159+
* @param key The key to remove
160+
*/
161+
- (void)removeAttributeForKey:(NSString *)key NS_SWIFT_NAME(removeAttribute(key:));
162+
140163
/**
141164
* Clears all attachments in the scope.
142165
*/

Sources/Sentry/SentryCrashIntegration.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ - (void)configureScope
239239
userInfo[@"release"] = self.options.releaseName;
240240
userInfo[@"dist"] = self.options.dist;
241241

242+
// Crashes don't use the attributes field, we remove them to avoid uploading them
243+
// unnecessarily.
244+
[userInfo removeObjectForKey:@"attributes"];
245+
242246
[SentryDependencyContainer.sharedInstance.crashReporter setUserInfo:userInfo];
243247

244248
[outerScope addObserver:self.scopeObserver];

Sources/Sentry/SentryCrashScopeObserver.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ - (void)setLevel:(enum SentryLevel)level
9191
sentrycrash_scopesync_setLevel([json bytes]);
9292
}
9393

94+
- (void)setAttributes:(nullable NSDictionary<NSString *, id> *)attributes
95+
{
96+
// Nothing to do here, crash events don't support attributes
97+
}
98+
9499
- (void)addSerializedBreadcrumb:(NSDictionary *)crumb
95100
{
96101
NSData *json = [self toJSONEncodedCString:crumb];

0 commit comments

Comments
 (0)