This is an automated email from the ASF dual-hosted git repository.
zqr10159 pushed a commit to branch 2.0.0
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/2.0.0 by this push:
new fb89e34485 Enable source-only metrics inventory queries
fb89e34485 is described below
commit fb89e344856a12607b7805b79dd74b683b59b3d2
Author: Logic <[email protected]>
AuthorDate: Wed Jun 10 07:57:42 2026 +0800
Enable source-only metrics inventory queries
---
.../ingestion/otlp/metrics/otlp-metrics-page.tsx | 15 +++---
web-next/app/ingestion/otlp/metrics/page.test.tsx | 57 ++++++++++++++++++++++
2 files changed, 66 insertions(+), 6 deletions(-)
diff --git a/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
b/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
index 431c375dcd..b4e2bc5f5c 100644
--- a/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
+++ b/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
@@ -2299,7 +2299,11 @@ export default function OtlpMetricsPage() {
getRowKey={row => row.rowKey}
selectedRowKey={selectedMetricSeries?.key}
onRowClick={row => {
- if (row.series) applySelectedMetricSeries(row.series);
+ if (row.series) {
+ applySelectedMetricSeries(row.series);
+ return;
+ }
+ applyMetricInventoryQuery(row.title, null);
}}
getRowProps={row => ({
'data-otlp-metrics-series-row': 'selectable-series',
@@ -2319,24 +2323,23 @@ export default function OtlpMetricsPage() {
{
key: 'name',
header: t('otlp.metrics.series.context.metric-name'),
- render: row => row.series ? (
+ render: row => (
<HzButton
type="button"
size="xs"
intent="ghost"
-
data-otlp-metrics-inventory-query-action={row.series.key}
+
data-otlp-metrics-inventory-query-action={row.series?.key || row.rowKey}
data-otlp-metrics-inventory-query-action-owner="hertzbeat-ui-button"
+
data-otlp-metrics-inventory-query-source={row.series ? 'console-series' :
row.inventorySource || 'source-inventory'}
aria-label={t('otlp.metrics.inventory.query-action.aria', { metric: row.title
})}
onClick={event => {
event.stopPropagation();
- applyMetricInventoryQuery(row.title, row.series);
+ applyMetricInventoryQuery(row.title, row.series
|| null);
}}
className="min-w-0 justify-start truncate
font-mono"
>
{row.title}
</HzButton>
- ) : (
- <HzDataCellText variant="title"
display="block">{row.title}</HzDataCellText>
)
},
{
diff --git a/web-next/app/ingestion/otlp/metrics/page.test.tsx
b/web-next/app/ingestion/otlp/metrics/page.test.tsx
index 68df062b36..6d9be37537 100644
--- a/web-next/app/ingestion/otlp/metrics/page.test.tsx
+++ b/web-next/app/ingestion/otlp/metrics/page.test.tsx
@@ -506,6 +506,7 @@ beforeEach(() => {
mockState.replace.mockReset();
mockState.lastLoad = null;
mockState.metricSeries = [];
+ (mockState.renderData as any).inventory = undefined;
apiMessageGet.mockReset();
loadOtlpMetricsConsole.mockReset();
loadOtlpMetricsInventory.mockReset();
@@ -2603,6 +2604,62 @@ describe('otlp metrics page', () => {
expect(params.get('template')).toBe('spring-boot');
});
+ it('loads a source-only inventory metric into the route-backed query
builder', async () => {
+ mockState.searchParams = new
URLSearchParams('serviceName=checkout&serviceNamespace=payments&environment=prod&timeRange=last-1h');
+ mockState.metricSeries = [];
+ (mockState.renderData as any).inventory = {
+ source: 'promql-inventory',
+ context: {
+ entityId: 7,
+ entityName: 'Checkout API',
+ serviceName: 'checkout',
+ serviceNamespace: 'payments',
+ environment: 'prod'
+ },
+ items: [
+ {
+ metricName: 'source.only.latency',
+ family: 'latency',
+ timeSeriesCount: 4,
+ latestObservedAt: 2000,
+ labels: {
+ service_name: 'checkout',
+ http_route: '/checkout'
+ }
+ }
+ ]
+ };
+
+ const { default: OtlpMetricsPage } = await import('./page');
+ interactionContainer = document.createElement('div');
+ document.body.appendChild(interactionContainer);
+ interactionRoot = createRoot(interactionContainer);
+
+ await act(async () => {
+ interactionRoot?.render(<OtlpMetricsPage />);
+ await Promise.resolve();
+ });
+
+ const queryAction =
interactionContainer.querySelector('[data-otlp-metrics-inventory-query-action="inventory-source.only.latency-0"]')
as HTMLButtonElement | null;
+ expect(queryAction).not.toBeNull();
+
expect(queryAction?.getAttribute('data-otlp-metrics-inventory-query-source')).toBe('promql-inventory');
+ expect(queryAction?.textContent).toContain('source.only.latency');
+
+ await act(async () => {
+ queryAction?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ await Promise.resolve();
+ });
+
+ const href = String(mockState.replace.mock.calls.at(-1)?.[0]);
+ const params = new URL(href, 'http://localhost').searchParams;
+ expect(params.get('query')).toBe('source.only.latency');
+ expect(params.get('series')).toBeNull();
+ expect(params.get('serviceName')).toBe('checkout');
+ expect(params.get('serviceNamespace')).toBe('payments');
+ expect(params.get('environment')).toBe('prod');
+ expect(params.get('timeRange')).toBe('last-1h');
+ });
+
it('renders a route-backed table inspector for selected metric raw samples',
async () => {
mockState.searchParams = new
URLSearchParams('inspector=table&query=http.server.duration');
mockState.metricSeries = [
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]