Skip to content

Commit 8466253

Browse files
committed
feat: Add attributes data to SentryScope
1 parent 095d886 commit 8466253

File tree

6 files changed

+278
-0
lines changed

6 files changed

+278
-0
lines changed

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.now.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
@@ -1241,6 +1241,16 @@
12411241
</button>
12421242
</subviews>
12431243
</stackView>
1244+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nv9-fs-cCH" userLabel="Show Scope VC">
1245+
<rect key="frame" x="0.0" y="486" width="160" height="28"/>
1246+
<accessibility key="accessibilityConfiguration" identifier="show-scope-vc"/>
1247+
<fontDescription key="fontDescription" type="system" pointSize="13"/>
1248+
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
1249+
<state key="normal" title="Scope"/>
1250+
<connections>
1251+
<segue destination="ARr-dx-Xze" kind="show" id="eAg-n6-RXj"/>
1252+
</connections>
1253+
</button>
12441254
</subviews>
12451255
</stackView>
12461256
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="ULj-Tl-kYs">
@@ -1815,6 +1825,116 @@
18151825
</objects>
18161826
<point key="canvasLocation" x="-201" y="3364"/>
18171827
</scene>
1828+
<!--Scope Debug-->
1829+
<scene sceneID="cos-sS-BSR">
1830+
<objects>
1831+
<viewController title="Scope Debug" id="ARr-dx-Xze" customClass="ScopeViewController" customModule="iOS_Swift" customModuleProvider="target" sceneMemberID="viewController">
1832+
<view key="view" contentMode="scaleToFill" id="IXm-Dl-P82">
1833+
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
1834+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1835+
<subviews>
1836+
<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">
1837+
<rect key="frame" x="16" y="80" width="120" height="21"/>
1838+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
1839+
<nil key="textColor"/>
1840+
<nil key="highlightedColor"/>
1841+
</label>
1842+
<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">
1843+
<rect key="frame" x="16" y="253" width="288" height="29"/>
1844+
<fontDescription key="fontDescription" type="system" pointSize="12"/>
1845+
<nil key="textColor"/>
1846+
<nil key="highlightedColor"/>
1847+
</label>
1848+
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="No attributes" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="ctB-hS-Uvr">
1849+
<rect key="frame" x="16" y="335" width="288" height="217"/>
1850+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
1851+
<constraints>
1852+
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="KV6-nJ-j9f"/>
1853+
</constraints>
1854+
<color key="textColor" systemColor="labelColor"/>
1855+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1856+
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
1857+
</textView>
1858+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fHY-KL-zoc">
1859+
<rect key="frame" x="63.5" y="292" width="193" height="35"/>
1860+
<state key="normal" title="Button"/>
1861+
<buttonConfiguration key="configuration" style="plain" title="Get Current Attributes"/>
1862+
<connections>
1863+
<action selector="updateAttributesTextView:" destination="ARr-dx-Xze" eventType="touchUpInside" id="Xsy-sH-iMB"/>
1864+
</connections>
1865+
</button>
1866+
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Cmk-s4-Os4">
1867+
<rect key="frame" x="144" y="73.5" width="160" height="34"/>
1868+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1869+
<textInputTraits key="textInputTraits"/>
1870+
</textField>
1871+
<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">
1872+
<rect key="frame" x="16" y="174.5" width="117" height="21"/>
1873+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
1874+
<nil key="textColor"/>
1875+
<nil key="highlightedColor"/>
1876+
</label>
1877+
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="a8X-Mk-Wb8">
1878+
<rect key="frame" x="141" y="168" width="163" height="34"/>
1879+
<fontDescription key="fontDescription" type="system" pointSize="14"/>
1880+
<textInputTraits key="textInputTraits"/>
1881+
</textField>
1882+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ldh-mW-4FD">
1883+
<rect key="frame" x="82" y="123.5" width="156" height="35"/>
1884+
<state key="normal" title="Button"/>
1885+
<buttonConfiguration key="configuration" style="plain" title="Remove Attribute"/>
1886+
<connections>
1887+
<action selector="removeAttribute:" destination="ARr-dx-Xze" eventType="touchUpInside" id="w18-qb-kM9"/>
1888+
</connections>
1889+
</button>
1890+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Eyt-Da-PWz">
1891+
<rect key="frame" x="76.5" y="218" width="167" height="35"/>
1892+
<state key="normal" title="Button"/>
1893+
<buttonConfiguration key="configuration" style="plain" title="Set Attribute Value"/>
1894+
<connections>
1895+
<action selector="setAttribute:" destination="ARr-dx-Xze" eventType="touchUpInside" id="Cbt-i9-QEv"/>
1896+
</connections>
1897+
</button>
1898+
</subviews>
1899+
<viewLayoutGuide key="safeArea" id="UBd-ip-qaw"/>
1900+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
1901+
<constraints>
1902+
<constraint firstItem="Cmk-s4-Os4" firstAttribute="leading" secondItem="xb7-S3-3s7" secondAttribute="trailing" constant="8" symbolic="YES" id="0v4-D5-asd"/>
1903+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="a8X-Mk-Wb8" secondAttribute="trailing" constant="16" id="1PS-dq-wfp"/>
1904+
<constraint firstItem="Eyt-Da-PWz" firstAttribute="top" secondItem="a8X-Mk-Wb8" secondAttribute="bottom" constant="16" id="9w7-Ef-rQF"/>
1905+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="top" secondItem="fHY-KL-zoc" secondAttribute="bottom" constant="8" symbolic="YES" id="9zm-ED-VUa"/>
1906+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="bottom" secondItem="UBd-ip-qaw" secondAttribute="bottom" constant="-16" id="DDh-Lp-MhL"/>
1907+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="Cmk-s4-Os4" secondAttribute="trailing" constant="16" id="FKD-bi-OMi"/>
1908+
<constraint firstItem="QHu-ah-Fmv" firstAttribute="top" secondItem="Eyt-Da-PWz" secondAttribute="bottom" id="FjT-0R-bhy"/>
1909+
<constraint firstItem="fHY-KL-zoc" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="G4z-ue-zpZ"/>
1910+
<constraint firstItem="xb7-S3-3s7" firstAttribute="top" secondItem="UBd-ip-qaw" secondAttribute="top" constant="16" id="GwE-oo-jpO"/>
1911+
<constraint firstItem="ctB-hS-Uvr" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="Ntd-Yu-W6y"/>
1912+
<constraint firstItem="a8X-Mk-Wb8" firstAttribute="leading" secondItem="Eap-5e-Pfq" secondAttribute="trailing" constant="8" symbolic="YES" id="ana-uV-xp2"/>
1913+
<constraint firstItem="UBd-ip-qaw" firstAttribute="trailing" secondItem="ctB-hS-Uvr" secondAttribute="trailing" constant="16" id="ewx-Aj-ceC"/>
1914+
<constraint firstItem="ldh-mW-4FD" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="fWX-1T-sKi"/>
1915+
<constraint firstItem="Eyt-Da-PWz" firstAttribute="centerX" secondItem="UBd-ip-qaw" secondAttribute="centerX" id="gOC-8q-KHd"/>
1916+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="pTT-cw-o5Y"/>
1917+
<constraint firstItem="fHY-KL-zoc" firstAttribute="top" secondItem="QHu-ah-Fmv" secondAttribute="bottom" constant="10" id="qA5-ib-uPE"/>
1918+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="centerY" secondItem="a8X-Mk-Wb8" secondAttribute="centerY" id="qvj-kR-bOa"/>
1919+
<constraint firstItem="ldh-mW-4FD" firstAttribute="top" secondItem="Cmk-s4-Os4" secondAttribute="bottom" constant="16" id="r0m-Bt-dtP"/>
1920+
<constraint firstItem="xb7-S3-3s7" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="rDH-7y-xfI"/>
1921+
<constraint firstItem="Eap-5e-Pfq" firstAttribute="top" secondItem="ldh-mW-4FD" secondAttribute="bottom" constant="16" id="x5j-hs-JHs"/>
1922+
<constraint firstItem="xb7-S3-3s7" firstAttribute="centerY" secondItem="Cmk-s4-Os4" secondAttribute="centerY" id="xOj-UV-Kte"/>
1923+
<constraint firstAttribute="trailing" secondItem="QHu-ah-Fmv" secondAttribute="trailing" constant="16" id="y42-oq-LBV"/>
1924+
<constraint firstItem="QHu-ah-Fmv" firstAttribute="leading" secondItem="UBd-ip-qaw" secondAttribute="leading" constant="16" id="yCc-ZU-OQv"/>
1925+
</constraints>
1926+
</view>
1927+
<navigationItem key="navigationItem" id="nX7-Ze-SG9"/>
1928+
<connections>
1929+
<outlet property="attributeNameField" destination="Cmk-s4-Os4" id="cUb-lc-TJx"/>
1930+
<outlet property="attributeValueField" destination="a8X-Mk-Wb8" id="vou-eg-nhn"/>
1931+
<outlet property="attributesTextView" destination="ctB-hS-Uvr" id="fr2-ut-urt"/>
1932+
</connections>
1933+
</viewController>
1934+
<placeholder placeholderIdentifier="IBFirstResponder" id="1Q9-z5-Hib" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
1935+
</objects>
1936+
<point key="canvasLocation" x="4362.686567164179" y="-774.37070938215095"/>
1937+
</scene>
18181938
</scenes>
18191939
<resources>
18201940
<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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ NS_SWIFT_NAME(Scope)
4040
*/
4141
@property (nonatomic, readonly, copy) NSDictionary<NSString *, NSString *> *tags;
4242

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

144+
/**
145+
* Set global attributes. Attributes are searchable key/value string pairs attached to every log
146+
* message.
147+
*/
148+
- (void)setAttributeValue:(id)value forKey:(NSString *)key NS_SWIFT_NAME(setAttribute(value:key:));
149+
150+
/**
151+
* Remove the attribute for the specified key.
152+
*/
153+
- (void)removeAttributeForKey:(NSString *)key NS_SWIFT_NAME(removeAttribute(key:));
154+
139155
/**
140156
* Clears all attachments in the scope.
141157
*/

Sources/Sentry/SentryScope.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ @interface SentryScope ()
2828

2929
@property (atomic, strong) NSMutableArray<SentryBreadcrumb *> *breadcrumbArray;
3030

31+
@property (atomic, strong) NSMutableDictionary<NSString *, id> *attributesDictionary;
32+
3133
@end
3234

3335
@implementation SentryScope {
@@ -49,6 +51,7 @@ - (instancetype)initWithMaxBreadcrumbs:(NSInteger)maxBreadcrumbs
4951
self.contextDictionary = [NSMutableDictionary new];
5052
self.attachmentArray = [NSMutableArray new];
5153
self.fingerprintArray = [NSMutableArray new];
54+
self.attributesDictionary = [NSMutableDictionary new];
5255
_spanLock = [[NSObject alloc] init];
5356
self.observers = [NSMutableArray new];
5457
self.propagationContext = [[SentryPropagationContext alloc] init];
@@ -672,6 +675,40 @@ - (NSString *)propagationContextTraceIdString
672675
return [self.propagationContext.traceId sentryIdString];
673676
}
674677

678+
- (NSDictionary<NSString *, id> *)attributes
679+
{
680+
@synchronized(_attributesDictionary) {
681+
return _attributesDictionary.copy;
682+
}
683+
}
684+
685+
- (void)setAttributeValue:(id)value forKey:(NSString *)key
686+
{
687+
if (key == nil || key.length == 0) {
688+
SENTRY_LOG_ERROR(@"Attribute's key cannot be nil nor empty");
689+
return;
690+
}
691+
692+
@synchronized(_attributesDictionary) {
693+
_attributesDictionary[key] = value;
694+
695+
// ScopeObservers are not called since at this moment attributes are only used for Logs,
696+
// which the LogBatcher obtains manually. At this moment not even Spans use this attributes.
697+
// Should this change, we will need to call the observers.
698+
}
699+
}
700+
701+
- (void)removeAttributeForKey:(NSString *)key
702+
{
703+
@synchronized(_attributesDictionary) {
704+
[_attributesDictionary removeObjectForKey:key];
705+
706+
// ScopeObservers are not called since at this moment attributes are only used for Logs,
707+
// which the LogBatcher obtains manually. At this moment not even Spans use this attributes.
708+
// Should this change, we will need to call the observers.
709+
}
710+
}
711+
675712
@end
676713

677714
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)