Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
8 changes: 8 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,8 @@
D48891CC2E98F22A00212823 /* SentryInfoPlistWrapperProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891C62E98F21D00212823 /* SentryInfoPlistWrapperProvider.swift */; };
D48891CE2E98F28E00212823 /* SentryInfoPlistWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891CD2E98F28E00212823 /* SentryInfoPlistWrapper.swift */; };
D48891D02E98F2E700212823 /* SentryInfoPlistError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891CF2E98F2E600212823 /* SentryInfoPlistError.swift */; };
D48954722EF5ABE00086F240 /* SentryAttributeValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */; };
D48954742EF5B00A0086F240 /* SentryAttributeValuable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */; };
D48E8B8B2D3E79610032E35E /* SentryTraceOrigin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48E8B8A2D3E79610032E35E /* SentryTraceOrigin.swift */; };
D48E8B9D2D3E82AC0032E35E /* SentrySpanOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48E8B9C2D3E82AC0032E35E /* SentrySpanOperation.swift */; };
D490648A2DFAE1F600555785 /* SentryScreenshotOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49064892DFAE1F600555785 /* SentryScreenshotOptions.swift */; };
Expand Down Expand Up @@ -2163,6 +2165,8 @@
D48891C62E98F21D00212823 /* SentryInfoPlistWrapperProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistWrapperProvider.swift; sourceTree = "<group>"; };
D48891CD2E98F28E00212823 /* SentryInfoPlistWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistWrapper.swift; sourceTree = "<group>"; };
D48891CF2E98F2E600212823 /* SentryInfoPlistError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistError.swift; sourceTree = "<group>"; };
D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAttributeValue.swift; sourceTree = "<group>"; };
D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAttributeValuable.swift; sourceTree = "<group>"; };
D48E8B8A2D3E79610032E35E /* SentryTraceOrigin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceOrigin.swift; sourceTree = "<group>"; };
D48E8B9C2D3E82AC0032E35E /* SentrySpanOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpanOperation.swift; sourceTree = "<group>"; };
D49064892DFAE1F600555785 /* SentryScreenshotOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryScreenshotOptions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4906,6 +4910,8 @@
620078752D38F1110022CB67 /* Codable */,
92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */,
92ECD73F2E05AD500063EC10 /* SentryAttribute.swift */,
D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */,
D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */,
92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */,
9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */,
F458D1122E180BB00028273E /* SentryFileManagerProtocol.swift */,
Expand Down Expand Up @@ -5816,6 +5822,7 @@
D8739D142BEE5049007D2F66 /* SentryRRWebSpanEvent.swift in Sources */,
FAAB2F972E4D345800FE8B7E /* SentryUIDeviceWrapper.swift in Sources */,
7B6438AB26A70F24000D0F65 /* UIViewController+Sentry.m in Sources */,
D48954722EF5ABE00086F240 /* SentryAttributeValue.swift in Sources */,
84302A812B5767A50027A629 /* SentryLaunchProfiling.m in Sources */,
D490648A2DFAE1F600555785 /* SentryScreenshotOptions.swift in Sources */,
FA6FC0AA2E0B6B1100ED2669 /* SentrySdkInfo.swift in Sources */,
Expand Down Expand Up @@ -6008,6 +6015,7 @@
FA67DCFD2DDBD4EA00896B02 /* SentryANRTracker.swift in Sources */,
FA67DCFE2DDBD4EA00896B02 /* SentryANRTrackerV2Delegate.swift in Sources */,
FABE8E152E307A5E0040809A /* SentrySDK.swift in Sources */,
D48954742EF5B00A0086F240 /* SentryAttributeValuable.swift in Sources */,
FA67DCFF2DDBD4EA00896B02 /* SentryMXManager.swift in Sources */,
D41415A72DEEE532003B14D5 /* SentryRedactViewHelper.swift in Sources */,
FA66143A2E4B593900657755 /* SentryApplicationExtensions.swift in Sources */,
Expand Down
36 changes: 36 additions & 0 deletions Sources/Swift/Protocol/SentryAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ public final class SentryAttribute: NSObject {
super.init()
}

internal init(attributableValue: SentryAttributeValue) {
switch attributableValue {
case .boolean(let value):
self.type = "boolean"
self.value = value
case .string(let value):
self.type = "string"
self.value = value
case .integer(let value):
self.type = "integer"
self.value = value
case .double(let value):
self.type = "double"
self.value = value
case .booleanArray(let array):
self.type = "boolean[]"
self.value = array
case .stringArray(let array):
self.type = "string[]"
self.value = array
case .integerArray(let array):
self.type = "integer[]"
self.value = array
case .doubleArray(let array):
self.type = "double[]"
self.value = array
}
}

internal init(value: Any) {
switch value {
case let stringValue as String:
Expand All @@ -57,6 +86,13 @@ public final class SentryAttribute: NSObject {
case let floatValue as Float:
self.type = "double"
self.value = Double(floatValue)
case let attributable as SentryAttributeValuable:
let value = attributable.asAttributeValue
self.type = value.type
self.value = value.anyValue
case let attribute as SentryAttribute:
self.type = attribute.type
self.value = attribute.value
default:
// For any other type, convert to string representation
self.type = "string"
Expand Down
33 changes: 33 additions & 0 deletions Sources/Swift/Protocol/SentryAttributeValuable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public protocol SentryAttributeValuable {
var asAttributeValue: SentryAttributeValue { get }
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 have sentry in the naming everywhere. I think this would make it a bit clearer that this returns a SentryAttributeValue.

Suggested change
var asAttributeValue: SentryAttributeValue { get }
var asSentryAttributeValue: SentryAttributeValue { get }

}

extension String: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .string(self)
}
}

extension Bool: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .boolean(self)
}
}

extension Int: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .integer(self)
}
}

extension Double: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .double(self)
}
}

extension Float: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .double(Double(self))
}
}
107 changes: 107 additions & 0 deletions Sources/Swift/Protocol/SentryAttributeValue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
public enum SentryAttributeValue: Equatable, Hashable {
case string(String)
case boolean(Bool)
case integer(Int)
case double(Double)
case stringArray([String])
case booleanArray([Bool])
case integerArray([Int])
case doubleArray([Double])

var type: String {
switch self {
case .string:
return "string"
case .boolean:
return "boolean"
case .integer:
return "integer"
case .double:
return "double"
case .stringArray:
return "string[]"
case .booleanArray:
return "boolean[]"
case .integerArray:
return "integer[]"
case .doubleArray:
return "double[]"
}
}
}

extension SentryAttributeValue: Encodable {
private enum CodingKeys: String, CodingKey {
case type
case value
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)

switch self {
case .string(let value):
try container.encode(value, forKey: .value)
case .boolean(let value):
try container.encode(value, forKey: .value)
case .integer(let value):
try container.encode(value, forKey: .value)
case .double(let value):
try container.encode(value, forKey: .value)
case .stringArray(let value):
try container.encode(value, forKey: .value)
case .booleanArray(let value):
try container.encode(value, forKey: .value)
case .integerArray(let value):
try container.encode(value, forKey: .value)
case .doubleArray(let value):
try container.encode(value, forKey: .value)
}
}
}

extension SentryAttributeValue {
static func from(anyValue value: Any) -> Self {
if let val = value as? String {
return .string(val)
}
if let val = value as? Bool {
return .boolean(val)
}
if let val = value as? Int {
return .integer(val)
}
if let val = value as? Double {
return .double(val)
}
if let val = value as? Float {
return .double(Double(val))
}
if let val = value as? SentryAttributeValue {
return val
}
return .string(String(describing: value))
}

var anyValue: Any {
switch self {
case .string(let value):
return value
case .boolean(let value):
return value
case .integer(let value):
return value
case .double(let value):
return value
case .stringArray(let value):
return value
case .booleanArray(let value):
return value
case .integerArray(let value):
return value
case .doubleArray(let value):
return value
}
}
}
2 changes: 1 addition & 1 deletion Sources/Swift/Tools/Batcher/BatcherItem.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
protocol BatcherItem: Encodable {
var attributes: [String: SentryAttribute] { get set }
var attributesMap: [String: SentryAttributeValue] { get set }
Copy link
Member Author

Choose a reason for hiding this comment

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

This was renamed because it would otherwise collide with the type of SentryAttribute.attributes

var traceId: SentryId { get set }
var body: String { get }
}
60 changes: 30 additions & 30 deletions Sources/Swift/Tools/Batcher/BatcherScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,91 +21,91 @@ extension BatcherScope {
config: Config,
metadata: Metadata
) {
addDefaultAttributes(to: &item.attributes, config: config, metadata: metadata)
addOSAttributes(to: &item.attributes, config: config)
addDeviceAttributes(to: &item.attributes, config: config)
addUserAttributes(to: &item.attributes, config: config)
addReplayAttributes(to: &item.attributes, config: config)
addScopeAttributes(to: &item.attributes, config: config)
addDefaultUserIdIfNeeded(to: &item.attributes, config: config, metadata: metadata)
addDefaultAttributes(to: &item.attributesMap, config: config, metadata: metadata)
addOSAttributes(to: &item.attributesMap, config: config)
addDeviceAttributes(to: &item.attributesMap, config: config)
addUserAttributes(to: &item.attributesMap, config: config)
addReplayAttributes(to: &item.attributesMap, config: config)
addScopeAttributes(to: &item.attributesMap, config: config)
addDefaultUserIdIfNeeded(to: &item.attributesMap, config: config, metadata: metadata)

item.traceId = SentryId(uuidString: propagationContextTraceIdString)
}

private func addDefaultAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig, metadata: any BatcherMetadata) {
attributes["sentry.sdk.name"] = .init(string: SentryMeta.sdkName)
attributes["sentry.sdk.version"] = .init(string: SentryMeta.versionString)
attributes["sentry.environment"] = .init(string: metadata.environment)
private func addDefaultAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig, metadata: any BatcherMetadata) {
attributes["sentry.sdk.name"] = .string(SentryMeta.sdkName)
attributes["sentry.sdk.version"] = .string(SentryMeta.versionString)
attributes["sentry.environment"] = .string(metadata.environment)
if let releaseName = metadata.releaseName {
attributes["sentry.release"] = .init(string: releaseName)
attributes["sentry.release"] = .string(releaseName)
}
if let span = self.span {
attributes["span_id"] = .init(string: span.spanId.sentrySpanIdString)
attributes["span_id"] = .string(span.spanId.sentrySpanIdString)
}
}

private func addOSAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) {
private func addOSAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig) {
guard let osContext = self.getContextForKey(SENTRY_CONTEXT_OS_KEY) else {
return
}
if let osName = osContext["name"] as? String {
attributes["os.name"] = .init(string: osName)
attributes["os.name"] = .string(osName)
}
if let osVersion = osContext["version"] as? String {
attributes["os.version"] = .init(string: osVersion)
attributes["os.version"] = .string(osVersion)
}
}

private func addDeviceAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) {
private func addDeviceAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig) {
guard let deviceContext = self.getContextForKey(SENTRY_CONTEXT_DEVICE_KEY) else {
return
}
// For Apple devices, brand is always "Apple"
attributes["device.brand"] = .init(string: "Apple")
attributes["device.brand"] = .string("Apple")

if let deviceModel = deviceContext["model"] as? String {
attributes["device.model"] = .init(string: deviceModel)
attributes["device.model"] = .string(deviceModel)
}
if let deviceFamily = deviceContext["family"] as? String {
attributes["device.family"] = .init(string: deviceFamily)
attributes["device.family"] = .string(deviceFamily)
}
}

private func addUserAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) {
private func addUserAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig) {
guard config.sendDefaultPii else {
return
}
if let userId = userObject?.userId {
attributes["user.id"] = .init(string: userId)
attributes["user.id"] = .string(userId)
}
if let userName = userObject?.name {
attributes["user.name"] = .init(string: userName)
attributes["user.name"] = .string(userName)
}
if let userEmail = userObject?.email {
attributes["user.email"] = .init(string: userEmail)
attributes["user.email"] = .string(userEmail)
}
}

private func addReplayAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) {
private func addReplayAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig) {
#if canImport(UIKit) && !SENTRY_NO_UIKIT
#if os(iOS) || os(tvOS)
if let scopeReplayId = replayId {
// Session mode: use scope replay ID
attributes["sentry.replay_id"] = .init(string: scopeReplayId)
attributes["sentry.replay_id"] = .string(scopeReplayId)
}
#endif
#endif
}

private func addScopeAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) {
private func addScopeAttributes(to attributes: inout [String: SentryAttributeValue], config: any BatcherConfig) {
// Scope attributes should not override any existing attribute in the item
for (key, value) in self.attributes where attributes[key] == nil {
attributes[key] = .init(value: value)
attributes[key] = SentryAttributeValue.from(anyValue: value)
}
}

private func addDefaultUserIdIfNeeded(
to attributes: inout [String: SentryAttribute],
to attributes: inout [String: SentryAttributeValue],
config: any BatcherConfig,
metadata: any BatcherMetadata
) {
Expand All @@ -116,7 +116,7 @@ extension BatcherScope {
if let installationId = metadata.installationId {
// We only want to set the id if the customer didn't set a user so we at least set something to
// identify the user.
attributes["user.id"] = .init(value: installationId)
attributes["user.id"] = .string(installationId)
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion Sources/Swift/Tools/SentryLogBatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,17 @@ import Foundation
}
}

extension SentryLog: BatcherItem {}
extension SentryLog: BatcherItem {
var attributesMap: [String: SentryAttributeValue] {
get {
attributes.mapValues { value in
SentryAttributeValue.from(anyValue: value)
}
}
set {
attributes = newValue.mapValues { value in
SentryAttribute(attributableValue: value)
}
}
}
}
Loading
Loading