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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
yes, it is a side thought, for sure shouldn't be a part of this PR.
| targetPackageName = "com.datadog.android.telemetry.model" | ||
| ignoredFiles = arrayOf( | ||
| "_common-schema.json", | ||
| "usage-schema.json" |
There was a problem hiding this comment.
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.
oh ok...thought it will need also the _common-schema as usage-schema is referencing it
|
|
||
| // region Internal | ||
|
|
||
| private fun handleTelemetryEvent(event: Map<*, *>) { |
There was a problem hiding this comment.
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.
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.
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.
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.
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.
yup it is exactly the case in line 117 also so you are right
b76ea8a to
9475ffd
Compare
0xnm
left a comment
There was a problem hiding this comment.
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.
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.
| " 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" + |
| 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.
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.
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.
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.
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#logApisUsagemethod 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 wholeTelemetryrelated logic in the SDK to make it more scalable. For this we introduced the newInternalTelemetryEventwhich will be shared through out all the module from thedd-sdk-android-internalmodule.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)