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 6527b579b3 Support log trend IN filters
6527b579b3 is described below

commit 6527b579b3728cb38e062d196eff1c7417bd2a6f
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 21:12:02 2026 +0800

    Support log trend IN filters
---
 .../logs/service/impl/LogQueryServiceImpl.java     | 17 +++---
 .../logs/controller/LogQueryControllerTest.java    | 71 ++++++++++++++++++++++
 2 files changed, 81 insertions(+), 7 deletions(-)

diff --git 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/service/impl/LogQueryServiceImpl.java
 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/service/impl/LogQueryServiceImpl.java
index 495d85f0c5..4bac5a6a2d 100644
--- 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/service/impl/LogQueryServiceImpl.java
+++ 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/service/impl/LogQueryServiceImpl.java
@@ -320,8 +320,8 @@ public class LogQueryServiceImpl implements LogQueryService 
{
                                           String serviceName, String 
serviceNamespace, String environment,
                                           String resourceFilter, String 
attributeFilter,
                                           boolean hideInternal, boolean 
hideNoise) {
-        Map<String, String> resourceFilters = 
parseLogAttributeFilter(resourceFilter);
-        Map<String, String> attributeFilters = 
parseLogAttributeFilter(attributeFilter);
+        Map<String, String> resourceFilters = 
parseLogAttributeFilter(resourceFilter, true);
+        Map<String, String> attributeFilters = 
parseLogAttributeFilter(attributeFilter, true);
         return trendStatsWithFilters(start, end, traceId, spanId, 
severityNumber, severityText, search,
                 serviceName, serviceNamespace, environment, resourceFilters, 
attributeFilters, hideInternal, hideNoise);
     }
@@ -334,8 +334,8 @@ public class LogQueryServiceImpl implements LogQueryService 
{
                                           boolean hideInternal, boolean 
hideNoise) {
         LogServiceContext context = 
resolveEntityFirstLogServiceContext(entityId, serviceName, serviceNamespace, 
environment);
         Map<String, String> resourceFilters = removeEntityScopeResourceFilters(
-                context, parseLogAttributeFilter(resourceFilter));
-        Map<String, String> attributeFilters = 
parseLogAttributeFilter(attributeFilter);
+                context, parseLogAttributeFilter(resourceFilter, true));
+        Map<String, String> attributeFilters = 
parseLogAttributeFilter(attributeFilter, true);
         return trendStatsWithFilters(start, end, traceId, spanId, 
severityNumber, severityText, search,
                 context.serviceName(), context.serviceNamespace(), 
context.environment(), resourceFilters, attributeFilters,
                 hideInternal, hideNoise);
@@ -347,9 +347,12 @@ public class LogQueryServiceImpl implements 
LogQueryService {
                                                       Map<String, String> 
resourceFilters,
                                                       Map<String, String> 
attributeFilters,
                                                       boolean hideInternal, 
boolean hideNoise) {
-        Map<String, Long> aggregate = readHourlyStats(start, end, traceId, 
spanId, severityNumber,
-                severityText, search, serviceName, serviceNamespace, 
environment, resourceFilters, attributeFilters,
-                hideInternal, hideNoise);
+        Map<String, Long> aggregate = null;
+        if (!hasComplexAttributeFilters(resourceFilters, attributeFilters)) {
+            aggregate = readHourlyStats(start, end, traceId, spanId, 
severityNumber,
+                    severityText, search, serviceName, serviceNamespace, 
environment, resourceFilters, attributeFilters,
+                    hideInternal, hideNoise);
+        }
         if (aggregate != null) {
             Map<String, Object> result = new HashMap<>();
             result.put("hourlyStats", aggregate);
diff --git 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
index 3a168f4758..6c83fabc10 100644
--- 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
+++ 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
@@ -29,6 +29,10 @@ import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 import static 
org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -1448,6 +1452,73 @@ class LogQueryControllerTest {
                 .andExpect(jsonPath("$.data.hourlyStats").isMap());
     }
 
+    @Test
+    void testTrendStatsAppliesInAndNotInFiltersWithRowFallback() throws 
Exception {
+        long bucketTimeUnixNano = 1734005477630000000L;
+        String bucketKey = LocalDateTime.ofInstant(
+                        Instant.ofEpochMilli(bucketTimeUnixNano / 1_000_000L),
+                        ZoneId.systemDefault())
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00"));
+        LogEntry stableLog = LogEntry.builder()
+                .timeUnixNano(bucketTimeUnixNano)
+                .severityText("INFO")
+                .body("stable checkout trend")
+                .resource(new HashMap<>(Map.of(
+                        "service.name", "checkout",
+                        "service.version", "1.2.3",
+                        "host.name", "checkout-1")))
+                .attributes(new HashMap<>(Map.of("http.route", "/checkout")))
+                .build();
+        LogEntry secondStableLog = LogEntry.builder()
+                .timeUnixNano(bucketTimeUnixNano + 1_000_000L)
+                .severityText("ERROR")
+                .body("second checkout trend")
+                .resource(new HashMap<>(Map.of(
+                        "service.name", "checkout",
+                        "service.version", "1.2.4",
+                        "host.name", "checkout-2")))
+                .attributes(new HashMap<>(Map.of("http.route", "/checkout")))
+                .build();
+        LogEntry canaryLog = LogEntry.builder()
+                .timeUnixNano(bucketTimeUnixNano + 2_000_000L)
+                .severityText("WARN")
+                .body("canary checkout trend")
+                .resource(new HashMap<>(Map.of(
+                        "service.name", "checkout",
+                        "service.version", "1.2.3",
+                        "host.name", "checkout-canary")))
+                .attributes(new HashMap<>(Map.of("http.route", "/checkout")))
+                .build();
+        LogEntry cartLog = LogEntry.builder()
+                .timeUnixNano(bucketTimeUnixNano + 3_000_000L)
+                .severityText("INFO")
+                .body("cart checkout trend")
+                .resource(new HashMap<>(Map.of(
+                        "service.name", "checkout",
+                        "service.version", "1.2.4",
+                        "host.name", "checkout-3")))
+                .attributes(new HashMap<>(Map.of("http.route", "/cart")))
+                .build();
+        when(historyDataReader.queryLogsByMultipleConditions(any(), any(), 
any(),
+                any(), any(), any(), any())).thenReturn(List.of(stableLog, 
secondStableLog, canaryLog, cartLog));
+
+        mockMvc.perform(MockMvcRequestBuilders.get("/api/logs/stats/trend")
+                        .param("resourceFilter", "service.version IN ('1.2.3', 
'1.2.4') "
+                                + "and host.name NOT IN ('checkout-canary')")
+                        .param("attributeFilter", "http.route IN 
('/checkout')"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.code").value((int) 
CommonConstants.SUCCESS_CODE))
+                .andExpect(jsonPath("$.data.hourlyStats.length()").value(1))
+                .andExpect(jsonPath("$.data.hourlyStats['" + bucketKey + 
"']").value(2));
+
+        verify(historyDataReader).queryLogsByMultipleConditions(any(), any(), 
any(),
+                any(), any(), any(), any());
+        verify(historyDataReader, never()).countLogsByHour(any(), any(), 
any(), any(), any(), any(), any(),
+                anySet(), eq(false), any(), any(), any(), any(),
+                org.mockito.ArgumentMatchers.<Map<String, String>>any(),
+                org.mockito.ArgumentMatchers.<Map<String, String>>any());
+    }
+
     @Test
     void testTrendStatsUsesStorageAggregateWhenAvailable() throws Exception {
         when(historyDataReader.countLogsByHour(any(), any(), any(), any(),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to