Skip to content

Structured Logs: Add default attributes #5593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 102 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
19e9379
create envelope with logs
denrase Jul 1, 2025
d9b8c95
cleanup
denrase Jul 1, 2025
83bd61c
chang m signature
denrase Jul 2, 2025
b45690a
sentry though sentry client extension
denrase Jul 2, 2025
f327cd1
implment capture log methods in hub/client
denrase Jul 2, 2025
589401e
add logger and expose it though sentry sdk
denrase Jul 2, 2025
4c51322
expose in header
denrase Jul 2, 2025
0123785
Add SentryEnvelopeItemTypeLog
denrase Jul 2, 2025
db6d522
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 2, 2025
8fb5446
use spi where needed, fix retain cycle in log batcher
denrase Jul 3, 2025
9ee8bdd
fix visibilies of classes
denrase Jul 3, 2025
784572d
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 3, 2025
b99d360
make log models @_spi(Private)
denrase Jul 3, 2025
abdd76f
remove not needed import
denrase Jul 3, 2025
19f18e3
add import for envelope
denrase Jul 3, 2025
a19b7d3
run format
denrase Jul 3, 2025
aeda277
add test for log batcher
denrase Jul 3, 2025
11d3f6f
make logs opt-in with experimental
denrase Jul 3, 2025
cf03ead
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 3, 2025
413ab54
add log batcher tests
denrase Jul 3, 2025
c16a196
fix import
denrase Jul 3, 2025
b25f2b4
rename local var
denrase Jul 3, 2025
a37d516
update imports
denrase Jul 3, 2025
db9a205
wrap client in batcher
denrase Jul 3, 2025
6fae27f
remove unused import
denrase Jul 3, 2025
b56d9bc
remove unused class
denrase Jul 3, 2025
041398d
remove unues member
denrase Jul 3, 2025
7b866db
move log envelope creation into log batcher
denrase Jul 3, 2025
ebc8172
add todo
denrase Jul 3, 2025
dfe4374
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 3, 2025
9bfc45e
remove envelope item type for now
denrase Jul 3, 2025
4392145
remove duplicate tests
denrase Jul 3, 2025
9eec4d3
cleanup
denrase Jul 3, 2025
1aea7a5
remove duplicate tests
denrase Jul 3, 2025
d196d7c
make test easier to read
denrase Jul 3, 2025
53f87a6
remove todo
denrase Jul 3, 2025
47aba91
attempt to make SPM work
denrase Jul 3, 2025
3b1b79b
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 8, 2025
9437255
Structured Logs: Add default attributes
denrase Jul 9, 2025
d041e0a
set propagationContext.trace id on log
denrase Jul 9, 2025
8bff93c
move edge cases intro own tests
denrase Jul 9, 2025
29b87ed
remove CGFloat and NSNumber support from attribute mapping for now
denrase Jul 9, 2025
d8e7ce2
remove logger from dependency container
denrase Jul 9, 2025
4bdf75a
make internal dependencies private
denrase Jul 9, 2025
1a8bfc7
move logs out of hub, change to struct again, move envelope creation …
denrase Jul 9, 2025
caf288b
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 9, 2025
42a4526
add missing test
denrase Jul 9, 2025
d9c5af1
remove unused mehtods
denrase Jul 9, 2025
aacb55a
remove #import "SentryEnvelope+Private.h" from sentry private
denrase Jul 9, 2025
735bc52
remove public defs
denrase Jul 9, 2025
c5b4c7a
remove unneeded self. reference
denrase Jul 9, 2025
84bc2f1
Only expose logs helper for client
denrase Jul 9, 2025
6ca5be3
ref correclty
denrase Jul 9, 2025
e417492
add client report data categories
denrase Jul 10, 2025
232e03e
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 10, 2025
8eefd2e
add documentation for the Sentry logger
denrase Jul 10, 2025
023080f
make logger nullable and reset when sentry is closed
denrase Jul 10, 2025
f39c854
remove nonnull
denrase Jul 10, 2025
370f408
only create batcher with non-nil client and enabled logs
denrase Jul 10, 2025
9bc7f9e
Add CL entry
denrase Jul 10, 2025
0f4d91d
fix test expectations
denrase Jul 10, 2025
bfee650
ref
denrase Jul 10, 2025
1bcf4af
cleanup
denrase Jul 10, 2025
e308df0
fix linter issue
denrase Jul 10, 2025
3d379f3
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 11, 2025
006f8a7
update cl
denrase Jul 11, 2025
059559b
generate public api
denrase Jul 11, 2025
612dfa9
Merge branch 'feat/structured-logs-send-via-envelopes' into feat/stru…
denrase Jul 11, 2025
53a6b44
remove merge conflict
denrase Jul 11, 2025
b39e95e
add comment
denrase Jul 11, 2025
d59f0da
update cl
denrase Jul 11, 2025
13f155c
move default attribites to logger
denrase Jul 11, 2025
2ef0469
recreate with comandline tools 16.3
denrase Jul 11, 2025
591612c
recreate with command line tools 16.3
denrase Jul 11, 2025
7e95ca1
Merge branch 'feat/structured-logs-send-via-envelopes' into feat/stru…
denrase Jul 11, 2025
573138b
remove unused imports
denrase Jul 11, 2025
9fa370c
Merge branch 'main' into feat/structured-logs-send-via-envelopes
denrase Jul 14, 2025
9f3fb65
Merge branch 'feat/structured-logs-send-via-envelopes' into feat/stru…
denrase Jul 14, 2025
ee5f4cb
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 14, 2025
0116280
keep sentry log immutable
denrase Jul 14, 2025
49bc53c
more pr feedback
denrase Jul 14, 2025
a7b0f6b
update exptension
denrase Jul 14, 2025
e16abec
update file ref
denrase Jul 14, 2025
463411a
update cl
denrase Jul 14, 2025
b94901b
change to method
denrase Jul 14, 2025
c8c7393
test with xceo 16.3
denrase Jul 14, 2025
9b89aff
revert
denrase Jul 14, 2025
141cf4d
change to peroperty access syntax
denrase Jul 14, 2025
cd9e535
add own implementation file
denrase Jul 14, 2025
13e54b1
fix exclude
denrase Jul 14, 2025
23742ae
workaround for spm issue
denrase Jul 14, 2025
f9e1338
remove ref to SentryScope+PropagationContext.m
denrase Jul 14, 2025
5586ae2
format
denrase Jul 14, 2025
db3ab0f
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 16, 2025
ddc20d8
extract adding of attributes
denrase Jul 16, 2025
1a59aa4
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 17, 2025
b9fa5e8
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 17, 2025
eb3cc2f
rename header
denrase Jul 17, 2025
06fe2fb
add comment for interca
denrase Jul 17, 2025
78b1e92
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 18, 2025
945a2aa
Merge branch 'main' into feat/structured-logs-default-attributes
denrase Jul 19, 2025
b18e987
increase binzry size diff max
denrase Jul 21, 2025
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features

- Add experimental support for capturing structured logs via `SentrySDK.logger` (#5532)
- Add experimental support for capturing structured logs via `SentrySDK.logger` (#5532, #5593)

### Improvements

Expand Down
6 changes: 5 additions & 1 deletion Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
925824C22CB5897700C9B20B /* SentrySessionReplayIntegration-Hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = D80382BE2C09C6FD0090E048 /* SentrySessionReplayIntegration-Hybrid.h */; settings = {ATTRIBUTES = (Private, ); }; };
92672BB629C9A2A9006B021C /* SentryBreadcrumb+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
927A5CC42DD7626B00B82404 /* SentryEnvelopeItemHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */; };
928207C42E251B8F009285A4 /* SentryScope+PrivateSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */; };
9286059529A5096600F96038 /* SentryGeo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9286059429A5096600F96038 /* SentryGeo.h */; settings = {ATTRIBUTES = (Public, ); }; };
9286059729A5098900F96038 /* SentryGeo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9286059629A5098900F96038 /* SentryGeo.m */; };
9286059929A50BAB00F96038 /* SentryGeoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9286059829A50BAA00F96038 /* SentryGeoTests.swift */; };
Expand Down Expand Up @@ -1964,9 +1965,9 @@
84A789092C0E9F5800FF0803 /* SentrySpan+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentrySpan+Private.h"; path = "include/SentrySpan+Private.h"; sourceTree = "<group>"; };
84A8891A28DBD28900C51DFD /* SentryDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDevice.h; path = include/SentryDevice.h; sourceTree = "<group>"; };
84A8891B28DBD28900C51DFD /* SentryDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryDevice.m; sourceTree = "<group>"; };
84A8892028DBD8D600C51DFD /* SentryDeviceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryDeviceTests.m; sourceTree = "<group>"; };
84A898522E163072009A551E /* SentryProfileConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryProfileConfiguration.h; path = ../include/SentryProfileConfiguration.h; sourceTree = "<group>"; };
84A898532E163072009A551E /* SentryProfileConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryProfileConfiguration.m; sourceTree = "<group>"; };
84A8892028DBD8D600C51DFD /* SentryDeviceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryDeviceTests.m; sourceTree = "<group>"; };
84A898CD2E1DBDD1009A551E /* SentryAppStartProfilingConfigurationChangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAppStartProfilingConfigurationChangeTests.swift; sourceTree = "<group>"; };
84A899342E218C5F009A551E /* CLAUDE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CLAUDE.md; sourceTree = "<group>"; };
84A903702D39F66F00690CE4 /* SentryUserFeedbackFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackFormViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2060,6 +2061,7 @@
92235CAF2E155B2600865983 /* SentryLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLoggerTests.swift; sourceTree = "<group>"; };
92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryBreadcrumb+Private.h"; path = "include/HybridPublic/SentryBreadcrumb+Private.h"; sourceTree = "<group>"; };
927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeItemHeaderTests.swift; sourceTree = "<group>"; };
928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryScope+PrivateSwift.h"; path = "include/SentryScope+PrivateSwift.h"; sourceTree = "<group>"; };
9286059429A5096600F96038 /* SentryGeo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryGeo.h; path = Public/SentryGeo.h; sourceTree = "<group>"; };
9286059629A5098900F96038 /* SentryGeo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryGeo.m; sourceTree = "<group>"; };
9286059829A50BAA00F96038 /* SentryGeoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentryGeoTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2782,6 +2784,7 @@
7D0637022382B34300B30749 /* SentryScope.h */,
7D65260B237F649E00113EA2 /* SentryScope.m */,
632331F7240506DF008D91D6 /* SentryScope+Private.h */,
928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */,
7BCFBD662681C95000BC27D8 /* SentryScopeObserver.h */,
15360CEF2433A16D00112302 /* SentryInstallation.h */,
15360CEC2433A15500112302 /* SentryInstallation.m */,
Expand Down Expand Up @@ -4848,6 +4851,7 @@
63FE710320DA4C1000CDBAE8 /* SentryCrashMachineContext_Apple.h in Headers */,
844EDC6F294143B900C86F34 /* SentryNSProcessInfoWrapper.h in Headers */,
D8479328278873A100BE8E99 /* SentryByteCountFormatter.h in Headers */,
928207C42E251B8F009285A4 /* SentryScope+PrivateSwift.h in Headers */,
63AA76981EB9C1C200D153DE /* SentryClient.h in Headers */,
0A9E917128DC7E7000FB4182 /* SentryInternalCDefines.h in Headers */,
63FE711F20DA4C1000CDBAE8 /* SentryCrashObjC.h in Headers */,
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/SentryScope.m
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,11 @@ - (NSDictionary *)buildTraceContext:(nullable id<SentrySpan>)span
}
}

- (NSString *)propagationContextTraceIdString
{
return [self.propagationContext.traceId sentryIdString];
}

@end

NS_ASSUME_NONNULL_END
1 change: 1 addition & 0 deletions Sources/Sentry/include/SentryPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#import "SentryNSDictionarySanitize.h"
#import "SentryProfiler+Private.h"
#import "SentryRandom.h"
#import "SentryScope+PrivateSwift.h"
#import "SentrySdkInfo.h"
#import "SentrySerialization.h"
#import "SentrySession.h"
Expand Down
14 changes: 14 additions & 0 deletions Sources/Sentry/include/SentryScope+PrivateSwift.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import "SentryScope.h"

NS_ASSUME_NONNULL_BEGIN

// Added to only expose a limited sub-set of internal API needed in the Swift layer.
@interface SentryScope ()

// This is a workaround to make the traceId available in the Swift layer.
// Can't expose the SentryId directly for some reason.
@property (nonatomic, readonly) NSString *propagationContextTraceIdString;

@end

NS_ASSUME_NONNULL_END
8 changes: 4 additions & 4 deletions Sources/Swift/Protocol/SentryLog.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
struct SentryLog: Codable {
let timestamp: Date
var traceId: SentryId
let traceId: SentryId
let level: SentryLog.Level
let body: String
let attributes: [String: SentryLog.Attribute]
Expand All @@ -19,18 +19,18 @@ struct SentryLog: Codable {
/// by the time processing completes, it is guaranteed to be a valid non-empty trace id.
init(
timestamp: Date,
traceId: SentryId? = nil,
traceId: SentryId,
level: SentryLog.Level,
body: String,
attributes: [String: SentryLog.Attribute],
severityNumber: Int? = nil
) {
self.timestamp = timestamp
self.traceId = traceId ?? SentryId.empty
self.traceId = traceId
self.level = level
self.body = body
self.attributes = attributes
self.severityNumber = severityNumber
self.severityNumber = severityNumber ?? level.toSeverityNumber()
}

init(from decoder: any Decoder) throws {
Expand Down
3 changes: 3 additions & 0 deletions Sources/Swift/Tools/SentryLogBatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import Foundation

private let client: SentryClient

let options: Options

@_spi(Private) public init(client: SentryClient) {
self.client = client
self.options = client.options
super.init()
}

Expand Down
39 changes: 32 additions & 7 deletions Sources/Swift/Tools/SentryLogger.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@_implementationOnly import _SentryPrivate

import Foundation

/// **EXPERIMENTAL** - A structured logging API for Sentry.
Expand Down Expand Up @@ -101,13 +103,36 @@ public final class SentryLogger: NSObject {
guard let batcher else {
return
}
let logAttributes = attributes.mapValues { SentryLog.Attribute(value: $0) }
let log = SentryLog(
timestamp: dateProvider.date(),
level: level,
body: body,
attributes: logAttributes

var logAttributes = attributes.mapValues { SentryLog.Attribute(value: $0) }
addDefaultAttributes(to: &logAttributes)

let propagationContextTraceIdString = hub.scope.propagationContextTraceIdString
let propagationContextTraceId = SentryId(uuidString: propagationContextTraceIdString)

batcher.add(
SentryLog(
timestamp: dateProvider.date(),
traceId: propagationContextTraceId,
level: level,
body: body,
attributes: logAttributes
)
)
batcher.add(log)
}

private func addDefaultAttributes(to attributes: inout [String: SentryLog.Attribute]) {
guard let batcher else {
return
}
attributes["sentry.sdk.name"] = .string(SentryMeta.sdkName)
attributes["sentry.sdk.version"] = .string(SentryMeta.versionString)
attributes["sentry.environment"] = .string(batcher.options.environment)
if let releaseName = batcher.options.releaseName {
attributes["sentry.release"] = .string(releaseName)
}
if let span = hub.scope.span {
attributes["sentry.trace.parent_span_id"] = .string(span.spanId.sentrySpanIdString)
}
}
}
2 changes: 1 addition & 1 deletion Tests/Perf/metrics-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ startupTimeTest:

binarySizeTest:
diffMin: 200 KiB
diffMax: 870 KiB
diffMax: 875 KiB
69 changes: 69 additions & 0 deletions Tests/SentryTests/Protocol/SentryLogTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,75 @@ final class SentryLogTests: XCTestCase {
}
""".utf8)

// MARK: - Severity Number Fallback Tests

func testConstructorWithExplicitSeverityNumber() throws {
let log = SentryLog(
timestamp: Date(),
traceId: SentryId(),
level: .info,
body: "Test message",
attributes: [:],
severityNumber: 99 // Explicit value different from level's default
)

XCTAssertEqual(log.severityNumber, 99, "Should use explicitly provided severity number")
XCTAssertEqual(log.level, .info)
}

func testConstructorWithoutSeverityNumberFallsBackToLevel() throws {
let log = SentryLog(
timestamp: Date(),
traceId: SentryId(),
level: .info,
body: "Test message",
attributes: [:]
// severityNumber not provided - should default to nil and fallback to level
)

XCTAssertEqual(log.severityNumber, 9, "Should derive severity number from info level")
XCTAssertEqual(log.level, .info)
}

func testConstructorWithNilSeverityNumberFallsBackToLevel() throws {
let log = SentryLog(
timestamp: Date(),
traceId: SentryId(),
level: .error,
body: "Error message",
attributes: [:],
severityNumber: nil // Explicitly nil
)

XCTAssertEqual(log.severityNumber, 17, "Should derive severity number from error level")
XCTAssertEqual(log.level, .error)
}

func testSeverityNumberFallbackForAllLevels() throws {
let testCases: [(SentryLog.Level, Int)] = [
(.trace, 1),
(.debug, 5),
(.info, 9),
(.warn, 13),
(.error, 17),
(.fatal, 21)
]

for (level, expectedSeverity) in testCases {
let log = SentryLog(
timestamp: Date(),
traceId: SentryId(),
level: level,
body: "Test message",
attributes: [:]
// severityNumber not provided
)

XCTAssertEqual(log.severityNumber, expectedSeverity,
"Level \(level) should derive severity number \(expectedSeverity)")
}
}

func testEncode() throws {
let data = try encodeToJSONData(data: log)
let json = try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any])
Expand Down
1 change: 1 addition & 0 deletions Tests/SentryTests/SentryLogBatcherTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class SentryLogBatcherTests: XCTestCase {
) -> SentryLog {
return SentryLog(
timestamp: Date(timeIntervalSince1970: 1_627_846_801),
traceId: SentryId.empty,
level: level,
body: body,
attributes: attributes
Expand Down
58 changes: 56 additions & 2 deletions Tests/SentryTests/SentryLoggerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,59 @@ final class SentryLoggerTests: XCTestCase {
XCTAssertEqual(logs[5].level, .fatal)
}

// MARK: - Default Attributes Tests

func testCaptureLog_AddsDefaultAttributes() {
fixture.options.environment = "test-environment"
fixture.options.releaseName = "1.0.0"

let span = fixture.hub.startTransaction(name: "Test Transaction", operation: "test-operation")
fixture.hub.scope.span = span

sut.info("Test log message")

let capturedLog = getLastCapturedLog()

// Verify default attributes were added to the log
XCTAssertEqual(capturedLog.attributes["sentry.sdk.name"]?.value as? String, SentryMeta.sdkName)
XCTAssertEqual(capturedLog.attributes["sentry.sdk.version"]?.value as? String, SentryMeta.versionString)
XCTAssertEqual(capturedLog.attributes["sentry.environment"]?.value as? String, "test-environment")
XCTAssertEqual(capturedLog.attributes["sentry.release"]?.value as? String, "1.0.0")
XCTAssertEqual(capturedLog.attributes["sentry.trace.parent_span_id"]?.value as? String, span.spanId.sentrySpanIdString)
}

func testCaptureLog_DoesNotAddNilDefaultAttributes() {
fixture.options.releaseName = nil

sut.info("Test log message")

let capturedLog = getLastCapturedLog()

XCTAssertNil(capturedLog.attributes["sentry.release"])
XCTAssertNil(capturedLog.attributes["sentry.trace.parent_span_id"])

// But should still have the non-nil defaults
XCTAssertEqual(capturedLog.attributes["sentry.sdk.name"]?.value as? String, SentryMeta.sdkName)
XCTAssertEqual(capturedLog.attributes["sentry.sdk.version"]?.value as? String, SentryMeta.versionString)
XCTAssertEqual(capturedLog.attributes["sentry.environment"]?.value as? String, fixture.options.environment)
}

func testCaptureLog_SetsTraceIdFromPropagationContext() {
fixture.options.experimental.enableLogs = true

let expectedTraceId = SentryId()
let propagationContext = SentryPropagationContext(trace: expectedTraceId, spanId: SpanId())

fixture.hub.scope.propagationContext = propagationContext

sut.info("Test log message with trace ID")

let capturedLog = getLastCapturedLog()

// Verify that the log's trace ID matches the one from the propagation context
XCTAssertEqual(capturedLog.traceId, expectedTraceId)
}

// MARK: - Helper Methods

private func assertLogCaptured(
Expand All @@ -292,8 +345,9 @@ final class SentryLoggerTests: XCTestCase {
XCTAssertEqual(capturedLog.body, expectedBody, "Log body mismatch", file: file, line: line)
XCTAssertEqual(capturedLog.timestamp, fixture.dateProvider.date(), "Log timestamp mismatch", file: file, line: line)

let numberOfDefaultAttributes = 4
// Compare attributes
XCTAssertEqual(capturedLog.attributes.count, expectedAttributes.count, "Attribute count mismatch", file: file, line: line)
XCTAssertEqual(capturedLog.attributes.count, expectedAttributes.count + numberOfDefaultAttributes, "Attribute count mismatch", file: file, line: line)

for (key, expectedAttribute) in expectedAttributes {
guard let actualAttribute = capturedLog.attributes[key] else {
Expand Down Expand Up @@ -322,7 +376,7 @@ final class SentryLoggerTests: XCTestCase {
let logs = fixture.batcher.addInvocations.invocations
guard let lastLog = logs.last else {
XCTFail("No logs captured")
return SentryLog(timestamp: Date(), level: .info, body: "", attributes: [:])
return SentryLog(timestamp: Date(), traceId: .empty, level: .info, body: "", attributes: [:])
}
return lastLog
}
Expand Down
Loading