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 0e1c69e54f Preserve metrics alert draft signal context
0e1c69e54f is described below
commit 0e1c69e54fe5cb3cd4f1653e6429b5b96f81736b
Author: Logic <[email protected]>
AuthorDate: Wed Jun 10 08:48:13 2026 +0800
Preserve metrics alert draft signal context
---
web-next/lib/otlp-metrics/view-model.test.ts | 17 +++++++++++++++++
web-next/lib/otlp-metrics/view-model.ts | 11 ++++++++++-
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/web-next/lib/otlp-metrics/view-model.test.ts
b/web-next/lib/otlp-metrics/view-model.test.ts
index 41b0c6435c..86d9628eb4 100644
--- a/web-next/lib/otlp-metrics/view-model.test.ts
+++ b/web-next/lib/otlp-metrics/view-model.test.ts
@@ -911,6 +911,7 @@ describe('otlp metrics view model', () => {
{
traceId: 'trace-1',
spanId: 'span-1',
+ operationName: 'POST /checkout',
query: 'http.server.duration',
filter: 'service.name="checkout"',
formula: 'rate(http.server.duration[5m])',
@@ -1004,6 +1005,7 @@ describe('otlp metrics view model', () => {
expect(alertParams.get('intent')).toBe('create');
expect(alertParams.get('entityId')).toBe('7');
expect(alertParams.get('serviceName')).toBe('checkout');
+ expect(alertParams.get('operationName')).toBe('POST /checkout');
expect(alertParams.get('environment')).toBe('prod');
expect(alertParams.get('timeRange')).toBe('last-1h');
expect(alertParams.get('source')).toBe('otlp');
@@ -1013,6 +1015,12 @@ describe('otlp metrics view model', () => {
expect(alertParams.get('alertDatasource')).toBe('promql');
expect(alertParams.get('alertQuery')).toContain('query=http.server.duration');
expect(alertParams.get('alertQuery')).toContain('filter=service.name="checkout"');
+ expect(alertParams.get('alertQuery')).toContain('entityId=7');
+ expect(alertParams.get('alertQuery')).toContain('entityName=Checkout API');
+
expect(alertParams.get('alertQuery')).toContain('serviceNamespace=payments');
+ expect(alertParams.get('alertQuery')).toContain('operationName=POST
/checkout');
+ expect(alertParams.get('alertQuery')).toContain('traceId=trace-1');
+ expect(alertParams.get('alertQuery')).toContain('spanId=span-1');
const handlingUrl = new URL(result.alertHandlingHref,
'https://example.com');
expect(handlingUrl.pathname).toBe('/alert');
@@ -1040,6 +1048,7 @@ describe('otlp metrics view model', () => {
expect(dashboardHref.searchParams.get('panelDatasource')).toBe('promql');
expect(dashboardHref.searchParams.get('panelQuery')).toContain('query=http.server.duration');
expect(dashboardHref.searchParams.get('panelQuery')).toContain('filter=service.name="checkout"');
+
expect(dashboardHref.searchParams.get('panelQuery')).toContain('operationName=POST
/checkout');
});
it('lets a selected metric series drive service, entity, logs, traces, and
alert handoffs', () => {
@@ -1076,6 +1085,7 @@ describe('otlp metrics view model', () => {
'deployment.environment.name': 'prod-east',
trace_id: 'trace-series-42',
span_id: 'span-series-42',
+ http_route: '/inventory/{id}',
'hertzbeat.entity_id': '42',
'hertzbeat.entity_name': 'Inventory API',
'hertzbeat.collector': 'collector-b',
@@ -1096,6 +1106,7 @@ describe('otlp metrics view model', () => {
expect(logParams.get('environment')).toBe('prod-east');
expect(logParams.get('traceId')).toBe('trace-series-42');
expect(logParams.get('spanId')).toBe('span-series-42');
+ expect(logParams.get('operationName')).toBe('/inventory/{id}');
expect(logParams.get('collector')).toBe('collector-b');
expect(logParams.get('template')).toBe('fastapi');
@@ -1105,6 +1116,7 @@ describe('otlp metrics view model', () => {
expect(traceParams.get('environment')).toBe('prod-east');
expect(traceParams.get('traceId')).toBe('trace-series-42');
expect(traceParams.get('spanId')).toBe('span-series-42');
+ expect(traceParams.get('operationName')).toBe('/inventory/{id}');
expect(traceParams.get('entityId')).toBe('42');
const entityHref = new URL(result.entityHref, 'https://example.com');
@@ -1116,6 +1128,11 @@ describe('otlp metrics view model', () => {
expect(alertHandlingHref.searchParams.get('search')).toBe('inventory');
expect(alertHandlingHref.searchParams.get('entityId')).toBe('42');
expect(alertHandlingHref.searchParams.get('serviceName')).toBe('inventory');
+
expect(alertHandlingHref.searchParams.get('operationName')).toBe('/inventory/{id}');
+
+ const alertRulesHref = new URL(result.alertRulesHref,
'https://example.com');
+
expect(alertRulesHref.searchParams.get('operationName')).toBe('/inventory/{id}');
+
expect(alertRulesHref.searchParams.get('alertQuery')).toContain('operationName=/inventory/{id}');
});
it('opens trace-linked metric logs as history records without an extra text
search filter', () => {
diff --git a/web-next/lib/otlp-metrics/view-model.ts
b/web-next/lib/otlp-metrics/view-model.ts
index c82769cf3f..53fa2ff451 100644
--- a/web-next/lib/otlp-metrics/view-model.ts
+++ b/web-next/lib/otlp-metrics/view-model.ts
@@ -145,8 +145,14 @@ export function buildMetricsAlertRuleDraft(
['filter', query.filter],
['aggregation', query.aggregation],
['groupBy', query.groupBy],
+ ['entityId', query.entityId || routeContext.entityId],
+ ['entityName', query.entityName || routeContext.entityName],
['serviceName', query.serviceName || routeContext.serviceName],
- ['environment', query.environment || routeContext.environment]
+ ['serviceNamespace', query.serviceNamespace ||
routeContext.serviceNamespace],
+ ['operationName', query.operationName || routeContext.operationName],
+ ['environment', query.environment || routeContext.environment],
+ ['traceId', query.traceId || routeContext.traceId],
+ ['spanId', query.spanId || routeContext.spanId]
]
.map(([key, value]) => {
const normalized = compactAlertDraftValue(value);
@@ -185,6 +191,7 @@ function buildMetricSeriesSignalContext(series:
OtlpMetricSeriesView | null | un
serviceName: readSeriesLabel(series, 'service.name', 'service_name',
'serviceName'),
serviceNamespace: readSeriesLabel(series, 'service.namespace',
'service_namespace', 'serviceNamespace'),
environment: readSeriesLabel(series, 'deployment.environment.name',
'deployment_environment_name', 'deployment_environment', 'environment'),
+ operationName: readSeriesLabel(series, 'operationName', 'operation_name',
'operation', 'http.route', 'http_route'),
traceId: readSeriesLabel(series, 'traceId', 'trace_id', 'trace.id',
'trace_id_hex'),
spanId: readSeriesLabel(series, 'spanId', 'span_id', 'span.id',
'span_id_hex'),
collector: readSeriesLabel(series, 'hertzbeat.collector',
'hertzbeat_collector', 'collector'),
@@ -960,6 +967,7 @@ export function buildMetricsHandoffLinks(
const end = query.end || (data.context?.end != null ?
String(data.context.end) : routeContext.end);
const traceId = firstText(selectedContext.traceId, query.traceId,
routeContext.traceId);
const spanId = firstText(selectedContext.spanId, query.spanId,
routeContext.spanId);
+ const operationName = firstText(selectedContext.operationName,
query.operationName, routeContext.operationName);
const signalContext: SignalRouteContext = {
...routeContext,
...query,
@@ -971,6 +979,7 @@ export function buildMetricsHandoffLinks(
timeRange: routeContext.timeRange,
traceId,
spanId,
+ operationName,
source: routeContext.source || 'otlp',
collector: selectedContext.collector || query.collector ||
routeContext.collector,
template: selectedContext.template || query.template ||
routeContext.template,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]