Skip to content

Commit 223521f

Browse files
Merge pull request #2665 from DataDog/chore/rum-13731-improve-attribute-error-handling
RUM-13731 Improve Logs attribute encoding error handling Co-authored-by: mariedm <marie.denis@datadoghq.com>
2 parents c430fca + df4d2d4 commit 223521f

6 files changed

Lines changed: 500 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Unreleased
22

3+
- [IMPROVEMENT] Skip malformed Logs attributes individually instead of dropping the entire
4+
event, and log clear error messages. See [#2665][]
5+
36
# 3.6.0 / 28-01-2026
47

58
- [FEATURE] Add `DatadogProfiling` module to profile app launches. See [#2654][]
@@ -1032,7 +1035,8 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
10321035
[#2647]: https://github.com/DataDog/dd-sdk-ios/pull/2647
10331036
[#2640]: https://github.com/DataDog/dd-sdk-ios/pull/2640
10341037
[#2639]: https://github.com/DataDog/dd-sdk-ios/pull/2639
1035-
[#2654]: https://github.com/DataDog/dd-sdk-ios/pull/2654
1038+
[#2654]: https://github.com/DataDog/dd-sdk-ios/pull/2654
1039+
[#2665]: https://github.com/DataDog/dd-sdk-ios/pull/2665
10361040

10371041
[@00fa9a]: https://github.com/00FA9A
10381042
[@britton-earnin]: https://github.com/Britton-Earnin

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,10 @@
11481148
96D331ED2CFF740700649EE8 /* GraphicImagePrivacyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96D331EC2CFF740700649EE8 /* GraphicImagePrivacyTests.swift */; };
11491149
96E414142C2AF56F005A6119 /* UIProgressViewRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E414132C2AF56F005A6119 /* UIProgressViewRecorder.swift */; };
11501150
96E414162C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E414152C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift */; };
1151+
96E746A72F30E317006B3419 /* AttributeEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E746A62F30E317006B3419 /* AttributeEncoding.swift */; };
1152+
96E746A82F30E317006B3419 /* AttributeEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E746A62F30E317006B3419 /* AttributeEncoding.swift */; };
1153+
96E746AA2F30E535006B3419 /* AttributeEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E746A92F30E535006B3419 /* AttributeEncodingTests.swift */; };
1154+
96E746AB2F30E535006B3419 /* AttributeEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E746A92F30E535006B3419 /* AttributeEncodingTests.swift */; };
11511155
96E863722C9C547B0023BF78 /* SessionReplayOverrideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E863712C9C547B0023BF78 /* SessionReplayOverrideTests.swift */; };
11521156
96F25A822CC7EA4400459567 /* SessionReplayPrivacyOverrides+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F25A802CC7EA4300459567 /* SessionReplayPrivacyOverrides+objc.swift */; };
11531157
96F25A832CC7EA4400459567 /* UIView+SessionReplayPrivacyOverrides+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F25A812CC7EA4300459567 /* UIView+SessionReplayPrivacyOverrides+objc.swift */; };
@@ -3512,6 +3516,8 @@
35123516
96D331EC2CFF740700649EE8 /* GraphicImagePrivacyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicImagePrivacyTests.swift; sourceTree = "<group>"; };
35133517
96E414132C2AF56F005A6119 /* UIProgressViewRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewRecorder.swift; sourceTree = "<group>"; };
35143518
96E414152C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewRecorderTests.swift; sourceTree = "<group>"; };
3519+
96E746A62F30E317006B3419 /* AttributeEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeEncoding.swift; sourceTree = "<group>"; };
3520+
96E746A92F30E535006B3419 /* AttributeEncodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeEncodingTests.swift; sourceTree = "<group>"; };
35153521
96E863712C9C547B0023BF78 /* SessionReplayOverrideTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionReplayOverrideTests.swift; sourceTree = "<group>"; };
35163522
96E863752C9C7E800023BF78 /* DDSessionReplayOverridesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDSessionReplayOverridesTests.swift; sourceTree = "<group>"; };
35173523
96F25A802CC7EA4300459567 /* SessionReplayPrivacyOverrides+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionReplayPrivacyOverrides+objc.swift"; sourceTree = "<group>"; };
@@ -7147,6 +7153,7 @@
71477153
children = (
71487154
D23039CB298D5235001A1FA3 /* Attributes.swift */,
71497155
D23039CC298D5235001A1FA3 /* AttributesSanitizer.swift */,
7156+
96E746A62F30E317006B3419 /* AttributeEncoding.swift */,
71507157
);
71517158
path = Attributes;
71527159
sourceTree = "<group>";
@@ -7615,12 +7622,10 @@
76157622
D2D748342DC223EE00C61353 /* Trace */ = {
76167623
isa = PBXGroup;
76177624
children = (
7618-
D2C179E02DD2388800556F68 /* TraceCoreContext.swift */,
7619-
D2D748382DC2306100C61353 /* TraceID.swift */,
76207625
09A9369A2F0EB989000B6379 /* SamplingMechanismType.swift */,
76217626
09A9369B2F0EB989000B6379 /* SamplingPriority.swift */,
76227627
09A9369C2F0EB989000B6379 /* SpanContext.swift */,
7623-
D2C179E02DD2388800556F68 /* SpanCoreContext.swift */,
7628+
D2C179E02DD2388800556F68 /* TraceCoreContext.swift */,
76247629
D2D748392DC2306100C61353 /* SpanID.swift */,
76257630
D2D748382DC2306100C61353 /* TraceID.swift */,
76267631
);
@@ -7630,6 +7635,7 @@
76307635
D2DA238B298D588A00C6C7E6 /* DatadogInternalTests */ = {
76317636
isa = PBXGroup;
76327637
children = (
7638+
96E746A92F30E535006B3419 /* AttributeEncodingTests.swift */,
76337639
D26416B52A30E84F00BCD9F7 /* CoreRegistryTest.swift */,
76347640
D2D36DCA2AC6DCCA0021F28A /* DatadogCoreProtocolTests.swift */,
76357641
D2DA2397298D58F300C6C7E6 /* Codable */,
@@ -10307,6 +10313,7 @@
1030710313
960A0D3C2D6E2490004BB999 /* CustomDump.swift in Sources */,
1030810314
960A0D3D2D6E2490004BB999 /* ReflectionMirror.swift in Sources */,
1030910315
D2BEEDB52B3360820065F3AC /* URLSessionSwizzler.swift in Sources */,
10316+
96E746A82F30E317006B3419 /* AttributeEncoding.swift in Sources */,
1031010317
D2EBEE2129BA160F00B15732 /* W3CHTTPHeaders.swift in Sources */,
1031110318
6167E6F62B81E94C00C3CA2D /* DDThread.swift in Sources */,
1031210319
D2BEEDAC2B3356710065F3AC /* URLSessionTaskSwizzler.swift in Sources */,
@@ -11506,6 +11513,7 @@
1150611513
D2DA2358298D57AA00C6C7E6 /* CoreLogger.swift in Sources */,
1150711514
D2160CA329C0DE5700FAA9A5 /* NetworkInstrumentationFeature.swift in Sources */,
1150811515
D2EBEE2D29BA161100B15732 /* HTTPHeadersReader.swift in Sources */,
11516+
96E746A72F30E317006B3419 /* AttributeEncoding.swift in Sources */,
1150911517
D24EC3DA2DD1F117007A7E8F /* SessionReplayCoreContext.swift in Sources */,
1151011518
E2AA55E82C32C6D9002FEF28 /* ApplicationNotifications.swift in Sources */,
1151111519
D22789362D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */,
@@ -11680,6 +11688,7 @@
1168011688
3CCECDB22BC68A0A0013C125 /* SpanIDTests.swift in Sources */,
1168111689
D2181A8E2B051B7900A518C0 /* URLSessionSwizzlerTests.swift in Sources */,
1168211690
D2A783DA29A530EF003B03BB /* SwiftExtensionsTests.swift in Sources */,
11691+
96E746AA2F30E535006B3419 /* AttributeEncodingTests.swift in Sources */,
1168311692
D2D36DCB2AC6DCCA0021F28A /* DatadogCoreProtocolTests.swift in Sources */,
1168411693
D2160CD429C0DF6700FAA9A5 /* NetworkInstrumentationFeatureTests.swift in Sources */,
1168511694
D263BCB629DB014900FA0E21 /* TimeInterval+ConvenienceTests.swift in Sources */,
@@ -11694,6 +11703,7 @@
1169411703
buildActionMask = 2147483647;
1169511704
files = (
1169611705
D2BEEDB02B335C400065F3AC /* URLSessionTaskSwizzlerTests.swift in Sources */,
11706+
96E746AB2F30E535006B3419 /* AttributeEncodingTests.swift in Sources */,
1169711707
D26416B72A30E84F00BCD9F7 /* CoreRegistryTest.swift in Sources */,
1169811708
61F3E3672BC595F600C7881E /* HTTPHeadersReaderTests.swift in Sources */,
1169911709
D2EBEE4029BA163F00B15732 /* B3HTTPHeadersWriterTests.swift in Sources */,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
9+
/// Context for attribute encoding, used to provide clearer error messages to customers.
10+
public enum AttributeEncodingContext {
11+
/// User-provided custom attribute
12+
case custom
13+
/// User info extra attribute (usr.*)
14+
case userInfo
15+
/// Account info extra attribute (account.*)
16+
case accountInfo
17+
/// Internal SDK attribute
18+
case `internal`
19+
20+
var errorMessagePrefix: String {
21+
switch self {
22+
case .custom:
23+
return ""
24+
case .userInfo:
25+
return "user info "
26+
case .accountInfo:
27+
return "account "
28+
case .internal:
29+
return "internal "
30+
}
31+
}
32+
}
33+
34+
public extension KeyedEncodingContainer {
35+
/// Encodes an attribute, catching and logging any encoding failures.
36+
/// If encoding fails, the attribute is skipped and an error is logged, but execution continues.
37+
/// This prevents a single malformed attribute from causing the entire event to be dropped.
38+
///
39+
/// - Parameters:
40+
/// - value: The encodable value to encode
41+
/// - key: The coding key for this attribute
42+
/// - attributeName: The name of the attribute as known by the customer (for error reporting)
43+
/// - context: The context of this attribute (custom, userInfo, accountInfo, or internal)
44+
mutating func encodeAttribute<T: Encodable>(
45+
_ value: T,
46+
forKey key: Key,
47+
attributeName: String,
48+
context: AttributeEncodingContext = .custom
49+
) {
50+
do {
51+
try encode(value, forKey: key)
52+
} catch {
53+
let contextPrefix = context.errorMessagePrefix
54+
DD.logger.error(
55+
"Failed to encode \(contextPrefix)attribute '\(attributeName)': \(error). This attribute will be dropped from the event."
56+
)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)