-
Notifications
You must be signed in to change notification settings - Fork 61
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
RUM-6129 Introduce the API usage telemetry event and API #2258
RUM-6129 Introduce the API usage telemetry event and API #2258
Conversation
9b196a9
to
cefb3c2
Compare
cefb3c2
to
94d452d
Compare
94d452d
to
e2bd3f2
Compare
message = message, | ||
additionalProperties = null | ||
) | ||
rumFeature?.sendEvent(bundleEventIntoTelemetry(telemetryEvent)) |
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.
Note
Since the event itself can be of type Any
, you could avoid the Map<…>
altogether and send the InternalTelemetryEvent
directly. Same for all the methods in this class.
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.
yes but then I will need an extra case in the onReceive
of the RumFeature
only for this so I would rather bundle it here.
batchUploadFrequency = configuration.coreConfig.uploadFrequency.baseStepMs, | ||
batchProcessingLevel = configuration.coreConfig.batchProcessingLevel.maxBatchesPerUploadJob | ||
) | ||
rumFeature.sendEvent( |
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.
Warning
This is another argument to send the event InternalTelemetryEvent
directly. Here you're not reusing the bundleEventIntoTelemetry()
function you made, meaning that any further change around the telemetry would need to happen in two places, which is error prone.
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.
hmmm actually I see your point also regarding the first comment. I don't know why I kept using the Map...we could just send the InternalTelemetryEvent
everywhere
apiUsageEvent: InternalTelemetryEvent.ApiUsage, | ||
samplingRate: Float | ||
) { | ||
println("AU [T]: ${apiUsageEvent::class.simpleName} | $samplingRate%") |
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.
Is it useful to log apiUsageEvent::class.simpleName
if AU
already stands for ApiUsage
?
*/ | ||
@InternalApi | ||
fun logApiUsage( | ||
apiUsageEvent: InternalTelemetryEvent.ApiUsage, |
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.
won't it cause any issues if we are adding the class from implementation
dependency to the class which is visible on the customer side via api
dependency type? any compilation/IDE issues?
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 didn't see any but will try to test it from the sample app
overwrite = forge.aBool(), | ||
noView = forge.aBool(), | ||
noActiveView = forge.aBool(), | ||
additionalProperties = forge.aMap { forge.aString() to forge.aString() }.toMutableMap() |
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.
minor: calls like forge.aMap
, etc. have a Forge
as lambda receiver, so it can be written as forge.aMap { aString() to aString() }
public static final field Companion Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Companion; | ||
public fun <init> (Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Dd;JLjava/lang/String;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Source;Ljava/lang/String;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Application;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Session;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$View;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Action;Ljava/util/List;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Telemetry;)V | ||
public synthetic fun <init> (Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Dd;JLjava/lang/String;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Source;Ljava/lang/String;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Application;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Session;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$View;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Action;Ljava/util/List;Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Telemetry;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
public final fun component1 ()Lcom/datadog/android/telemetry/model/CommonTelemetryProperties$Dd; |
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.
What bothers me is that by adding new data classes in our schema which have many params, we add so many useless destructuring methods which just grow binary footprint for nothing. Maybe there is a way to prevent them from being generated using some 3rd party plugins.
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 am going to have a look for something but not going to spend too much on 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.
yes, it is a side thought, for sure shouldn't be a part of this PR.
@@ -15,10 +15,6 @@ tasks.register( | |||
) { | |||
inputDirPath = "src/main/json/telemetry" | |||
targetPackageName = "com.datadog.android.telemetry.model" | |||
ignoredFiles = arrayOf( | |||
"_common-schema.json", | |||
"usage-schema.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.
blocking: you need to remove only usage-schema.json
from here. _common-schema
is already referenced and being used by individual event schema files.
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.
oh ok...thought it will need also the _common-schema
as usage-schema
is referencing it
@@ -298,6 +292,20 @@ internal class RumFeature( | |||
|
|||
// region Internal | |||
|
|||
private fun handleTelemetryEvent(event: Map<*, *>) { |
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.
probably with that change we can send class directly instead of using Map
? It will require a change of the check right at the beginning of onReceive
, but it is a minor thing.
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.
yup exactly what @xgouchet was pointing out also, I am going to apply the changes now.
val onlyOnce: Boolean = false, | ||
override val eventTime: Time = Time(), | ||
val isMetric: Boolean = false | ||
internal data class TelemetryEventWrapper( |
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.
we probably can keep the same name for the class - SendTelemetry
. but not a big deal
|
||
else -> null |
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.
shouldn't we log here?
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.
actually that's a sealed class so we don't really need the else
here anyway. In case we do not treat a case we will be warned by the editor.
) | ||
) | ||
} | ||
else -> null |
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.
we don't need else
here I think, because we are using sealed class. So if we remove else
here and add one more sub-type to InternalTelemetryEvent.ApiUsage
, without adding additional branch in when
, we will get a compilation error, which is for good.
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.
yup it is exactly the case in line 117
also so you are right
b76ea8a
to
9475ffd
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.
I added few comments/suggestions, but nothing blocking.
!is Map<*, *> -> { | ||
sdkCore.internalLogger.log( | ||
InternalLogger.Level.WARN, | ||
InternalLogger.Target.USER, | ||
{ UNKNOWN_EVENT_TYPE_PROPERTY_VALUE.format(Locale.US, event["type"]) } | ||
{ UNSUPPORTED_EVENT_TYPE.format(Locale.US, event::class.java.canonicalName) } | ||
) | ||
return | ||
} | ||
|
||
else -> when (event["type"]) { |
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 think it is better to swap these branches, and just do:
is Map<*, *> ->
else ->
reason: !is
is harder to read and then in else
it it takes some time to understand that type of the object there is Map
.
@@ -656,8 +596,8 @@ internal class RumFeature( | |||
internal const val LOG_ERROR_WITH_STACKTRACE_EVENT_MISSING_MANDATORY_FIELDS = | |||
"RUM feature received a log event with stacktrace" + | |||
" where mandatory message field is either missing or has a wrong type." | |||
internal const val TELEMETRY_MISSING_MESSAGE_FIELD = "RUM feature received a telemetry" + | |||
" event, but mandatory message field is either missing or has a wrong type." | |||
internal const val TELEMETRY_MISSING_EVENT_FIELD_WARNING_MESSAGE = "RUM feature received a telemetry" + |
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.
is it still needed?
val telemetryEvent: Any? = when (event.type) { | ||
TelemetryType.DEBUG -> { | ||
val timestamp = wrappedEvent.eventTime.timestamp + datadogContext.time.serverTimeOffsetMs | ||
val telemetryEvent: Any? = when (event) { |
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.
does it become exhaustive when
? The reason why I'm asking it because there are 2 sealed classes involved here, not only one. Will it be a compilation error if we add say InternalTelemetry.Log.XXX
into classes definition?
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 think the way it behaves is that whenever you add a new type of the sealed class it will warn you that is not handled but will double check it.
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.
yes, but you now have 2 sealed base classes in this hierarchy, not one, that is why I'm curious.
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.
Yeah, I confirm whenever you add a new type to that sealed class it is only to crash at compile time saying that you need to be exhaustive.
9475ffd
to
db9a4f1
Compare
db9a4f1
to
531b346
Compare
What does this PR do?
In this PR we are introducing the new
InternalLogger#logApisUsage
method which will be used to report usage of different APIs as Telemetry from our sdk. In order to achieve this we had to refactor and simplify the wholeTelemetry
related logic in the SDK to make it more scalable. For this we introduced the newInternalTelemetryEvent
which will be shared through out all the module from thedd-sdk-android-internal
module.Motivation
What inspired you to submit this pull request?
Additional Notes
Anything else we should know when reviewing?
Review checklist (to be filled by reviewers)