Skip to content

Conversation

@omkarK06
Copy link
Contributor

@omkarK06 omkarK06 commented Oct 13, 2025

PR Type

Bug fix, Enhancement


Description

  • Expand trace time window ±10s

  • Use selected stream in metrics

  • Unify metrics queries placeholders

  • Minor template formatting fixes


Diagram Walkthrough

flowchart LR
  UI["TraceDetails.vue UI"]
  TimeWin["±10s time window"]
  MetricsDash["TracesMetricsDashboard.vue"]
  MetricsJSON["metrics.json queries"]
  StreamSel["Selected stream injected"]

  UI -- fetch traces/details --> TimeWin
  MetricsDash -- replace [STREAM_NAME] --> StreamSel
  MetricsDash -- apply WHERE --> MetricsJSON
  MetricsJSON -- updated SQL & fields --> MetricsDash
Loading

File Walkthrough

Relevant files
Bug fix
TraceDetails.vue
Add time padding and tidy template                                             

web/src/plugins/traces/TraceDetails.vue

  • Add ±10s padding to trace search
  • Add ±10s padding to trace details fetch
  • Minor template formatting and structure cleanup
+267/-259
Enhancement
TracesMetricsDashboard.vue
Use selected stream in metrics queries                                     

web/src/plugins/traces/metrics/TracesMetricsDashboard.vue

  • Inject selected stream into query via [STREAM_NAME]
  • Preserve and apply WHERE_CLAUSE replacement
+7/-0     
metrics.json
Unify metrics SQL and field mappings                                         

web/src/plugins/traces/metrics/metrics.json

  • Replace default stream with [STREAM_NAME] placeholder
  • Unify queries for duration, rate, error rate
  • Update field aliases to new columns
+11/-11 

@github-actions github-actions bot added ☢️ Bug Something isn't working Review effort 2/5 labels Oct 13, 2025
@github-actions
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

The replacement of [STREAM_NAME] injects a quoted value, but queries use [STREAM_NAME] in FROM clause. Confirm the engine expects a quoted stream identifier vs table name; double quoting may break queries if the engine already adds quotes or expects no quotes.

convertedDashboard.tabs[0].panels[index]["queries"][0].query = panel[
  "queries"
][0].query.replace(
  "[STREAM_NAME]",
  `"${searchObj.data.stream.selectedStream.value}"`,
);
UX/Performance

Expanding time range by ±10s for both search and details may increase data volume. Validate that pagination/size=1 and downstream components handle larger windows without noticeable lag; consider guarding against negative start_time and large 'to' overflow.

  start_time: Number(router.currentRoute.value.query.from) - 10000,
  end_time: Number(router.currentRoute.value.query.to) + 10000,
  filter: filter || "",
  size: 1,
  from: 0,
  stream_name: streamName,
})
.then(async (res: any) => {
  const trace = getTracesMetaData(res.data.hits)[0];
  if (!trace) {
    showTraceDetailsError();
    return;
  }
  searchObj.data.traceDetails.selectedTrace = trace;

  let startTime = Number(router.currentRoute.value.query.from);
  let endTime = Number(router.currentRoute.value.query.to);
  if (
    res.data.hits.length === 1 &&
    res.data.hits[0].start_time &&
    res.data.hits[0].end_time
  ) {
    startTime = Math.floor(res.data.hits[0].start_time / 1000);
    endTime = Math.ceil(res.data.hits[0].end_time / 1000);

    // If the trace is not in the current time range, update the time range
    if (
      !(
        startTime >= Number(router.currentRoute.value.query.from) &&
        endTime <= Number(router.currentRoute.value.query.to)
      )
    ) {
      updateUrlQueryParams({
        from: startTime,
        to: endTime,
      });
    }
  }

  getTraceDetails({
    stream: streamName,
    trace_id: trace.trace_id,
    from: startTime - 10000,
    to: endTime + 10000,
  });
Metric Semantics

Panels now expose aliases max_trace_duration, max_rate, error_rate. Verify panel field mappings, legends, and aggregations align with intended semantics (e.g., summing rate vs counting traces) and that HAVING placeholders are supported in all queries.

      "query": "WITH base_traces AS ( SELECT min(_timestamp) AS _time, max(duration) AS max_duration, count(distinct(trace_id)) as rate, SUM(CASE WHEN span_status='ERROR' then 1 else 0 end) as error FROM [STREAM_NAME] [WHERE_CLAUSE] GROUP BY trace_id ) SELECT histogram(_time) AS time_bucket, max(max_duration) AS max_trace_duration, sum(rate) AS max_rate, max(error) AS error_rate FROM base_traces GROUP BY time_bucket [HAVING] ORDER BY time_bucket ASC",
      "vrlFunctionQuery": "",
      "customQuery": true,
      "fields": {
        "stream": "default",
        "stream_type": "traces",
        "x": [
          {
            "label": "",
            "alias": "time_bucket",
            "column": "time_bucket",
            "color": null,
            "isDerived": true,
            "havingConditions": [],
            "treatAsNonTimestamp": true
          }
        ],
        "y": [
          {
            "label": "",
            "alias": "max_trace_duration",
            "column": "max_trace_duration",
            "color": "#5960b2",
            "isDerived": true,
            "havingConditions": [],
            "treatAsNonTimestamp": true
          }
        ],
        "z": [],
        "breakdown": [],
        "filter": {
          "filterType": "group",
          "logicalOperator": "AND",
          "conditions": []
        }
      },
      "config": {
        "promql_legend": "",
        "layer_type": "scatter",
        "weight_fixed": 1,
        "limit": 0,
        "min": 0,
        "max": 100,
        "time_shift": []
      }
    }
  ],
  "layout": {
    "x": 33,
    "y": 0,
    "w": 15,
    "h": 7,
    "i": 2
  },
  "htmlContent": "",
  "markdownContent": "",
  "customChartContent": " // To know more about ECharts , \n// visit: https://echarts.apache.org/examples/en/index.html \n// Example: https://echarts.apache.org/examples/en/editor.html?c=line-simple \n// Define your ECharts 'option' here. \n// 'data' variable is available for use and contains the response data from the search result and it is an array.\noption = {  \n \n};\n  "
},
{
  "id": "Panel_ID8254010",
  "type": "line",
  "title": "Rate",
  "description": "",
  "config": {
    "show_legends": false,
    "legends_position": null,
    "decimals": 0,
    "line_thickness": 1.5,
    "step_value": "0",
    "top_results_others": false,
    "axis_border_show": false,
    "label_option": {
      "rotate": 0
    },
    "show_symbol": false,
    "line_interpolation": "smooth",
    "legend_width": {
      "unit": "px"
    },
    "base_map": {
      "type": "osm"
    },
    "map_type": {
      "type": "world"
    },
    "map_view": {
      "zoom": 1,
      "lat": 0,
      "lng": 0
    },
    "map_symbol_style": {
      "size": "by Value",
      "size_by_value": {
        "min": 1,
        "max": 100
      },
      "size_fixed": 2
    },
    "drilldown": [],
    "mark_line": [],
    "override_config": [],
    "connect_nulls": true,
    "no_value_replacement": "",
    "wrap_table_cells": false,
    "table_transpose": false,
    "table_dynamic_columns": false,
    "color": {
      "mode": "palette-classic-by-series",
      "fixedColor": [
        "#53ca53"
      ],
      "seriesBy": "last",
      "colorBySeries": []
    },
    "trellis": {
      "layout": null,
      "num_of_columns": 1,
      "group_by_y_axis": false
    },
    "dataZoom": { "yAxisIndex": "none" }
  },
  "queryType": "sql",
  "queries": [
    {
      "query": "WITH base_traces AS ( SELECT min(_timestamp) AS _time, max(duration) AS max_duration, count(distinct(trace_id)) as rate, SUM(CASE WHEN span_status='ERROR' then 1 else 0 end) as error FROM [STREAM_NAME] [WHERE_CLAUSE] GROUP BY trace_id ) SELECT histogram(_time) AS time_bucket, max(max_duration) AS max_trace_duration, sum(rate) AS max_rate, max(error) AS error_rate FROM base_traces GROUP BY time_bucket [HAVING] ORDER BY time_bucket ASC",
      "vrlFunctionQuery": "",
      "customQuery": true,
      "fields": {
        "stream": "default",
        "stream_type": "traces",
        "x": [
          {
            "label": "",
            "alias": "time_bucket",
            "column": "time_bucket",
            "color": null,
            "isDerived": false,
            "havingConditions": [],
            "treatAsNonTimestamp": true
          }
        ],
        "y": [
          {
            "label": "",
            "alias": "max_rate",
            "column": "max_rate",
            "color": "#5960b2",
            "aggregationFunction": "count",
            "isDerived": false,
            "havingConditions": [],
            "treatAsNonTimestamp": true
          }
        ],
        "z": [],
        "breakdown": [],
        "filter": {
          "filterType": "group",
          "logicalOperator": "AND",
          "conditions": []
        }
      },
      "config": {
        "promql_legend": "",
        "layer_type": "scatter",
        "weight_fixed": 1,
        "limit": 0,
        "min": 0,
        "max": 100,
        "time_shift": []
      }
    }
  ],
  "layout": {
    "x": 0,
    "y": 0,
    "w": 16,
    "h": 7,
    "i": 3
  },
  "htmlContent": "",
  "markdownContent": "",
  "customChartContent": " // To know more about ECharts , \n// visit: https://echarts.apache.org/examples/en/index.html \n// Example: https://echarts.apache.org/examples/en/editor.html?c=line-simple \n// Define your ECharts 'option' here. \n// 'data' variable is available for use and contains the response data from the search result and it is an array.\noption = {  \n \n};\n  "
},
{
  "id": "Panel_ID2185010",
  "type": "bar",
  "title": "Errors",
  "description": "",
  "config": {
    "show_legends": false,
    "legends_position": null,
    "decimals": 0,
    "line_thickness": 1.5,
    "step_value": "0",
    "top_results_others": false,
    "axis_border_show": false,
    "label_option": {
      "rotate": 0
    },
    "show_symbol": true,
    "line_interpolation": "smooth",
    "legend_width": {
      "unit": "px"
    },
    "base_map": {
      "type": "osm"
    },
    "map_type": {
      "type": "world"
    },
    "map_view": {
      "zoom": 1,
      "lat": 0,
      "lng": 0
    },
    "map_symbol_style": {
      "size": "by Value",
      "size_by_value": {
        "min": 1,
        "max": 100
      },
      "size_fixed": 2
    },
    "drilldown": [],
    "mark_line": [],
    "override_config": [],
    "connect_nulls": true,
    "no_value_replacement": "",
    "wrap_table_cells": false,
    "table_transpose": false,
    "table_dynamic_columns": false,
    "color": {
      "mode": "fixed",
      "fixedColor": [
        "#ff0000"
      ],
      "seriesBy": "last",
      "colorBySeries": []
    },
    "trellis": {
      "layout": null,
      "num_of_columns": 1,
      "group_by_y_axis": false
    },
    "dataZoom": { "yAxisIndex": "none" }
  },
  "queryType": "sql",
  "queries": [
    {
      "query": "WITH base_traces AS ( SELECT min(_timestamp) AS _time, max(duration) AS max_duration, count(distinct(trace_id)) as rate, SUM(CASE WHEN span_status='ERROR' then 1 else 0 end) as error FROM [STREAM_NAME] [WHERE_CLAUSE] GROUP BY trace_id ) SELECT histogram(_time) AS time_bucket, max(max_duration) AS max_trace_duration, sum(rate) AS max_rate, max(error) AS error_rate FROM base_traces GROUP BY time_bucket [HAVING] ORDER BY time_bucket ASC",
      "vrlFunctionQuery": "",
      "customQuery": true,
      "fields": {
        "stream": "default",
        "stream_type": "traces",
        "x": [
          {
            "label": "",
            "alias": "time_bucket",
            "column": "time_bucket",
            "color": null,
            "aggregationFunction": "histogram",
            "sortBy": "ASC",
            "isDerived": false,
            "havingConditions": [],
            "treatAsNonTimestamp": false
          }
        ],
        "y": [
          {
            "label": "",
            "alias": "error_rate",
            "column": "error_rate",
            "color": null,

@github-actions
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Validate and clamp time range

Guard against NaN and negative timestamps when offsetting from/to. Clamp to zero and
only apply offsets if the base values are valid numbers to prevent backend errors.

web/src/plugins/traces/TraceDetails.vue [703-712]

-searchService
-  .get_traces({
-    org_identifier: orgIdentifier,
-    start_time: Number(router.currentRoute.value.query.from) - 10000,
-    end_time: Number(router.currentRoute.value.query.to) + 10000,
-    filter: filter || "",
-    size: 1,
-    from: 0,
-    stream_name: streamName,
+const baseFrom = Number(router.currentRoute.value.query.from);
+const baseTo = Number(router.currentRoute.value.query.to);
+const fromTs = Number.isFinite(baseFrom) ? Math.max(0, baseFrom - 10000) : 0;
+const toTs = Number.isFinite(baseTo) ? Math.max(0, baseTo + 10000) : 0;
+searchService.get_traces({
+  org_identifier: orgIdentifier,
+  start_time: fromTs,
+  end_time: toTs,
+  filter: filter || "",
+  size: 1,
+  from: 0,
+  stream_name: streamName,
+});
Suggestion importance[1-10]: 8

__

Why: Guards against NaN and negative timestamps introduced by the new ±10000 offsets, preventing backend errors; precise to the changed lines and improves robustness.

Medium
Sanitize detail query times

Ensure startTime and endTime are valid and maintain ordering after applying offsets.
Clamp lower bound to zero and swap if necessary to avoid inverted ranges.

web/src/plugins/traces/TraceDetails.vue [745-751]

+let from = Number.isFinite(startTime) ? Math.max(0, startTime - 10000) : 0;
+let to = Number.isFinite(endTime) ? Math.max(0, endTime + 10000) : 0;
+if (to < from) [from, to] = [to, from];
 getTraceDetails({
   stream: streamName,
   trace_id: trace.trace_id,
-  from: startTime - 10000,
-  to: endTime + 10000,
+  from,
+  to,
 });
Suggestion importance[1-10]: 7

__

Why: Ensures validity and ordering of offset times for getTraceDetails, addressing potential edge cases; beneficial but less critical than the initial traces fetch.

Medium
Security
Safely interpolate stream name

Escape double quotes in selectedStream.value before interpolating to prevent
breaking the SQL and possible injection. Also default to an empty-safe stream if the
value is falsy to avoid rendering invalid queries.

web/src/plugins/traces/metrics/TracesMetricsDashboard.vue [254-260]

-convertedDashboard.tabs[0].panels[index]["queries"][0].query = panel[
-  "queries"
-][0].query.replace(
+const streamVal = String(searchObj.data.stream.selectedStream.value || "").replace(/"/g, '\\"');
+convertedDashboard.tabs[0].panels[index]["queries"][0].query = panel["queries"][0].query.replace(
   "[STREAM_NAME]",
-  `"${searchObj.data.stream.selectedStream.value}"`,
+  `"${streamVal}"`
 );
Suggestion importance[1-10]: 7

__

Why: Correctly targets the newly added query replacement and reduces risk of malformed SQL by escaping quotes; moderate impact since it hardens input handling without changing behavior.

Medium

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Summary

Fixed hardcoded stream name in traces RED (Rate, Errors, Duration) metrics queries by replacing the hardcoded "default" stream name with a dynamic [STREAM_NAME] placeholder that gets populated with the currently selected stream.

Key Changes:

  • Updated metrics.json to replace hardcoded FROM "default" with FROM [STREAM_NAME] placeholder in all three RED metric queries
  • Consolidated three separate queries into one optimized base query that calculates all metrics (duration, rate, error) in a single CTE
  • Added stream name replacement logic in TracesMetricsDashboard.vue to substitute placeholder with searchObj.data.stream.selectedStream.value
  • Added 10-second buffer to trace detail query time windows to ensure edge cases are captured

Impact:
This fix ensures RED metrics dashboards correctly query the user's selected trace stream rather than always querying the hardcoded "default" stream, which would have shown incorrect or missing metrics for users with differently-named trace streams.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are well-scoped and fix a clear bug where metrics queries used a hardcoded stream name. The fix follows established patterns in the codebase for dynamic stream selection, and the additional query optimization consolidates redundant CTEs. The time buffer addition is a defensive improvement for edge cases.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
web/src/plugins/traces/metrics/metrics.json 4/5 Replaced hardcoded "default" stream with [STREAM_NAME] placeholder and unified all three RED queries to use a single optimized base query
web/src/plugins/traces/metrics/TracesMetricsDashboard.vue 5/5 Added dynamic stream name replacement logic that substitutes [STREAM_NAME] placeholder with the currently selected stream name
web/src/plugins/traces/TraceDetails.vue 5/5 Formatting changes only (indentation/whitespace), added 10-second buffer to trace query time windows

Sequence Diagram

sequenceDiagram
    participant User
    participant TracesUI
    participant TracesMetricsDashboard
    participant Dashboard
    participant QueryEngine
    participant Database

    User->>TracesUI: Select stream (e.g., "my_traces")
    TracesUI->>TracesMetricsDashboard: Pass selectedStream.value
    TracesMetricsDashboard->>TracesMetricsDashboard: Load metrics.json template
    Note over TracesMetricsDashboard: Template contains [STREAM_NAME] placeholder
    TracesMetricsDashboard->>TracesMetricsDashboard: Replace [STREAM_NAME] with "my_traces"
    Note over TracesMetricsDashboard: Query: FROM "my_traces" WHERE...
    TracesMetricsDashboard->>TracesMetricsDashboard: Replace [WHERE_CLAUSE] with filters
    TracesMetricsDashboard->>Dashboard: Pass dashboard with final queries
    Dashboard->>QueryEngine: Execute SQL queries (Duration, Rate, Errors)
    QueryEngine->>Database: Query "my_traces" stream
    Database-->>QueryEngine: Return results
    QueryEngine-->>Dashboard: Return metrics data
    Dashboard-->>User: Display RED metrics charts
Loading

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@testdino-playwright-reporter
Copy link

⚠️ Test Run Unstable


Author: omkark06 | Branch: fix/traces-red-stream-name-fix | Commit: 042615c

Testdino Test Results

Status Total Passed Failed Skipped Flaky Pass Rate Duration
1 test failed 185 167 1 15 2 90% 23m 21s

Test Failure Analysis

  1. alerts-import.spec.js: Visibility assertion failure due to multiple matching elements
    1. Alerts Import/Export Destination Import from URL and File: 'Successfully imported' resolved to 2 elements, causing strict mode violation.

Root Cause Analysis

  • The recent changes in TraceDetails.vue may have affected the visibility of notification messages, leading to the test failure.

Recommended Actions

  1. Modify the test to specify a more unique text for visibility checks to avoid multiple matches.
  2. Review the notification rendering logic in TraceDetails.vue to ensure distinct messages are used for different import actions.
  3. Increase the timeout duration in the test to accommodate potential delays in rendering notifications.

View Detailed Results

@omkarK06 omkarK06 force-pushed the fix/traces-red-stream-name-fix branch from 042615c to 86bb9da Compare October 13, 2025 11:55
@testdino-playwright-reporter
Copy link

⚠️ Test Run Unstable


Author: omkark06 | Branch: fix/traces-red-stream-name-fix | Commit: 86bb9da

Testdino Test Results

Status Total Passed Failed Skipped Flaky Pass Rate Duration
All tests passed 292 262 0 19 11 90% 56m 5s

View Detailed Results

@testdino-playwright-reporter
Copy link

⚠️ Test Run Unstable


Author: Shrinath Rao | Branch: fix/traces-red-stream-name-fix | Commit: b3908b3

Testdino Test Results

Status Total Passed Failed Skipped Flaky Pass Rate Duration
All tests passed 364 335 0 19 10 92% 5m 1s

View Detailed Results

@omkarK06 omkarK06 force-pushed the fix/traces-red-stream-name-fix branch from b3908b3 to aea82de Compare October 14, 2025 06:55
@testdino-playwright-reporter
Copy link

⚠️ Test Run Unstable


Author: omkark06 | Branch: fix/traces-red-stream-name-fix | Commit: aea82de

Testdino Test Results

Status Total Passed Failed Skipped Flaky Pass Rate Duration
All tests passed 364 333 0 19 12 91% 5m 0s

View Detailed Results

@omkarK06 omkarK06 merged commit 62f2c35 into main Oct 14, 2025
32 checks passed
@omkarK06 omkarK06 deleted the fix/traces-red-stream-name-fix branch October 14, 2025 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

☢️ Bug Something isn't working Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants