Fix span parsing of startTime from OT JSON

Correctly parse startTime from OT JSON if span doesn't contain startTimeNano field

GitOrigin-RevId: 16e8b4fe02f437ef47ca914dc8105afcf85ec9d0
This commit is contained in:
Dmitrii Denisov
2025-07-14 17:59:42 +00:00
committed by intellij-monorepo-bot
parent b5230c0e59
commit b6ceefd050
3 changed files with 11865 additions and 11 deletions
@@ -8,7 +8,6 @@ import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonNames
import java.time.Instant
import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration
@@ -43,7 +42,8 @@ internal fun toSpanElement(span: SpanData): SpanElement {
isWarmup = isWarmup(tags),
name = span.operationName,
duration = span.durationNano ?: (span.duration.times(1000)),
startTimestamp = span.startTimeNano,
startTimestamp = span.startTimeNano ?: span.startTime?.let { Instant.ofEpochMilli(it / 1_000) }
?: throw IllegalStateException("startTime or startTimeNano should exists"),
spanId = span.spanID,
parentSpanId = span.getParentSpanId(),
tags = tags,
@@ -143,8 +143,8 @@ data class SpanData(
// see com.intellij.platform.diagnostic.telemetry.exporters.JaegerJsonSpanExporter.export
@Contextual val duration: Duration,
@Contextual val durationNano: Duration? = null,
@JsonNames("startTime")
@Contextual val startTimeNano: Instant,
@Contextual val startTimeNano: Instant? = null,
@Contextual val startTime: Long? = null,
@JvmField @Serializable(with = CachedReferencesListSerializer::class) val references: List<SpanRef> = emptyList(),
@JvmField @Serializable(with = CachedTagListSerializer::class) val tags: List<SpanTag> = emptyList(),
@@ -1,6 +1,7 @@
package com.intellij.tools.ide.metrics.collector
import com.intellij.tools.ide.metrics.collector.metrics.PerformanceMetrics.Metric
import com.intellij.tools.ide.metrics.collector.telemetry.OpentelemetrySpanJsonParser
import com.intellij.tools.ide.metrics.collector.telemetry.SpanFilter
import com.intellij.tools.ide.metrics.collector.telemetry.getMetricsBasedOnDiffBetweenSpans
import com.intellij.tools.ide.metrics.collector.telemetry.getMetricsForStartup
@@ -13,6 +14,7 @@ import org.junit.jupiter.api.Test
import java.nio.file.FileSystemAlreadyExistsException
import java.nio.file.FileSystems
import java.nio.file.Paths
import java.time.Instant
import kotlin.io.path.div
internal val openTelemetryReports by lazy {
@@ -29,6 +31,14 @@ internal val openTelemetryReports by lazy {
class OpenTelemetrySpanExtractionTest {
@Test
fun gatewayClientHostMetricsCollected() {
val file = (openTelemetryReports / "client-opentelemetry.json")
val result = OpentelemetrySpanJsonParser(SpanFilter.nameInList("rdct.station.discovery: scheduled", "rdct.gateway.LinkHandler.reporting.capabilities")).getSpanElements(file)
result.filter { it.startTimestamp == Instant.ofEpochMilli(1751546271647270 / 1000) && it.name == "rdct.station.discovery: scheduled" }.shouldHaveSize(1)
result.filter { it.startTimestamp == Instant.ofEpochMilli(1751546271719000 / 1000) && it.name == "rdct.gateway.LinkHandler.reporting.capabilities" }.shouldHaveSize(1)
}
@Test
fun startupMetricsCollected() {
val file = (openTelemetryReports / "startup.json")
@@ -313,13 +323,13 @@ class OpenTelemetrySpanExtractionTest {
(openTelemetryReports / "opentelemetry_with_warmup_spans.json"),
"localInspections", "GeneralHighlightingPass")
assertThat(metrics).containsAll(listOf(
Metric.newDuration("semanticHighlighting_1", 33),
Metric.newDuration("semanticHighlighting_2", 34),
Metric.newDuration("semanticHighlighting_3", 35),
Metric.newDuration("semanticHighlighting_4", 45),
Metric.newDuration("semanticHighlighting_5", 47),
Metric.newDuration("semanticHighlighting", 194),
Metric.newDuration("semanticHighlighting#mean_value", 38),
Metric.newDuration("semanticHighlighting_1", 347),
Metric.newDuration("semanticHighlighting_2", 337),
Metric.newDuration("semanticHighlighting_3", 342),
Metric.newDuration("semanticHighlighting_4", 349),
Metric.newDuration("semanticHighlighting_5", 350),
Metric.newDuration("semanticHighlighting", 1725),
Metric.newDuration("semanticHighlighting#mean_value", 345),
))
val find = metrics.find { it.id.name == "semanticHighlighting_6" }
assert(find == null) {