Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a64ff19
Move SentryAsyncLogWrapper out of SentryLogC.h. Move `SenryLog.c` dec…
denrase Jun 20, 2025
807cb7e
Update visibility to private
denrase Jun 20, 2025
f630a8b
Rename SentryLog to SentryLogSwift
denrase Jun 20, 2025
8749842
Rename file
denrase Jun 20, 2025
e668737
remove unused SentryLogLevel in defines
denrase Jun 20, 2025
f441b96
Add log models and serialization/deserialization tests
denrase Jun 20, 2025
e8851ad
addadd cl entry
denrase Jun 20, 2025
4b2981c
Rename `SentrLogSwift` to SentrySDKLog
denrase Jun 24, 2025
3cd7e3b
remove duplicate imports
denrase Jun 24, 2025
d10a5c2
Rename SentryLogLevel to SentryLog.Level and restore previous ObjC Se…
denrase Jun 24, 2025
05a9399
Merge branch 'main' into feat/structured-logs-models
denrase Jun 24, 2025
cbb641c
Use new naming
denrase Jun 24, 2025
9b61085
rename files
denrase Jun 24, 2025
238bf17
Add docs link to toSeverityNumber()
denrase Jun 24, 2025
6074420
Rename SentryLogAttribute to SentryLog.Attribute and change it to an …
denrase Jun 24, 2025
39c2e9e
Add encodeToJSONData helper with correct date encoding strategy
denrase Jun 24, 2025
6cd8671
Fix failing SentrySDKLogTests
denrase Jun 24, 2025
dddb9dd
run clang format
denrase Jun 24, 2025
d36a441
run swiftlint —fix
denrase Jun 24, 2025
ad20321
remove new sentry log classes
denrase Jun 24, 2025
1d89693
Revert "remove new sentry log classes"
denrase Jun 24, 2025
c5a365e
update cl
denrase Jun 25, 2025
50fa3e3
Merge branch 'main' into feat/structured-logs-models
denrase Jun 26, 2025
ddf7abf
fix cl
denrase Jun 26, 2025
c461f7f
cleanup
denrase Jun 26, 2025
43f0736
format
denrase Jun 26, 2025
3eeb816
Merge branch 'main' into feat/structured-logs-models
denrase Jun 27, 2025
bfe2a69
fix project file
denrase Jun 27, 2025
a06a3da
Merge branch 'main' into feat/structured-logs-models
denrase Jun 30, 2025
f3a910e
fix cl
denrase Jun 30, 2025
005e3c6
Merge branch 'main' into feat/structured-logs-models
denrase Jul 1, 2025
ad54f0b
remove cl entry
denrase Jul 1, 2025
542a919
add sev tests
denrase Jul 1, 2025
0e65135
use XCTUnwrap instead of XCTAssertNotNil
denrase Jul 1, 2025
605f164
make encode funcs internal
denrase Jul 1, 2025
548eb4e
remove separate codable files
denrase Jul 1, 2025
0484a04
dont use forece unwraps in tests
denrase Jul 1, 2025
5a8661a
use double instead of
denrase Jul 1, 2025
124d3e3
run swift lint fix
denrase Jul 1, 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
24 changes: 24 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -792,10 +792,16 @@
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 */; };
92B6BDA92E05B8F600D538B3 /* SentryLogLevelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B6BDA82E05B8F000D538B3 /* SentryLogLevelTests.swift */; };
92B6BDAD2E05B9FB00D538B3 /* SentryLogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B6BDAC2E05B9F700D538B3 /* SentryLogTests.swift */; };
92D957732E05A44600E20E66 /* SentryAsyncLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 92D957722E05A44600E20E66 /* SentryAsyncLog.m */; };
92D957772E05A4F300E20E66 /* SentryAsyncLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 92D957762E05A4F300E20E66 /* SentryAsyncLog.h */; };
92E5F3D62CDBB3BF00B7AD98 /* SentrySampling.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8C57A525EEFC42001CEEFA /* SentrySampling.h */; };
92ECD7202E05A7DF0063EC10 /* SentryLogC.h in Headers */ = {isa = PBXBuildFile; fileRef = D8AE48B12C5786AA0092A2A6 /* SentryLogC.h */; settings = {ATTRIBUTES = (Private, ); }; };
92ECD73C2E05ACE00063EC10 /* SentryLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */; };
92ECD73E2E05AD320063EC10 /* SentryLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */; };
92ECD7402E05AD580063EC10 /* SentryLogAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD73F2E05AD500063EC10 /* SentryLogAttribute.swift */; };
92ECD7482E05B57C0063EC10 /* SentryLogAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECD7472E05B5760063EC10 /* SentryLogAttributeTests.swift */; };
92F6726B29C8B7B100BFD34D /* SentryUser+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F6726A29C8B7B000BFD34D /* SentryUser+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
A811D867248E2770008A41EA /* SentrySystemEventBreadcrumbsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A811D866248E2770008A41EA /* SentrySystemEventBreadcrumbsTest.swift */; };
A839D89824864B80003B7AFD /* SentrySystemEventBreadcrumbs.h in Headers */ = {isa = PBXBuildFile; fileRef = A839D89724864B80003B7AFD /* SentrySystemEventBreadcrumbs.h */; };
Expand Down Expand Up @@ -2025,8 +2031,14 @@
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>"; };
92B6BDA82E05B8F000D538B3 /* SentryLogLevelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogLevelTests.swift; sourceTree = "<group>"; };
92B6BDAC2E05B9F700D538B3 /* SentryLogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogTests.swift; sourceTree = "<group>"; };
92D957722E05A44600E20E66 /* SentryAsyncLog.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryAsyncLog.m; sourceTree = "<group>"; };
92D957762E05A4F300E20E66 /* SentryAsyncLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryAsyncLog.h; path = include/SentryAsyncLog.h; sourceTree = "<group>"; };
92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLog.swift; sourceTree = "<group>"; };
92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogLevel.swift; sourceTree = "<group>"; };
92ECD73F2E05AD500063EC10 /* SentryLogAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogAttribute.swift; sourceTree = "<group>"; };
92ECD7472E05B5760063EC10 /* SentryLogAttributeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogAttributeTests.swift; sourceTree = "<group>"; };
92F6726A29C8B7B000BFD34D /* SentryUser+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryUser+Private.h"; path = "include/HybridPublic/SentryUser+Private.h"; sourceTree = "<group>"; };
A811D866248E2770008A41EA /* SentrySystemEventBreadcrumbsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySystemEventBreadcrumbsTest.swift; sourceTree = "<group>"; };
A839D89724864B80003B7AFD /* SentrySystemEventBreadcrumbs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySystemEventBreadcrumbs.h; path = include/SentrySystemEventBreadcrumbs.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3233,6 +3245,9 @@
7B3D0474249A3D5800E106B6 /* Protocol */ = {
isa = PBXGroup;
children = (
92B6BDAC2E05B9F700D538B3 /* SentryLogTests.swift */,
92ECD7472E05B5760063EC10 /* SentryLogAttributeTests.swift */,
92B6BDA82E05B8F000D538B3 /* SentryLogLevelTests.swift */,
D46712612DCD059500D4074A /* SentryRedactDefaultOptionsTests.swift */,
620078762D3906AD0022CB67 /* Codable */,
7BC6EBF7255C05060059822A /* TestData.swift */,
Expand Down Expand Up @@ -4473,6 +4488,9 @@
isa = PBXGroup;
children = (
620078752D38F1110022CB67 /* Codable */,
92ECD73F2E05AD500063EC10 /* SentryLogAttribute.swift */,
92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */,
92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */,
);
path = Protocol;
sourceTree = "<group>";
Expand Down Expand Up @@ -5346,6 +5364,7 @@
63EED6C02237923600E02400 /* SentryOptions.m in Sources */,
620078722D38F00D0022CB67 /* SentryGeoCodable.swift in Sources */,
D8CB741B2947286500A5F964 /* SentryEnvelopeItemHeader.m in Sources */,
92ECD73C2E05ACE00063EC10 /* SentryLog.swift in Sources */,
D8CB7417294724CC00A5F964 /* SentryEnvelopeAttachmentHeader.m in Sources */,
D84793262788737D00BE8E99 /* SentryByteCountFormatter.m in Sources */,
63AA769E1EB9C57A00D153DE /* SentryError.mm in Sources */,
Expand Down Expand Up @@ -5532,6 +5551,8 @@
6344DDB11EC308E400D9160D /* SentryCrashInstallationReporter.m in Sources */,
84BA62272CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift in Sources */,
D85596F3280580F10041FF8B /* SentryScreenshotIntegration.m in Sources */,
92ECD73E2E05AD320063EC10 /* SentryLogLevel.swift in Sources */,
92ECD7402E05AD580063EC10 /* SentryLogAttribute.swift in Sources */,
7BAF3DCE243DCBFE008A5414 /* SentryTransportFactory.m in Sources */,
844EDC70294143B900C86F34 /* SentryNSProcessInfoWrapper.mm in Sources */,
7D5C441A237C2E1F00DAB0A3 /* SentrySDK.m in Sources */,
Expand Down Expand Up @@ -5652,7 +5673,9 @@
D82915632C85EF0C00A6CDD4 /* SentryViewPhotographerTests.swift in Sources */,
D8DBE0CA2C0E093000FAB1FD /* SentryTouchTrackerTests.swift in Sources */,
D8F67AF42BE10F9600C9197B /* SentryUIRedactBuilderTests.swift in Sources */,
92ECD7482E05B57C0063EC10 /* SentryLogAttributeTests.swift in Sources */,
63B819141EC352A7002FDF4C /* SentryInterfacesTests.m in Sources */,
92B6BDA92E05B8F600D538B3 /* SentryLogLevelTests.swift in Sources */,
62F05D2B2C0DB1F100916E3F /* SentryLogTestHelper.m in Sources */,
7B68345128F7EB3D00FB7064 /* SentryMeasurementUnitTests.swift in Sources */,
7B14089A248791660035403D /* SentryCrashStackEntryMapperTests.swift in Sources */,
Expand Down Expand Up @@ -5866,6 +5889,7 @@
8F73BC312B02B87E00C3CEF4 /* SentryInstallationTests.swift in Sources */,
7B569E002590EEF600B653FC /* SentryScope+Equality.m in Sources */,
D8BFE37929A76666002E73F3 /* SentryTimeToDisplayTrackerTest.swift in Sources */,
92B6BDAD2E05B9FB00D538B3 /* SentryLogTests.swift in Sources */,
D84541182A2DC2CD00E2B11C /* SentryBinaryImageCacheTests.swift in Sources */,
7BF536D424BEF255004FA6A2 /* SentryAssertions.swift in Sources */,
7BC6EC14255C415E0059822A /* SentryExceptionTests.swift in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions Sources/Swift/Protocol/Codable/SentryCodable.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
@_implementationOnly import _SentryPrivate
import Foundation

func encodeToJSONData<T: Encodable>(data: T) throws -> Data {
let jsonEncoder = JSONEncoder()
jsonEncoder.dateEncodingStrategy = .secondsSince1970
return try jsonEncoder.encode(data)
}

func decodeFromJSONData<T: Decodable>(jsonData: Data) -> T? {
if jsonData.isEmpty {
return nil
Expand Down
67 changes: 67 additions & 0 deletions Sources/Swift/Protocol/SentryLog.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
struct SentryLog: Codable {
let timestamp: Date
var traceId: SentryId
let level: SentryLog.Level
let body: String
let attributes: [String: SentryLog.Attribute]
let severityNumber: Int?

private enum CodingKeys: String, CodingKey {
case timestamp
case traceId = "trace_id"
case level
case body
case attributes
case severityNumber = "severity_number"
}

/// The traceId is initially an empty default value and is populated during processing;
/// by the time processing completes, it is guaranteed to be a valid non-empty trace id.
init(
timestamp: Date,
traceId: SentryId? = nil,
level: SentryLog.Level,
body: String,
attributes: [String: SentryLog.Attribute],
severityNumber: Int? = nil
) {
self.timestamp = timestamp
self.traceId = traceId ?? SentryId.empty
self.level = level
self.body = body
self.attributes = attributes
self.severityNumber = severityNumber
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let timestamp = try container.decode(Date.self, forKey: .timestamp)
let traceIdString = try container.decode(String.self, forKey: .traceId)
let traceId = SentryId(uuidString: traceIdString)
let level = try container.decode(SentryLog.Level.self, forKey: .level)
let body = try container.decode(String.self, forKey: .body)
let attributes = try container.decode([String: SentryLog.Attribute].self, forKey: .attributes)
let severityNumber = try container.decodeIfPresent(Int.self, forKey: .severityNumber)

self.init(
timestamp: timestamp,
traceId: traceId,
level: level,
body: body,
attributes: attributes,
severityNumber: severityNumber
)
}

func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(timestamp, forKey: .timestamp)
try container.encode(traceId.sentryIdString, forKey: .traceId)
try container.encode(level, forKey: .level)
try container.encode(body, forKey: .body)
try container.encode(attributes, forKey: .attributes)
try container.encodeIfPresent(severityNumber, forKey: .severityNumber)
}
}
66 changes: 66 additions & 0 deletions Sources/Swift/Protocol/SentryLogAttribute.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
extension SentryLog {
enum Attribute: Codable {
case string(String)
case boolean(Bool)
case integer(Int)
case double(Double)

var type: String {
switch self {
case .string: return "string"
case .boolean: return "boolean"
case .integer: return "integer"
case .double: return "double"
}
}

var value: 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
}
}

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

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let type = try container.decode(String.self, forKey: .type)
switch type {
case "string":
self = .string(try container.decode(String.self, forKey: .value))
case "boolean":
self = .boolean(try container.decode(Bool.self, forKey: .value))
case "integer":
self = .integer(try container.decode(Int.self, forKey: .value))
case "double":
self = .double(try container.decode(Double.self, forKey: .value))
default:
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "Unknown type: \(type)")
}
}

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)
}
}
}
}
28 changes: 28 additions & 0 deletions Sources/Swift/Protocol/SentryLogLevel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
extension SentryLog {
enum Level: String, Codable {
case trace
case debug
case info
case warn
case error
case fatal

// Docs: https://develop.sentry.dev/sdk/telemetry/logs/#log-severity-number
func toSeverityNumber() -> Int {
switch self {
case .trace:
return 1
case .debug:
return 5
case .info:
return 9
case .warn:
return 13
case .error:
return 17
case .fatal:
return 21
}
}
}
}
27 changes: 27 additions & 0 deletions Tests/SentryTests/Protocol/Codable/SentryCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,33 @@ import XCTest

class SentryCodableTests: XCTestCase {

func testEncodeToJSONData_EncodesCorrectDateFormat() throws {
let date = Date(timeIntervalSince1970: 1_234_567_890.987654)
let jsonData = try encodeToJSONData(data: date)
let json = String(data: jsonData, encoding: .utf8)
XCTAssertEqual("1234567890.987654", json)
}

func testDecode_DecodesFromCorrectDateFormat() throws {
let json = "\"2009-02-13T23:31:30.000Z\""
guard let jsonData = json.data(using: .utf8) else {
XCTFail("Could not convert json string to data.")
return
}
let date: Date? = decodeFromJSONData(jsonData: jsonData)
XCTAssertEqual(date, Date(timeIntervalSince1970: 1_234_567_890))
}

func testDecode_DecodesFromCorrectDateTimestampFormat() throws {
let json = "1234567890.987654"
guard let jsonData = json.data(using: .utf8) else {
XCTFail("Could not convert json string to data.")
return
}
let date: Date? = decodeFromJSONData(jsonData: jsonData)
XCTAssertEqual(date, Date(timeIntervalSince1970: 1_234_567_890.987654))
}

func testDecodeWithEmptyData_ReturnsNil() {
XCTAssertNil(decodeFromJSONData(jsonData: Data()) as Geo?)
}
Expand Down
Loading
Loading