-
Notifications
You must be signed in to change notification settings - Fork 129
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
RUMM-3320 [V1] Fix APM local spans not correlating with RUM views #1355
RUMM-3320 [V1] Fix APM local spans not correlating with RUM views #1355
Conversation
Datadog ReportBranch report: ✅ |
9ca02e8
to
11c68d3
Compare
@@ -562,7 +562,7 @@ class LoggerTests: XCTestCase { | |||
|
|||
logMatchers.forEach { | |||
$0.assertValue( | |||
forKeyPath: RUMContextAttributes.IDs.sessionID, | |||
forKeyPath: "application_id", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is currently enforcing the schema used by logs backend.
I'm wondering if it would be preferable to use the output of the mapping function mapInternalAttributeKey
here instead of the actual expected key and leave that enforce to E2E tests.
Tests/DatadogTests/Datadog/WebView/WKUserContentController+DatadogTests.swift
Outdated
Show resolved
Hide resolved
@@ -72,7 +72,7 @@ internal struct TracingMessageReceiver: FeatureMessageReceiver { | |||
/// | |||
/// - Parameter context: The updated core context. | |||
private func update(context: DatadogContext) -> Bool { | |||
if let attributes: [String: String] = context.featuresAttributes["rum"]?.ids { | |||
if let attributes: [String: String?] = context.featuresAttributes["rum"]?.ids { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this change was needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because user_action.id
is an optional attribute, we wouldn't be able to read other context if casting to non-optional [String: String]
. There was a bug where this context was read by tracer:
[
"rum": [
"application.id": "...",
"session.id": "...",
"view.id": "...",
"user_action.id": "..."
]
]
but this wasn't:
[
"rum": [
"application.id": "...",
"session.id": "...",
"view.id": "..."
]
]
This surfaces the fragility of feature's context propagation in V2. Each feature sets this dictionary on one end and some features expect it on the other end. The communication channel is [String: Any]
dictionary held by core.
cefdd18
to
1013519
Compare
@@ -72,7 +72,7 @@ internal struct TracingMessageReceiver: FeatureMessageReceiver { | |||
/// | |||
/// - Parameter context: The updated core context. | |||
private func update(context: DatadogContext) -> Bool { | |||
if let attributes: [String: String] = context.featuresAttributes["rum"]?.ids { | |||
if let attributes: [String: String?] = context.featuresAttributes["rum"]?.ids { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because user_action.id
is an optional attribute, we wouldn't be able to read other context if casting to non-optional [String: String]
. There was a bug where this context was read by tracer:
[
"rum": [
"application.id": "...",
"session.id": "...",
"view.id": "...",
"user_action.id": "..."
]
]
but this wasn't:
[
"rum": [
"application.id": "...",
"session.id": "...",
"view.id": "..."
]
]
This surfaces the fragility of feature's context propagation in V2. Each feature sets this dictionary on one end and some features expect it on the other end. The communication channel is [String: Any]
dictionary held by core.
/// The ID of RUM application (`String`). | ||
internal static let applicationID = "application_id" | ||
internal static let applicationID = "application.id" | ||
|
||
/// The ID of current RUM session (standard UUID `String`, lowercased). | ||
/// In case the session is rejected (not sampled), RUM context is set to empty (`[:]`) in core. | ||
internal static let sessionID = "session_id" | ||
internal static let sessionID = "session.id" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocker 🚫/ This change break Session Replay, which expects "application_id"
and "session_id"
. With being unable to recognise these new values SR thinks there is no RUM, so it doesn't record anything.
- We need to update these values here.
- Also please update these values in comments of RUM dependency definition within SR module here - this definition is a central place where SR defines everything it expects or provides to other modules in SDK context.
I bump the need for 2 things:
- Integration Tests for SR: it is now possible on
feature/v2
- I created REPLAY-1863 - Integration Unit Tests: to validate feature's contract in unit tests - @maxep do we have JIRA for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integration Tests for SR: it is now possible on
feature/v2
If we base this on feature/v2
, we won't be able to release it as part of the next 1.x
. So I don't think we should block this on SR integration tests, especially since it was reported by users. We should update the values in SR but we can't add integration tests with just yet.
Integration Unit Tests: to validate feature's contract in unit tests - @maxep do we have JIRA for this?
We don't, currently integration units are part of DatadogTests
target. But I will create a ticket to migrate them 👍
internal func mapInternalAttributeKey(_ originalAttribute: String) -> String { | ||
switch originalAttribute { | ||
case "application.id": | ||
return "application_id" | ||
case "session.id": | ||
return "session_id" | ||
default: | ||
return originalAttribute | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion/ Could we declare constants for "application.id"
and "session.id"
as part of RUMDependency
namespace defined in Logging
module? Similar to how we define RUMDependency
in Session Replay - single place with listing all attributes that features exchange between each other.
cc @maxep
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we work with V1 code (no modules yet), it would have to be enum LogsRUMDependency
(or similar) to scope it properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having constants makes sense, should be defined alongside the message receiver IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Message receiver can be a good place to define "received" attributes. Where to define "provided" attributes?
Looking at mentioned RUMDependency in SR there are two parts:
internal enum RUMDependency {
// MARK: Contract from RUM to SR:
static let rumBaggageKey = "rum"
static let serverTimeOffsetKey = "server_time_offset"
static let ids = "ids"
// MARK: Contract from SR to RUM (mirror of `SessionReplayDependency` in RUM):
static let srBaggageKey = "session-replay"
static let hasReplay = "has_replay"
}
and I believe it is good and clear enough - single place describing the whole contract.
If we want to separate it, it's OK for me as long as it is clear and consistent across all modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and I believe it is good and clear enough - single place describing the whole contract.
Yep, totally. Just to add my 2 cents: I don't think we should enforce any convention across modules on that. Some Features will use/share a lot of context values, some won't. We can have some flexibility on how Features are implemented internally IMHO
internal func mapInternalTags(_ originalTag: String) -> String { | ||
switch originalTag { | ||
case "application.id": | ||
return "_dd.application.id" | ||
case "session.id": | ||
return "_dd.session.id" | ||
case "view.id": | ||
return "_dd.view.id" | ||
case "user_action.id": | ||
return "_dd.action.id" | ||
default: | ||
return originalTag | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion/ Similar ask to the one in Logging:
suggestion/ Could we declare constants for
"application.id"
and"session.id"
as part ofRUMDependency
namespace defined inTracing
module? Similar to how we defineRUMDependency
in Session Replay - single place with listing all attributes that features exchange between each other.
cc @maxep
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the constants are shared eg "application_id", declaring them each module (Tracing, Session Replay etc) seems error prone. IMO, it should go atleast in Internal module which is shared by all other modules.
Not related to this, but having an API package for each feature makes these things easier to maintain, where there could be RUMApi
module with plain structs, APIs and constants (ideal world) which can be taken as dependency by others without all the baggage of RUM.
1013519
to
bf28ee9
Compare
Datadog ReportBranch report: ❄️ New Flaky Tests (3)
|
bf28ee9
to
ccd6149
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxep || @ganeshnj I pushed this topic forward and now it is ready for review.
- Only fixing Session Replay compared to previous work (the change here has low impact as we don't provide SR in V1 except private beta branch, but I did it anyway).
- Like I mentioned on Slack, this is only minimal change to make it work.
- We're fixing V1 here - V2 fix is in RUMM-3320 fix: [V2] Fix APM local spans not correlating with RUM views #1375
- This PR is targeting
release/1.22.0
branch - I will do release after merge.
CI is green, Bitrise checks are false:
@@ -39,6 +39,12 @@ private class DebugRUMSessionViewModel: ObservableObject { | |||
@Published var errorMessage: String = "" | |||
@Published var resourceKey: String = "" | |||
|
|||
@Published var logMessage: String = "" | |||
@Published var spanOperationName: String = "" | |||
@Published var instrumentedRequestURL: String = "https://api.shopist.io/checkout.json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently it returns
{
"status": 404,
"error": "Not Found"
}
expected ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is POST
endpoint (GET
always returns 404
), but getting some % of errors on it is expected as this is part of demo environment 👍.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! I left a question.
What and why?
APM <-> RUM correlation was not working for local traces due to mismatch in span tag format between the SDK and APM Backend.
fix: #1292
How?
This PR removes the direct use of
RUMContextAttributes
in both Log and Span encoding.Instead, a mapping layer is added that transforms keys into the required format for each product.
On the Example app, changes are made to allow enabling a simple debugging option. It's now possible to single a single log, a single span, and a single traced request on a particular RUM session.
Review checklist
Custom CI job configuration (optional)