diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c77f9cee..5942e815d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add attributes data to `SentryScope` (#6830) +- Add `SentryScope` attributes into log messages (#6834) ## 9.0.0-rc.0 diff --git a/Sources/Swift/Tools/SentryLogBatcher.swift b/Sources/Swift/Tools/SentryLogBatcher.swift index 5151907c0e..1abdf69461 100644 --- a/Sources/Swift/Tools/SentryLogBatcher.swift +++ b/Sources/Swift/Tools/SentryLogBatcher.swift @@ -90,6 +90,7 @@ import Foundation addDeviceAttributes(to: &log.attributes, scope: scope) addUserAttributes(to: &log.attributes, scope: scope) addReplayAttributes(to: &log.attributes, scope: scope) + addScopeAttributes(to: &log.attributes, scope: scope) let propagationContextTraceIdString = scope.propagationContextTraceIdString log.traceId = SentryId(uuidString: propagationContextTraceIdString) @@ -187,6 +188,13 @@ import Foundation #endif #endif } + + private func addScopeAttributes(to attributes: inout [String: SentryLog.Attribute], scope: Scope) { + // Scope attributes should not override any existing attribute in the log + for (key, value) in scope.attributes where attributes[key] == nil { + attributes[key] = .init(value: value) + } + } // Only ever call this from the serial dispatch queue. private func encodeAndBuffer(log: SentryLog) { diff --git a/Tests/SentryTests/SentryLogBatcherTests.swift b/Tests/SentryTests/SentryLogBatcherTests.swift index 962eba5238..350dcb2047 100644 --- a/Tests/SentryTests/SentryLogBatcherTests.swift +++ b/Tests/SentryTests/SentryLogBatcherTests.swift @@ -494,6 +494,47 @@ final class SentryLogBatcherTests: XCTestCase { XCTAssertNil(attributes["device.family"]) } + func testAddLog_AddsScopeAttributes() throws { + let scope = Scope() + scope.setAttribute(value: "aString", key: "string-attribute") + scope.setAttribute(value: false, key: "bool-attribute") + scope.setAttribute(value: 1.765, key: "double-attribute") + scope.setAttribute(value: 5, key: "integer-attribute") + + let log = createTestLog(body: "Test log message with user") + sut.addLog(log, scope: scope) + sut.captureLogs() + + let capturedLogs = testDelegate.getCapturedLogs() + let capturedLog = try XCTUnwrap(capturedLogs.first) + let attributes = capturedLog.attributes + + XCTAssertEqual(attributes["string-attribute"]?.value as? String, "aString") + XCTAssertEqual(attributes["string-attribute"]?.type, "string") + XCTAssertEqual(attributes["bool-attribute"]?.value as? Bool, false) + XCTAssertEqual(attributes["bool-attribute"]?.type, "boolean") + XCTAssertEqual(attributes["double-attribute"]?.value as? Double, 1.765) + XCTAssertEqual(attributes["double-attribute"]?.type, "double") + XCTAssertEqual(attributes["integer-attribute"]?.value as? Int, 5) + XCTAssertEqual(attributes["integer-attribute"]?.type, "integer") + } + + func testAddLog_ScopeAttributesDoNotOverrideLogAttribute() throws { + let scope = Scope() + scope.setAttribute(value: true, key: "log-attribute") + + let log = createTestLog(body: "Test log message with user", attributes: [ "log-attribute": .init(value: false)]) + sut.addLog(log, scope: scope) + sut.captureLogs() + + let capturedLogs = testDelegate.getCapturedLogs() + let capturedLog = try XCTUnwrap(capturedLogs.first) + let attributes = capturedLog.attributes + + XCTAssertEqual(attributes["log-attribute"]?.value as? Bool, false) + XCTAssertEqual(attributes["log-attribute"]?.type, "boolean") + } + // MARK: - Replay Attributes Tests #if canImport(UIKit) && !SENTRY_NO_UIKIT