diff --git a/.gitignore b/.gitignore index a16931f9bdf..1ad4737dcc1 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,4 @@ analyzer # Output folder for Sentry local builds XCFrameworkBuildPath/ +*.xcresult diff --git a/CHANGELOG.md b/CHANGELOG.md index c75dc8ddc7b..ef57404bc76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +### Fixes + +- Change default attributes of Logs to only include user attributes when `options.sendDefaultPii = true` (#7055) +- Rename log attribute `sentry.trace.parent_span_id` to `span_id` (#7055) + ## 9.1.0 > [!Warning] diff --git a/Sources/Swift/Tools/Batcher/Batcher.swift b/Sources/Swift/Tools/Batcher/Batcher.swift index c8ab2b8d008..3560b0c5845 100644 --- a/Sources/Swift/Tools/Batcher/Batcher.swift +++ b/Sources/Swift/Tools/Batcher/Batcher.swift @@ -11,6 +11,8 @@ protocol BatcherProtocol: AnyObject { final class Batcher, Item: BatcherItem, Scope: BatcherScope>: BatcherProtocol { struct Config: BatcherConfig { + let sendDefaultPii: Bool + let flushTimeout: TimeInterval let maxItemCount: Int let maxBufferSizeBytes: Int diff --git a/Sources/Swift/Tools/Batcher/BatcherConfig.swift b/Sources/Swift/Tools/Batcher/BatcherConfig.swift index ba2861b07b7..ff0a18f7255 100644 --- a/Sources/Swift/Tools/Batcher/BatcherConfig.swift +++ b/Sources/Swift/Tools/Batcher/BatcherConfig.swift @@ -1,6 +1,8 @@ protocol BatcherConfig { associatedtype Item + var sendDefaultPii: Bool { get } + var flushTimeout: TimeInterval { get } var maxItemCount: Int { get } var maxBufferSizeBytes: Int { get } diff --git a/Sources/Swift/Tools/Batcher/BatcherScope.swift b/Sources/Swift/Tools/Batcher/BatcherScope.swift index 983d0d7d6f3..4e43743bd48 100644 --- a/Sources/Swift/Tools/Batcher/BatcherScope.swift +++ b/Sources/Swift/Tools/Batcher/BatcherScope.swift @@ -40,7 +40,7 @@ extension BatcherScope { attributes["sentry.release"] = .init(string: releaseName) } if let span = self.span { - attributes["sentry.trace.parent_span_id"] = .init(string: span.spanId.sentrySpanIdString) + attributes["span_id"] = .init(string: span.spanId.sentrySpanIdString) } } @@ -72,6 +72,9 @@ extension BatcherScope { } private func addUserAttributes(to attributes: inout [String: SentryAttribute], config: any BatcherConfig) { + guard config.sendDefaultPii else { + return + } if let userId = userObject?.userId { attributes["user.id"] = .init(string: userId) } diff --git a/Sources/Swift/Tools/SentryLogBatcher.swift b/Sources/Swift/Tools/SentryLogBatcher.swift index 49f1c54c7cc..6132de7b8b0 100644 --- a/Sources/Swift/Tools/SentryLogBatcher.swift +++ b/Sources/Swift/Tools/SentryLogBatcher.swift @@ -63,6 +63,7 @@ import Foundation ) { self.batcher = Batcher( config: .init( + sendDefaultPii: options.sendDefaultPii, flushTimeout: flushTimeout, maxItemCount: maxLogCount, maxBufferSizeBytes: maxBufferSizeBytes, diff --git a/Tests/SentryTests/Batcher/BatcherScopeTests.swift b/Tests/SentryTests/Batcher/BatcherScopeTests.swift index 8dc4a30495a..3eadad787a6 100644 --- a/Tests/SentryTests/Batcher/BatcherScopeTests.swift +++ b/Tests/SentryTests/Batcher/BatcherScopeTests.swift @@ -25,6 +25,8 @@ final class BatcherScopeTests: XCTestCase { private struct TestConfig: BatcherConfig { typealias Item = TestItem + let sendDefaultPii: Bool + let flushTimeout: TimeInterval let maxItemCount: Int let maxBufferSizeBytes: Int @@ -47,6 +49,7 @@ final class BatcherScopeTests: XCTestCase { var userObject: User? var contextStore: [String: [String: Any]] = [:] var attributes: [String: Any] = [:] + var sendDefaultPii = true func getContextForKey(_ key: String) -> [String: Any]? { return contextStore[key] @@ -145,7 +148,7 @@ final class BatcherScopeTests: XCTestCase { scope.applyToItem(&item, config: config, metadata: metadata) // -- Assert -- - XCTAssertEqual(item.attributes["sentry.trace.parent_span_id"]?.value as? String, span.spanId.sentrySpanIdString) + XCTAssertEqual(item.attributes["span_id"]?.value as? String, span.spanId.sentrySpanIdString) } func testApplyToItem_withoutSpan_shouldNotAddParentSpanId() { @@ -654,7 +657,7 @@ final class BatcherScopeTests: XCTestCase { XCTAssertEqual(item.attributes["sentry.sdk.version"]?.value as? String, SentryMeta.versionString) XCTAssertEqual(item.attributes["sentry.environment"]?.value as? String, "production") XCTAssertEqual(item.attributes["sentry.release"]?.value as? String, "1.0.0") - XCTAssertEqual(item.attributes["sentry.trace.parent_span_id"]?.value as? String, span.spanId.sentrySpanIdString) + XCTAssertEqual(item.attributes["span_id"]?.value as? String, span.spanId.sentrySpanIdString) // OS attributes XCTAssertEqual(item.attributes["os.name"]?.value as? String, "iOS") @@ -712,6 +715,7 @@ final class BatcherScopeTests: XCTestCase { private func createTestConfig() -> TestConfig { return TestConfig( + sendDefaultPii: true, flushTimeout: 0.1, maxItemCount: 10, maxBufferSizeBytes: 8_000, diff --git a/Tests/SentryTests/Batcher/BatcherTests.swift b/Tests/SentryTests/Batcher/BatcherTests.swift index c18767b0369..bc63f5f7162 100644 --- a/Tests/SentryTests/Batcher/BatcherTests.swift +++ b/Tests/SentryTests/Batcher/BatcherTests.swift @@ -9,7 +9,7 @@ private struct TestScope: BatcherScope { var userObject: User? var contextStore: [String: [String: Any]] = [:] var attributes: [String: Any] = [:] - + init(propagationContextTraceIdString: String = SentryId().sentryIdString) { self.propagationContextTraceIdString = propagationContextTraceIdString } @@ -89,12 +89,14 @@ final class BatcherTests: XCTestCase { } private func getSut( + sendDefaultPii: Bool = false, flushTimeout: TimeInterval = 0.1, maxItemCount: Int = 10, maxBufferSizeBytes: Int = 8_000, beforeSendItem: ((TestItem) -> TestItem?)? = nil ) -> Batcher { var config = Batcher.Config( + sendDefaultPii: sendDefaultPii, flushTimeout: flushTimeout, maxItemCount: maxItemCount, maxBufferSizeBytes: maxBufferSizeBytes, diff --git a/Tests/SentryTests/SentryLogBatcherTests.swift b/Tests/SentryTests/SentryLogBatcherTests.swift index 4d588c5e277..dc77c1ae1af 100644 --- a/Tests/SentryTests/SentryLogBatcherTests.swift +++ b/Tests/SentryTests/SentryLogBatcherTests.swift @@ -325,7 +325,7 @@ final class SentryLogBatcherTests: XCTestCase { XCTAssertEqual(attributes["sentry.sdk.version"]?.value as? String, SentryMeta.versionString) XCTAssertEqual(attributes["sentry.environment"]?.value as? String, "test-environment") XCTAssertEqual(attributes["sentry.release"]?.value as? String, "1.0.0") - XCTAssertEqual(attributes["sentry.trace.parent_span_id"]?.value as? String, span.spanId.sentrySpanIdString) + XCTAssertEqual(attributes["span_id"]?.value as? String, span.spanId.sentrySpanIdString) } func testAddLog_DoesNotAddNilDefaultAttributes() throws { @@ -344,7 +344,7 @@ final class SentryLogBatcherTests: XCTestCase { let attributes = capturedLog.attributes XCTAssertNil(attributes["sentry.release"]) - XCTAssertNil(attributes["sentry.trace.parent_span_id"]) + XCTAssertNil(attributes["span_id"]) XCTAssertEqual(attributes["sentry.sdk.name"]?.value as? String, SentryMeta.sdkName) XCTAssertEqual(attributes["sentry.sdk.version"]?.value as? String, SentryMeta.versionString) XCTAssertNotNil(attributes["sentry.environment"]) @@ -368,35 +368,70 @@ final class SentryLogBatcherTests: XCTestCase { XCTAssertEqual(capturedLog.traceId, expectedTraceId) } - func testAddLog_AddsUserAttributes() throws { + func testAddLog_whenSendDefaultPiiTrue_shouldAddUserAttributes() throws { // -- Arrange -- + options.sendDefaultPii = true + let user = User() user.userId = "123" user.email = "test@test.com" user.name = "test-name" scope.setUser(user) + let sut = getSut() let log = createTestLog(body: "Test log message with user") - + // -- Act -- sut.addLog(log, scope: scope) sut.captureLogs() - + // -- Assert -- let capturedLogs = testDelegate.getCapturedLogs() let capturedLog = try XCTUnwrap(capturedLogs.first) let attributes = capturedLog.attributes - + XCTAssertEqual(attributes["user.id"]?.value as? String, "123") XCTAssertEqual(attributes["user.name"]?.value as? String, "test-name") XCTAssertEqual(attributes["user.email"]?.value as? String, "test@test.com") } - - func testAddLog_DoesNotAddNilUserAttributes() throws { + + func testAddLog_whenSendDefaultPiiFalse_shouldNotAddUserAttributes() throws { + // -- Arrange -- + let installationId = SentryInstallation.id(withCacheDirectoryPath: options.cacheDirectoryPath) + options.sendDefaultPii = false + + let user = User() + user.userId = "123" + user.email = "test@test.com" + user.name = "test-name" + scope.setUser(user) + + let sut = getSut() + let log = createTestLog(body: "Test log message with user") + + // -- Act -- + sut.addLog(log, scope: scope) + sut.captureLogs() + + // -- Assert -- + let capturedLogs = testDelegate.getCapturedLogs() + let capturedLog = try XCTUnwrap(capturedLogs.first) + let attributes = capturedLog.attributes + + // The installation id is used as a fallback for the user.id + XCTAssertEqual(attributes["user.id"]?.value as? String, installationId) + XCTAssertNil(attributes["user.name"]) + XCTAssertNil(attributes["user.email"]) + } + + func testAddLog_whenSendDefaultPiiTrue_shouldNotAddNilUserAttributes() throws { // -- Arrange -- + options.sendDefaultPii = true + let user = User() user.userId = "123" scope.setUser(user) + let sut = getSut() let log = createTestLog(body: "Test log message with partial user") diff --git a/Tests/SentryTests/SentryScopeSwiftTests.swift b/Tests/SentryTests/SentryScopeSwiftTests.swift index e595b2cd7c7..f29f15ff984 100644 --- a/Tests/SentryTests/SentryScopeSwiftTests.swift +++ b/Tests/SentryTests/SentryScopeSwiftTests.swift @@ -157,7 +157,7 @@ class SentryScopeSwiftTests: XCTestCase { XCTAssertEqual(try XCTUnwrap(scope.serialize() as? [String: AnyHashable]), snapshot) XCTAssertNotEqual(try XCTUnwrap(scope.serialize() as? [String: AnyHashable]), try XCTUnwrap(cloned.serialize() as? [String: AnyHashable])) } - + func testApplyToEvent() { let actual = fixture.scope.applyTo(event: fixture.event, maxBreadcrumbs: 10) let actualContext = actual?.context as? [String: [String: String]]