From f0150932f69f794a3d3f6a5eca83738bb732bfdb Mon Sep 17 00:00:00 2001 From: songzhendong12315 Date: Sun, 18 Oct 2026 22:49:45 +0800 Subject: [PATCH 1/2] Fix Zabbix agent sample timestamps dropping the sub-second part. Agent data items carry clock in seconds and ns within that second. The sample time used only the seconds, so meter timestamps were always whole seconds. --- docs/en/changes/changes.md | 1 + .../zabbix/provider/ZabbixMetrics.java | 11 +++++- .../provider/ZabbixMetricsTimestampTest.java | 37 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md index 8bc2676a288c..4afb1cea508d 100644 --- a/docs/en/changes/changes.md +++ b/docs/en/changes/changes.md @@ -56,6 +56,7 @@ * Fix the Envoy ALS sidecar internal request and response latencies dropping whole seconds: only the `nanos` part of each protobuf `Duration` was read. The internal response latency is now left out of the internal latency metrics when the access log lacks the upstream or downstream timing it is computed from, such as when Envoy answers an upstream timeout with its own 504, instead of counting the whole wait or going negative. * Sync the Envoy access log proto, `envoy/data/accesslog/v3/accesslog.proto`, with Envoy v1.40.0, adding the `envoy/annotations/deprecation.proto` it imports. With it, the Envoy ALS sidecar internal latencies leave out what the access log cannot measure instead of counting it: the internal request latency of an HTTP request Envoy retried or hedged, or of a TCP connection that took more than one connect attempt (`upstream_request_attempt_count` above 1), as Envoy measures the upstream timings from the start of the downstream request or connection, so the time to the first upstream byte can include the earlier attempts and the delays between them; and the internal response latency when the response details are set to anything but `via_upstream`, as for Envoy's own 504 of a timeout during a retry backoff, which can carry an earlier attempt's upstream timing, and, conservatively, for an upstream response that a later reset or timeout ended. The adapters report such a latency as `-1`, and the `*_sidecar_internal_req_latency_nanos` and `*_sidecar_internal_resp_latency_nanos` metrics in `mesh.oal` filter it out with `filter(... >= 0)`. A customized `mesh.oal` needs the same filters, or the `-1` values are averaged in. * Fix OAL `>`, `>=`, `<` and `<=` filters on a `long` or `double` source field, and `==` on a `long` one, failing to compile when they compare it with a number, such as `filter(sideCar.internalRequestLatencyNanos >= 0)`. The generated code passed the number as an `int`, and the comparison matchers have no `match(long, int)` or `match(double, int)` overload, as Javassist does not widen an argument. The number is now written in the field's type, `0L` or `0.0`; filters on `int` fields, which already worked, generate the same code. +* Fix the Zabbix receiver storing agent samples at whole-second precision. Each `agent data` item carries `clock` (Unix seconds) and `ns` (nanoseconds within that second); the sample time used only `clock * 1000`, so the sub-second part was dropped and the timestamp was always an exact second. The sample time is now `clock * 1000 + ns / 1_000_000`. #### UI * Add a Virtual GenAI evaluation-record page and evaluation-score chart in Horizon UI, so operators can inspect evaluation result, level, reason, judge model, timestamp, trace linkage, and the `gen_ai_model_evaluation_score_ppm` trend for evaluated records. diff --git a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java index c05772029fe7..a21a705e2418 100644 --- a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java +++ b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java @@ -62,6 +62,15 @@ @Slf4j public class ZabbixMetrics { + /** + * Sample time in milliseconds. {@code clockSeconds} is the Unix second and {@code nanos} is the + * nanoseconds within that second. Meter samples are milliseconds, so the sub-millisecond part of + * {@code nanos} is dropped. + */ + static long sampleTimeMillis(final long clockSeconds, final long nanos) { + return TimeUnit.SECONDS.toMillis(clockSeconds) + TimeUnit.NANOSECONDS.toMillis(nanos); + } + private final List originalConfigs; /** @@ -272,7 +281,7 @@ private SampleBuilder parseAgentData(ZabbixRequest.AgentData data) { } return builder.hostName(data.getHost()) - .timestamp(TimeUnit.SECONDS.toMillis(data.getClock())) + .timestamp(sampleTimeMillis(data.getClock(), data.getNs())) .value(Double.parseDouble(data.getValue())) .build(); } diff --git a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java new file mode 100644 index 000000000000..027b7e99f56f --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.oap.server.receiver.zabbix.provider; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ZabbixMetricsTimestampTest { + + @Test + public void sampleTimeKeepsMillisecondsFromNs() { + // clock=1609588563, ns=87682907 -> 87ms within that second. ns-only leftover is dropped. + assertEquals(1_609_588_563_087L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 87_682_907L)); + assertEquals(1_609_588_563_000L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 0L)); + // Truncate toward zero at the millisecond boundaries. 999999999ns stays in the same second. + assertEquals(1_609_588_563_000L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 999_999L)); + assertEquals(1_609_588_563_001L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 1_000_000L)); + assertEquals(1_609_588_563_999L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 999_999_999L)); + } +} From eb024a80d943ede4bbb4a19d028edec783bb99ba Mon Sep 17 00:00:00 2001 From: Wu Sheng Date: Mon, 19 Oct 2026 21:35:46 +0800 Subject: [PATCH 2/2] Reword the Zabbix sample-time changelog line and tidy its test Stored metrics are minute-bucketed and never held the whole-second time, so the changelog line now says where the sub-second part can show: values from custom Zabbix rules using MAL rate, irate or increase. Results from the bundled rules and the minute a sample lands in are unchanged. Also drop the test comments that restated the assertions, and move sampleTimeMillis below the instance fields with the other methods. --- docs/en/changes/changes.md | 2 +- .../zabbix/provider/ZabbixMetrics.java | 18 +++++++++--------- .../provider/ZabbixMetricsTimestampTest.java | 2 -- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md index 4afb1cea508d..53c842f08abc 100644 --- a/docs/en/changes/changes.md +++ b/docs/en/changes/changes.md @@ -56,7 +56,7 @@ * Fix the Envoy ALS sidecar internal request and response latencies dropping whole seconds: only the `nanos` part of each protobuf `Duration` was read. The internal response latency is now left out of the internal latency metrics when the access log lacks the upstream or downstream timing it is computed from, such as when Envoy answers an upstream timeout with its own 504, instead of counting the whole wait or going negative. * Sync the Envoy access log proto, `envoy/data/accesslog/v3/accesslog.proto`, with Envoy v1.40.0, adding the `envoy/annotations/deprecation.proto` it imports. With it, the Envoy ALS sidecar internal latencies leave out what the access log cannot measure instead of counting it: the internal request latency of an HTTP request Envoy retried or hedged, or of a TCP connection that took more than one connect attempt (`upstream_request_attempt_count` above 1), as Envoy measures the upstream timings from the start of the downstream request or connection, so the time to the first upstream byte can include the earlier attempts and the delays between them; and the internal response latency when the response details are set to anything but `via_upstream`, as for Envoy's own 504 of a timeout during a retry backoff, which can carry an earlier attempt's upstream timing, and, conservatively, for an upstream response that a later reset or timeout ended. The adapters report such a latency as `-1`, and the `*_sidecar_internal_req_latency_nanos` and `*_sidecar_internal_resp_latency_nanos` metrics in `mesh.oal` filter it out with `filter(... >= 0)`. A customized `mesh.oal` needs the same filters, or the `-1` values are averaged in. * Fix OAL `>`, `>=`, `<` and `<=` filters on a `long` or `double` source field, and `==` on a `long` one, failing to compile when they compare it with a number, such as `filter(sideCar.internalRequestLatencyNanos >= 0)`. The generated code passed the number as an `int`, and the comparison matchers have no `match(long, int)` or `match(double, int)` overload, as Javassist does not widen an argument. The number is now written in the field's type, `0L` or `0.0`; filters on `int` fields, which already worked, generate the same code. -* Fix the Zabbix receiver storing agent samples at whole-second precision. Each `agent data` item carries `clock` (Unix seconds) and `ns` (nanoseconds within that second); the sample time used only `clock * 1000`, so the sub-second part was dropped and the timestamp was always an exact second. The sample time is now `clock * 1000 + ns / 1_000_000`. +* Fix the Zabbix receiver dropping the sub-second part of agent sample times. Each `agent data` item carries `clock` (Unix seconds) and `ns` (nanoseconds within that second), but the sample time used only `clock * 1000`; it is now `clock * 1000 + ns / 1_000_000`. The sub-second precision can affect values produced by custom Zabbix rules using MAL `rate`, `irate` or `increase`; results from the bundled Zabbix rules and the assignment of samples to minute buckets are unchanged. #### UI * Add a Virtual GenAI evaluation-record page and evaluation-score chart in Horizon UI, so operators can inspect evaluation result, level, reason, judge model, timestamp, trace linkage, and the `gen_ai_model_evaluation_score_ppm` trend for evaluated records. diff --git a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java index a21a705e2418..13aa8b35142f 100644 --- a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java +++ b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetrics.java @@ -62,15 +62,6 @@ @Slf4j public class ZabbixMetrics { - /** - * Sample time in milliseconds. {@code clockSeconds} is the Unix second and {@code nanos} is the - * nanoseconds within that second. Meter samples are milliseconds, so the sub-millisecond part of - * {@code nanos} is dropped. - */ - static long sampleTimeMillis(final long clockSeconds, final long nanos) { - return TimeUnit.SECONDS.toMillis(clockSeconds) + TimeUnit.NANOSECONDS.toMillis(nanos); - } - private final List originalConfigs; /** @@ -129,6 +120,15 @@ private void initConfigs(MeterSystem meterSystem) { }).appendMetrics(c))); } + /** + * Sample time in milliseconds. {@code clockSeconds} is the Unix second and {@code nanos} is the + * nanoseconds within that second. Meter samples are milliseconds, so the sub-millisecond part of + * {@code nanos} is dropped. + */ + static long sampleTimeMillis(final long clockSeconds, final long nanos) { + return TimeUnit.SECONDS.toMillis(clockSeconds) + TimeUnit.NANOSECONDS.toMillis(nanos); + } + /** * Metrics convert to meter system statics */ diff --git a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java index 027b7e99f56f..a83ef11800fd 100644 --- a/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java +++ b/oap-server/server-receiver-plugin/skywalking-zabbix-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zabbix/provider/ZabbixMetricsTimestampTest.java @@ -26,10 +26,8 @@ public class ZabbixMetricsTimestampTest { @Test public void sampleTimeKeepsMillisecondsFromNs() { - // clock=1609588563, ns=87682907 -> 87ms within that second. ns-only leftover is dropped. assertEquals(1_609_588_563_087L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 87_682_907L)); assertEquals(1_609_588_563_000L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 0L)); - // Truncate toward zero at the millisecond boundaries. 999999999ns stays in the same second. assertEquals(1_609_588_563_000L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 999_999L)); assertEquals(1_609_588_563_001L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 1_000_000L)); assertEquals(1_609_588_563_999L, ZabbixMetrics.sampleTimeMillis(1_609_588_563L, 999_999_999L));