@@ -255,63 +255,73 @@ class DefaultBenchmarkSpanPrinter(
255255 /* *
256256 * Calculate statistical metrics (average, percentiles) for all status entries across the batch of spans.
257257 *
258- * For each unique status name found across all spans, this method:
258+ * For each status occurrence found across all spans (including duplicates) , this method:
259259 * 1. Collects timing values (time since previous status, time since start) from all spans
260260 * 2. Calculates configured statistical metrics (e.g., Avg, P50, P75, P90)
261261 * 3. Returns the results sorted by the first configured metric's time since start value
262262 *
263+ * Note: If the same status name appears multiple times, each occurrence is tracked separately
264+ * with an enumeration (e.g., "status [1]", "status [2]").
265+ *
263266 * @param spans List of spans to analyze (all spans should have the same span name)
264267 *
265- * @return List of statistical data for each unique status, sorted by the first configured metric's time since start
268+ * @return List of statistical data for each status occurrence , sorted by the first configured metric's time since start
266269 */
267270 private fun calculateStatistics (spans : List <IBenchmarkSpan >): List <StatisticalStatusData > {
268271 if (spans.isEmpty()) return emptyList()
269272
270- // Collect all unique status names across all spans
271- val allStatusNames = mutableSetOf<String >()
273+ // Build a map of status position -> (display name, list of timing data)
274+ // We need to track each occurrence separately across all spans
275+ data class StatusOccurrence (val statusName : String , val occurrenceIndex : Int )
276+ val statusOccurrencesMap = mutableMapOf<StatusOccurrence , MutableList <Pair <Long , Long >>>() // Pair<timeSincePrevious, timeSinceStart>
277+
272278 for (span in spans) {
273- for ((statusName, _) in span.getStatuses()) {
274- allStatusNames.add(statusName)
275- }
276- }
279+ val statuses = span.getStatuses()
280+ val startTime = span.getStartTimeInNanoSeconds()
277281
278- val result = mutableListOf<StatisticalStatusData >()
282+ // Track how many times we've seen each status name in this span
283+ val statusCounts = mutableMapOf<String , Int >()
279284
280- for (statusName in allStatusNames) {
281- val timeSincePreviousValues = mutableListOf<Long >()
282- val timeSinceStartValues = mutableListOf<Long >()
285+ statuses.forEachIndexed { statusIndex, (statusName, timestamp) ->
286+ // Determine which occurrence this is (1st, 2nd, 3rd, etc.)
287+ val occurrenceIndex = statusCounts.getOrDefault(statusName, 0 ) + 1
288+ statusCounts[statusName] = occurrenceIndex
283289
284- for (span in spans) {
285- val statuses = span.getStatuses()
286- val startTime = span.getStartTimeInNanoSeconds()
290+ val timeSinceStartMs = TimeUnit .NANOSECONDS .toMillis(timestamp - startTime)
287291
288- // Find this status in the span
289- val statusIndex = statuses.indexOfFirst { it.first == statusName }
290- if (statusIndex >= 0 ) {
291- val entry = statuses[statusIndex]
292- val timeSinceStartMs = TimeUnit .NANOSECONDS .toMillis(entry.second - startTime)
292+ val previousTime = if (statusIndex > 0 ) {
293+ statuses[statusIndex - 1 ].second
294+ } else {
295+ startTime
296+ }
297+ val timeSincePreviousMs = TimeUnit .NANOSECONDS .toMillis(timestamp - previousTime)
293298
294- val previousTime = if (statusIndex > 0 ) {
295- statuses[statusIndex - 1 ].second
296- } else {
297- startTime
298- }
299- val timeSincePreviousMs = TimeUnit .NANOSECONDS .toMillis(entry.second - previousTime)
299+ val occurrence = StatusOccurrence (statusName, occurrenceIndex)
300+ statusOccurrencesMap.getOrPut(occurrence) { mutableListOf () }
301+ .add(Pair (timeSincePreviousMs, timeSinceStartMs))
302+ }
303+ }
300304
301- timeSincePreviousValues.add(timeSincePreviousMs)
302- timeSinceStartValues.add(timeSinceStartMs)
303- }
305+ val result = mutableListOf<StatisticalStatusData >()
306+
307+ for ((occurrence, timingPairs) in statusOccurrencesMap) {
308+ val timeSincePreviousValues = timingPairs.map { it.first }
309+ val timeSinceStartValues = timingPairs.map { it.second }
310+
311+ // Create display name with occurrence number if there are multiple occurrences
312+ val displayName = if (statusOccurrencesMap.keys.count { it.statusName == occurrence.statusName } > 1 ) {
313+ " ${occurrence.statusName} [${occurrence.occurrenceIndex} ]"
314+ } else {
315+ occurrence.statusName
304316 }
305317
306- if (timeSincePreviousValues.isNotEmpty()) {
307- result.add(
308- StatisticalStatusData (
309- statusName = statusName,
310- timeSinceStartStats = calculateMetrics(timeSinceStartValues),
311- timeSincePreviousStats = calculateMetrics(timeSincePreviousValues)
312- )
318+ result.add(
319+ StatisticalStatusData (
320+ statusName = displayName,
321+ timeSinceStartStats = calculateMetrics(timeSinceStartValues),
322+ timeSincePreviousStats = calculateMetrics(timeSincePreviousValues)
313323 )
314- }
324+ )
315325 }
316326
317327 // Sort by the first configured metric's Time Since Start value, or by status name if no metrics
0 commit comments