From 4ee38565e2bd1faf47aa93527adce2982c432df6 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Wed, 2 Jul 2025 17:12:36 +0800 Subject: [PATCH 01/39] feat: Receive OTLP/HTTP logs and transform to unified entity --- .../hertzbeat/common/entity/log/LogEntry.java | 155 ++++++++++++++ hertzbeat-log/pom.xml | 72 +++++++ .../controller/LogIngestionController.java | 107 ++++++++++ .../log/service/LogProtocolAdapter.java | 25 +++ .../service/impl/OtlpLogProtocolAdapter.java | 202 ++++++++++++++++++ hertzbeat-manager/pom.xml | 7 +- .../src/main/resources/sureness.yml | 1 + material/licenses/LICENSE | 1 + material/licenses/backend/LICENSE | 1 + pom.xml | 26 ++- 10 files changed, 595 insertions(+), 2 deletions(-) create mode 100644 hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java create mode 100644 hertzbeat-log/pom.xml create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java new file mode 100644 index 00000000000..2a625b1d251 --- /dev/null +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java @@ -0,0 +1,155 @@ +package org.apache.hertzbeat.common.entity.log; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.Map; + +/** + * OpenTelemetry Log Entry entity based on OpenTelemetry log data model specification. + * + * @see OpenTelemetry Log Data Model + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LogEntry { + + /** + * Time when the event occurred. + * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + */ + private Long timeUnixNano; + + /** + * Time when the event was observed. + * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + */ + private Long observedTimeUnixNano; + + /** + * Numerical value of the severity. + * Smaller numerical values correspond to less severe events (such as debug events), + * larger numerical values correspond to more severe events (such as errors and critical events). + */ + private Integer severityNumber; + + /** + * The severity text (also known as log level). + * This is the original string representation of the severity as it is known at the source. + */ + private String severityText; + + /** + * A value containing the body of the log record. + * Can be for example a human-readable string message (including multi-line) + * or it can be a structured data composed of arrays and maps of other values. + */ + private Object body; + + /** + * Additional information about the specific event occurrence. + * Unlike the Resource field, these are NOT fixed for the lifetime of the process. + */ + private Map attributes; + + /** + * Dropped attributes count. + * Number of attributes that were discarded due to limits being exceeded. + */ + private Integer droppedAttributesCount; + + /** + * A unique identifier for a trace. + * All spans from the same trace share the same trace_id. + * The ID is a 16-byte array represented as a hex string. + */ + private String traceId; + + /** + * A unique identifier for a span within a trace. + * The ID is an 8-byte array represented as a hex string. + */ + private String spanId; + + /** + * Trace flag as defined in W3C Trace Context specification. + * At the time of writing the specification defines one flag - the SAMPLED flag. + */ + private Integer traceFlags; + + /** + * Resource information. + * Information about the entity producing the telemetry. + */ + private Map resource; + + /** + * Instrumentation Scope information. + * Information about the instrumentation scope that emitted the log. + */ + private InstrumentationScope instrumentationScope; + + /** + * Instrumentation Scope information for logs. + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class InstrumentationScope { + + /** + * The name of the instrumentation scope. + * This should be the fully-qualified name of the instrumentation library. + */ + private String name; + + /** + * The version of the instrumentation scope. + */ + private String version; + + /** + * Additional attributes that describe the scope. + */ + private Map attributes; + + /** + * Number of attributes that were discarded due to limits being exceeded. + */ + private Integer droppedAttributesCount; + } + + /** + * Convenience method to get timestamp as Instant. + */ + public Instant getTimestamp() { + return timeUnixNano != null ? Instant.ofEpochSecond(timeUnixNano / 1_000_000_000L, timeUnixNano % 1_000_000_000L) : null; + } + + /** + * Convenience method to get observed timestamp as Instant. + */ + public Instant getObservedTimestamp() { + return observedTimeUnixNano != null ? Instant.ofEpochSecond(observedTimeUnixNano / 1_000_000_000L, observedTimeUnixNano % 1_000_000_000L) : null; + } + + /** + * Convenience method to set timestamp from Instant. + */ + public void setTimestamp(Instant timestamp) { + this.timeUnixNano = timestamp != null ? timestamp.getEpochSecond() * 1_000_000_000L + timestamp.getNano() : null; + } + + /** + * Convenience method to set observed timestamp from Instant. + */ + public void setObservedTimestamp(Instant timestamp) { + this.observedTimeUnixNano = timestamp != null ? timestamp.getEpochSecond() * 1_000_000_000L + timestamp.getNano() : null; + } +} diff --git a/hertzbeat-log/pom.xml b/hertzbeat-log/pom.xml new file mode 100644 index 00000000000..e1859501b1d --- /dev/null +++ b/hertzbeat-log/pom.xml @@ -0,0 +1,72 @@ + + + + + org.apache.hertzbeat + hertzbeat + 2.0-SNAPSHOT + + 4.0.0 + + hertzbeat-log + ${project.artifactId} + + + + + org.apache.hertzbeat + hertzbeat-base + provided + + + + org.apache.hertzbeat + hertzbeat-warehouse + + + + org.springframework.boot + spring-boot-starter-web + provided + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + provided + + + + io.opentelemetry.proto + opentelemetry-proto + + + + com.google.protobuf + protobuf-java + + + + com.google.protobuf + protobuf-java-util + + + + \ No newline at end of file diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java new file mode 100644 index 00000000000..e5ccb04ef4c --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java @@ -0,0 +1,107 @@ +/* + * 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.hertzbeat.log.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import java.util.List; + +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.dto.Message; +import org.apache.hertzbeat.log.service.LogProtocolAdapter; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * Log Ingestion Controller + */ +@Tag(name = "Log Ingestion Controller") +@RestController +@Slf4j +public class LogIngestionController { + + private static final String DEFAULT_PROTOCOL = "otlp"; + private final List protocolAdapters; + + public LogIngestionController(List protocolAdapters) { + this.protocolAdapters = protocolAdapters; + } + + /** + * Receive log payload pushed from external system specifying the log protocol. + * Examples: + * - POST /api/logs/ingest/otlp (content body is OTLP JSON) + * + * @param protocol log protocol identifier + * @param content raw request body + */ + @PostMapping("/api/logs/ingest/{protocol}") + public ResponseEntity> ingestExternLog(@PathVariable("protocol") String protocol, + @RequestBody String content) { + log.info("Receive extern log from protocol: {}, content length: {}", protocol, content == null ? 0 : content.length()); + if (!StringUtils.hasText(protocol)) { + protocol = DEFAULT_PROTOCOL; // Default to OTLP if no protocol specified + } + for (LogProtocolAdapter adapter : protocolAdapters) { + if (adapter.supportSource().equalsIgnoreCase(protocol)) { + try { + adapter.ingest(content); + return ResponseEntity.ok(Message.success("Add extern log success")); + } catch (Exception e) { + log.error("Add extern log failed: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Message.fail(CommonConstants.FAIL_CODE, "Add extern log failed: " + e.getMessage())); + } + } + } + log.warn("Not support extern log from protocol: {}", protocol); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Message.fail(CommonConstants.FAIL_CODE, "Not support the " + protocol + " protocol log")); + } + + /** + * Receive default log payload (when protocol is not specified). + * It will look for a service whose supportSource() returns "otlp". + */ + @PostMapping("/api/logs/ingest") + public ResponseEntity> ingestDefaultExternLog(@RequestBody String content) { + log.info("Receive default extern log content, length: {}", content == null ? 0 : content.length()); + LogProtocolAdapter adapter = protocolAdapters.stream() + .filter(item -> DEFAULT_PROTOCOL.equalsIgnoreCase(item.supportSource())) + .findFirst() + .orElse(null); + if (adapter != null) { + try { + adapter.ingest(content); + return ResponseEntity.ok(Message.success("Add extern log success")); + } catch (Exception e) { + log.error("Add extern log failed: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Message.fail(CommonConstants.FAIL_CODE, "Add extern log failed: " + e.getMessage())); + } + } + log.error("Not support default extern log protocol"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Message.fail(CommonConstants.FAIL_CODE, "Not support the default protocol log")); + } +} diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java new file mode 100644 index 00000000000..924684fced1 --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java @@ -0,0 +1,25 @@ +package org.apache.hertzbeat.log.service; + +/** + * Adapter interface for ingesting logs pushed via different protocols + * (e.g. OTLP, Loki, Filebeat, Vector). + * + * Implementations should: + * 1. Parse raw HTTP payload of their protocol. + * 2. Convert data to LogEntry. + * 3. Forward / persist it to downstream pipeline. + */ +public interface LogProtocolAdapter { + + /** + * Ingest raw log payload pushed from external system. + * + * @param content raw request body string + */ + void ingest(String content); + + /** + * Identifier of the protocol this adapter supports ("otlp", "vector", etc.) + */ + String supportSource(); +} \ No newline at end of file diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java new file mode 100644 index 00000000000..d4da575b78c --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -0,0 +1,202 @@ +package org.apache.hertzbeat.log.service.impl; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.proto.common.v1.AnyValue; +import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.logs.v1.LogRecord; +import io.opentelemetry.proto.logs.v1.ResourceLogs; +import io.opentelemetry.proto.logs.v1.ScopeLogs; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.log.service.LogProtocolAdapter; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Adapter for OpenTelemetry OTLP/HTTP JSON log ingestion. + */ +@Slf4j +@Service +public class OtlpLogProtocolAdapter implements LogProtocolAdapter { + + private static final String SOURCE_NAME = "otlp"; + + @Override + public void ingest(String content) { + if (content == null || content.isEmpty()) { + log.warn("Received empty OTLP log payload - skip processing."); + return; + } + ExportLogsServiceRequest.Builder builder = ExportLogsServiceRequest.newBuilder(); + try { + JsonFormat.parser().ignoringUnknownFields().merge(content, builder); + ExportLogsServiceRequest request = builder.build(); + + // Extract LogEntry instances from the request + List logEntries = extractLogEntries(request); + log.info("Successfully extracted {} log entries from OTLP payload", logEntries.size()); + + // TODO: Persist / forward processed logs to data warehouse / queue + // For now, just log the entries for debugging + logEntries.forEach(entry -> log.info("Extracted log entry: {}", entry)); + + } catch (InvalidProtocolBufferException e) { + log.error("Failed to parse OTLP log payload: {}", e.getMessage()); + throw new IllegalArgumentException("Invalid OTLP log content", e); + } + } + + /** + * Extract LogEntry instances from ExportLogsServiceRequest. + * + * @param request the OTLP export logs service request + * @return list of extracted log entries + */ + private List extractLogEntries(ExportLogsServiceRequest request) { + List logEntries = new ArrayList<>(); + + for (ResourceLogs resourceLogs : request.getResourceLogsList()) { + // Extract resource attributes + Map resourceAttributes = extractAttributes( + resourceLogs.getResource().getAttributesList() + ); + + for (ScopeLogs scopeLogs : resourceLogs.getScopeLogsList()) { + // Extract instrumentation scope information + LogEntry.InstrumentationScope instrumentationScope = extractInstrumentationScope(scopeLogs); + + for (LogRecord logRecord : scopeLogs.getLogRecordsList()) { + LogEntry logEntry = convertLogRecordToLogEntry( + logRecord, + resourceAttributes, + instrumentationScope + ); + logEntries.add(logEntry); + } + } + } + + return logEntries; + } + + /** + * Convert OpenTelemetry LogRecord to LogEntry. + */ + private LogEntry convertLogRecordToLogEntry( + LogRecord logRecord, + Map resourceAttributes, + LogEntry.InstrumentationScope instrumentationScope) { + + return LogEntry.builder() + .timeUnixNano(logRecord.getTimeUnixNano()) + .observedTimeUnixNano(logRecord.getObservedTimeUnixNano()) + .severityNumber(logRecord.getSeverityNumberValue()) + .severityText(logRecord.getSeverityText()) + .body(extractBody(logRecord.getBody())) + .attributes(extractAttributes(logRecord.getAttributesList())) + .droppedAttributesCount(logRecord.getDroppedAttributesCount()) + .traceId(bytesToHex(logRecord.getTraceId().toByteArray())) + .spanId(bytesToHex(logRecord.getSpanId().toByteArray())) + .traceFlags(logRecord.getFlags()) + .resource(resourceAttributes) + .instrumentationScope(instrumentationScope) + .build(); + } + + /** + * Extract instrumentation scope information from ScopeLogs. + */ + private LogEntry.InstrumentationScope extractInstrumentationScope(ScopeLogs scopeLogs) { + if (!scopeLogs.hasScope()) { + return null; + } + + var scope = scopeLogs.getScope(); + return LogEntry.InstrumentationScope.builder() + .name(scope.getName()) + .version(scope.getVersion()) + .attributes(extractAttributes(scope.getAttributesList())) + .droppedAttributesCount(scope.getDroppedAttributesCount()) + .build(); + } + + /** + * Extract attributes from a list of KeyValue pairs. + */ + private Map extractAttributes(List keyValueList) { + Map attributes = new HashMap<>(); + for (KeyValue kv : keyValueList) { + attributes.put(kv.getKey(), extractAnyValue(kv.getValue())); + } + return attributes; + } + + /** + * Extract body content from AnyValue. + */ + private Object extractBody(AnyValue body) { + return extractAnyValue(body); + } + + /** + * Extract value from OpenTelemetry AnyValue. + */ + private Object extractAnyValue(AnyValue anyValue) { + switch (anyValue.getValueCase()) { + case STRING_VALUE: + return anyValue.getStringValue(); + case BOOL_VALUE: + return anyValue.getBoolValue(); + case INT_VALUE: + return anyValue.getIntValue(); + case DOUBLE_VALUE: + return anyValue.getDoubleValue(); + case ARRAY_VALUE: + List arrayList = new ArrayList<>(); + for (AnyValue item : anyValue.getArrayValue().getValuesList()) { + arrayList.add(extractAnyValue(item)); + } + return arrayList; + case KVLIST_VALUE: + Map kvMap = new HashMap<>(); + for (KeyValue kv : anyValue.getKvlistValue().getValuesList()) { + kvMap.put(kv.getKey(), extractAnyValue(kv.getValue())); + } + return kvMap; + case BYTES_VALUE: + return anyValue.getBytesValue().toByteArray(); + case VALUE_NOT_SET: + default: + return null; + } + } + + /** + * Convert byte array to hex string. + */ + private String bytesToHex(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return null; + } + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + @Override + public String supportSource() { + return SOURCE_NAME; + } +} \ No newline at end of file diff --git a/hertzbeat-manager/pom.xml b/hertzbeat-manager/pom.xml index 7351b3fc159..327c48a6646 100644 --- a/hertzbeat-manager/pom.xml +++ b/hertzbeat-manager/pom.xml @@ -89,11 +89,16 @@ org.apache.hertzbeat hertzbeat-grafana - + org.apache.hertzbeat hertzbeat-otel + + + org.apache.hertzbeat + hertzbeat-log + org.springframework.boot diff --git a/hertzbeat-manager/src/main/resources/sureness.yml b/hertzbeat-manager/src/main/resources/sureness.yml index d846c6a6165..337a658a393 100644 --- a/hertzbeat-manager/src/main/resources/sureness.yml +++ b/hertzbeat-manager/src/main/resources/sureness.yml @@ -79,6 +79,7 @@ excludedResource: - /api/push/**===* - /api/status/page/public/**===* - /api/manager/sse/**===* + - /api/logs/ingest/**===* # web ui resource - /===get - /assets/**===get diff --git a/material/licenses/LICENSE b/material/licenses/LICENSE index 216ef39fdec..4428fe93cde 100644 --- a/material/licenses/LICENSE +++ b/material/licenses/LICENSE @@ -438,6 +438,7 @@ The text of each license is the standard Apache 2.0 license. https://mvnrepository.com/artifact/io.opentelemetry.instrumentation/opentelemetry-spring-boot-starter-2.15.0 Apache-2.0 https://mvnrepository.com/artifact/io.opentelemetry.instrumentation/opentelemetry-logback-appender-1.0 Apache-2.0 https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper/3.9.3 Apache-2.0 + https://mvnrepository.com/artifact/io.opentelemetry.proto/opentelemetry-proto/1.7.0-alpha Apache-2.0 ======================================================================== BSD-2-Clause licenses diff --git a/material/licenses/backend/LICENSE b/material/licenses/backend/LICENSE index 492f0f3f8aa..826b50efcaf 100644 --- a/material/licenses/backend/LICENSE +++ b/material/licenses/backend/LICENSE @@ -436,6 +436,7 @@ The text of each license is the standard Apache 2.0 license. https://mvnrepository.com/artifact/com.google.flatbuffers/flatbuffers-java/1.12.0 https://mvnrepository.com/artifact/com.vesoft/client/3.6.0 https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper/3.9.3 Apache-2.0 + https://mvnrepository.com/artifact/io.opentelemetry.proto/opentelemetry-proto/1.7.0-alpha Apache-2.0 ======================================================================== diff --git a/pom.xml b/pom.xml index 359e2908928..dac892af539 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,7 @@ hertzbeat-e2e hertzbeat-base hertzbeat-mcp + hertzbeat-log @@ -177,6 +178,8 @@ 2.13.1 2.15.0 2.14.0-alpha + + 1.3.1-alpha @@ -235,12 +238,18 @@ hertzbeat-grafana ${hertzbeat.version} - + org.apache.hertzbeat hertzbeat-otel ${hertzbeat.version} + + + org.apache.hertzbeat + hertzbeat-log + ${hertzbeat.version} + org.apache.hertzbeat @@ -427,6 +436,10 @@ com.google.guava guava + + com.google.protobuf + protobuf-java + @@ -489,6 +502,17 @@ pom import + + io.opentelemetry.proto + opentelemetry-proto + ${opentelemetry-proto.version} + + + com.google.protobuf + protobuf-java + + + From abce871dd87701da9d5a97525f20e1b12a041bb3 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Wed, 2 Jul 2025 17:25:10 +0800 Subject: [PATCH 02/39] improvement: Add license --- .../hertzbeat/common/entity/log/LogEntry.java | 17 +++++++++++++++++ .../log/service/LogProtocolAdapter.java | 18 +++++++++++++++++- .../service/impl/OtlpLogProtocolAdapter.java | 17 +++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java index 2a625b1d251..970d04e7357 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/log/LogEntry.java @@ -1,3 +1,20 @@ +/* + * 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.hertzbeat.common.entity.log; import lombok.AllArgsConstructor; diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java index 924684fced1..0afb25089c5 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java @@ -1,9 +1,25 @@ +/* + * 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.hertzbeat.log.service; /** * Adapter interface for ingesting logs pushed via different protocols * (e.g. OTLP, Loki, Filebeat, Vector). - * * Implementations should: * 1. Parse raw HTTP payload of their protocol. * 2. Convert data to LogEntry. diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index d4da575b78c..32c70d8e92f 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -1,3 +1,20 @@ +/* + * 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.hertzbeat.log.service.impl; import com.google.protobuf.InvalidProtocolBufferException; From 05a410b79d2b618957c5a43613258a306ebaa07d Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Wed, 2 Jul 2025 17:31:52 +0800 Subject: [PATCH 03/39] improvement: modify source to protocol --- .../hertzbeat/log/controller/LogIngestionController.java | 6 +++--- .../apache/hertzbeat/log/service/LogProtocolAdapter.java | 2 +- .../hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java index e5ccb04ef4c..faeaf4de1ef 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java @@ -63,7 +63,7 @@ public ResponseEntity> ingestExternLog(@PathVariable("protocol") S protocol = DEFAULT_PROTOCOL; // Default to OTLP if no protocol specified } for (LogProtocolAdapter adapter : protocolAdapters) { - if (adapter.supportSource().equalsIgnoreCase(protocol)) { + if (adapter.supportProtocol().equalsIgnoreCase(protocol)) { try { adapter.ingest(content); return ResponseEntity.ok(Message.success("Add extern log success")); @@ -81,13 +81,13 @@ public ResponseEntity> ingestExternLog(@PathVariable("protocol") S /** * Receive default log payload (when protocol is not specified). - * It will look for a service whose supportSource() returns "otlp". + * It will look for a service whose supportProtocol() returns "otlp". */ @PostMapping("/api/logs/ingest") public ResponseEntity> ingestDefaultExternLog(@RequestBody String content) { log.info("Receive default extern log content, length: {}", content == null ? 0 : content.length()); LogProtocolAdapter adapter = protocolAdapters.stream() - .filter(item -> DEFAULT_PROTOCOL.equalsIgnoreCase(item.supportSource())) + .filter(item -> DEFAULT_PROTOCOL.equalsIgnoreCase(item.supportProtocol())) .findFirst() .orElse(null); if (adapter != null) { diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java index 0afb25089c5..c12987e2e44 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/LogProtocolAdapter.java @@ -37,5 +37,5 @@ public interface LogProtocolAdapter { /** * Identifier of the protocol this adapter supports ("otlp", "vector", etc.) */ - String supportSource(); + String supportProtocol(); } \ No newline at end of file diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index 32c70d8e92f..0afd89dcac8 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -42,7 +42,7 @@ @Service public class OtlpLogProtocolAdapter implements LogProtocolAdapter { - private static final String SOURCE_NAME = "otlp"; + private static final String PROTOCOL_NAME = "otlp"; @Override public void ingest(String content) { @@ -213,7 +213,7 @@ private String bytesToHex(byte[] bytes) { } @Override - public String supportSource() { - return SOURCE_NAME; + public String supportProtocol() { + return PROTOCOL_NAME; } } \ No newline at end of file From 420c9f012fad477ad90a82cdc1bc77673851e06c Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 3 Jul 2025 15:49:34 +0800 Subject: [PATCH 04/39] feat: add otlp log integration ui --- pom.xml | 4 - .../log-integration.component.html | 85 ++++++++++ .../log-integration.component.less | 84 ++++++++++ .../log-integration.component.spec.ts | 23 +++ .../log-integration.component.ts | 152 ++++++++++++++++++ .../src/app/routes/log/log-routing.module.ts | 33 ++++ web-app/src/app/routes/log/log.module.ts | 48 ++++++ .../src/app/routes/routes-routing.module.ts | 1 + web-app/src/assets/app-data.json | 14 ++ .../assets/doc/log-integration/otlp.en-US.md | 112 +++++++++++++ .../assets/doc/log-integration/otlp.zh-CN.md | 105 ++++++++++++ web-app/src/assets/i18n/en-US.json | 6 + web-app/src/assets/i18n/zh-CN.json | 12 +- web-app/src/assets/img/integration/otlp.svg | 6 + 14 files changed, 680 insertions(+), 5 deletions(-) create mode 100644 web-app/src/app/routes/log/log-integration/log-integration.component.html create mode 100644 web-app/src/app/routes/log/log-integration/log-integration.component.less create mode 100644 web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts create mode 100644 web-app/src/app/routes/log/log-integration/log-integration.component.ts create mode 100644 web-app/src/app/routes/log/log-routing.module.ts create mode 100644 web-app/src/app/routes/log/log.module.ts create mode 100644 web-app/src/assets/doc/log-integration/otlp.en-US.md create mode 100644 web-app/src/assets/doc/log-integration/otlp.zh-CN.md create mode 100644 web-app/src/assets/img/integration/otlp.svg diff --git a/pom.xml b/pom.xml index dac892af539..b034ec4baaf 100644 --- a/pom.xml +++ b/pom.xml @@ -436,10 +436,6 @@ com.google.guava guava - - com.google.protobuf - protobuf-java - diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.html b/web-app/src/app/routes/log/log-integration/log-integration.component.html new file mode 100644 index 00000000000..6b3e91c8765 --- /dev/null +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.html @@ -0,0 +1,85 @@ + + + + + + +
+
+

{{ 'log.integration.source' | i18n }}

+
+
+ + {{ source.name }} +
+
+
+
+ +
+

{{ selectedSource.name }}

+ +
+ +
+
+
+ + +
+
+

{{ 'log.integration.token.desc' | i18n }}

+
+
+
+ +
+
+ {{ token }} +
+
+
+

{{ 'log.integration.token.notice' | i18n }}

+
+
+
\ No newline at end of file diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.less b/web-app/src/app/routes/log/log-integration/log-integration.component.less new file mode 100644 index 00000000000..7ae298020a8 --- /dev/null +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.less @@ -0,0 +1,84 @@ +@import "~src/styles/theme"; + +.log-integration-container { + display: flex; + height: 100%; + background: @common-background-color; + border-radius: 4px; + + .data-sources { + width: 240px; + border-right: 1px solid #f0f0f0; + padding: 16px; + background: @common-background-color; + + h2 { + margin-bottom: 16px; + font-size: 16px; + font-weight: 500; + } + + .source-list { + .source-item { + display: flex; + align-items: center; + padding: 12px; + cursor: pointer; + border-radius: 4px; + transition: all 0.3s; + + &:hover { + background: rgba(0, 0, 0, 0.05); + } + + &.active { + background: rgba(0, 0, 0, 0.1); + } + + img { + width: 24px; + height: 24px; + margin-right: 8px; + } + } + } + } + + .doc-content { + flex: 1; + padding: 24px; + overflow-y: auto; + background: @common-background-color; + + h2 { + margin-bottom: 24px; + font-size: 20px; + font-weight: 500; + } + } +} + +[data-theme='dark'] { + :host { + .log-integration-container { + background: @common-background-color-dark; + .data-sources { + background: @common-background-color-dark; + } + .doc-content { + background: @common-background-color-dark; + } + .source-list { + .source-item { + &:hover { + background: rgba(255, 255, 255, 0.4); + } + + &.active { + background: rgba(255, 255, 255, 0.6); + } + } + } + } + } +} \ No newline at end of file diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts new file mode 100644 index 00000000000..e5d53607d68 --- /dev/null +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LogIntegrationComponent } from './log-integration.component'; + +describe('LogIntegrationComponent', () => { + let component: LogIntegrationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LogIntegrationComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LogIntegrationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.ts new file mode 100644 index 00000000000..8ca1b9de05c --- /dev/null +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.ts @@ -0,0 +1,152 @@ +/* + * 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. + */ + +import { CommonModule } from '@angular/common'; +import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { Component, Inject, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { I18NService } from '@core'; +import { ALAIN_I18N_TOKEN, I18nPipe } from '@delon/theme'; +import { SharedModule } from '@shared'; +import { NzDividerComponent } from 'ng-zorro-antd/divider'; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { NzNotificationService } from 'ng-zorro-antd/notification'; +import { MarkdownModule } from 'ngx-markdown'; + +import { AuthService } from '../../../service/auth.service'; + +interface DataSource { + id: string; + name: string; + icon: string; +} + +const MARKDOWN_DOC_PATH = './assets/doc/log-integration'; + +@Component({ + selector: 'app-log-integration', + standalone: true, + imports: [CommonModule, I18nPipe, MarkdownModule, HttpClientModule, NzDividerComponent, SharedModule], + templateUrl: './log-integration.component.html', + styleUrl: './log-integration.component.less' +}) +export class LogIntegrationComponent implements OnInit { + dataSources: DataSource[] = [ + { + id: 'otlp', + name: this.i18nSvc.fanyi('log.integration.source.otlp'), + icon: 'assets/img/integration/otlp.svg' + } + ]; + + selectedSource: DataSource | null = null; + markdownContent: string = ''; + token: string = ''; + isModalVisible: boolean = false; + generateLoading: boolean = false; + + constructor( + private http: HttpClient, + private authSvc: AuthService, + @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService, + private notifySvc: NzNotificationService, + private modal: NzModalService, + private router: Router, + private route: ActivatedRoute + ) {} + + ngOnInit() { + this.route.params.subscribe(params => { + const sourceId = params['source']; + if (sourceId) { + // Find matching data source + const source = this.dataSources.find(s => s.id === sourceId); + if (source) { + this.selectedSource = source; + } else { + // If no matching source found, use the first one as default + this.selectedSource = this.dataSources[0]; + this.router.navigate(['/log/integration/', this.selectedSource.id]); + } + } else { + // When no route params, use the first data source + this.selectedSource = this.dataSources[0]; + this.router.navigate(['/log/integration/', this.selectedSource.id]); + } + + if (this.selectedSource) { + this.loadMarkdownContent(this.selectedSource); + } + }); + } + + selectSource(source: DataSource) { + this.selectedSource = source; + this.loadMarkdownContent(source); + this.router.navigate(['/log/integration', source.id]); + } + + public generateToken() { + this.generateLoading = true; + this.authSvc.generateToken().subscribe(message => { + if (message.code === 0) { + this.token = message.data?.token; + this.isModalVisible = true; + } else { + this.notifySvc.warning('Failed to generate token', message.msg); + } + this.generateLoading = false; + }); + } + + handleCancel(): void { + this.isModalVisible = false; + this.token = ''; + } + + handleOk(): void { + this.isModalVisible = false; + this.token = ''; + } + + private loadMarkdownContent(source: DataSource) { + const lang = this.i18nSvc.currentLang; + const path = `${MARKDOWN_DOC_PATH}/${source.id}.${lang}.md`; + + this.http.get(path, { responseType: 'text' }).subscribe({ + next: content => { + this.markdownContent = content; + }, + error: error => { + const enPath = `${MARKDOWN_DOC_PATH}/${source.id}.en-US.md`; + this.http.get(enPath, { responseType: 'text' }).subscribe(content => (this.markdownContent = content)); + } + }); + } + + copyToken() { + const el = document.createElement('textarea'); + el.value = this.token; + document.body.appendChild(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.copy-success'), this.i18nSvc.fanyi('log.integration.token.notice')); + } +} diff --git a/web-app/src/app/routes/log/log-routing.module.ts b/web-app/src/app/routes/log/log-routing.module.ts new file mode 100644 index 00000000000..3674e029907 --- /dev/null +++ b/web-app/src/app/routes/log/log-routing.module.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { LogIntegrationComponent } from './log-integration/log-integration.component'; + +const routes: Routes = [ + { path: '', component: LogIntegrationComponent }, + { path: 'integration/:source', component: LogIntegrationComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class LogRoutingModule {} diff --git a/web-app/src/app/routes/log/log.module.ts b/web-app/src/app/routes/log/log.module.ts new file mode 100644 index 00000000000..cdfd962ec6d --- /dev/null +++ b/web-app/src/app/routes/log/log.module.ts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import { NgModule, Type } from '@angular/core'; +import { SharedModule } from '@shared'; +import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; +import { NzTableModule } from 'ng-zorro-antd/table'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzPaginationModule } from 'ng-zorro-antd/pagination'; +import { NzEmptyModule } from 'ng-zorro-antd/empty'; +import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb'; +import { LogIntegrationComponent } from './log-integration/log-integration.component'; +import { LogRoutingModule } from './log-routing.module'; + +const COMPONENTS: Array> = [ +]; + +@NgModule({ + imports: [ + SharedModule, + LogRoutingModule, + NzTableModule, + NzTagModule, + NzDatePickerModule, + NzPaginationModule, + NzEmptyModule, + NzBreadCrumbModule, + LogIntegrationComponent + ], + declarations: COMPONENTS +}) +export class LogModule {} diff --git a/web-app/src/app/routes/routes-routing.module.ts b/web-app/src/app/routes/routes-routing.module.ts index bdc9b4c1085..84b457d2472 100644 --- a/web-app/src/app/routes/routes-routing.module.ts +++ b/web-app/src/app/routes/routes-routing.module.ts @@ -28,6 +28,7 @@ const routes: Routes = [ data: { titleI18n: 'menu.monitor.center' } }, { path: 'alert', loadChildren: () => import('./alert/alert.module').then(m => m.AlertModule) }, + { path: 'log', loadChildren: () => import('./log/log.module').then(m => m.LogModule) }, { path: 'setting', loadChildren: () => import('./setting/setting.module').then(m => m.SettingModule) } ] }, diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index 5f55c39b36e..0ef708c3792 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -186,6 +186,20 @@ } ] }, + { + "text": "Log", + "i18n": "menu.log", + "group": true, + "hideInBreadcrumb": true, + "children": [ + { + "text": "Integration", + "i18n": "menu.log.integration", + "icon": "anticon-api", + "link": "/log/integration/otlp" + } + ] + }, { "text": "Advanced", "i18n": "menu.advanced", diff --git a/web-app/src/assets/doc/log-integration/otlp.en-US.md b/web-app/src/assets/doc/log-integration/otlp.en-US.md new file mode 100644 index 00000000000..c5eafceb25b --- /dev/null +++ b/web-app/src/assets/doc/log-integration/otlp.en-US.md @@ -0,0 +1,112 @@ +> HertzBeat supports OpenTelemetry Logs Protocol (OTLP), allowing external systems to push log data to the HertzBeat log platform via OTLP. + +### API Endpoint + +`POST /api/logs/ingest/otlp` + +Or use the default endpoint (automatically uses OTLP protocol): + +`POST /api/logs/ingest` + +### Request Headers + +- `Content-Type`: `application/json` +- `Authorization`: `Bearer {token}` + +### Request Body + +Supports standard OTLP JSON format log data: + +```json +{ + "resourceLogs": [ + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "my-service" + } + }, + { + "key": "service.version", + "value": { + "stringValue": "1.0.0" + } + } + ] + }, + "scopeLogs": [ + { + "scope": { + "name": "my-logger", + "version": "1.0.0" + }, + "logRecords": [ + { + "timeUnixNano": "1640995200000000000", + "severityNumber": 9, + "severityText": "INFO", + "body": { + "stringValue": "This is a log message" + }, + "attributes": [ + { + "key": "user.id", + "value": { + "stringValue": "12345" + } + } + ] + } + ] + } + ] + } + ] +} +``` + +### Configuration Examples + +#### OpenTelemetry Collector Configuration + +```yaml +exporters: + otlphttp: + endpoint: "http://{hertzbeat_host}:1157/api/logs/ingest/otlp" + headers: + authorization: "Bearer {token}" + +service: + pipelines: + logs: + receivers: [filelog] + processors: [batch] + exporters: [otlphttp] +``` + +### Configuration Verification + +1. Configure external systems to send OTLP logs to HertzBeat specified interface +2. Check received log data in HertzBeat log platform +3. Verify log data format and content correctness + +### Common Issues + +#### Log Sending Failures +- Ensure HertzBeat service address is accessible from external systems +- Check if Token is correctly configured +- Verify request header Content-Type is set to application/json + +#### Log Format Errors +- Ensure sending standard OTLP JSON format +- Check timestamp format is nanosecond precision +- Verify severityNumber value range (1-24) + +#### Performance Optimization Tips +- Use batch processing to send logs, reducing network requests +- Set appropriate log levels, avoid sending too many DEBUG logs + +For more information, please refer to [OpenTelemetry Logs Specification](https://opentelemetry.io/docs/specs/otel/logs/) \ No newline at end of file diff --git a/web-app/src/assets/doc/log-integration/otlp.zh-CN.md b/web-app/src/assets/doc/log-integration/otlp.zh-CN.md new file mode 100644 index 00000000000..a52064b5bbb --- /dev/null +++ b/web-app/src/assets/doc/log-integration/otlp.zh-CN.md @@ -0,0 +1,105 @@ +> HertzBeat 支持 OpenTelemetry Logs Protocol (OTLP) 协议,外部系统可以通过 OTLP 方式将日志数据推送到 HertzBeat 日志平台。 + +### 接口端点 + +`POST /api/logs/ingest/otlp` + +或使用默认接口(自动使用OTLP协议): + +`POST /api/logs/ingest` + +### 请求头 + +- `Content-Type`: `application/json` +- `Authorization`: `Bearer {token}` + +### 请求体 + +支持标准的 OTLP JSON 格式日志数据: + +```json +{ + "resourceLogs": [ + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "my-service" + } + }, + { + "key": "service.version", + "value": { + "stringValue": "1.0.0" + } + } + ] + }, + "scopeLogs": [ + { + "scope": { + "name": "my-logger", + "version": "1.0.0" + }, + "logRecords": [ + { + "timeUnixNano": "1640995200000000000", + "severityNumber": 9, + "severityText": "INFO", + "body": { + "stringValue": "This is a log message" + }, + "attributes": [ + { + "key": "user.id", + "value": { + "stringValue": "12345" + } + } + ] + } + ] + } + ] + } + ] +} +``` + +### 配置示例 + +#### OpenTelemetry Collector 配置 + +```yaml +exporters: + otlphttp: + logs_endpoint: http://{hertzbeat_host}:1157/api/logs/ingest/otlp + compression: none + encoding: json +``` + +### 配置验证 + +1. 配置外部系统发送OTLP日志到HertzBeat指定接口 +2. 在HertzBeat日志平台中查看接收到的日志数据 +3. 验证日志数据格式和内容是否正确 + +### 常见问题 + +#### 日志发送失败 +- 确保HertzBeat服务地址可以被外部系统访问 +- 检查Token是否正确配置 +- 验证请求头Content-Type设置为application/json + +#### 日志格式错误 +- 确保发送的是标准OTLP JSON格式 +- 检查时间戳格式是否为纳秒精度 +- 验证severityNumber值范围(1-24) + +#### 性能优化建议 +- 使用批处理方式发送日志,减少网络请求 +- 合理设置日志级别,避免发送过多DEBUG日志 + +更多信息请参考 [OpenTelemetry日志规范](https://opentelemetry.io/docs/specs/otel/logs/) \ No newline at end of file diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 62e33b669f8..286cc0385f0 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -107,6 +107,12 @@ "alert.integration.token.new": "Click to Generate Token", "alert.integration.token.notice": "Token only be displayed once. Please keep your token secure. Do not share it with others.", "alert.integration.token.title": "Access Token", + "log.help.integration": "Unified management of logs, integrating and receiving log messages from external log sources, and performing actions such as collection, storage, alerting, and analysis.", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.integration.source": "Integration Source", + "log.integration.source.otlp": "OTLP Protocol", + "log.integration.token.desc": "Token you generated that can be used to access the HertzBeat API.", + "log.integration.token.new": "Click to Generate Token", "alert.notice.receiver": "Notice Receiver", "alert.notice.receiver.delete": "Delete Receiver", "alert.notice.receiver.edit": "Edit Receiver", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index c57b2aa7610..6d5166d762a 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -102,11 +102,19 @@ "alert.integration.source.zabbix": "Zabbix", "alert.integration.source.alibabacloud-sls": "阿里云日志服务 SLS", "alert.integration.source.huaweicloud-ces": "华为云监控服务", - "alert.integration.source.volcengine":"火山引擎云监控", + "alert.integration.source.volcengine": "火山引擎云监控", "alert.integration.token.desc": "生成的 Token 可用于访问 HertzBeat API", "alert.integration.token.new": "点击生成 Token", "alert.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", "alert.integration.token.title": "访问认证 Token", + "log.help.integration": "统一管理日志,集成接入外部日志源的日志消息,对其进行收集,存储,告警和分析等。", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.integration.source": "集成日志源", + "log.integration.source.otlp": "OTLP 协议", + "log.integration.token.desc": "生成的 Token 可用于访问 HertzBeat 日志接入API", + "log.integration.token.new": "点击生成 Token", + "log.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", + "log.integration.token.title": "访问认证 Token", "alert.notice.receiver": "通知媒介", "alert.notice.receiver.delete": "删除接收对象", "alert.notice.receiver.edit": "编辑接收对象", @@ -585,6 +593,8 @@ "menu.alert.integration": "集成接入", "menu.alert.setting": "阈值规则", "menu.alert.silence": "告警静默", + "menu.log": "日志", + "menu.log.integration": "集成接入", "menu.clear.local.storage": "清理本地缓存", "menu.dashboard": "仪表盘", "menu.extras": "更多", diff --git a/web-app/src/assets/img/integration/otlp.svg b/web-app/src/assets/img/integration/otlp.svg new file mode 100644 index 00000000000..df48fd61185 --- /dev/null +++ b/web-app/src/assets/img/integration/otlp.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 5e50d5f124f37222bc80cac05c14f3f6e45a7b7b Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 3 Jul 2025 15:51:00 +0800 Subject: [PATCH 05/39] bugfix: fix error en doc --- .../src/assets/doc/log-integration/otlp.en-US.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/web-app/src/assets/doc/log-integration/otlp.en-US.md b/web-app/src/assets/doc/log-integration/otlp.en-US.md index c5eafceb25b..387472c53fc 100644 --- a/web-app/src/assets/doc/log-integration/otlp.en-US.md +++ b/web-app/src/assets/doc/log-integration/otlp.en-US.md @@ -75,16 +75,9 @@ Supports standard OTLP JSON format log data: ```yaml exporters: otlphttp: - endpoint: "http://{hertzbeat_host}:1157/api/logs/ingest/otlp" - headers: - authorization: "Bearer {token}" - -service: - pipelines: - logs: - receivers: [filelog] - processors: [batch] - exporters: [otlphttp] + logs_endpoint: http://{hertzbeat_host}:1157/api/logs/ingest/otlp + compression: none + encoding: json ``` ### Configuration Verification From a14a27939b7cc0a005461015de663f4d55bc0b07 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Fri, 4 Jul 2025 15:09:34 +0800 Subject: [PATCH 06/39] improvement: code style --- .../log/log-integration/log-integration.component.html | 2 +- .../log-integration/log-integration.component.spec.ts | 5 ++--- web-app/src/app/routes/log/log-routing.module.ts | 1 + web-app/src/app/routes/log/log.module.ts | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.html b/web-app/src/app/routes/log/log-integration/log-integration.component.html index 6b3e91c8765..9e3deba70f0 100644 --- a/web-app/src/app/routes/log/log-integration/log-integration.component.html +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.html @@ -82,4 +82,4 @@

{{ selectedSource.name }}

{{ 'log.integration.token.notice' | i18n }}

- \ No newline at end of file + diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts index e5d53607d68..92e8b52524c 100644 --- a/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts @@ -9,9 +9,8 @@ describe('LogIntegrationComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [LogIntegrationComponent] - }) - .compileComponents(); - + }).compileComponents(); + fixture = TestBed.createComponent(LogIntegrationComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/web-app/src/app/routes/log/log-routing.module.ts b/web-app/src/app/routes/log/log-routing.module.ts index 3674e029907..73562c633be 100644 --- a/web-app/src/app/routes/log/log-routing.module.ts +++ b/web-app/src/app/routes/log/log-routing.module.ts @@ -19,6 +19,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; + import { LogIntegrationComponent } from './log-integration/log-integration.component'; const routes: Routes = [ diff --git a/web-app/src/app/routes/log/log.module.ts b/web-app/src/app/routes/log/log.module.ts index cdfd962ec6d..bce994868e3 100644 --- a/web-app/src/app/routes/log/log.module.ts +++ b/web-app/src/app/routes/log/log.module.ts @@ -19,17 +19,17 @@ import { NgModule, Type } from '@angular/core'; import { SharedModule } from '@shared'; +import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb'; import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; +import { NzEmptyModule } from 'ng-zorro-antd/empty'; +import { NzPaginationModule } from 'ng-zorro-antd/pagination'; import { NzTableModule } from 'ng-zorro-antd/table'; import { NzTagModule } from 'ng-zorro-antd/tag'; -import { NzPaginationModule } from 'ng-zorro-antd/pagination'; -import { NzEmptyModule } from 'ng-zorro-antd/empty'; -import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb'; + import { LogIntegrationComponent } from './log-integration/log-integration.component'; import { LogRoutingModule } from './log-routing.module'; -const COMPONENTS: Array> = [ -]; +const COMPONENTS: Array> = []; @NgModule({ imports: [ From dd405a29096fa2d4dfbd5d70aa20a2bcb41b92d1 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sat, 5 Jul 2025 09:51:28 +0800 Subject: [PATCH 07/39] refactor: realtime to metrics_realtime and log_realtime, periodic to metrics_periodic and log_periodic --- .../calculate/PeriodicAlertRuleScheduler.java | 14 ++++-- .../calculate/RealTimeAlertCalculator.java | 2 +- .../alert/service/AlertDefineService.java | 2 +- .../service/impl/AlertDefineServiceImpl.java | 9 ++-- .../RealTimeAlertCalculatorMatchTest.java | 6 +-- .../alert/service/AlertDefineServiceTest.java | 10 ++-- .../common/constants/CommonConstants.java | 18 +++++-- .../common/entity/alerter/AlertDefine.java | 2 +- .../db/migration/h2/V173__update_column.sql | 30 ++++++++++++ .../migration/mysql/V173__update_column.sql | 49 +++++++++++++++++++ .../postgresql/V173__update_column.sql | 32 ++++++++++++ web-app/src/app/pojo/AlertDefine.ts | 4 +- .../alert-setting.component.html | 36 +++++++------- .../alert-setting/alert-setting.component.ts | 14 +++--- 14 files changed, 180 insertions(+), 48 deletions(-) create mode 100644 hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql create mode 100644 hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql create mode 100644 hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java index 95a3b69208e..5c7f86765f9 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java @@ -17,8 +17,11 @@ package org.apache.hertzbeat.alert.calculate; -import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_PERIODIC; +import static org.apache.hertzbeat.common.constants.CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC; import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -76,7 +79,8 @@ public void updateSchedule(AlertDefine rule) { return; } cancelSchedule(rule.getId()); - if (rule.getType().equals(ALERT_THRESHOLD_TYPE_PERIODIC)) { + if (rule.getType().equals(METRICS_ALERT_THRESHOLD_TYPE_PERIODIC) + || rule.getType().equals(LOG_ALERT_THRESHOLD_TYPE_PERIODIC)) { ScheduledFuture future = scheduledExecutor.scheduleAtFixedRate(() -> { calculator.calculate(rule); }, 0, rule.getPeriod(), java.util.concurrent.TimeUnit.SECONDS); @@ -87,7 +91,11 @@ public void updateSchedule(AlertDefine rule) { @Override public void run(String... args) throws Exception { log.info("Starting periodic alert rule scheduler..."); - List periodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(ALERT_THRESHOLD_TYPE_PERIODIC); + List metricsPeriodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRICS_ALERT_THRESHOLD_TYPE_PERIODIC); + List logPeriodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_PERIODIC); + List periodicRules = new ArrayList<>(metricsPeriodicRules.size() + logPeriodicRules.size()); + periodicRules.addAll(metricsPeriodicRules); + periodicRules.addAll(logPeriodicRules); for (AlertDefine rule : periodicRules) { updateSchedule(rule); } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java index 79c6583cbed..1da4af4dd7b 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java @@ -146,7 +146,7 @@ private void calculate(CollectRep.MetricsData metricsData) { int code = metricsData.getCode().getNumber(); Map labels = metricsData.getLabels(); Map annotations = metricsData.getAnnotations(); - List thresholds = this.alertDefineService.getRealTimeAlertDefines(); + List thresholds = this.alertDefineService.getMetricsRealTimeAlertDefines(); // Filter thresholds by app, metrics, labels and instance thresholds = filterThresholdsByAppAndMetrics(thresholds, app, metrics, labels, instance, priority); if (thresholds.isEmpty()) { diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java index 261b121cae9..0cb4d22c12a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java @@ -109,7 +109,7 @@ public interface AlertDefineService { * Get the real-time alarm definition list * @return Real-time alarm definition list */ - List getRealTimeAlertDefines(); + List getMetricsRealTimeAlertDefines(); /** * Get define preview diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index 8cce4294b87..3253addaa4a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -87,7 +87,8 @@ public AlertDefineServiceImpl(List alertDefineImExpo @Override public void validate(AlertDefine alertDefine, boolean isModify) throws IllegalArgumentException { if (StringUtils.hasText(alertDefine.getExpr())) { - if (CommonConstants.ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType())) { + if (CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType()) + || CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType())) { try { JexlExpressionRunner.compile(alertDefine.getExpr()); } catch (Exception e) { @@ -214,10 +215,10 @@ public void importConfig(MultipartFile file) throws Exception { } @Override - public List getRealTimeAlertDefines() { + public List getMetricsRealTimeAlertDefines() { List alertDefines = CacheFactory.getAlertDefineCache(); if (alertDefines == null) { - alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.ALERT_THRESHOLD_TYPE_REALTIME); + alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME); CacheFactory.setAlertDefineCache(alertDefines); } return alertDefines; @@ -229,7 +230,7 @@ public List> getDefinePreview(String datasource, String type return Collections.emptyList(); } switch (type) { - case CommonConstants.ALERT_THRESHOLD_TYPE_PERIODIC: + case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC: return dataSourceService.calculate(datasource, expr); default: log.error("Get define preview unsupported type: {}", type); diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java index f5a07c388ec..a82f90b0190 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java @@ -144,7 +144,7 @@ void testPrometheusReplaceMultipleJobsApp() throws InterruptedException { List allDefines = Collections.singletonList(matchDefine); - when(alertDefineService.getRealTimeAlertDefines()).thenReturn(allDefines); + when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); realTimeAlertCalculator.startCalculate(); @@ -187,7 +187,7 @@ void testPrometheusReplaceApp() throws InterruptedException { List allDefines = Collections.singletonList(matchDefine); - when(alertDefineService.getRealTimeAlertDefines()).thenReturn(allDefines); + when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); realTimeAlertCalculator.startCalculate(); @@ -236,7 +236,7 @@ void testCalculateWithNormalApp() throws InterruptedException { List allDefines = Collections.singletonList(matchDefine); - when(alertDefineService.getRealTimeAlertDefines()).thenReturn(allDefines); + when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); realTimeAlertCalculator.startCalculate(); diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java index 49ffd3120ee..cb5175af269 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java @@ -39,8 +39,8 @@ import java.util.Map; import java.util.Optional; -import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_PERIODIC; -import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_REALTIME; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -164,14 +164,14 @@ void getDefinePreview() { } }; when(dataSourceService.calculate(eq("promql"), eq(expr))).thenReturn(Lists.newArrayList(countValue1)); - List> result = alertDefineService.getDefinePreview("promql", ALERT_THRESHOLD_TYPE_PERIODIC, expr); + List> result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_PERIODIC, expr); assertNotNull(result); assertEquals(1307, result.get(0).get("__value__")); - result = alertDefineService.getDefinePreview("promql", ALERT_THRESHOLD_TYPE_PERIODIC, null); + result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_PERIODIC, null); assertEquals(0, result.size()); - result = alertDefineService.getDefinePreview("promql", ALERT_THRESHOLD_TYPE_REALTIME, null); + result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_REALTIME, null); assertEquals(0, result.size()); } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java index 0e036d30109..01fe6ba35bf 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java @@ -148,14 +148,24 @@ public interface CommonConstants { String ALERT_STATUS_PENDING = "pending"; /** - * alert threshold type: realtime + * metrics alert threshold type: realtime */ - String ALERT_THRESHOLD_TYPE_REALTIME = "realtime"; + String METRICS_ALERT_THRESHOLD_TYPE_REALTIME = "metrics_realtime"; /** - * alert threshold type: periodic + * metrics alert threshold type: periodic */ - String ALERT_THRESHOLD_TYPE_PERIODIC = "periodic"; + String METRICS_ALERT_THRESHOLD_TYPE_PERIODIC = "metrics_periodic"; + + /** + * log alert threshold type: realtime + */ + String LOG_ALERT_THRESHOLD_TYPE_REALTIME = "log_realtime"; + + /** + * log alert threshold type: periodic + */ + String LOG_ALERT_THRESHOLD_TYPE_PERIODIC = "log_periodic"; /** * Field parameter type: number diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java index de058ec5314..98de8b6d2b2 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java @@ -65,7 +65,7 @@ public class AlertDefine { @NotNull private String name; - @Schema(title = "Rule Type: realtime, periodic", example = "0") + @Schema(title = "Rule Type: metrics_realtime, metrics_periodic, log_realtime, log_periodic", example = "metrics_realtime") private String type; @Schema(title = "Alarm Threshold Expr", example = "usage>90", accessMode = READ_WRITE) diff --git a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql new file mode 100644 index 00000000000..3aa262cf29c --- /dev/null +++ b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql @@ -0,0 +1,30 @@ +-- 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. + +-- ensure every sql can rerun without error + +-- Update hzb_alert_define table type column to support log monitoring + +-- Update type from 'realtime' to 'metrics_realtime' +UPDATE HZB_ALERT_DEFINE +SET type = 'metrics_realtime' +WHERE type = 'realtime'; + +-- Update type from 'periodic' to 'metrics_periodic' +UPDATE HZB_ALERT_DEFINE +SET type = 'metrics_periodic' +WHERE type = 'periodic'; diff --git a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql new file mode 100644 index 00000000000..8ff8b593017 --- /dev/null +++ b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql @@ -0,0 +1,49 @@ +-- 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. + +-- ensure every sql can rerun without error + +-- Update hzb_alert_define table type column to support log monitoring + +DELIMITER // +CREATE PROCEDURE UpdateAlertDefineType() +BEGIN + DECLARE table_exists INT; + + -- Check if the table exists + SELECT COUNT(*) INTO table_exists + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE'; + + IF table_exists = 1 THEN + -- Update type from 'realtime' to 'metrics_realtime' + UPDATE HZB_ALERT_DEFINE + SET type = 'metrics_realtime' + WHERE type = 'realtime'; + + -- Update type from 'periodic' to 'metrics_periodic' + UPDATE HZB_ALERT_DEFINE + SET type = 'metrics_periodic' + WHERE type = 'periodic'; + END IF; +END // + +DELIMITER ; + +CALL UpdateAlertDefineType(); +DROP PROCEDURE IF EXISTS UpdateAlertDefineType; +COMMIT; \ No newline at end of file diff --git a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql new file mode 100644 index 00000000000..1346668f778 --- /dev/null +++ b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql @@ -0,0 +1,32 @@ +-- 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. + +-- ensure every sql can rerun without error + +-- Update hzb_alert_define table type column to support log monitoring + +-- Update type from 'realtime' to 'metrics_realtime' +UPDATE HZB_ALERT_DEFINE +SET type = 'metrics_realtime' +WHERE type = 'realtime'; + +-- Update type from 'periodic' to 'metrics_periodic' +UPDATE HZB_ALERT_DEFINE +SET type = 'metrics_periodic' +WHERE type = 'periodic'; + +commit; diff --git a/web-app/src/app/pojo/AlertDefine.ts b/web-app/src/app/pojo/AlertDefine.ts index bfaf6dbf0fe..fa696104852 100644 --- a/web-app/src/app/pojo/AlertDefine.ts +++ b/web-app/src/app/pojo/AlertDefine.ts @@ -20,8 +20,8 @@ export class AlertDefine { id!: number; name!: string; - // realtime, periodic - type: string = 'realtime'; + // metrics_realtime, metrics_periodic, log_realtime, log_periodic + type: string = 'metrics_realtime'; // datasource when type is periodic, promql | sql datasource: string = 'promql'; expr!: string; diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 13bd055b587..7366997a5d6 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -113,10 +113,10 @@ {{ data.name }} - + {{ 'alert.setting.type.realtime' | i18n }} - + {{ 'alert.setting.type.periodic' | i18n }} @@ -141,7 +141,9 @@ nz-tooltip nzType="primary" [nzLoading]="isLoadingEdit === data.id" - [nzTooltipTitle]="data.type == 'realtime' ? ('alert.setting.edit.realtime' | i18n) : ('alert.setting.edit.periodic' | i18n)" + [nzTooltipTitle]=" + data.type == 'metrics_realtime' ? ('alert.setting.edit.realtime' | i18n) : ('alert.setting.edit.periodic' | i18n) + " > @@ -176,10 +178,10 @@ [(nzVisible)]="isManageModalVisible" [nzTitle]=" isManageModalAdd - ? define.type == 'periodic' + ? define.type == 'metrics_periodic' ? ('alert.setting.new.periodic' | i18n) : ('alert.setting.new.realtime' | i18n) - : define.type == 'periodic' + : define.type == 'metrics_periodic' ? ('alert.setting.edit.periodic' | i18n) : ('alert.setting.edit.realtime' | i18n) " @@ -199,7 +201,7 @@ - + {{ 'alert.setting.target' | i18n }} @@ -216,7 +218,7 @@ > - + {{ 'alert.setting.rule' | i18n }} @@ -247,7 +249,7 @@ - +
@@ -289,7 +291,7 @@
- + @@ -398,7 +400,7 @@ - + {{ 'alert.setting.bind.monitors' | i18n }} @@ -409,7 +411,7 @@ - + {{ 'alert.setting.preview.expr' | i18n }} @@ -419,7 +421,7 @@ - + {{ 'alert.setting.rule' | i18n }} @@ -433,7 +435,7 @@ - + @@ -479,7 +481,7 @@ - + {{ 'alert.setting.period' | i18n }} @@ -531,7 +533,7 @@
-
+
{{ env.name }} @@ -764,12 +766,12 @@

EXCEL

>
- +

{{ 'alert.setting.type.realtime' | i18n }}

{{ 'alert.setting.type.realtime.desc' | i18n }}

- +

{{ 'alert.setting.type.periodic' | i18n }}

{{ 'alert.setting.type.periodic.desc' | i18n }}

diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index d21a3efd52a..7423f44b98b 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -253,8 +253,8 @@ export class AlertSettingComponent implements OnInit { this.userExpr = ''; this.selectedMonitorIds = new Set(); this.selectedLabels = new Set(); - // Set default period for periodic alert - if (type === 'periodic') { + // Set default period for metrics_periodic alert + if (type === 'metrics_periodic') { this.define.period = 300; } this.resetQbDataDefault(); @@ -493,15 +493,15 @@ export class AlertSettingComponent implements OnInit { if (this.define.labels && this.define.labels['severity']) { this.severity = this.define.labels['severity']; } - // Set default period for periodic alert if not set - if (this.define.type === 'periodic' && !this.define.period) { + // Set default period for metrics_periodic alert if not set + if (this.define.type === 'metrics_periodic' && !this.define.period) { this.define.period = 300; } - // Set default type as realtime if not set + // Set default type as metrics_realtime if not set if (!this.define.type) { - this.define.type = 'realtime'; + this.define.type = 'metrics_realtime'; } - if (this.define.type == 'realtime') { + if (this.define.type == 'metrics_realtime') { // Parse expression to cascade values this.cascadeValues = this.exprToCascadeValues(this.define.expr); this.userExpr = this.exprToUserExpr(this.define.expr); From 600e50548ba24bb18f4c17e2f60d31c890c52a86 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sat, 5 Jul 2025 16:03:38 +0800 Subject: [PATCH 08/39] feat: support send logEntry to commonDataQueue --- .../dispatch/export/NettyDataQueue.java | 11 +++ .../common/config/CommonProperties.java | 9 +++ .../common/queue/CommonDataQueue.java | 15 ++++ .../queue/impl/InMemoryCommonDataQueue.java | 14 ++++ .../queue/impl/KafkaCommonDataQueue.java | 42 +++++++++++ .../queue/impl/RedisCommonDataQueue.java | 30 ++++++++ .../serialize/KafkaLogEntryDeserializer.java | 70 +++++++++++++++++ .../serialize/KafkaLogEntrySerializer.java | 70 +++++++++++++++++ .../common/serialize/RedisLogEntryCodec.java | 75 +++++++++++++++++++ 9 files changed, 336 insertions(+) create mode 100644 hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntryDeserializer.java create mode 100644 hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntrySerializer.java create mode 100644 hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/RedisLogEntryCodec.java diff --git a/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java b/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java index 1e5830e5262..a39deeea69c 100644 --- a/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java +++ b/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -87,4 +88,14 @@ public void sendMetricsDataToStorage(CollectRep.MetricsData metricsData) { public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { collectJobService.sendAsyncServiceDiscoveryData(metricsData); } + + @Override + public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + + } + + @Override + public LogEntry pollLogEntry() throws InterruptedException { + return null; + } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java index b35fb54ce71..f69d4721f9c 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java @@ -115,6 +115,11 @@ public static class RedisProperties { */ private String alertsDataQueueName; + /** + * Queue name for log entry data + */ + private String logEntryQueueName; + } /** @@ -140,5 +145,9 @@ public static class KafkaProperties extends BaseKafkaProperties { * alerts data topic */ private String alertsDataTopic; + /** + * log entry data topic + */ + private String logEntryDataTopic; } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java index fd234fa27d4..cdc2dae256b 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java @@ -17,6 +17,7 @@ package org.apache.hertzbeat.common.queue; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; /** @@ -62,4 +63,18 @@ public interface CommonDataQueue { * @param metricsData service discovery data */ void sendServiceDiscoveryData(CollectRep.MetricsData metricsData); + + /** + * send log entry to queue + * @param logEntry log entry data based on OpenTelemetry log data model + * @throws InterruptedException when sending is interrupted + */ + void sendLogEntry(LogEntry logEntry) throws InterruptedException; + + /** + * poll log entry from queue + * @return log entry data + * @throws InterruptedException when poll timeout + */ + LogEntry pollLogEntry() throws InterruptedException; } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java index 396935f2d05..cb238a9fe47 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java @@ -22,6 +22,7 @@ import java.util.concurrent.LinkedBlockingQueue; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.constants.DataQueueConstants; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.springframework.beans.factory.DisposableBean; @@ -46,11 +47,13 @@ public class InMemoryCommonDataQueue implements CommonDataQueue, DisposableBean private final LinkedBlockingQueue metricsDataToAlertQueue; private final LinkedBlockingQueue metricsDataToStorageQueue; private final LinkedBlockingQueue serviceDiscoveryDataQueue; + private final LinkedBlockingQueue logEntryQueue; public InMemoryCommonDataQueue() { metricsDataToAlertQueue = new LinkedBlockingQueue<>(); metricsDataToStorageQueue = new LinkedBlockingQueue<>(); serviceDiscoveryDataQueue = new LinkedBlockingQueue<>(); + logEntryQueue = new LinkedBlockingQueue<>(); } public Map getQueueSizeMetricsInfo() { @@ -90,10 +93,21 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { serviceDiscoveryDataQueue.offer(metricsData); } + @Override + public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + logEntryQueue.put(logEntry); + } + + @Override + public LogEntry pollLogEntry() throws InterruptedException { + return logEntryQueue.take(); + } + @Override public void destroy() { metricsDataToAlertQueue.clear(); metricsDataToStorageQueue.clear(); serviceDiscoveryDataQueue.clear(); + logEntryQueue.clear(); } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java index d829178b0db..9feed8f5657 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java @@ -26,8 +26,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.config.CommonProperties; import org.apache.hertzbeat.common.constants.DataQueueConstants; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.apache.hertzbeat.common.serialize.KafkaLogEntryDeserializer; +import org.apache.hertzbeat.common.serialize.KafkaLogEntrySerializer; import org.apache.hertzbeat.common.serialize.KafkaMetricsDataDeserializer; import org.apache.hertzbeat.common.serialize.KafkaMetricsDataSerializer; import org.apache.kafka.clients.consumer.ConsumerConfig; @@ -59,14 +62,18 @@ public class KafkaCommonDataQueue implements CommonDataQueue, DisposableBean { private final ReentrantLock metricDataToAlertLock = new ReentrantLock(); private final ReentrantLock metricDataToStorageLock = new ReentrantLock(); private final ReentrantLock serviceDiscoveryDataLock = new ReentrantLock(); + private final ReentrantLock logEntryLock = new ReentrantLock(); private final LinkedBlockingQueue metricsDataToAlertQueue; private final LinkedBlockingQueue metricsDataToStorageQueue; private final LinkedBlockingQueue serviceDiscoveryDataQueue; + private final LinkedBlockingQueue logEntryQueue; private final CommonProperties.KafkaProperties kafka; private KafkaProducer metricsDataProducer; + private KafkaProducer logEntryProducer; private KafkaConsumer metricsDataToAlertConsumer; private KafkaConsumer metricsDataToStorageConsumer; private KafkaConsumer serviceDiscoveryDataConsumer; + private KafkaConsumer logEntryConsumer; public KafkaCommonDataQueue(CommonProperties properties) { if (properties == null || properties.getQueue() == null || properties.getQueue().getKafka() == null) { @@ -77,6 +84,7 @@ public KafkaCommonDataQueue(CommonProperties properties) { metricsDataToAlertQueue = new LinkedBlockingQueue<>(); metricsDataToStorageQueue = new LinkedBlockingQueue<>(); serviceDiscoveryDataQueue = new LinkedBlockingQueue<>(); + logEntryQueue = new LinkedBlockingQueue<>(); initDataQueue(); } @@ -87,6 +95,7 @@ private void initDataQueue() { producerConfig.put(ProducerConfig.ACKS_CONFIG, "all"); producerConfig.put(ProducerConfig.RETRIES_CONFIG, 3); metricsDataProducer = new KafkaProducer<>(producerConfig, new LongSerializer(), new KafkaMetricsDataSerializer()); + logEntryProducer = new KafkaProducer<>(producerConfig, new LongSerializer(), new KafkaLogEntrySerializer()); Map consumerConfig = new HashMap<>(4); consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getServers()); @@ -111,6 +120,11 @@ private void initDataQueue() { serviceDiscoveryDataConsumer = new KafkaConsumer<>(serviceDiscoveryDataConsumerConfig, new LongDeserializer(), new KafkaMetricsDataDeserializer()); serviceDiscoveryDataConsumer.subscribe(Collections.singletonList(kafka.getServiceDiscoveryDataTopic())); + + Map logEntryConsumerConfig = new HashMap<>(consumerConfig); + logEntryConsumerConfig.put("group.id", "log-entry-consumer"); + logEntryConsumer = new KafkaConsumer<>(logEntryConsumerConfig, new LongDeserializer(), new KafkaLogEntryDeserializer()); + logEntryConsumer.subscribe(Collections.singletonList(kafka.getLogEntryDataTopic())); } catch (Exception e) { log.error("please config common.queue.kafka props correctly", e); throw e; @@ -192,6 +206,28 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } } + @Override + public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + if (logEntryProducer != null) { + try { + ProducerRecord record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry); + logEntryProducer.send(record); + } catch (Exception e) { + log.error("Failed to send LogEntry to Kafka: {}", e.getMessage()); + // Fallback to memory queue if Kafka fails + logEntryQueue.put(logEntry); + } + } else { + log.warn("logEntryProducer is not enabled, using memory queue"); + logEntryQueue.put(logEntry); + } + } + + @Override + public LogEntry pollLogEntry() throws InterruptedException { + return genericPollDataFunction(logEntryQueue, logEntryConsumer, logEntryLock); + } + @Override public void destroy() throws Exception { if (metricsDataProducer != null) { @@ -206,5 +242,11 @@ public void destroy() throws Exception { if (serviceDiscoveryDataConsumer != null) { serviceDiscoveryDataConsumer.close(); } + if (logEntryProducer != null) { + logEntryProducer.close(); + } + if (logEntryConsumer != null) { + logEntryConsumer.close(); + } } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java index 696350f0ca4..e3851175123 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java @@ -24,8 +24,10 @@ import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.config.CommonProperties; import org.apache.hertzbeat.common.constants.DataQueueConstants; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.apache.hertzbeat.common.serialize.RedisLogEntryCodec; import org.apache.hertzbeat.common.serialize.RedisMetricsDataCodec; import org.springframework.beans.factory.DisposableBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -46,9 +48,12 @@ public class RedisCommonDataQueue implements CommonDataQueue, DisposableBean { private final RedisClient redisClient; private final StatefulRedisConnection connection; private final RedisCommands syncCommands; + private final StatefulRedisConnection logEntryConnection; + private final RedisCommands logEntrySyncCommands; private final String metricsDataQueueNameToStorage; private final String metricsDataQueueNameForServiceDiscovery; private final String metricsDataQueueNameToAlerter; + private final String logEntryQueueName; private final CommonProperties.RedisProperties redisProperties; public RedisCommonDataQueue(CommonProperties properties) { @@ -69,9 +74,13 @@ public RedisCommonDataQueue(CommonProperties properties) { RedisMetricsDataCodec codec = new RedisMetricsDataCodec(); this.connection = redisClient.connect(codec); this.syncCommands = connection.sync(); + RedisLogEntryCodec logCodec = new RedisLogEntryCodec(); + this.logEntryConnection = redisClient.connect(logCodec); + this.logEntrySyncCommands = logEntryConnection.sync(); this.metricsDataQueueNameToStorage = redisProperties.getMetricsDataQueueNameToPersistentStorage(); this.metricsDataQueueNameForServiceDiscovery = redisProperties.getMetricsDataQueueNameForServiceDiscovery(); this.metricsDataQueueNameToAlerter = redisProperties.getMetricsDataQueueNameToAlerter(); + this.logEntryQueueName = redisProperties.getLogEntryQueueName(); } @Override @@ -131,9 +140,30 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } } + @Override + public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + try { + logEntrySyncCommands.lpush(logEntryQueueName, logEntry); + } catch (Exception e) { + log.error("Failed to send LogEntry to Redis: {}", e.getMessage()); + throw new InterruptedException("Failed to send LogEntry to Redis"); + } + } + + @Override + public LogEntry pollLogEntry() throws InterruptedException { + try { + return logEntrySyncCommands.rpop(logEntryQueueName); + } catch (Exception e) { + log.error("Failed to poll LogEntry from Redis: {}", e.getMessage()); + throw new InterruptedException("Failed to poll LogEntry from Redis"); + } + } + @Override public void destroy() { connection.close(); + logEntryConnection.close(); redisClient.shutdown(); } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntryDeserializer.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntryDeserializer.java new file mode 100644 index 00000000000..ef19f4c74c5 --- /dev/null +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntryDeserializer.java @@ -0,0 +1,70 @@ +/* + * 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.hertzbeat.common.serialize; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.kafka.common.header.Headers; +import org.apache.kafka.common.serialization.Deserializer; + +/** + * Kafka LogEntry deserializer using JSON + */ +@Slf4j +public class KafkaLogEntryDeserializer implements Deserializer { + + private final ObjectMapper objectMapper; + + public KafkaLogEntryDeserializer() { + this.objectMapper = new ObjectMapper(); + } + + @Override + public void configure(Map configs, boolean isKey) { + Deserializer.super.configure(configs, isKey); + } + + @Override + public LogEntry deserialize(String topic, byte[] data) { + if (data == null || data.length == 0) { + log.warn("Empty data received for topic: {}", topic); + return null; + } + try { + String jsonString = new String(data, StandardCharsets.UTF_8); + return objectMapper.readValue(jsonString, LogEntry.class); + } catch (JsonProcessingException e) { + log.error("Failed to deserialize LogEntry from JSON for topic: {}", topic, e); + return null; + } + } + + @Override + public LogEntry deserialize(String topic, Headers headers, byte[] data) { + return deserialize(topic, data); + } + + @Override + public void close() { + Deserializer.super.close(); + } +} \ No newline at end of file diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntrySerializer.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntrySerializer.java new file mode 100644 index 00000000000..e64b7588a2e --- /dev/null +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/KafkaLogEntrySerializer.java @@ -0,0 +1,70 @@ +/* + * 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.hertzbeat.common.serialize; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.kafka.common.header.Headers; +import org.apache.kafka.common.serialization.Serializer; + +/** + * Kafka LogEntry serializer using JSON + */ +@Slf4j +public class KafkaLogEntrySerializer implements Serializer { + + private final ObjectMapper objectMapper; + + public KafkaLogEntrySerializer() { + this.objectMapper = new ObjectMapper(); + } + + @Override + public void configure(Map configs, boolean isKey) { + Serializer.super.configure(configs, isKey); + } + + @Override + public byte[] serialize(String topic, LogEntry logEntry) { + if (logEntry == null) { + log.warn("LogEntry is null for topic: {}", topic); + return null; + } + try { + String jsonString = objectMapper.writeValueAsString(logEntry); + return jsonString.getBytes(StandardCharsets.UTF_8); + } catch (JsonProcessingException e) { + log.error("Failed to serialize LogEntry to JSON for topic: {}", topic, e); + return null; + } + } + + @Override + public byte[] serialize(String topic, Headers headers, LogEntry logEntry) { + return serialize(topic, logEntry); + } + + @Override + public void close() { + Serializer.super.close(); + } +} \ No newline at end of file diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/RedisLogEntryCodec.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/RedisLogEntryCodec.java new file mode 100644 index 00000000000..82d4312ca3d --- /dev/null +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/RedisLogEntryCodec.java @@ -0,0 +1,75 @@ +/* + * 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.hertzbeat.common.serialize; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.lettuce.core.codec.RedisCodec; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.log.LogEntry; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +/** + * Redis LogEntry codec using JSON serialization + */ +@Slf4j +public class RedisLogEntryCodec implements RedisCodec { + + private final ObjectMapper objectMapper; + + public RedisLogEntryCodec() { + this.objectMapper = new ObjectMapper(); + } + + @Override + public String decodeKey(ByteBuffer byteBuffer) { + return Unpooled.wrappedBuffer(byteBuffer).toString(StandardCharsets.UTF_8); + } + + @Override + public LogEntry decodeValue(ByteBuffer byteBuffer) { + if (byteBuffer == null || !byteBuffer.hasRemaining()) { + return null; + } + try { + String jsonString = Unpooled.wrappedBuffer(byteBuffer).toString(StandardCharsets.UTF_8); + return objectMapper.readValue(jsonString, LogEntry.class); + } catch (Exception e) { + log.error("Failed to decode LogEntry from JSON: {}", e.getMessage()); + return null; + } + } + + @Override + public ByteBuffer encodeKey(String s) { + return ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public ByteBuffer encodeValue(LogEntry logEntry) { + try { + String jsonString = objectMapper.writeValueAsString(logEntry); + return ByteBuffer.wrap(jsonString.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + log.error("Failed to encode LogEntry to JSON: {}", e.getMessage()); + return null; + } + } +} \ No newline at end of file From 1cd5f851b515d4cf096e9eb8909e198e08af3b12 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sat, 5 Jul 2025 17:18:39 +0800 Subject: [PATCH 09/39] feat: send log to commonDataQueue --- .../service/impl/OtlpLogProtocolAdapter.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index 0afd89dcac8..55d6e2d39e2 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -27,6 +27,7 @@ import io.opentelemetry.proto.logs.v1.ScopeLogs; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.apache.hertzbeat.log.service.LogProtocolAdapter; import org.springframework.stereotype.Service; @@ -44,6 +45,12 @@ public class OtlpLogProtocolAdapter implements LogProtocolAdapter { private static final String PROTOCOL_NAME = "otlp"; + private final CommonDataQueue commonDataQueue; + + public OtlpLogProtocolAdapter(CommonDataQueue commonDataQueue) { + this.commonDataQueue = commonDataQueue; + } + @Override public void ingest(String content) { if (content == null || content.isEmpty()) { @@ -59,9 +66,14 @@ public void ingest(String content) { List logEntries = extractLogEntries(request); log.info("Successfully extracted {} log entries from OTLP payload", logEntries.size()); - // TODO: Persist / forward processed logs to data warehouse / queue - // For now, just log the entries for debugging - logEntries.forEach(entry -> log.info("Extracted log entry: {}", entry)); + logEntries.forEach(entry -> { + try { + commonDataQueue.sendLogEntry(entry); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("Failed to send log entry to queue: {}", e.getMessage()); + } + }); } catch (InvalidProtocolBufferException e) { log.error("Failed to parse OTLP log payload: {}", e.getMessage()); From d12e0d68434b9c3a200312eeb3397e67c4e5e4ed Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 13 Jul 2025 11:19:45 +0800 Subject: [PATCH 10/39] feat: Add log realtime calculator and some ui --- .../AbstractRealTimeAlertCalculator.java | 241 ++++++++++++++ .../calculate/LogRealTimeAlertCalculator.java | 120 +++++++ ...va => MetricsRealTimeAlertCalculator.java} | 173 ++-------- .../alert/service/AlertDefineService.java | 18 +- .../service/impl/AlertDefineServiceImpl.java | 35 +- .../alert/util/AlertTemplateUtil.java | 122 ++++++- .../LogRealTimeAlertCalculatorTest.java | 300 ++++++++++++++++++ ...ricsRealTimeAlertCalculatorMatchTest.java} | 14 +- ...> MetricsRealTimeAlertCalculatorTest.java} | 6 +- .../alert/util/AlertTemplateUtilTest.java | 195 ++++++++++++ .../hertzbeat/common/cache/CacheFactory.java | 46 ++- .../common/constants/CommonConstants.java | 9 +- .../service/impl/OtlpLogProtocolAdapter.java | 43 ++- .../apache/hertzbeat/manager/ManagerTest.java | 4 +- .../alert-setting.component.html | 281 ++++++++++++++-- .../alert-setting/alert-setting.component.ts | 225 +++++++++++-- web-app/src/assets/i18n/en-US.json | 14 +- web-app/src/assets/i18n/ja-JP.json | 29 +- web-app/src/assets/i18n/pt-BR.json | 22 +- web-app/src/assets/i18n/zh-CN.json | 26 +- web-app/src/assets/i18n/zh-TW.json | 28 +- 21 files changed, 1683 insertions(+), 268 deletions(-) create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java rename hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/{RealTimeAlertCalculator.java => MetricsRealTimeAlertCalculator.java} (64%) create mode 100644 hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{RealTimeAlertCalculatorMatchTest.java => MetricsRealTimeAlertCalculatorMatchTest.java} (95%) rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{RealTimeAlertCalculatorTest.java => MetricsRealTimeAlertCalculatorTest.java} (94%) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java new file mode 100644 index 00000000000..493aa55eccb --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java @@ -0,0 +1,241 @@ +/* + * 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.hertzbeat.alert.calculate; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlExpression; +import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.dao.SingleAlertDao; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.alert.util.AlertUtil; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; +import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.apache.hertzbeat.common.util.JexlExpressionRunner; + +import java.util.HashMap; +import java.util.Map; + +/** + * Abstract base class for real-time alert calculators + * @param The type of data being processed for alerts + */ +@Slf4j +public abstract class AbstractRealTimeAlertCalculator { + + protected static final int CALCULATE_THREADS = 3; + + protected final AlerterWorkerPool workerPool; + protected final CommonDataQueue dataQueue; + protected final AlertDefineService alertDefineService; + protected final AlarmCommonReduce alarmCommonReduce; + protected final AlarmCacheManager alarmCacheManager; + + /** + * Constructor with auto-start enabled + */ + protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { + this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, true); + } + + /** + * Constructor with configurable auto-start + * + * @param workerPool The worker pool used for concurrent alert calculation. + * @param dataQueue The queue from which data is pulled and pushed. + * @param alertDefineService The service providing alert definition rules. + * @param singleAlertDao The DAO for fetching persisted alert states from storage. + * @param alarmCommonReduce The component responsible for reducing and sending alerts. + * @param alarmCacheManager The cache manager for managing alert states. + * @param start If true, the alert calculation threads will start automatically; + * set to false to disable thread start (useful for unit testing). + */ + protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { + this.workerPool = workerPool; + this.dataQueue = dataQueue; + this.alarmCommonReduce = alarmCommonReduce; + this.alertDefineService = alertDefineService; + this.alarmCacheManager = alarmCacheManager; + if (start) { + startCalculate(); + } + } + + /** + * Start the alert calculation threads + */ + public void startCalculate() { + Runnable runnable = () -> { + while (!Thread.currentThread().isInterrupted()) { + try { + T data = pollData(); + if (data != null) { + calculate(data); + processDataAfterCalculation(data); + } + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("calculate alarm error: {}.", e.getMessage(), e); + } + } + }; + for (int i = 0; i < CALCULATE_THREADS; i++) { + workerPool.executeJob(runnable); + } + } + + /** + * Poll data from the queue + * @return The data to process + * @throws InterruptedException if interrupted while waiting + */ + protected abstract T pollData() throws InterruptedException; + + /** + * Process the data after alert calculation + * @param data The data that was processed + */ + protected abstract void processDataAfterCalculation(T data); + + /** + * Calculate alerts based on the data + * @param data The data to calculate alerts for + */ + protected abstract void calculate(T data); + + /** + * Execute an alert expression + * @param fieldValueMap The field value map for expression evaluation + * @param expr The expression to evaluate + * @param ignoreJexlException Whether to ignore JEXL exceptions + * @return true if the expression matches, false otherwise + */ + protected boolean execAlertExpression(Map fieldValueMap, String expr, boolean ignoreJexlException) { + Boolean match; + JexlExpression expression; + try { + expression = JexlExpressionRunner.compile(expr); + } catch (JexlException jexlException) { + log.warn("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage()); + throw jexlException; + } catch (Exception e) { + log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); + throw e; + } + + try { + match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap); + } catch (JexlException jexlException) { + if (ignoreJexlException) { + log.debug("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); + } else { + log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); + } + throw jexlException; + } catch (Exception e) { + log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); + throw e; + } + return match != null && match; + } + + /** + * Handle alert after threshold rule match + */ + protected void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, + Map fieldValueMap, AlertDefine define, Map annotations) { + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(fingerprint); + fieldValueMap.putAll(define.getLabels()); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + Map alertLabels = new HashMap<>(8); + alertLabels.putAll(fingerPrints); + Map alertAnnotations = new HashMap<>(8); + if (annotations != null) { + alertAnnotations.putAll(annotations); + } + if (define.getAnnotations() != null) { + alertAnnotations.putAll(define.getAnnotations()); + } + // render var content in annotations + for (Map.Entry entry : alertAnnotations.entrySet()) { + entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + SingleAlert newAlert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + // render var content in content + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(fingerprint, newAlert); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(fingerprint, existingAlert); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + + /** + * Handle recovered alert + */ + protected void handleRecoveredAlert(Map fingerprints) { + String fingerprint = AlertUtil.calculateFingerprint(fingerprints); + SingleAlert firingAlert = alarmCacheManager.removeFiring(fingerprint); + if (firingAlert != null) { + // todo consider multi times to tig for resolved alert + firingAlert.setTriggerTimes(1); + firingAlert.setEndAt(System.currentTimeMillis()); + firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); + alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); + } + alarmCacheManager.removePending(fingerprint); + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java new file mode 100644 index 00000000000..c0f31c27e57 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java @@ -0,0 +1,120 @@ +/* + * 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.hertzbeat.alert.calculate; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.dao.SingleAlertDao; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Calculate alarms based on the alarm definition rules and log data + */ +@Component +@Slf4j +public class LogRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator { + + public static final String LOG_PREFIX = "LOG"; + + @Autowired + public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager); + } + + /** + * Constructor for LogRealTimeAlertCalculator with a toggle to control whether to start alert calculation threads. + * + * @param workerPool The worker pool used for concurrent alert calculation. + * @param dataQueue The queue from which log data is pulled and pushed. + * @param alertDefineService The service providing alert definition rules. + * @param singleAlertDao The DAO for fetching persisted alert states from storage. + * @param alarmCommonReduce The component responsible for reducing and sending alerts. + * @param alarmCacheManager The cache manager for managing alert states. + * @param start If true, the alert calculation threads will start automatically; + * set to false to disable thread start (useful for unit testing). + */ + public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, start); + } + + @Override + protected LogEntry pollData() throws InterruptedException { + return dataQueue.pollLogEntry(); + } + + @Override + protected void processDataAfterCalculation(LogEntry logEntry) { + // todo send log to storage + } + + @Override + protected void calculate(LogEntry logEntry) { + long currentTimeMilli = System.currentTimeMillis(); + List thresholds = this.alertDefineService.getLogRealTimeAlertDefines(); + + Map commonContext = new HashMap<>(8); + commonContext.put(LOG_PREFIX, logEntry); + + for (AlertDefine define : thresholds) { + if (define.getLabels() == null) { + define.setLabels(new HashMap<>(8)); + } + if (define.getAnnotations() == null) { + define.setAnnotations(new HashMap<>(8)); + } + final String expr = define.getExpr(); + if (StringUtils.isBlank(expr)) { + continue; + } + Map commonFingerPrints = new HashMap<>(8); + // here use the alert name as finger, not care the alert name may be changed + // todo add more fingerprints + commonFingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + commonFingerPrints.putAll(define.getLabels()); + + try { + boolean match = execAlertExpression(commonContext, expr, false); + try { + if (match) { + afterThresholdRuleMatch(currentTimeMilli, commonFingerPrints, commonContext, define, null); + } else { + handleRecoveredAlert(commonFingerPrints); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } catch (Exception ignored) {} + } + } +} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java similarity index 64% rename from hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java rename to hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java index 1da4af4dd7b..8dce12df9db 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java @@ -17,7 +17,6 @@ package org.apache.hertzbeat.alert.calculate; - import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -28,22 +27,16 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.jexl3.JexlException; -import org.apache.commons.jexl3.JexlExpression; import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.AlerterWorkerPool; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; -import org.apache.hertzbeat.alert.util.AlertTemplateUtil; -import org.apache.hertzbeat.alert.util.AlertUtil; import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; -import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.apache.hertzbeat.common.util.CommonUtil; -import org.apache.hertzbeat.common.util.JexlExpressionRunner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -53,9 +46,7 @@ */ @Component @Slf4j -public class RealTimeAlertCalculator { - - private static final int CALCULATE_THREADS = 3; +public class MetricsRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator { private static final String KEY_INSTANCE = "__instance__"; private static final String KEY_INSTANCE_NAME = "__instancename__"; @@ -76,63 +67,43 @@ public class RealTimeAlertCalculator { private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\\s*\"(\\d+)\"\\)"); private static final Pattern METRICS_PATTERN = Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)"); - private final AlerterWorkerPool workerPool; - private final CommonDataQueue dataQueue; - private final AlertDefineService alertDefineService; - private final AlarmCommonReduce alarmCommonReduce; - private final AlarmCacheManager alarmCacheManager; - @Autowired - public RealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { - this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, true); + public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager); } /** - * Constructor for RealTimeAlertCalculator with a toggle to control whether to start alert calculation threads. + * Constructor for MetricsRealTimeAlertCalculator with a toggle to control whether to start alert calculation threads. * * @param workerPool The worker pool used for concurrent alert calculation. * @param dataQueue The queue from which metric data is pulled and pushed. * @param alertDefineService The service providing alert definition rules. * @param singleAlertDao The DAO for fetching persisted alert states from storage. * @param alarmCommonReduce The component responsible for reducing and sending alerts. + * @param alarmCacheManager The cache manager for managing alert states. * @param start If true, the alert calculation threads will start automatically; * set to false to disable thread start (useful for unit testing). */ - public RealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { - this.workerPool = workerPool; - this.dataQueue = dataQueue; - this.alarmCommonReduce = alarmCommonReduce; - this.alertDefineService = alertDefineService; - this.alarmCacheManager = alarmCacheManager; - if (start) { - startCalculate(); - } + public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, start); } - public void startCalculate() { - Runnable runnable = () -> { - while (!Thread.currentThread().isInterrupted()) { - try { - CollectRep.MetricsData metricsData = dataQueue.pollMetricsDataToAlerter(); - calculate(metricsData); - dataQueue.sendMetricsDataToStorage(metricsData); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - log.error("calculate alarm error: {}.", e.getMessage(), e); - } - } - }; - for (int i = 0; i < CALCULATE_THREADS; i++) { - workerPool.executeJob(runnable); - } + @Override + protected CollectRep.MetricsData pollData() throws InterruptedException { + return dataQueue.pollMetricsDataToAlerter(); + } + + @Override + protected void processDataAfterCalculation(CollectRep.MetricsData metricsData) { + dataQueue.sendMetricsDataToStorage(metricsData); } - private void calculate(CollectRep.MetricsData metricsData) { + @Override + protected void calculate(CollectRep.MetricsData metricsData) { long currentTimeMilli = System.currentTimeMillis(); String instance = String.valueOf(metricsData.getId()); String instanceName = metricsData.getInstanceName(); @@ -272,8 +243,9 @@ private void calculate(CollectRep.MetricsData metricsData) { * @param thresholds Alert definitions to filter * @param app Current app name * @param metrics Current metrics name + * @param labels Current labels * @param instance Current instance id - * @param priority Current priority + * @param priority Current priority * @return Filtered alert definitions */ public List filterThresholdsByAppAndMetrics(List thresholds, String app, String metrics, Map labels, String instance, int priority) { @@ -334,105 +306,6 @@ public List filterThresholdsByAppAndMetrics(List thres .collect(Collectors.toList()); } - private void handleRecoveredAlert(Map fingerprints) { - String fingerprint = AlertUtil.calculateFingerprint(fingerprints); - SingleAlert firingAlert = alarmCacheManager.removeFiring(fingerprint); - if (firingAlert != null) { - // todo consider multi times to tig for resolved alert - firingAlert.setTriggerTimes(1); - firingAlert.setEndAt(System.currentTimeMillis()); - firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); - alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); - } - alarmCacheManager.removePending(fingerprint); - } - - private void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, - Map fieldValueMap, AlertDefine define, Map annotations) { - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(fingerprint); - fieldValueMap.putAll(define.getLabels()); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - Map alertLabels = new HashMap<>(8); - alertLabels.putAll(fingerPrints); - Map alertAnnotations = new HashMap<>(8); - if (annotations != null) { - alertAnnotations.putAll(annotations); - } - if (define.getAnnotations() != null) { - alertAnnotations.putAll(define.getAnnotations()); - } - // render var content in annotations - for (Map.Entry entry : alertAnnotations.entrySet()) { - entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); - } - SingleAlert newAlert = SingleAlert.builder() - .labels(alertLabels) - .annotations(alertAnnotations) - // render var content in content - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(fingerprint, newAlert); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(fingerprint, existingAlert); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - - private boolean execAlertExpression(Map fieldValueMap, String expr, boolean ignoreJexlException) { - Boolean match; - JexlExpression expression; - try { - expression = JexlExpressionRunner.compile(expr); - } catch (JexlException jexlException) { - log.warn("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage()); - throw jexlException; - } catch (Exception e) { - log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); - throw e; - } - - try { - match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap); - } catch (JexlException jexlException) { - if (ignoreJexlException) { - log.debug("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); - } else { - log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); - } - throw jexlException; - } catch (Exception e) { - log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); - throw e; - } - return match != null && match; - } - private Set kvLabelsToKvStringSet(Map labels) { if (labels == null || labels.isEmpty()) { return Collections.singleton(""); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java index 0cb4d22c12a..6b8692ae526 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/AlertDefineService.java @@ -22,6 +22,7 @@ import org.apache.hertzbeat.common.support.exception.AlertExpressionException; import org.springframework.data.domain.Page; import org.springframework.web.multipart.MultipartFile; +import org.apache.hertzbeat.common.constants.CommonConstants; import java.util.List; import java.util.Map; @@ -106,11 +107,24 @@ public interface AlertDefineService { void importConfig(MultipartFile file) throws Exception; /** - * Get the real-time alarm definition list - * @return Real-time alarm definition list + * Get the real-time metrics alarm definition list + * @return Real-time metrics alarm definition list */ List getMetricsRealTimeAlertDefines(); + /** + * Get the real-time log alarm definition list + * @return Real-time log alarm definition list + */ + List getLogRealTimeAlertDefines(); + + /** + * Get the alarm definition list by type + * @param type Alarm definition type, must be one of the constants defined in {@link CommonConstants} + * @return List of enabled alarm definitions matching the specified type, empty list if none found + */ + List getAlertDefinesByType(String type); + /** * Get define preview * @return Data queried based on expressions diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index 3253addaa4a..f9168452bdc 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -216,10 +216,20 @@ public void importConfig(MultipartFile file) throws Exception { @Override public List getMetricsRealTimeAlertDefines() { - List alertDefines = CacheFactory.getAlertDefineCache(); + List alertDefines = CacheFactory.getMetricsAlertDefineCache(); if (alertDefines == null) { alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME); - CacheFactory.setAlertDefineCache(alertDefines); + CacheFactory.setMetricsAlertDefineCache(alertDefines); + } + return alertDefines; + } + + @Override + public List getLogRealTimeAlertDefines() { + List alertDefines = CacheFactory.getLogAlertDefineCache(); + if (alertDefines == null) { + alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME); + CacheFactory.setLogAlertDefineCache(alertDefines); } return alertDefines; } @@ -237,4 +247,25 @@ public List> getDefinePreview(String datasource, String type return Collections.emptyList(); } } + + @Override + public List getAlertDefinesByType(String type) { + if (!StringUtils.hasText(type)) { + throw new IllegalArgumentException("Alert definition type cannot be null or empty"); + } + + switch (type) { + case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME: + case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC: + case CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME: + case CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC: + // Valid type, proceed with query + break; + default: + throw new IllegalArgumentException("Unsupported alert definition type: " + type); + } + + // Query enabled alert definitions by type + return alertDefineDao.findAlertDefinesByTypeAndEnableTrue(type); + } } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertTemplateUtil.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertTemplateUtil.java index a673813ded1..491fe8ed57c 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertTemplateUtil.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertTemplateUtil.java @@ -17,6 +17,8 @@ package org.apache.hertzbeat.alert.util; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -33,10 +35,11 @@ private AlertTemplateUtil() { } /** - * Match the variable ${key} + * Match the variable ${key} or ${key.property.subproperty} * eg: Alert, the instance: ${instance} metrics: ${metrics} is over flow. + * eg: Log alert: ${log.attributes.level} - ${log.message} */ - private static final Pattern PATTERN = Pattern.compile("\\$\\{(\\w+)\\}"); + private static final Pattern PATTERN = Pattern.compile("\\$\\{([\\w.]+)}"); public static String render(String template, Map replaceData) { if (!StringUtils.hasText(template)) { @@ -50,7 +53,8 @@ public static String render(String template, Map replaceData) { Matcher matcher = PATTERN.matcher(template); StringBuilder builder = new StringBuilder(); while (matcher.find()) { - Object objectValue = replaceData.getOrDefault(matcher.group(1), "NullValue"); + String propertyPath = matcher.group(1); + Object objectValue = getNestedProperty(replaceData, propertyPath); String value = objectValue != null ? objectValue.toString() : "NullValue"; matcher.appendReplacement(builder, Matcher.quoteReplacement(value)); } @@ -61,4 +65,116 @@ public static String render(String template, Map replaceData) { return template; } } + + /** + * Get nested property value from object using property path like "log.attributes.demo" + * @param replaceData the root data map + * @param propertyPath the property path, e.g., "log.attributes.demo" + * @return the property value or null if not found + */ + private static Object getNestedProperty(Map replaceData, String propertyPath) { + if (propertyPath == null || propertyPath.isEmpty()) { + return null; + } + + String[] parts = propertyPath.split("\\."); + Object current = replaceData.get(parts[0]); + + if (current == null) { + return null; + } + + // If only one part, return the direct value + if (parts.length == 1) { + return current; + } + + // Navigate through the property path + for (int i = 1; i < parts.length; i++) { + current = getPropertyValue(current, parts[i]); + if (current == null) { + return null; + } + } + + return current; + } + + /** + * Get property value from an object using reflection + * @param obj the object + * @param propertyName the property name + * @return the property value or null if not found + */ + private static Object getPropertyValue(Object obj, String propertyName) { + if (obj == null || propertyName == null) { + return null; + } + + try { + // Handle Map objects + if (obj instanceof Map) { + return ((Map) obj).get(propertyName); + } + + Class clazz = obj.getClass(); + + // Try getter method first (getPropertyName or isPropertyName for boolean) + String getterName = "get" + capitalize(propertyName); + String booleanGetterName = "is" + capitalize(propertyName); + + try { + Method getter = clazz.getMethod(getterName); + return getter.invoke(obj); + } catch (NoSuchMethodException e) { + // Try boolean getter + try { + Method booleanGetter = clazz.getMethod(booleanGetterName); + return booleanGetter.invoke(obj); + } catch (NoSuchMethodException ignored) { + // Continue to field access + } + } + + // Try direct field access + try { + Field field = clazz.getDeclaredField(propertyName); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + // Field not found + } + + // Try parent classes + Class superClass = clazz.getSuperclass(); + while (superClass != null && !superClass.equals(Object.class)) { + try { + Field field = superClass.getDeclaredField(propertyName); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + // Continue with parent class + } + superClass = superClass.getSuperclass(); + } + + } catch (Exception e) { + log.debug("Failed to access property '{}' on object of type {}: {}", + propertyName, obj.getClass().getSimpleName(), e.getMessage()); + } + + return null; + } + + /** + * Capitalize the first letter of a string + * @param str the input string + * @return the capitalized string + */ + private static String capitalize(String str) { + if (str == null || str.isEmpty()) { + return str; + } + return str.substring(0, 1).toUpperCase() + str.substring(1); + } } diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java new file mode 100644 index 00000000000..8989f466240 --- /dev/null +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java @@ -0,0 +1,300 @@ +/* + * 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.hertzbeat.alert.calculate; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.dao.SingleAlertDao; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.HashMap; +import java.util.Map; + +class LogRealTimeAlertCalculatorTest { + + private LogRealTimeAlertCalculator calculator; + + @BeforeEach + void setUp() { + AlerterWorkerPool mockPool = Mockito.mock(AlerterWorkerPool.class); + CommonDataQueue mockQueue = Mockito.mock(CommonDataQueue.class); + AlertDefineService mockAlertDefineService = Mockito.mock(AlertDefineService.class); + SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class); + AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class); + AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class); + + calculator = new LogRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, + mockDao, mockReduce, alarmCacheManager, false); + } + + @Test + void testExecAlertExpression_SeverityNumberAndSeverityText_ShouldMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "ERROR", "System error occurred", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match when severityNumber is 2 and severityText contains 'ERROR'"); + } + + @Test + void testExecAlertExpression_SeverityNumberMatches_SeverityTextNotMatch_ShouldNotMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "INFO", "Information message", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertFalse(result, "Expression should not match when severityText doesn't contain 'ERROR'"); + } + + @Test + void testExecAlertExpression_SeverityNumberNotMatch_SeverityTextMatches_ShouldNotMatch() { + // Arrange + LogEntry logEntry = createLogEntry(1, "ERROR", "System error occurred", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertFalse(result, "Expression should not match when severityNumber is not 2"); + } + + @Test + void testExecAlertExpression_CaseInsensitiveContains_ShouldMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "error", "System error occurred", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 2) && (contains(log.severityText.toLowerCase(), \"error\"))"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match with case-insensitive comparison"); + } + + @Test + void testExecAlertExpression_SeverityGreaterThan_ShouldMatch() { + // Arrange + LogEntry logEntry = createLogEntry(5, "CRITICAL", "Critical error", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "log.severityNumber > 2"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match when severityNumber is greater than 2"); + } + + @Test + void testExecAlertExpression_BodyContains_ShouldMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "ERROR", "Database connection failed", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "contains(log.body, \"Database\")"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match when body contains 'Database'"); + } + + @Test + void testExecAlertExpression_AttributesCheck_ShouldMatch() { + // Arrange + Map attributes = new HashMap<>(); + attributes.put("service", "user-service"); + attributes.put("environment", "production"); + + LogEntry logEntry = createLogEntry(3, "WARN", "Service warning", attributes); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "log.attributes.service == \"user-service\""; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match when attributes contain expected service"); + } + + @Test + void testExecAlertExpression_ComplexExpression_ShouldMatch() { + // Arrange + Map attributes = new HashMap<>(); + attributes.put("service", "user-service"); + attributes.put("environment", "production"); + + LogEntry logEntry = createLogEntry(4, "ERROR", "Authentication failed", attributes); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber >= 3) && (contains(log.severityText, \"ERROR\")) && (log.attributes.environment == \"production\")"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Complex expression should match all conditions"); + } + + @Test + void testExecAlertExpression_OrExpression_ShouldMatch() { + // Arrange + LogEntry logEntry = createLogEntry(1, "DEBUG", "Debug information", null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 5) || (contains(log.body, \"Debug\"))"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "OR expression should match when at least one condition is true"); + } + + @Test + void testExecAlertExpression_TimestampCheck_ShouldMatch() { + // Arrange + long currentTime = System.currentTimeMillis(); + long nanoTime = currentTime * 1_000_000; // Convert to nanoseconds + + LogEntry logEntry = LogEntry.builder() + .timeUnixNano(nanoTime) + .severityNumber(2) + .severityText("ERROR") + .body("Timestamp test") + .build(); + + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "log.timeUnixNano > 0"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Expression should match when timestamp is greater than 0"); + } + + @Test + void testExecAlertExpression_NullBody_ShouldNotMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "ERROR", null, null); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "contains(log.body, \"error\")"; + + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + // Act & Assert + assertFalse(result, "Expression should not match when body is null"); + } + + @Test + void testExecAlertExpression_EmptyAttributes_ShouldNotMatch() { + // Arrange + LogEntry logEntry = createLogEntry(2, "ERROR", "Test message", new HashMap<>()); + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "log.attributes.service == \"user-service\""; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertFalse(result, "Expression should not match when attributes is empty"); + } + + @Test + void testExecAlertExpression_MultipleLogFields_ShouldMatch() { + // Arrange + Map attributes = new HashMap<>(); + attributes.put("level", "error"); + attributes.put("component", "authentication"); + + LogEntry logEntry = LogEntry.builder() + .severityNumber(4) + .severityText("ERROR") + .body("Login failed for user admin") + .attributes(attributes) + .traceId("abc123") + .spanId("def456") + .build(); + + Map fieldValueMap = new HashMap<>(); + fieldValueMap.put("log", logEntry); + + String expr = "(log.severityNumber == 4) && " + + "(contains(log.body, \"Login failed\")) && " + + "(log.attributes.component == \"authentication\") && " + + "(log.traceId == \"abc123\")"; + + // Act + boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + + // Assert + assertTrue(result, "Complex expression with multiple log fields should match"); + } + + private LogEntry createLogEntry(Integer severityNumber, String severityText, Object body, Map attributes) { + return LogEntry.builder() + .severityNumber(severityNumber) + .severityText(severityText) + .body(body) + .attributes(attributes) + .timeUnixNano(System.currentTimeMillis() * 1_000_000L) // Current time in nanoseconds + .build(); + } +} diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java similarity index 95% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java index a82f90b0190..cb5a0113abf 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorMatchTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java @@ -48,7 +48,7 @@ /** * */ -public class RealTimeAlertCalculatorMatchTest { +public class MetricsRealTimeAlertCalculatorMatchTest { private final AlerterWorkerPool workerPool = new AlerterWorkerPool(); @@ -67,13 +67,13 @@ public class RealTimeAlertCalculatorMatchTest { @Mock private AlarmCacheManager alarmCacheManager; - private RealTimeAlertCalculator realTimeAlertCalculator; + private MetricsRealTimeAlertCalculator metricsRealTimeAlertCalculator; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); when(singleAlertDao.querySingleAlertsByStatus(any())).thenReturn(new ArrayList<>()); - realTimeAlertCalculator = new RealTimeAlertCalculator( + metricsRealTimeAlertCalculator = new MetricsRealTimeAlertCalculator( workerPool, dataQueue, alertDefineService, @@ -99,7 +99,7 @@ void testFilterThresholdsByAppAndMetrics_withInstanceExpr_HasSpace() { List allDefines = Collections.singletonList(matchDefine); - List filtered = realTimeAlertCalculator.filterThresholdsByAppAndMetrics(allDefines, app, "", Map.of(), instanceId, priority); + List filtered = metricsRealTimeAlertCalculator.filterThresholdsByAppAndMetrics(allDefines, app, "", Map.of(), instanceId, priority); // It should filter out 999999999. assertEquals(1, filtered.size()); @@ -147,7 +147,7 @@ void testPrometheusReplaceMultipleJobsApp() throws InterruptedException { when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); - realTimeAlertCalculator.startCalculate(); + metricsRealTimeAlertCalculator.startCalculate(); Thread.sleep(3000); @@ -190,7 +190,7 @@ void testPrometheusReplaceApp() throws InterruptedException { when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); - realTimeAlertCalculator.startCalculate(); + metricsRealTimeAlertCalculator.startCalculate(); Thread.sleep(3000); @@ -239,7 +239,7 @@ void testCalculateWithNormalApp() throws InterruptedException { when(alertDefineService.getMetricsRealTimeAlertDefines()).thenReturn(allDefines); when(dataQueue.pollMetricsDataToAlerter()).thenReturn(metricsData).thenThrow(new InterruptedException()); - realTimeAlertCalculator.startCalculate(); + metricsRealTimeAlertCalculator.startCalculate(); Thread.sleep(3000); diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java similarity index 94% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java index bdcc34ff776..558ecd43f52 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java @@ -35,9 +35,9 @@ import java.util.Map; -class RealTimeAlertCalculatorTest { +class MetricsRealTimeAlertCalculatorTest { - private RealTimeAlertCalculator calculator; + private MetricsRealTimeAlertCalculator calculator; @BeforeEach void setUp() { @@ -51,7 +51,7 @@ void setUp() { Mockito.when(mockDao.querySingleAlertsByStatus(Mockito.anyString())) .thenReturn(Collections.emptyList()); - calculator = new RealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, false); + calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, false); } @Test diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/AlertTemplateUtilTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/AlertTemplateUtilTest.java index 096a76e22ee..79b782e7e48 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/AlertTemplateUtilTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/AlertTemplateUtilTest.java @@ -87,4 +87,199 @@ void renderSpecialCharacters() { String expectedResult = "The price is $100 and the path is C:\\Users"; assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); } + + @Test + void renderNestedMapProperties() { + Map param = new HashMap<>(); + Map log = new HashMap<>(); + Map attributes = new HashMap<>(); + + attributes.put("level", "ERROR"); + attributes.put("thread", "main"); + log.put("attributes", attributes); + log.put("message", "Connection failed"); + log.put("timestamp", "2024-01-01T10:00:00Z"); + + param.put("log", log); + param.put("instance", "server-01"); + + String template = "Log alert: ${log.attributes.level} - ${log.message} on ${instance}"; + String expectedResult = "Log alert: ERROR - Connection failed on server-01"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderMultiLevelNestedProperties() { + Map param = new HashMap<>(); + Map config = new HashMap<>(); + Map database = new HashMap<>(); + Map connection = new HashMap<>(); + + connection.put("host", "localhost"); + connection.put("port", 3306); + database.put("connection", connection); + database.put("name", "hertzbeat"); + config.put("database", database); + + param.put("config", config); + + String template = "Database: ${config.database.name} at ${config.database.connection.host}:${config.database.connection.port}"; + String expectedResult = "Database: hertzbeat at localhost:3306"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderNonExistentNestedProperties() { + Map param = new HashMap<>(); + Map log = new HashMap<>(); + + log.put("message", "Test message"); + param.put("log", log); + + // Test non-existent nested property + String template = "Log alert: ${log.nonexistent.property} - ${log.message}"; + String expectedResult = "Log alert: NullValue - Test message"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderNonExistentTopLevelProperty() { + Map param = new HashMap<>(); + param.put("existing", "value"); + + String template = "Value: ${nonexistent.property}"; + String expectedResult = "Value: NullValue"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderNestedPropertiesWithNullValues() { + Map param = new HashMap<>(); + Map data = new HashMap<>(); + + data.put("value", null); + data.put("description", "Test data"); + param.put("data", data); + + String template = "Data: ${data.value} - ${data.description}"; + String expectedResult = "Data: NullValue - Test data"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderComplexNestedStructure() { + Map param = new HashMap<>(); + Map alert = new HashMap<>(); + Map monitor = new HashMap<>(); + Map target = new HashMap<>(); + Map metadata = new HashMap<>(); + + metadata.put("version", "1.0"); + metadata.put("author", "system"); + target.put("host", "192.168.1.100"); + target.put("port", 8080); + target.put("metadata", metadata); + monitor.put("name", "HTTP Monitor"); + monitor.put("target", target); + alert.put("monitor", monitor); + alert.put("severity", "CRITICAL"); + + param.put("alert", alert); + + String template = "Alert: ${alert.severity} from ${alert.monitor.name} targeting ${alert.monitor.target.host}:${alert.monitor.target.port} (v${alert.monitor.target.metadata.version})"; + String expectedResult = "Alert: CRITICAL from HTTP Monitor targeting 192.168.1.100:8080 (v1.0)"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderWithDifferentDataTypes() { + Map param = new HashMap<>(); + Map metrics = new HashMap<>(); + + metrics.put("cpu", 85.5); + metrics.put("memory", 1024); + metrics.put("active", true); + metrics.put("count", 42L); + + param.put("metrics", metrics); + + String template = "CPU: ${metrics.cpu}%, Memory: ${metrics.memory}MB, Active: ${metrics.active}, Count: ${metrics.count}"; + String expectedResult = "CPU: 85.5%, Memory: 1024MB, Active: true, Count: 42"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + // Helper classes for testing object property access + public static class TestObject { + private String name; + private int value; + private TestNestedObject nested; + + public TestObject(String name, int value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public int getValue() { + return value; + } + + public TestNestedObject getNested() { + return nested; + } + + public void setNested(TestNestedObject nested) { + this.nested = nested; + } + } + + public static class TestNestedObject { + private String description; + private boolean enabled; + + public TestNestedObject(String description, boolean enabled) { + this.description = description; + this.enabled = enabled; + } + + public String getDescription() { + return description; + } + + public boolean isEnabled() { + return enabled; + } + } + + @Test + void renderWithObjectProperties() { + Map param = new HashMap<>(); + TestNestedObject nested = new TestNestedObject("Test description", true); + TestObject obj = new TestObject("TestName", 123); + obj.setNested(nested); + + param.put("object", obj); + + String template = "Object: ${object.name} (value: ${object.value}) - ${object.nested.description}, enabled: ${object.nested.enabled}"; + String expectedResult = "Object: TestName (value: 123) - Test description, enabled: true"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } + + @Test + void renderWithMixedMapAndObjectProperties() { + Map param = new HashMap<>(); + Map config = new HashMap<>(); + TestObject obj = new TestObject("ConfigObject", 456); + + config.put("object", obj); + config.put("type", "mixed"); + param.put("config", config); + + String template = "Config type: ${config.type}, object name: ${config.object.name}, value: ${config.object.value}"; + String expectedResult = "Config type: mixed, object name: ConfigObject, value: 456"; + assertEquals(expectedResult, AlertTemplateUtil.render(template, param)); + } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java index 5f8776b72f2..1a9ace574af 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java @@ -82,26 +82,58 @@ public static void clearAlertSilenceCache() { } /** - * get alert define cache + * get metrics alert define cache * @return caffeine cache */ @SuppressWarnings("unchecked") - public static List getAlertDefineCache() { - return (List) COMMON_CACHE.get(CommonConstants.CACHE_ALERT_DEFINE); + public static List getMetricsAlertDefineCache() { + return (List) COMMON_CACHE.get(CommonConstants.METRICS_CACHE_ALERT_DEFINE); } /** - * set alert define cache + * set metrics alert define cache * @param alertDefines alert defines */ - public static void setAlertDefineCache(List alertDefines) { - COMMON_CACHE.put(CommonConstants.CACHE_ALERT_DEFINE, alertDefines); + public static void setMetricsAlertDefineCache(List alertDefines) { + COMMON_CACHE.put(CommonConstants.METRICS_CACHE_ALERT_DEFINE, alertDefines); } + /** + * clear metrics alert define cache + */ + public static void clearMetricsAlertDefineCache() { + COMMON_CACHE.remove(CommonConstants.METRICS_CACHE_ALERT_DEFINE); + } + + /** + * get log alert define cache + * @return caffeine cache + */ + @SuppressWarnings("unchecked") + public static List getLogAlertDefineCache() { + return (List) COMMON_CACHE.get(CommonConstants.LOG_CACHE_ALERT_DEFINE); + } + + /** + * set log alert define cache + * @param alertDefines alert defines + */ + public static void setLogAlertDefineCache(List alertDefines) { + COMMON_CACHE.put(CommonConstants.LOG_CACHE_ALERT_DEFINE, alertDefines); + } + + /** + * clear log alert define cache + */ + public static void clearLogAlertDefineCache() { + COMMON_CACHE.remove(CommonConstants.LOG_CACHE_ALERT_DEFINE); + } + /** * clear alert define cache */ public static void clearAlertDefineCache() { - COMMON_CACHE.remove(CommonConstants.CACHE_ALERT_DEFINE); + clearLogAlertDefineCache(); + clearMetricsAlertDefineCache(); } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java index 01fe6ba35bf..1106c094552 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java @@ -238,9 +238,14 @@ public interface CommonConstants { String CACHE_ALERT_SILENCE = "alert_silence"; /** - * cache key alert define + * cache key metrics alert define */ - String CACHE_ALERT_DEFINE = "alert_define"; + String METRICS_CACHE_ALERT_DEFINE = "metrics_alert_define"; + + /** + * cache key log alert define + */ + String LOG_CACHE_ALERT_DEFINE = "log_alert_define"; /** * cache key alert converge diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index 55d6e2d39e2..e3593960cdc 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -22,6 +22,7 @@ import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.common.v1.KeyValueList; import io.opentelemetry.proto.logs.v1.LogRecord; import io.opentelemetry.proto.logs.v1.ResourceLogs; import io.opentelemetry.proto.logs.v1.ScopeLogs; @@ -64,11 +65,12 @@ public void ingest(String content) { // Extract LogEntry instances from the request List logEntries = extractLogEntries(request); - log.info("Successfully extracted {} log entries from OTLP payload", logEntries.size()); + log.debug("Successfully extracted {} log entries from OTLP payload {}", logEntries.size(), content); logEntries.forEach(entry -> { try { commonDataQueue.sendLogEntry(entry); + log.debug("Log entry sent to queue: {}", entry); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("Failed to send log entry to queue: {}", e.getMessage()); @@ -159,11 +161,27 @@ private LogEntry.InstrumentationScope extractInstrumentationScope(ScopeLogs scop * Extract attributes from a list of KeyValue pairs. */ private Map extractAttributes(List keyValueList) { - Map attributes = new HashMap<>(); - for (KeyValue kv : keyValueList) { - attributes.put(kv.getKey(), extractAnyValue(kv.getValue())); + if (keyValueList == null || keyValueList.isEmpty()) { + return new HashMap<>(); + } + + AnyValue anyValue = AnyValue.newBuilder() + .setKvlistValue(KeyValueList.newBuilder() + .addAllValues(keyValueList) + .build()) + .build(); + Object extractedAnyValue = extractAnyValue(anyValue); + if (extractedAnyValue instanceof Map genericMap) { + Map resultMap = new HashMap<>(); + for (Map.Entry entry : genericMap.entrySet()) { + if (entry.getKey() instanceof String) { + resultMap.put((String) entry.getKey(), entry.getValue()); + } + } + return resultMap; + } else { + return new HashMap<>(); } - return attributes; } /** @@ -195,7 +213,7 @@ private Object extractAnyValue(AnyValue anyValue) { case KVLIST_VALUE: Map kvMap = new HashMap<>(); for (KeyValue kv : anyValue.getKvlistValue().getValuesList()) { - kvMap.put(kv.getKey(), extractAnyValue(kv.getValue())); + kvMap.put(normalizeKey(kv.getKey()), extractAnyValue(kv.getValue())); } return kvMap; case BYTES_VALUE: @@ -206,6 +224,19 @@ private Object extractAnyValue(AnyValue anyValue) { } } + /** + * Normalize key by replacing dots and spaces with underscores. + * + * @param key the original key + * @return normalized key with dots and spaces replaced by underscores + */ + private String normalizeKey(String key) { + if (key == null) { + return null; + } + return key.replace(".", "_").replace(" ", "_"); + } + /** * Convert byte array to hex string. */ diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java index de3cfdf750b..d4c4b03a249 100644 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java +++ b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java @@ -23,7 +23,7 @@ import javax.naming.NamingException; import org.apache.hertzbeat.alert.AlerterProperties; import org.apache.hertzbeat.alert.AlerterWorkerPool; -import org.apache.hertzbeat.alert.calculate.RealTimeAlertCalculator; +import org.apache.hertzbeat.alert.calculate.MetricsRealTimeAlertCalculator; import org.apache.hertzbeat.alert.controller.AlertDefineController; import org.apache.hertzbeat.alert.controller.AlertDefinesController; import org.apache.hertzbeat.alert.controller.AlertsController; @@ -72,7 +72,7 @@ void testAutoImport() { assertNotNull(ctx.getBean(AlertDefineController.class)); assertNotNull(ctx.getBean(AlerterWorkerPool.class)); assertNotNull(ctx.getBean(AlerterProperties.class)); - assertNotNull(ctx.getBean(RealTimeAlertCalculator.class)); + assertNotNull(ctx.getBean(MetricsRealTimeAlertCalculator.class)); assertNotNull(ctx.getBean(AlertsController.class)); assertNotNull(ctx.getBean(AlertDefinesController.class)); diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 7366997a5d6..80fdc3ade6c 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -114,10 +114,16 @@ - {{ 'alert.setting.type.realtime' | i18n }} + {{ 'alert.setting.type.metrics.realtime' | i18n }} - {{ 'alert.setting.type.periodic' | i18n }} + {{ 'alert.setting.type.metrics.periodic' | i18n }} + + + {{ 'alert.setting.type.log.realtime' | i18n }} + + + {{ 'alert.setting.type.log.periodic' | i18n }} @@ -141,9 +147,7 @@ nz-tooltip nzType="primary" [nzLoading]="isLoadingEdit === data.id" - [nzTooltipTitle]=" - data.type == 'metrics_realtime' ? ('alert.setting.edit.realtime' | i18n) : ('alert.setting.edit.periodic' | i18n) - " + [nzTooltipTitle]="getEditTooltipTitle(data) | i18n" > @@ -176,15 +180,7 @@ + + + {{ 'alert.setting.rule' | i18n }} + + + + + + + + + + + +
+ + + + +
    +
  • + {{ item.value }} + {{ item.label }} + + {{ + item.type === 0 + ? ('alert.setting.number' | i18n) + : item.type === 1 + ? ('alert.setting.string' | i18n) + : ('alert.setting.object' | i18n) + }} + +
  • + +
  • + {{ op.value }} + {{ op.description | i18n }} +
  • +
+
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + {{ field.name ? field.name : field.value }} + + {{ + field.type === 0 + ? ('alert.setting.number' | i18n) + : field.type === 1 + ? ('alert.setting.string' | i18n) + : field.type === 2 + ? ('alert.setting.object' | i18n) + : field.type === 3 + ? ('alert.setting.time' | i18n) + : ('alert.setting.object' | i18n) + }} + + + {{ field.unit }} + + + + + +
+ . + +
+
+
+ + + + + + + + +
+
+
+ + + {{ 'alert.setting.preview.expr' | i18n }} + + + +
{{ define.expr }}
+
+
+
{{ 'alert.setting.rule' | i18n }} @@ -533,18 +737,29 @@
-
-
-
- {{ env.name }} - {{ env.description | i18n }} +
+ {{ define.type }} +
+
+
+ {{ env.name }} + {{ env.description | i18n }} +
-
-
-
- {{ '${' + metric.value + '}' }} - {{ metric.label }} +
+
+ {{ '${' + metric.value + '}' }} + {{ metric.label }} +
+
+
+
+
+
+ {{ '${' + field.value + '}' }} + {{ field.label }} +
@@ -607,7 +822,6 @@ - EXCEL [nzTitle]="'alert.setting.new' | i18n" (nzOnCancel)="onSelectTypeModalCancel()" [nzFooter]="null" - nzWidth="460px" + nzWidth="500px" >
-

{{ 'alert.setting.type.realtime' | i18n }}

-

{{ 'alert.setting.type.realtime.desc' | i18n }}

+

{{ 'alert.setting.type.metrics.realtime' | i18n }}

+

{{ 'alert.setting.type.metrics.realtime.desc' | i18n }}

-

{{ 'alert.setting.type.periodic' | i18n }}

-

{{ 'alert.setting.type.periodic.desc' | i18n }}

+

{{ 'alert.setting.type.metrics.periodic' | i18n }}

+

{{ 'alert.setting.type.metrics.periodic.desc' | i18n }}

+
+ + +

{{ 'alert.setting.type.log.realtime' | i18n }}

+

{{ 'alert.setting.type.log.realtime.desc' | i18n }}

diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index 7423f44b98b..d733a4fcacc 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -59,7 +59,11 @@ export class AlertSettingComponent implements OnInit { this.qbFormCtrl = this.formBuilder.control(this.qbData, this.qbValidator); this.qbFormCtrl.valueChanges.subscribe(() => { this.userExpr = this.ruleset2expr(this.qbFormCtrl.value); - this.updateFinalExpr(); + if (this.define.type === 'log_realtime') { + this.updateLogFinalExpr(); + } else { + this.updateFinalExpr(); + } }); } @ViewChild('defineForm', { static: false }) defineForm!: NgForm; @@ -131,7 +135,77 @@ export class AlertSettingComponent implements OnInit { previewColumns: Array<{ title: string; key: string; width?: string }> = []; previewTableLoading = false; + /** + * Initialize log fields(todo: from backend api) + */ + initLogFields() { + this.logFields = [ + // Basic log fields + { value: 'log.timeUnixNano', label: 'Time (Unix Nano)', type: 0, unit: 'ns' }, + { value: 'log.observedTimeUnixNano', label: 'Observed Time (Unix Nano)', type: 0, unit: 'ns' }, + { value: 'log.severityNumber', label: 'Severity Number', type: 0 }, + { value: 'log.severityText', label: 'Severity Text', type: 1 }, + { value: 'log.body', label: 'Body', type: 1 }, + { value: 'log.droppedAttributesCount', label: 'Dropped Attributes Count', type: 0 }, + { value: 'log.traceId', label: 'Trace ID', type: 1 }, + { value: 'log.spanId', label: 'Span ID', type: 1 }, + { value: 'log.traceFlags', label: 'Trace Flags', type: 0 }, + // Object fields - will be handled dynamically + { value: 'log.attributes', label: 'Attributes', type: 2, isObject: true }, + { value: 'log.resource', label: 'Resource', type: 2, isObject: true }, + { value: 'log.instrumentationScope.name', label: 'Instrumentation Scope Name', type: 1 }, + { value: 'log.instrumentationScope.version', label: 'Instrumentation Scope Version', type: 1 }, + { value: 'log.instrumentationScope.attributes', label: 'Instrumentation Scope Attributes', type: 2, isObject: true }, + { value: 'log.instrumentationScope.droppedAttributesCount', label: 'Instrumentation Scope Dropped Attributes Count', type: 0 } + ]; + } + + updateLogQbConfig() { + if (this.define.type === 'log_realtime') { + let fields: any = {}; + this.logFields.forEach(item => { + fields[item.value] = { + name: item.label, + type: item.type, + unit: item.unit, + operators: this.getOperatorsByType(item.type) + }; + }); + this.qbConfig = { ...this.qbConfig, fields }; + } + } + + updateLogFinalExpr(): void { + if (this.define.type === 'log_realtime') { + this.define.expr = this.userExpr; + } + } + + onLogFieldChange(fieldValue: string, rule: any, onChange: any): void { + rule.field = fieldValue; + // Clear object attribute when field changes + rule.objectAttribute = ''; + onChange(fieldValue, rule); + } + + onMetricsFieldChange(fieldValue: string, rule: any, onChange: any): void { + rule.field = fieldValue; + onChange(fieldValue, rule); + } + + isObjectField(fieldValue: string): boolean { + if (!fieldValue) return false; + const field = this.logFields.find(f => f.value === fieldValue); + return !!(field && field.isObject); + } + + onObjectAttributeChange(attributeValue: string, rule: any, onChange: any): void { + rule.objectAttribute = attributeValue; + onChange(rule.field, rule); + } + ngOnInit(): void { + this.initLogFields(); this.loadAlertDefineTable(); // query monitoring hierarchy const getHierarchy$ = this.appDefineSvc @@ -257,6 +331,10 @@ export class AlertSettingComponent implements OnInit { if (type === 'metrics_periodic') { this.define.period = 300; } + // Initialize log fields for log_realtime alert + if (type === 'log_realtime') { + this.updateLogQbConfig(); + } this.resetQbDataDefault(); this.isManageModalAdd = true; this.isManageModalVisible = true; @@ -469,6 +547,7 @@ export class AlertSettingComponent implements OnInit { isExpr = false; userExpr!: string; severity!: string; + logFields: any[] = []; editAlertDefine(alertDefineId: number) { if (this.isLoadingEdit !== -1) return; @@ -516,6 +595,12 @@ export class AlertSettingComponent implements OnInit { this.tryParseThresholdExpr(this.userExpr); } }); + } else if (this.define.type == 'log_realtime') { + // Initialize log fields and parse expression + this.updateLogQbConfig(); + this.userExpr = this.define.expr || ''; + // Try to parse expression as visual rules + this.tryParseLogThresholdExpr(this.userExpr); } } else { this.notifySvc.error(this.i18nSvc.fanyi('common.notify.query-fail'), message.msg); @@ -528,10 +613,19 @@ export class AlertSettingComponent implements OnInit { } private getOperatorsByType(type: number): string[] { + let numericOperators = ['>', '<', '==', '!=', '<=', '>=', 'exists', '!exists']; + let stringOperators = ['equals', '!equals', 'contains', '!contains', 'matches', '!matches', 'exists', '!exists']; + let objectOperators = [...numericOperators, ...stringOperators]; + if (type === 0 || type === 3) { - return ['>', '<', '==', '!=', '<=', '>=', 'exists', '!exists']; + // Numeric and time types + return numericOperators; } else if (type === 1) { - return ['equals', '!equals', 'contains', '!contains', 'matches', '!matches', 'exists', '!exists']; + // String types + return stringOperators; + } else if (type === 2) { + // Object types (for log attributes, resource, etc.) + return objectOperators; } return []; } @@ -539,10 +633,16 @@ export class AlertSettingComponent implements OnInit { private rule2expr(rule: Rule): string { if (!rule.field) return ''; + // Get the effective field name (including object attributes for log fields) + let effectiveField = rule.field; + if (this.define.type === 'log_realtime' && (rule as any).objectAttribute) { + effectiveField = `${rule.field}.${(rule as any).objectAttribute}`; + } + switch (rule.operator) { case 'exists': case '!exists': - return `${rule.operator}(${rule.field})`; + return `${rule.operator}(${effectiveField})`; case 'equals': case '!equals': @@ -550,7 +650,7 @@ export class AlertSettingComponent implements OnInit { case '!contains': case 'matches': case '!matches': - return `${rule.operator}(${rule.field}, "${rule.value}")`; + return `${rule.operator}(${effectiveField}, "${rule.value}")`; case '>': case '>=': @@ -559,10 +659,10 @@ export class AlertSettingComponent implements OnInit { case '==': case '!=': // 如果字段包含方法调用 - if (rule.field.includes('.') && rule.field.includes('()')) { - return `${rule.field} ${rule.operator} ${rule.value}`; + if (effectiveField.includes('.') && effectiveField.includes('()')) { + return `${effectiveField} ${rule.operator} ${rule.value}`; } - return `${rule.field} ${rule.operator} ${rule.value}`; + return `${effectiveField} ${rule.operator} ${rule.value}`; default: return ''; @@ -734,10 +834,12 @@ export class AlertSettingComponent implements OnInit { const existsMatch = expr.match(/^(!)?exists\(([^)]+)\)$/); if (existsMatch) { const [_, not, field] = existsMatch; + const parsedRule = this.parseLogFieldAndAttribute(field); return { - field, - operator: not ? '!exists' : 'exists' - }; + field: parsedRule.field, + operator: not ? '!exists' : 'exists', + ...(parsedRule.objectAttribute && { objectAttribute: parsedRule.objectAttribute }) + } as any; } // Parse string functions (equals, contains, matches) @@ -745,22 +847,26 @@ export class AlertSettingComponent implements OnInit { if (funcMatch) { const [_, not, field, value] = funcMatch; const func = expr.match(/equals|contains|matches/)?.[0] || ''; + const parsedRule = this.parseLogFieldAndAttribute(field); return { - field, + field: parsedRule.field, operator: not ? `!${func}` : func, - value - }; + value, + ...(parsedRule.objectAttribute && { objectAttribute: parsedRule.objectAttribute }) + } as any; } // Parse numeric comparisons - const compareMatch = expr.match(/^(\w+(?:\.\w+)*)\s*([><=!]+)\s*(-?\d+(?:\.\d+)?)$/); + const compareMatch = expr.match(/^([\w.]+)\s*([><=!]+)\s*(-?\d+(?:\.\d+)?)$/); if (compareMatch) { const [_, field, operator, value] = compareMatch; + const parsedRule = this.parseLogFieldAndAttribute(field); return { - field, + field: parsedRule.field, operator, - value: Number(value) - }; + value: Number(value), + ...(parsedRule.objectAttribute && { objectAttribute: parsedRule.objectAttribute }) + } as any; } return null; @@ -770,6 +876,24 @@ export class AlertSettingComponent implements OnInit { } } + private parseLogFieldAndAttribute(fullField: string): { field: string; objectAttribute?: string } { + if (this.define.type !== 'log_realtime') { + return { field: fullField }; + } + + // Check if this is an object field with attribute + const objectFields = ['log.attributes', 'log.resource', 'log.instrumentationScope.attributes']; + + for (const objField of objectFields) { + if (fullField.startsWith(`${objField}.`)) { + const attribute = fullField.substring(objField.length + 1); + return { field: objField, objectAttribute: attribute }; + } + } + + return { field: fullField }; + } + getOperatorLabelByType = (operator: string) => { switch (operator) { case 'equals': @@ -1210,6 +1334,30 @@ export class AlertSettingComponent implements OnInit { } } + private tryParseLogThresholdExpr(expr: string | undefined): void { + if (!expr || !expr.trim()) { + this.resetQbDataDefault(); + this.isExpr = false; + return; + } + + try { + // First try to parse as visual rules for log expressions + const ruleset = this.expr2ruleset(expr); + if (ruleset && ruleset.rules && ruleset.rules.length > 0) { + this.resetQbData(ruleset); + this.isExpr = false; + return; + } + + // If cannot parse as visual rules, switch to expression mode + this.isExpr = true; + } catch (e) { + console.warn('Failed to parse log threshold expr:', e); + this.isExpr = true; + } + } + public updateFinalExpr(): void { const baseExpr = this.cascadeValuesToExpr(this.cascadeValues); const monitorBindExpr = this.generateMonitorBindExpr(); @@ -1502,4 +1650,45 @@ export class AlertSettingComponent implements OnInit { this.previewData = []; this.previewColumns = []; } + + /** + * Get the edit tooltip title i18n key based on the data type. + */ + getEditTooltipTitle(data: any): string { + if (data.type === 'metrics_realtime') { + return 'alert.setting.edit.metrics.realtime'; + } else if (data.type === 'log_realtime') { + return 'alert.setting.edit.log.realtime'; + } else { + return 'alert.setting.edit.metrics.periodic'; + } + } + + /** + * Get the modal title i18n key based on the add/edit mode and data type. + * Similar to a computed property in Vue. + */ + getModalTitle(): string { + if (this.isManageModalAdd) { + if (this.define.type === 'metrics_periodic') { + return 'alert.setting.new.metrics.periodic'; + } else if (this.define.type === 'log_periodic') { + return 'alert.setting.new.log.periodic'; + } else if (this.define.type === 'metrics_realtime') { + return 'alert.setting.new.metrics.realtime'; + } else { + return 'alert.setting.new.log.realtime'; + } + } else { + if (this.define.type === 'metrics_periodic') { + return 'alert.setting.edit.metrics.periodic'; + } else if (this.define.type === 'log_periodic') { + return 'alert.setting.edit.log.periodic'; + } else if (this.define.type === 'metrics_realtime') { + return 'alert.setting.edit.metrics.realtime'; + } else { + return 'alert.setting.edit.log.realtime'; + } + } + } } diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 286cc0385f0..7576e276d1b 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -252,8 +252,10 @@ "alert.setting.default.tip": "Whether this alarm threshold configuration applies to all this type of monitoring globally", "alert.setting.delete": "Delete Threshold Rule", "alert.setting.edit": "Edit Threshold Rule", - "alert.setting.edit.periodic": "Edit Periodic Threshold", - "alert.setting.edit.realtime": "Edit RealTime Threshold", + "alert.setting.edit.metrics.periodic": "Edit Metrics Periodic Threshold", + "alert.setting.edit.metrics.realtime": "Edit Metrics RealTime Threshold", + "alert.setting.edit.log.periodic": "Edit Log Periodic Threshold", + "alert.setting.edit.log.realtime": "Edit Log RealTime Threshold", "alert.setting.enable": "Enable Threshold", "alert.setting.enable.tip": "This alarm threshold configuration is enabled or disabled", "alert.setting.export": "Export Rule", @@ -274,9 +276,12 @@ "alert.setting.name": "Rule Name", "alert.setting.name.tip": "Rule name need to be unique", "alert.setting.new": "New Threshold Rule", - "alert.setting.new.periodic": "New Periodic Threshold", - "alert.setting.new.realtime": "New RealTime Threshold", + "alert.setting.new.metrics.periodic": "New Metrics Periodic Threshold", + "alert.setting.new.metrics.realtime": "New Metrics RealTime Threshold", + "alert.setting.new.log.periodic": "New Log Periodic Threshold", + "alert.setting.new.log.realtime": "New Log RealTime Threshold", "alert.setting.number": "Numeric", + "alert.setting.object": "Object", "alert.setting.operator": "Supported operator functions", "alert.setting.period": "Execution Period", "alert.setting.period.placeholder": "Please enter execution period, minimum 60 seconds", @@ -290,6 +295,7 @@ "alert.setting.rule.label": "Graphical setting alarm threshold rules, support multiple rules &&", "alert.setting.rule.metric.place-holder": "Please select metric", "alert.setting.rule.numeric-value.place-holder": "Please input numeric", + "alert.setting.rule.object.attribute.placeholder": "Please input object attribute", "alert.setting.rule.operator": "Operator", "alert.setting.rule.operator.exists": "value exists", "alert.setting.rule.operator.no-exists": "no value exists", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index 7f5aef98289..be97a9efdd9 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -102,7 +102,7 @@ "alert.integration.source.zabbix": "Zabbix", "alert.integration.source.alibabacloud-sls": "AlibabaCloud-SLS", "alert.integration.source.huaweicloud-ces": "Huawei Cloud Eye", - "alert.integration.source.volcengine":"火山エンジン監視", + "alert.integration.source.volcengine": "火山エンジン監視", "alert.integration.token.desc": "HertzBeat APIにアクセスするために生成したトークン。", "alert.integration.token.new": "トークンを生成するにはクリック", "alert.integration.token.notice": "トークンは一度だけ表示されます。トークンを安全に保管し、他人と共有しないでください。", @@ -246,8 +246,10 @@ "alert.setting.default.tip": "このアラーム閾値設定がグローバルにこのタイプの監視に適用されるかどうか", "alert.setting.delete": "閾値ルールを削除", "alert.setting.edit": "閾値ルールを編集", - "alert.setting.edit.periodic": "周期的な閾値を編集", - "alert.setting.edit.realtime": "リアルタイム閾値を編集", + "alert.setting.edit.metrics.periodic": "メトリクス周期閾値を編集", + "alert.setting.edit.metrics.realtime": "メトリクスリアルタイム閾値を編集", + "alert.setting.edit.log.periodic": "ログ周期閾値を編集", + "alert.setting.edit.log.realtime": "ログリアルタイム閾値を編集", "alert.setting.enable": "閾値を有効化", "alert.setting.enable.tip": "このアラーム閾値設定が有効か無効か", "alert.setting.export": "ルールをエクスポート", @@ -268,9 +270,12 @@ "alert.setting.name": "ルール名", "alert.setting.name.tip": "ルール名はユニークである必要があります", "alert.setting.new": "新規閾値ルール", - "alert.setting.new.periodic": "新規周期的閾値", - "alert.setting.new.realtime": "新規リアルタイム閾値", + "alert.setting.new.metrics.periodic": "新規メトリクス周期閾値", + "alert.setting.new.metrics.realtime": "新規メトリクスリアルタイム閾値", + "alert.setting.new.log.periodic": "新規ログ周期閾値", + "alert.setting.new.log.realtime": "新規ログリアルタイム閾値", "alert.setting.number": "数値", + "alert.setting.object": "オブジェクト", "alert.setting.operator": "サポートされている演算子関数", "alert.setting.period": "実行期間", "alert.setting.period.placeholder": "実行期間を入力してください。最小60秒", @@ -284,6 +289,7 @@ "alert.setting.rule.label": "グラフィカルにアラーム閾値ルールを設定。複数のルール && をサポート", "alert.setting.rule.metric.place-holder": "メトリクスを選択してください", "alert.setting.rule.numeric-value.place-holder": "数値を入力してください", + "alert.setting.rule.object.attribute.placeholder": "オブジェクト属性を入力してください", "alert.setting.rule.operator": "演算子", "alert.setting.rule.operator.exists": "値が存在する", "alert.setting.rule.operator.no-exists": "値が存在しない", @@ -326,11 +332,14 @@ "alert.setting.times": "トリガー回数", "alert.setting.times.tip": "アラームを送信する前に閾値が何回トリガーされるかを設定", "alert.setting.trigger": "アラームをトリガー", - "alert.setting.type": "閾値タイプ", - "alert.setting.type.periodic": "周期的", - "alert.setting.type.periodic.desc": "PromQLクエリを定期的に実行して閾値アラートをトリガーします。", - "alert.setting.type.realtime": "リアルタイム", - "alert.setting.type.realtime.desc": "リアルタイムメトリクス計算と閾値超過時の即時アラート。", + "alert.setting.type.metrics.periodic": "メトリクス周期", + "alert.setting.type.metrics.periodic.desc": "PromQLクエリを定期的に実行して閾値アラートをトリガーします。", + "alert.setting.type.metrics.realtime": "メトリクスリアルタイム", + "alert.setting.type.metrics.realtime.desc": "リアルタイムメトリクス計算と閾値超過時の即時アラート。", + "alert.setting.type.log.periodic": "ログ周期", + "alert.setting.type.log.periodic.desc": "SQLクエリを定期的に実行して閾値アラートをトリガーします。", + "alert.setting.type.log.realtime": "ログリアルタイム", + "alert.setting.type.log.realtime.desc": "リアルタイムログ計算と閾値超過時の即時アラート。", "alert.severity": "アラームの重大度", "alert.severity.0": "緊急", "alert.severity.1": "クリティカル", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index a4e4b07a0c2..964292d43cc 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -292,10 +292,12 @@ "alert.setting.trigger": "Disparar alarmes e atualizar o status do monitor", "alert.setting.rule": "Regra de Limite", "alert.setting.number": "Numérico", + "alert.setting.object": "Objeto", "alert.setting.string": "Texto", "alert.setting.time": "Tempo", "alert.setting.rule.label": "Configuração gráfica de regras de limite de alarme, suporta múltiplas regras &&", "alert.setting.rule.metric.place-holder": "Selecione a métrica", + "alert.setting.rule.object.attribute.placeholder": "Digite o atributo do objeto", "alert.setting.rule.switch-expr.0": "Limite de Modelo", "alert.setting.rule.switch-expr.1": "Limite de Codificação", "alert.setting.rule.operator": "Operador", @@ -522,13 +524,25 @@ "alert.integration.token.title": "Token de autenticação de acesso", "alert.setting.name": "Nome do limite", "alert.setting.type": "Tipo de limite", + "alert.setting.type.metrics.periodic": "Métricas Periódicas", + "alert.setting.type.metrics.periodic.desc": "Executa consultas PromQL periodicamente para disparar alertas de limite.", + "alert.setting.type.metrics.realtime": "Métricas em Tempo Real", + "alert.setting.type.metrics.realtime.desc": "Cálculo de métricas em tempo real com alerta instantâneo ao ultrapassar o limite.", + "alert.setting.type.log.periodic": "Logs Periódicos", + "alert.setting.type.log.periodic.desc": "Executa consultas SQL periodicamente para disparar alertas de limite.", + "alert.setting.type.log.realtime": "Logs em Tempo Real", + "alert.setting.type.log.realtime.desc": "Cálculo de logs em tempo real com alerta instantâneo ao ultrapassar o limite.", "alert.setting.name.tip": "O nome da regra de limite precisa ser exclusivo", - "alert.setting.new.periodic": "Adicionar novo limite do plano", - "alert.setting.new.realtime": "Adicionado limite em tempo real", + "alert.setting.new.metrics.periodic": "Adicionar novo limite do plano", + "alert.setting.new.metrics.realtime": "Adicionado limite em tempo real", + "alert.setting.new.log.periodic": "Adicionar novo limite do plano", + "alert.setting.new.log.realtime": "Adicionado limite em tempo real", "alert.setting.period": "Ciclo de execução", "alert.setting.period.placeholder": "Insira o período de execução, mínimo de 60 segundos", - "alert.setting.edit.periodic": "Editar limiar do plano", - "alert.setting.edit.realtime": "Editar limites em tempo real", + "alert.setting.edit.metrics.periodic": "Editar Limite de Métricas Periódicas", + "alert.setting.edit.metrics.realtime": "Editar Limite de Métricas em Tempo Real", + "alert.setting.edit.log.periodic": "Editar Limite de Logs Periódicos", + "alert.setting.edit.log.realtime": "Editar Limite de Logs em Tempo Real", "alert.setting.bind.available": "Monitoramento opcional", "alert.setting.bind.manage": "Monitoramento relacionado", "alert.setting.bind.monitors": "Monitoramento relacionado", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 6d5166d762a..44c5ac69bd9 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -254,8 +254,10 @@ "alert.setting.default.tip": "此告警阈值配置是否应用于全局所有此类型监控", "alert.setting.delete": "删除阈值规则", "alert.setting.edit": "编辑阈值规则", - "alert.setting.edit.periodic": "编辑计划阈值", - "alert.setting.edit.realtime": "编辑实时阈值", + "alert.setting.edit.metrics.periodic": "编辑指标周期阈值", + "alert.setting.edit.metrics.realtime": "编辑指标实时阈值", + "alert.setting.edit.log.periodic": "编辑日志周期阈值", + "alert.setting.edit.log.realtime": "编辑日志实时阈值", "alert.setting.enable": "启用阈值", "alert.setting.enable.tip": "此告警阈值配置开启生效或关闭", "alert.setting.export": "导出规则", @@ -276,9 +278,12 @@ "alert.setting.name": "阈值名称", "alert.setting.name.tip": "阈值规则名称,需要有唯一性", "alert.setting.new": "新增阈值", - "alert.setting.new.periodic": "新增计划阈值", - "alert.setting.new.realtime": "新增实时阈值", + "alert.setting.new.metrics.periodic": "新增指标周期阈值", + "alert.setting.new.metrics.realtime": "新增指标实时阈值", + "alert.setting.new.log.periodic": "新增日志周期阈值", + "alert.setting.new.log.realtime": "新增日志实时阈值", "alert.setting.number": "数值型", + "alert.setting.object": "对象", "alert.setting.operator": "支持操作符函数", "alert.setting.period": "执行周期", "alert.setting.period.placeholder": "请输入执行周期,最小60秒", @@ -292,6 +297,7 @@ "alert.setting.rule.label": "图形化选择设置指标触发告警规则,支持多规则组合", "alert.setting.rule.metric.place-holder": "请选择指标", "alert.setting.rule.numeric-value.place-holder": "请输入匹配数值", + "alert.setting.rule.object.attribute.placeholder": "请输入对象属性", "alert.setting.rule.operator": "运算符", "alert.setting.rule.operator.exists": "存在值", "alert.setting.rule.operator.no-exists": "不存在值", @@ -335,10 +341,14 @@ "alert.setting.times.tip": "设置触发阈值多少次之后才会发送告警", "alert.setting.trigger": "异常时触发告警", "alert.setting.type": "阈值类型", - "alert.setting.type.periodic": "计划周期", - "alert.setting.type.periodic.desc": "周期性执行 PromQL 查询触发阈值告警", - "alert.setting.type.realtime": "实时计算", - "alert.setting.type.realtime.desc": "实时计算指标数据,触发阈值时立即告警", + "alert.setting.type.metrics.periodic": "指标周期", + "alert.setting.type.metrics.periodic.desc": "周期性执行PromQL查询,触发阈值告警。", + "alert.setting.type.metrics.realtime": "指标实时", + "alert.setting.type.metrics.realtime.desc": "实时指标计算,触发阈值告警。", + "alert.setting.type.log.periodic": "日志周期", + "alert.setting.type.log.periodic.desc": "周期性执行SQL查询,触发阈值告警。", + "alert.setting.type.log.realtime": "日志实时", + "alert.setting.type.log.realtime.desc": "实时日志计算,触发阈值告警。", "alert.severity": "告警级别", "alert.severity.0": "紧急告警", "alert.severity.1": "严重告警", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index b5ffb2fe61b..e6dad456d59 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -102,7 +102,7 @@ "alert.integration.source.zabbix": "Zabbix", "alert.integration.source.alibabacloud-sls": "阿里雲端日誌服務 SLS", "alert.integration.source.huaweicloud-ces": "華為雲監控服務", - "alert.integration.source.volcengine":"火山引擎監控", + "alert.integration.source.volcengine": "火山引擎監控", "alert.integration.token.desc": "生成的 Token 可用于访问 HertzBeat API", "alert.integration.token.new": "点击生成 Token", "alert.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", @@ -245,8 +245,10 @@ "alert.setting.default.tip": "此告警阈值配置是否應用于全局所有此類型監控", "alert.setting.delete": "刪除阈值規則", "alert.setting.edit": "編輯阈值規則", - "alert.setting.edit.periodic": "编辑计划阈值", - "alert.setting.edit.realtime": "编辑实时阈值", + "alert.setting.edit.metrics.periodic": "編輯指標周期阈值", + "alert.setting.edit.metrics.realtime": "編輯指標實時阈值", + "alert.setting.edit.log.periodic": "編輯日志周期阈值", + "alert.setting.edit.log.realtime": "編輯日志實時阈值", "alert.setting.enable": "啓用閾值", "alert.setting.enable.tip": "此告警阈值配置開啓生效或關閉", "alert.setting.export": "導出規則", @@ -267,9 +269,12 @@ "alert.setting.name": "阈值名称", "alert.setting.name.tip": "阈值规则名称,需要有唯一性", "alert.setting.new": "新增阈值規則", - "alert.setting.new.periodic": "新增计划阈值", - "alert.setting.new.realtime": "新增实时阈值", + "alert.setting.new.metrics.periodic": "新增指標周期阈值", + "alert.setting.new.metrics.realtime": "新增指標實時阈值", + "alert.setting.new.log.periodic": "新增日志周期阈值", + "alert.setting.new.log.realtime": "新增日志實時阈值", "alert.setting.number": "數值型", + "alert.setting.object": "對象", "alert.setting.operator": "支持操作符函數", "alert.setting.period": "執行週期", "alert.setting.period.placeholder": "請輸入執行週期,最小60秒", @@ -283,6 +288,7 @@ "alert.setting.rule.label": "圖形化選擇設置指標觸發告警規則,支援多規則與", "alert.setting.rule.metric.place-holder": "請選擇指標", "alert.setting.rule.numeric-value.place-holder": "請輸入匹配數值", + "alert.setting.rule.object.attribute.placeholder": "請輸入對象屬性", "alert.setting.rule.operator": "運算符", "alert.setting.rule.operator.exists": "存在值", "alert.setting.rule.operator.no-exists": "不存在值", @@ -326,10 +332,14 @@ "alert.setting.times.tip": "設置觸發阈值多少次之後才會發送告警", "alert.setting.trigger": "異常時觸發告警", "alert.setting.type": "阈值类型", - "alert.setting.type.periodic": "计划周期", - "alert.setting.type.periodic.desc": "週期性執行 PromQL 查詢觸發閾值警報", - "alert.setting.type.realtime": "实时计算", - "alert.setting.type.realtime.desc": "即時計算指標數據,觸發閾值時立即告警", + "alert.setting.type.metrics.periodic": "指標周期", + "alert.setting.type.metrics.periodic.desc": "週期性執行 PromQL 查詢觸發閾值警報", + "alert.setting.type.metrics.realtime": "指標實時", + "alert.setting.type.metrics.realtime.desc": "即時計算指標數據,觸發閾值時立即告警", + "alert.setting.type.log.periodic": "日志周期", + "alert.setting.type.log.periodic.desc": "週期性執行 SQL 查詢觸發閾值警報", + "alert.setting.type.log.realtime": "日志實時", + "alert.setting.type.log.realtime.desc": "即時計算日志數據,觸發閾值時立即告警", "alert.severity": "告警級別", "alert.severity.0": "緊急告警", "alert.severity.1": "嚴重告警", From 0f5cb355c4557c2b1c0c00ffd48079fcf1fe3aa0 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 13 Jul 2025 16:52:28 +0800 Subject: [PATCH 11/39] improvement: Modify xxx_realtime and xxx_periodic to realtime_xxx and periodic_xxx --- .../common/constants/CommonConstants.java | 8 ++-- .../common/entity/alerter/AlertDefine.java | 2 +- .../db/migration/h2/V173__update_column.sql | 8 ++-- .../migration/mysql/V173__update_column.sql | 8 ++-- .../postgresql/V173__update_column.sql | 8 ++-- web-app/src/app/pojo/AlertDefine.ts | 4 +- .../alert-setting.component.html | 46 +++++++++---------- .../alert-setting/alert-setting.component.ts | 46 +++++++++---------- 8 files changed, 65 insertions(+), 65 deletions(-) diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java index 1106c094552..314bced8328 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java @@ -150,22 +150,22 @@ public interface CommonConstants { /** * metrics alert threshold type: realtime */ - String METRICS_ALERT_THRESHOLD_TYPE_REALTIME = "metrics_realtime"; + String METRICS_ALERT_THRESHOLD_TYPE_REALTIME = "realtime_metrics"; /** * metrics alert threshold type: periodic */ - String METRICS_ALERT_THRESHOLD_TYPE_PERIODIC = "metrics_periodic"; + String METRICS_ALERT_THRESHOLD_TYPE_PERIODIC = "periodic_metrics"; /** * log alert threshold type: realtime */ - String LOG_ALERT_THRESHOLD_TYPE_REALTIME = "log_realtime"; + String LOG_ALERT_THRESHOLD_TYPE_REALTIME = "realtime_log"; /** * log alert threshold type: periodic */ - String LOG_ALERT_THRESHOLD_TYPE_PERIODIC = "log_periodic"; + String LOG_ALERT_THRESHOLD_TYPE_PERIODIC = "periodic_log"; /** * Field parameter type: number diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java index 98de8b6d2b2..b5a3b013445 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java @@ -65,7 +65,7 @@ public class AlertDefine { @NotNull private String name; - @Schema(title = "Rule Type: metrics_realtime, metrics_periodic, log_realtime, log_periodic", example = "metrics_realtime") + @Schema(title = "Rule Type: realtime_metrics, periodic_metrics, realtime_log, periodic_log", example = "realtime_metrics") private String type; @Schema(title = "Alarm Threshold Expr", example = "usage>90", accessMode = READ_WRITE) diff --git a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql index 3aa262cf29c..1615a5a9bb4 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql @@ -19,12 +19,12 @@ -- Update hzb_alert_define table type column to support log monitoring --- Update type from 'realtime' to 'metrics_realtime' +-- Update type from 'realtime' to 'realtime_metrics' UPDATE HZB_ALERT_DEFINE -SET type = 'metrics_realtime' +SET type = 'realtime_metrics' WHERE type = 'realtime'; --- Update type from 'periodic' to 'metrics_periodic' +-- Update type from 'periodic' to 'periodic_metrics' UPDATE HZB_ALERT_DEFINE -SET type = 'metrics_periodic' +SET type = 'periodic_metrics' WHERE type = 'periodic'; diff --git a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql index 8ff8b593017..678332850e1 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql @@ -30,14 +30,14 @@ BEGIN WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE'; IF table_exists = 1 THEN - -- Update type from 'realtime' to 'metrics_realtime' + -- Update type from 'realtime' to 'realtime_metrics' UPDATE HZB_ALERT_DEFINE - SET type = 'metrics_realtime' + SET type = 'realtime_metrics' WHERE type = 'realtime'; - -- Update type from 'periodic' to 'metrics_periodic' + -- Update type from 'periodic' to 'periodic_metrics' UPDATE HZB_ALERT_DEFINE - SET type = 'metrics_periodic' + SET type = 'periodic_metrics' WHERE type = 'periodic'; END IF; END // diff --git a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql index 1346668f778..e2822d58597 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql @@ -19,14 +19,14 @@ -- Update hzb_alert_define table type column to support log monitoring --- Update type from 'realtime' to 'metrics_realtime' +-- Update type from 'realtime' to 'realtime_metrics' UPDATE HZB_ALERT_DEFINE -SET type = 'metrics_realtime' +SET type = 'realtime_metrics' WHERE type = 'realtime'; --- Update type from 'periodic' to 'metrics_periodic' +-- Update type from 'periodic' to 'periodic_metrics' UPDATE HZB_ALERT_DEFINE -SET type = 'metrics_periodic' +SET type = 'periodic_metrics' WHERE type = 'periodic'; commit; diff --git a/web-app/src/app/pojo/AlertDefine.ts b/web-app/src/app/pojo/AlertDefine.ts index fa696104852..4d18174bda5 100644 --- a/web-app/src/app/pojo/AlertDefine.ts +++ b/web-app/src/app/pojo/AlertDefine.ts @@ -20,8 +20,8 @@ export class AlertDefine { id!: number; name!: string; - // metrics_realtime, metrics_periodic, log_realtime, log_periodic - type: string = 'metrics_realtime'; + // realtime_metrics, periodic_metrics, realtime_log, periodic_log + type: string = 'realtime_metrics'; // datasource when type is periodic, promql | sql datasource: string = 'promql'; expr!: string; diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 80fdc3ade6c..267b5931f9a 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -113,16 +113,16 @@ {{ data.name }} - + {{ 'alert.setting.type.metrics.realtime' | i18n }} - + {{ 'alert.setting.type.metrics.periodic' | i18n }} - + {{ 'alert.setting.type.log.realtime' | i18n }} - + {{ 'alert.setting.type.log.periodic' | i18n }} @@ -197,7 +197,7 @@ - + {{ 'alert.setting.target' | i18n }} @@ -214,7 +214,7 @@ > - + {{ 'alert.setting.rule' | i18n }} @@ -245,7 +245,7 @@ - +
@@ -287,7 +287,7 @@
- + @@ -396,7 +396,7 @@ - + {{ 'alert.setting.bind.monitors' | i18n }} @@ -407,7 +407,7 @@ - + {{ 'alert.setting.preview.expr' | i18n }} @@ -417,7 +417,7 @@ - + {{ 'alert.setting.rule' | i18n }} @@ -439,7 +439,7 @@ - +
@@ -487,7 +487,7 @@
- + @@ -615,7 +615,7 @@ - + {{ 'alert.setting.preview.expr' | i18n }} @@ -625,7 +625,7 @@ - + {{ 'alert.setting.rule' | i18n }} @@ -639,7 +639,7 @@ - + @@ -685,7 +685,7 @@
- + {{ 'alert.setting.period' | i18n }} @@ -737,9 +737,9 @@
-
+
{{ define.type }} -
+
{{ env.name }} @@ -754,7 +754,7 @@
-
+
{{ '${' + field.value + '}' }} @@ -980,17 +980,17 @@

EXCEL

>
- +

{{ 'alert.setting.type.metrics.realtime' | i18n }}

{{ 'alert.setting.type.metrics.realtime.desc' | i18n }}

- +

{{ 'alert.setting.type.metrics.periodic' | i18n }}

{{ 'alert.setting.type.metrics.periodic.desc' | i18n }}

- +

{{ 'alert.setting.type.log.realtime' | i18n }}

{{ 'alert.setting.type.log.realtime.desc' | i18n }}

diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index d733a4fcacc..a0fa1d2decf 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -59,7 +59,7 @@ export class AlertSettingComponent implements OnInit { this.qbFormCtrl = this.formBuilder.control(this.qbData, this.qbValidator); this.qbFormCtrl.valueChanges.subscribe(() => { this.userExpr = this.ruleset2expr(this.qbFormCtrl.value); - if (this.define.type === 'log_realtime') { + if (this.define.type === 'realtime_log') { this.updateLogFinalExpr(); } else { this.updateFinalExpr(); @@ -161,7 +161,7 @@ export class AlertSettingComponent implements OnInit { } updateLogQbConfig() { - if (this.define.type === 'log_realtime') { + if (this.define.type === 'realtime_log') { let fields: any = {}; this.logFields.forEach(item => { fields[item.value] = { @@ -176,7 +176,7 @@ export class AlertSettingComponent implements OnInit { } updateLogFinalExpr(): void { - if (this.define.type === 'log_realtime') { + if (this.define.type === 'realtime_log') { this.define.expr = this.userExpr; } } @@ -327,12 +327,12 @@ export class AlertSettingComponent implements OnInit { this.userExpr = ''; this.selectedMonitorIds = new Set(); this.selectedLabels = new Set(); - // Set default period for metrics_periodic alert - if (type === 'metrics_periodic') { + // Set default period for periodic_metrics alert + if (type === 'periodic_metrics') { this.define.period = 300; } - // Initialize log fields for log_realtime alert - if (type === 'log_realtime') { + // Initialize log fields for realtime_log alert + if (type === 'realtime_log') { this.updateLogQbConfig(); } this.resetQbDataDefault(); @@ -572,15 +572,15 @@ export class AlertSettingComponent implements OnInit { if (this.define.labels && this.define.labels['severity']) { this.severity = this.define.labels['severity']; } - // Set default period for metrics_periodic alert if not set - if (this.define.type === 'metrics_periodic' && !this.define.period) { + // Set default period for periodic_metrics alert if not set + if (this.define.type === 'periodic_metrics' && !this.define.period) { this.define.period = 300; } - // Set default type as metrics_realtime if not set + // Set default type as realtime_metrics if not set if (!this.define.type) { - this.define.type = 'metrics_realtime'; + this.define.type = 'realtime_metrics'; } - if (this.define.type == 'metrics_realtime') { + if (this.define.type == 'realtime_metrics') { // Parse expression to cascade values this.cascadeValues = this.exprToCascadeValues(this.define.expr); this.userExpr = this.exprToUserExpr(this.define.expr); @@ -595,7 +595,7 @@ export class AlertSettingComponent implements OnInit { this.tryParseThresholdExpr(this.userExpr); } }); - } else if (this.define.type == 'log_realtime') { + } else if (this.define.type == 'realtime_log') { // Initialize log fields and parse expression this.updateLogQbConfig(); this.userExpr = this.define.expr || ''; @@ -635,7 +635,7 @@ export class AlertSettingComponent implements OnInit { // Get the effective field name (including object attributes for log fields) let effectiveField = rule.field; - if (this.define.type === 'log_realtime' && (rule as any).objectAttribute) { + if (this.define.type === 'realtime_log' && (rule as any).objectAttribute) { effectiveField = `${rule.field}.${(rule as any).objectAttribute}`; } @@ -877,7 +877,7 @@ export class AlertSettingComponent implements OnInit { } private parseLogFieldAndAttribute(fullField: string): { field: string; objectAttribute?: string } { - if (this.define.type !== 'log_realtime') { + if (this.define.type !== 'realtime_log') { return { field: fullField }; } @@ -1655,9 +1655,9 @@ export class AlertSettingComponent implements OnInit { * Get the edit tooltip title i18n key based on the data type. */ getEditTooltipTitle(data: any): string { - if (data.type === 'metrics_realtime') { + if (data.type === 'realtime_metrics') { return 'alert.setting.edit.metrics.realtime'; - } else if (data.type === 'log_realtime') { + } else if (data.type === 'realtime_log') { return 'alert.setting.edit.log.realtime'; } else { return 'alert.setting.edit.metrics.periodic'; @@ -1670,21 +1670,21 @@ export class AlertSettingComponent implements OnInit { */ getModalTitle(): string { if (this.isManageModalAdd) { - if (this.define.type === 'metrics_periodic') { + if (this.define.type === 'periodic_metrics') { return 'alert.setting.new.metrics.periodic'; - } else if (this.define.type === 'log_periodic') { + } else if (this.define.type === 'periodic_log') { return 'alert.setting.new.log.periodic'; - } else if (this.define.type === 'metrics_realtime') { + } else if (this.define.type === 'realtime_metrics') { return 'alert.setting.new.metrics.realtime'; } else { return 'alert.setting.new.log.realtime'; } } else { - if (this.define.type === 'metrics_periodic') { + if (this.define.type === 'periodic_metrics') { return 'alert.setting.edit.metrics.periodic'; - } else if (this.define.type === 'log_periodic') { + } else if (this.define.type === 'periodic_log') { return 'alert.setting.edit.log.periodic'; - } else if (this.define.type === 'metrics_realtime') { + } else if (this.define.type === 'realtime_metrics') { return 'alert.setting.edit.metrics.realtime'; } else { return 'alert.setting.edit.log.realtime'; From 9a94f250c08bcf135fceb0bd43baef8bb80ec2a7 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 13 Jul 2025 17:12:31 +0800 Subject: [PATCH 12/39] improvement: Place the logs section above the alerts section --- .../src/app/routes/routes-routing.module.ts | 2 +- web-app/src/assets/app-data.json | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/web-app/src/app/routes/routes-routing.module.ts b/web-app/src/app/routes/routes-routing.module.ts index 84b457d2472..b1ff83b0082 100644 --- a/web-app/src/app/routes/routes-routing.module.ts +++ b/web-app/src/app/routes/routes-routing.module.ts @@ -27,8 +27,8 @@ const routes: Routes = [ loadChildren: () => import('./monitor/monitor.module').then(m => m.MonitorModule), data: { titleI18n: 'menu.monitor.center' } }, - { path: 'alert', loadChildren: () => import('./alert/alert.module').then(m => m.AlertModule) }, { path: 'log', loadChildren: () => import('./log/log.module').then(m => m.LogModule) }, + { path: 'alert', loadChildren: () => import('./alert/alert.module').then(m => m.AlertModule) }, { path: 'setting', loadChildren: () => import('./setting/setting.module').then(m => m.SettingModule) } ] }, diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index 0ef708c3792..04691b0f115 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -136,6 +136,20 @@ } ] }, + { + "text": "Log", + "i18n": "menu.log", + "group": true, + "hideInBreadcrumb": true, + "children": [ + { + "text": "Integration", + "i18n": "menu.log.integration", + "icon": "anticon-api", + "link": "/log/integration/otlp" + } + ] + }, { "text": "Alarm", "i18n": "menu.alert", @@ -186,20 +200,6 @@ } ] }, - { - "text": "Log", - "i18n": "menu.log", - "group": true, - "hideInBreadcrumb": true, - "children": [ - { - "text": "Integration", - "i18n": "menu.log.integration", - "icon": "anticon-api", - "link": "/log/integration/otlp" - } - ] - }, { "text": "Advanced", "i18n": "menu.advanced", From d06ea817cf00c803a84c4f7116665ace59b68a28 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Tue, 15 Jul 2025 16:15:24 +0800 Subject: [PATCH 13/39] improvement: move data type selector --- .../calculate/LogRealTimeAlertCalculator.java | 2 +- .../calculate/PeriodicAlertRuleScheduler.java | 6 +- .../service/impl/AlertDefineServiceImpl.java | 10 +-- .../alert/service/AlertDefineServiceTest.java | 10 +-- .../hertzbeat/common/cache/CacheFactory.java | 6 +- .../common/constants/CommonConstants.java | 12 +-- .../common/entity/alerter/AlertDefine.java | 2 +- .../service/impl/OtlpLogProtocolAdapter.java | 2 +- .../db/migration/h2/V173__update_column.sql | 8 +- .../migration/mysql/V173__update_column.sql | 8 +- .../postgresql/V173__update_column.sql | 8 +- web-app/src/app/pojo/AlertDefine.ts | 4 +- .../alert-setting.component.html | 83 +++++++++++------- .../alert-setting/alert-setting.component.ts | 84 ++++++++++++------- web-app/src/assets/i18n/en-US.json | 28 ++++--- web-app/src/assets/i18n/ja-JP.json | 24 ++---- web-app/src/assets/i18n/pt-BR.json | 32 +++---- web-app/src/assets/i18n/zh-CN.json | 32 +++---- web-app/src/assets/i18n/zh-TW.json | 28 +++---- 19 files changed, 212 insertions(+), 177 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java index c0f31c27e57..7f86473f38b 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java @@ -41,7 +41,7 @@ @Slf4j public class LogRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator { - public static final String LOG_PREFIX = "LOG"; + public static final String LOG_PREFIX = "log"; @Autowired public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java index 5c7f86765f9..5d076542a5a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java @@ -18,7 +18,7 @@ package org.apache.hertzbeat.alert.calculate; import static org.apache.hertzbeat.common.constants.CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC; -import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.ArrayList; @@ -79,7 +79,7 @@ public void updateSchedule(AlertDefine rule) { return; } cancelSchedule(rule.getId()); - if (rule.getType().equals(METRICS_ALERT_THRESHOLD_TYPE_PERIODIC) + if (rule.getType().equals(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC) || rule.getType().equals(LOG_ALERT_THRESHOLD_TYPE_PERIODIC)) { ScheduledFuture future = scheduledExecutor.scheduleAtFixedRate(() -> { calculator.calculate(rule); @@ -91,7 +91,7 @@ public void updateSchedule(AlertDefine rule) { @Override public void run(String... args) throws Exception { log.info("Starting periodic alert rule scheduler..."); - List metricsPeriodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRICS_ALERT_THRESHOLD_TYPE_PERIODIC); + List metricsPeriodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC); List logPeriodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_PERIODIC); List periodicRules = new ArrayList<>(metricsPeriodicRules.size() + logPeriodicRules.size()); periodicRules.addAll(metricsPeriodicRules); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index f9168452bdc..1c8c2514ed8 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -87,7 +87,7 @@ public AlertDefineServiceImpl(List alertDefineImExpo @Override public void validate(AlertDefine alertDefine, boolean isModify) throws IllegalArgumentException { if (StringUtils.hasText(alertDefine.getExpr())) { - if (CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType()) + if (CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType()) || CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType())) { try { JexlExpressionRunner.compile(alertDefine.getExpr()); @@ -218,7 +218,7 @@ public void importConfig(MultipartFile file) throws Exception { public List getMetricsRealTimeAlertDefines() { List alertDefines = CacheFactory.getMetricsAlertDefineCache(); if (alertDefines == null) { - alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME); + alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME); CacheFactory.setMetricsAlertDefineCache(alertDefines); } return alertDefines; @@ -240,7 +240,7 @@ public List> getDefinePreview(String datasource, String type return Collections.emptyList(); } switch (type) { - case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC: + case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC: return dataSourceService.calculate(datasource, expr); default: log.error("Get define preview unsupported type: {}", type); @@ -255,8 +255,8 @@ public List getAlertDefinesByType(String type) { } switch (type) { - case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME: - case CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC: + case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME: + case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC: case CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME: case CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC: // Valid type, proceed with query diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java index cb5175af269..824c19e9864 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java @@ -39,8 +39,8 @@ import java.util.Map; import java.util.Optional; -import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_PERIODIC; -import static org.apache.hertzbeat.common.constants.CommonConstants.METRICS_ALERT_THRESHOLD_TYPE_REALTIME; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC; +import static org.apache.hertzbeat.common.constants.CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -164,14 +164,14 @@ void getDefinePreview() { } }; when(dataSourceService.calculate(eq("promql"), eq(expr))).thenReturn(Lists.newArrayList(countValue1)); - List> result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_PERIODIC, expr); + List> result = alertDefineService.getDefinePreview("promql", METRIC_ALERT_THRESHOLD_TYPE_PERIODIC, expr); assertNotNull(result); assertEquals(1307, result.get(0).get("__value__")); - result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_PERIODIC, null); + result = alertDefineService.getDefinePreview("promql", METRIC_ALERT_THRESHOLD_TYPE_PERIODIC, null); assertEquals(0, result.size()); - result = alertDefineService.getDefinePreview("promql", METRICS_ALERT_THRESHOLD_TYPE_REALTIME, null); + result = alertDefineService.getDefinePreview("promql", METRIC_ALERT_THRESHOLD_TYPE_REALTIME, null); assertEquals(0, result.size()); } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java index 1a9ace574af..a58321ba698 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/cache/CacheFactory.java @@ -87,7 +87,7 @@ public static void clearAlertSilenceCache() { */ @SuppressWarnings("unchecked") public static List getMetricsAlertDefineCache() { - return (List) COMMON_CACHE.get(CommonConstants.METRICS_CACHE_ALERT_DEFINE); + return (List) COMMON_CACHE.get(CommonConstants.METRIC_CACHE_ALERT_DEFINE); } /** @@ -95,14 +95,14 @@ public static List getMetricsAlertDefineCache() { * @param alertDefines alert defines */ public static void setMetricsAlertDefineCache(List alertDefines) { - COMMON_CACHE.put(CommonConstants.METRICS_CACHE_ALERT_DEFINE, alertDefines); + COMMON_CACHE.put(CommonConstants.METRIC_CACHE_ALERT_DEFINE, alertDefines); } /** * clear metrics alert define cache */ public static void clearMetricsAlertDefineCache() { - COMMON_CACHE.remove(CommonConstants.METRICS_CACHE_ALERT_DEFINE); + COMMON_CACHE.remove(CommonConstants.METRIC_CACHE_ALERT_DEFINE); } /** diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java index 314bced8328..b5b66939c8b 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/CommonConstants.java @@ -148,14 +148,14 @@ public interface CommonConstants { String ALERT_STATUS_PENDING = "pending"; /** - * metrics alert threshold type: realtime + * metric alert threshold type: realtime */ - String METRICS_ALERT_THRESHOLD_TYPE_REALTIME = "realtime_metrics"; + String METRIC_ALERT_THRESHOLD_TYPE_REALTIME = "realtime_metric"; /** - * metrics alert threshold type: periodic + * metric alert threshold type: periodic */ - String METRICS_ALERT_THRESHOLD_TYPE_PERIODIC = "periodic_metrics"; + String METRIC_ALERT_THRESHOLD_TYPE_PERIODIC = "periodic_metric"; /** * log alert threshold type: realtime @@ -238,9 +238,9 @@ public interface CommonConstants { String CACHE_ALERT_SILENCE = "alert_silence"; /** - * cache key metrics alert define + * cache key metric alert define */ - String METRICS_CACHE_ALERT_DEFINE = "metrics_alert_define"; + String METRIC_CACHE_ALERT_DEFINE = "metric_alert_define"; /** * cache key log alert define diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java index b5a3b013445..2434df7f017 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java @@ -65,7 +65,7 @@ public class AlertDefine { @NotNull private String name; - @Schema(title = "Rule Type: realtime_metrics, periodic_metrics, realtime_log, periodic_log", example = "realtime_metrics") + @Schema(title = "Rule Type: realtime_metric, periodic_metric, realtime_log, periodic_log", example = "realtime_metric") private String type; @Schema(title = "Alarm Threshold Expr", example = "usage>90", accessMode = READ_WRITE) diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index e3593960cdc..98488085b4a 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -70,7 +70,7 @@ public void ingest(String content) { logEntries.forEach(entry -> { try { commonDataQueue.sendLogEntry(entry); - log.debug("Log entry sent to queue: {}", entry); + log.info("Log entry sent to queue: {}", entry); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("Failed to send log entry to queue: {}", e.getMessage()); diff --git a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql index 1615a5a9bb4..42490384122 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql @@ -19,12 +19,12 @@ -- Update hzb_alert_define table type column to support log monitoring --- Update type from 'realtime' to 'realtime_metrics' +-- Update type from 'realtime' to 'realtime_metric' UPDATE HZB_ALERT_DEFINE -SET type = 'realtime_metrics' +SET type = 'realtime_metric' WHERE type = 'realtime'; --- Update type from 'periodic' to 'periodic_metrics' +-- Update type from 'periodic' to 'periodic_metric' UPDATE HZB_ALERT_DEFINE -SET type = 'periodic_metrics' +SET type = 'periodic_metric' WHERE type = 'periodic'; diff --git a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql index 678332850e1..d312038e3c5 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql @@ -30,14 +30,14 @@ BEGIN WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE'; IF table_exists = 1 THEN - -- Update type from 'realtime' to 'realtime_metrics' + -- Update type from 'realtime' to 'realtime_metric' UPDATE HZB_ALERT_DEFINE - SET type = 'realtime_metrics' + SET type = 'realtime_metric' WHERE type = 'realtime'; - -- Update type from 'periodic' to 'periodic_metrics' + -- Update type from 'periodic' to 'periodic_metric' UPDATE HZB_ALERT_DEFINE - SET type = 'periodic_metrics' + SET type = 'periodic_metric' WHERE type = 'periodic'; END IF; END // diff --git a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql index e2822d58597..1e5d005b969 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql @@ -19,14 +19,14 @@ -- Update hzb_alert_define table type column to support log monitoring --- Update type from 'realtime' to 'realtime_metrics' +-- Update type from 'realtime' to 'realtime_metric' UPDATE HZB_ALERT_DEFINE -SET type = 'realtime_metrics' +SET type = 'realtime_metric' WHERE type = 'realtime'; --- Update type from 'periodic' to 'periodic_metrics' +-- Update type from 'periodic' to 'periodic_metric' UPDATE HZB_ALERT_DEFINE -SET type = 'periodic_metrics' +SET type = 'periodic_metric' WHERE type = 'periodic'; commit; diff --git a/web-app/src/app/pojo/AlertDefine.ts b/web-app/src/app/pojo/AlertDefine.ts index 4d18174bda5..364666a7f9d 100644 --- a/web-app/src/app/pojo/AlertDefine.ts +++ b/web-app/src/app/pojo/AlertDefine.ts @@ -20,8 +20,8 @@ export class AlertDefine { id!: number; name!: string; - // realtime_metrics, periodic_metrics, realtime_log, periodic_log - type: string = 'realtime_metrics'; + // realtime_metric, periodic_metric, realtime_log, periodic_log + type: string = 'realtime_metric'; // datasource when type is periodic, promql | sql datasource: string = 'promql'; expr!: string; diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 267b5931f9a..6a5a7fe5ed3 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -113,17 +113,17 @@ {{ data.name }} - - {{ 'alert.setting.type.metrics.realtime' | i18n }} + + {{ 'alert.setting.type.realtime.metric' | i18n }} - - {{ 'alert.setting.type.metrics.periodic' | i18n }} + + {{ 'alert.setting.type.periodic.metric' | i18n }} - {{ 'alert.setting.type.log.realtime' | i18n }} + {{ 'alert.setting.type.realtime.log' | i18n }} - {{ 'alert.setting.type.log.periodic' | i18n }} + {{ 'alert.setting.type.periodic.log' | i18n }} @@ -180,7 +180,7 @@ - + + + + {{ 'alert.setting.datatype' | i18n }} + + + + + + + + + {{ 'alert.setting.target' | i18n }} @@ -214,7 +239,7 @@ > - + {{ 'alert.setting.rule' | i18n }} @@ -245,7 +270,7 @@ - +
@@ -287,7 +312,7 @@
- + @@ -396,7 +421,7 @@ - + {{ 'alert.setting.bind.monitors' | i18n }} @@ -407,7 +432,7 @@ - + {{ 'alert.setting.preview.expr' | i18n }} @@ -417,6 +442,7 @@
+ {{ 'alert.setting.rule' | i18n }} @@ -439,6 +465,7 @@ + @@ -487,6 +514,7 @@
+ @@ -615,6 +643,7 @@ + {{ 'alert.setting.preview.expr' | i18n }} @@ -625,7 +654,7 @@ - + {{ 'alert.setting.rule' | i18n }} @@ -639,7 +668,7 @@ - + @@ -685,7 +714,7 @@
- + {{ 'alert.setting.period' | i18n }} @@ -737,9 +766,8 @@
-
- {{ define.type }} -
+
+
{{ env.name }} @@ -980,20 +1008,15 @@

EXCEL

>
- + -

{{ 'alert.setting.type.metrics.realtime' | i18n }}

-

{{ 'alert.setting.type.metrics.realtime.desc' | i18n }}

+

{{ 'alert.setting.type.realtime' | i18n }}

+

{{ 'alert.setting.type.realtime.desc' | i18n }}

- + -

{{ 'alert.setting.type.metrics.periodic' | i18n }}

-

{{ 'alert.setting.type.metrics.periodic.desc' | i18n }}

-
- - -

{{ 'alert.setting.type.log.realtime' | i18n }}

-

{{ 'alert.setting.type.log.realtime.desc' | i18n }}

+

{{ 'alert.setting.type.periodic' | i18n }}

+

{{ 'alert.setting.type.periodic.desc' | i18n }}

diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index a0fa1d2decf..31ef53a8cdd 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -130,6 +130,8 @@ export class AlertSettingComponent implements OnInit { ]; isSelectTypeModalVisible = false; + dataType: string = 'metric'; // Default to metric + alertType: string = 'realtime'; // Default to realtime previewData: any[] = []; previewColumns: Array<{ title: string; key: string; width?: string }> = []; @@ -321,24 +323,47 @@ export class AlertSettingComponent implements OnInit { onSelectAlertType(type: string) { this.isSelectTypeModalVisible = false; + this.alertType = type; this.define = new AlertDefine(); - this.define.type = type; this.severity = ''; this.userExpr = ''; this.selectedMonitorIds = new Set(); this.selectedLabels = new Set(); - // Set default period for periodic_metrics alert - if (type === 'periodic_metrics') { + // Set default period for periodic alert + if (type === 'periodic') { this.define.period = 300; } - // Initialize log fields for realtime_log alert - if (type === 'realtime_log') { - this.updateLogQbConfig(); - } + this.updateAlertDefineType(); this.resetQbDataDefault(); this.isManageModalAdd = true; this.isManageModalVisible = true; this.isManageModalOkLoading = false; + + } + + onDataTypeChange() { + this.updateAlertDefineType(); + } + + private updateAlertDefineType() { + // Combine main type with data type + if (this.alertType === 'realtime' && this.dataType === 'metric') { + this.define.type = 'realtime_metric'; + } else if (this.alertType === 'realtime' && this.dataType === 'log') { + this.define.type = 'realtime_log'; + this.updateLogQbConfig(); + } else if (this.alertType === 'periodic' && this.dataType === 'metric') { + this.define.type = 'periodic_metric'; + } else if (this.alertType === 'periodic' && this.dataType === 'log') { + this.define.type = 'periodic_log'; + this.updateLogQbConfig(); + } + + // Reset form state when switching data source type + this.userExpr = ''; + this.cascadeValues = []; + this.currentMetrics = []; + this.resetQbDataDefault(); } onSelectTypeModalCancel() { @@ -561,6 +586,7 @@ export class AlertSettingComponent implements OnInit { finalize(() => { getDefine$.unsubscribe(); this.isLoadingEdit = -1; + this.extractDataTypeAndAlertType(this.define.type); this.isManageModalVisible = true; this.clearPreview(); }) @@ -572,15 +598,15 @@ export class AlertSettingComponent implements OnInit { if (this.define.labels && this.define.labels['severity']) { this.severity = this.define.labels['severity']; } - // Set default period for periodic_metrics alert if not set - if (this.define.type === 'periodic_metrics' && !this.define.period) { + // Set default period for periodic_metric alert if not set + if (this.define.type === 'periodic_metric' && !this.define.period) { this.define.period = 300; } - // Set default type as realtime_metrics if not set + // Set default type as realtime_metric if not set if (!this.define.type) { - this.define.type = 'realtime_metrics'; + this.define.type = 'realtime_metric'; } - if (this.define.type == 'realtime_metrics') { + if (this.define.type == 'realtime_metric') { // Parse expression to cascade values this.cascadeValues = this.exprToCascadeValues(this.define.expr); this.userExpr = this.exprToUserExpr(this.define.expr); @@ -1655,12 +1681,10 @@ export class AlertSettingComponent implements OnInit { * Get the edit tooltip title i18n key based on the data type. */ getEditTooltipTitle(data: any): string { - if (data.type === 'realtime_metrics') { - return 'alert.setting.edit.metrics.realtime'; - } else if (data.type === 'realtime_log') { - return 'alert.setting.edit.log.realtime'; + if (data.type === 'realtime') { + return 'alert.setting.edit.realtime'; } else { - return 'alert.setting.edit.metrics.periodic'; + return 'alert.setting.edit.periodic'; } } @@ -1668,27 +1692,23 @@ export class AlertSettingComponent implements OnInit { * Get the modal title i18n key based on the add/edit mode and data type. * Similar to a computed property in Vue. */ - getModalTitle(): string { + get modalTitle(): string { if (this.isManageModalAdd) { - if (this.define.type === 'periodic_metrics') { - return 'alert.setting.new.metrics.periodic'; - } else if (this.define.type === 'periodic_log') { - return 'alert.setting.new.log.periodic'; - } else if (this.define.type === 'realtime_metrics') { - return 'alert.setting.new.metrics.realtime'; + if (this.alertType === 'periodic') { + return 'alert.setting.new.periodic'; } else { - return 'alert.setting.new.log.realtime'; + return 'alert.setting.new.realtime'; } } else { - if (this.define.type === 'periodic_metrics') { - return 'alert.setting.edit.metrics.periodic'; - } else if (this.define.type === 'periodic_log') { - return 'alert.setting.edit.log.periodic'; - } else if (this.define.type === 'realtime_metrics') { - return 'alert.setting.edit.metrics.realtime'; + if (this.alertType === 'periodic') { + return 'alert.setting.edit.periodic'; } else { - return 'alert.setting.edit.log.realtime'; + return 'alert.setting.edit.realtime'; } } } + private extractDataTypeAndAlertType(type: string): void { + this.dataType = type.split('_')[1] || 'metric'; + this.alertType = type.split('_')[0] || 'realtime'; + } } diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 7576e276d1b..e9f63c6f91f 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -246,16 +246,18 @@ "alert.setting.connect.label": "Label Associate", "alert.setting.connect.label.input": "Input Label", "alert.setting.connect.label.empty": "No matching monitoring objects found, please add labels", + "alert.setting.datatype": "Data Type", + "alert.setting.datatype.tip": "Select the data type for this threshold rule", + "alert.setting.datatype.metric": "Metric", + "alert.setting.datatype.log": "Log", "alert.setting.filter.name": "Input name to filter...", "alert.setting.filter.labels": "Input labels to filter...", "alert.setting.default": "Global Default", "alert.setting.default.tip": "Whether this alarm threshold configuration applies to all this type of monitoring globally", "alert.setting.delete": "Delete Threshold Rule", "alert.setting.edit": "Edit Threshold Rule", - "alert.setting.edit.metrics.periodic": "Edit Metrics Periodic Threshold", - "alert.setting.edit.metrics.realtime": "Edit Metrics RealTime Threshold", - "alert.setting.edit.log.periodic": "Edit Log Periodic Threshold", - "alert.setting.edit.log.realtime": "Edit Log RealTime Threshold", + "alert.setting.edit.periodic": "Edit Periodic Threshold", + "alert.setting.edit.realtime": "Edit RealTime Threshold", "alert.setting.enable": "Enable Threshold", "alert.setting.enable.tip": "This alarm threshold configuration is enabled or disabled", "alert.setting.export": "Export Rule", @@ -276,10 +278,8 @@ "alert.setting.name": "Rule Name", "alert.setting.name.tip": "Rule name need to be unique", "alert.setting.new": "New Threshold Rule", - "alert.setting.new.metrics.periodic": "New Metrics Periodic Threshold", - "alert.setting.new.metrics.realtime": "New Metrics RealTime Threshold", - "alert.setting.new.log.periodic": "New Log Periodic Threshold", - "alert.setting.new.log.realtime": "New Log RealTime Threshold", + "alert.setting.new.periodic": "New Periodic Threshold", + "alert.setting.new.realtime": "New RealTime Threshold", "alert.setting.number": "Numeric", "alert.setting.object": "Object", "alert.setting.operator": "Supported operator functions", @@ -339,10 +339,14 @@ "alert.setting.times.tip": "Set how many times the threshold is triggered before sending an alert", "alert.setting.trigger": "Trigger alarms", "alert.setting.type": "Threshold Type", - "alert.setting.type.periodic": "Periodic", - "alert.setting.type.periodic.desc": "Periodically execute PromQL queries to trigger threshold alerts.", - "alert.setting.type.realtime": "RealTime", - "alert.setting.type.realtime.desc": "Real-time metric calculation with instant alert on threshold breach.", + "alert.setting.type.periodic": "Periodic Threshold", + "alert.setting.type.periodic.desc": "Execute queries periodically to trigger threshold alerts.", + "alert.setting.type.periodic.log": "Log Periodic", + "alert.setting.type.periodic.metric": "Metric Periodic", + "alert.setting.type.realtime": "RealTime Threshold", + "alert.setting.type.realtime.desc": "Calculate data in real-time with instant alert on threshold breach", + "alert.setting.type.realtime.log": "Log RealTime", + "alert.setting.type.realtime.metric": "Metric RealTime", "alert.severity": "Alarm Severity", "alert.severity.0": "Emergency", "alert.severity.1": "Critical", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index be97a9efdd9..d732b675469 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -240,16 +240,18 @@ "alert.setting.connect.label": "ラベル関連", "alert.setting.connect.label.input": "ラベルを入力", "alert.setting.connect.label.empty": "関連するモニターが見つかりません。ラベルを追加してください", + "alert.setting.datatype": "データタイプ", + "alert.setting.datatype.tip": "この閾値ルールのデータタイプを選択してください", + "alert.setting.datatype.metric": "メトリクス", + "alert.setting.datatype.log": "ログ", "alert.setting.filter.name": "名前を入力してフィルター...", "alert.setting.filter.labels": "ラベルを入力してフィルター...", "alert.setting.default": "グローバルデフォルト", "alert.setting.default.tip": "このアラーム閾値設定がグローバルにこのタイプの監視に適用されるかどうか", "alert.setting.delete": "閾値ルールを削除", "alert.setting.edit": "閾値ルールを編集", - "alert.setting.edit.metrics.periodic": "メトリクス周期閾値を編集", - "alert.setting.edit.metrics.realtime": "メトリクスリアルタイム閾値を編集", - "alert.setting.edit.log.periodic": "ログ周期閾値を編集", - "alert.setting.edit.log.realtime": "ログリアルタイム閾値を編集", + "alert.setting.edit.periodic": "周期閾値を編集", + "alert.setting.edit.realtime": "リアルタイム閾値を編集", "alert.setting.enable": "閾値を有効化", "alert.setting.enable.tip": "このアラーム閾値設定が有効か無効か", "alert.setting.export": "ルールをエクスポート", @@ -270,10 +272,8 @@ "alert.setting.name": "ルール名", "alert.setting.name.tip": "ルール名はユニークである必要があります", "alert.setting.new": "新規閾値ルール", - "alert.setting.new.metrics.periodic": "新規メトリクス周期閾値", - "alert.setting.new.metrics.realtime": "新規メトリクスリアルタイム閾値", - "alert.setting.new.log.periodic": "新規ログ周期閾値", - "alert.setting.new.log.realtime": "新規ログリアルタイム閾値", + "alert.setting.new.periodic": "新規周期閾値", + "alert.setting.new.realtime": "新規リアルタイム閾値", "alert.setting.number": "数値", "alert.setting.object": "オブジェクト", "alert.setting.operator": "サポートされている演算子関数", @@ -332,14 +332,6 @@ "alert.setting.times": "トリガー回数", "alert.setting.times.tip": "アラームを送信する前に閾値が何回トリガーされるかを設定", "alert.setting.trigger": "アラームをトリガー", - "alert.setting.type.metrics.periodic": "メトリクス周期", - "alert.setting.type.metrics.periodic.desc": "PromQLクエリを定期的に実行して閾値アラートをトリガーします。", - "alert.setting.type.metrics.realtime": "メトリクスリアルタイム", - "alert.setting.type.metrics.realtime.desc": "リアルタイムメトリクス計算と閾値超過時の即時アラート。", - "alert.setting.type.log.periodic": "ログ周期", - "alert.setting.type.log.periodic.desc": "SQLクエリを定期的に実行して閾値アラートをトリガーします。", - "alert.setting.type.log.realtime": "ログリアルタイム", - "alert.setting.type.log.realtime.desc": "リアルタイムログ計算と閾値超過時の即時アラート。", "alert.severity": "アラームの重大度", "alert.severity.0": "緊急", "alert.severity.1": "クリティカル", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index 964292d43cc..4023ba3aee0 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -282,7 +282,15 @@ }, "question.link": "https://hertzbeat.apache.org/docs/help/issue/", "alert.setting.new": "Nova Regra de Limite", + "alert.setting.new.periodic": "Nova Regra de Limite Periódica", + "alert.setting.new.realtime": "Nova Regra de Limite em Tempo Real", "alert.setting.edit": "Editar Regra de Limite", + "alert.setting.edit.periodic": "Editar Limite Periódico", + "alert.setting.edit.realtime": "Editar Limite em Tempo Real", + "alert.setting.datatype": "Tipo de Dados", + "alert.setting.datatype.tip": "Selecione o tipo de dados para esta regra de limite", + "alert.setting.datatype.metric": "Métrica", + "alert.setting.datatype.log": "Logs", "alert.setting.delete": "Excluir Regra de Limite", "alert.setting.export": "Exportar Regra", "alert.setting.import": "Importar Regra", @@ -524,25 +532,17 @@ "alert.integration.token.title": "Token de autenticação de acesso", "alert.setting.name": "Nome do limite", "alert.setting.type": "Tipo de limite", - "alert.setting.type.metrics.periodic": "Métricas Periódicas", - "alert.setting.type.metrics.periodic.desc": "Executa consultas PromQL periodicamente para disparar alertas de limite.", - "alert.setting.type.metrics.realtime": "Métricas em Tempo Real", - "alert.setting.type.metrics.realtime.desc": "Cálculo de métricas em tempo real com alerta instantâneo ao ultrapassar o limite.", - "alert.setting.type.log.periodic": "Logs Periódicos", - "alert.setting.type.log.periodic.desc": "Executa consultas SQL periodicamente para disparar alertas de limite.", - "alert.setting.type.log.realtime": "Logs em Tempo Real", - "alert.setting.type.log.realtime.desc": "Cálculo de logs em tempo real com alerta instantâneo ao ultrapassar o limite.", + "alert.setting.type.periodic": "Limite Periódico", + "alert.setting.type.periodic.desc": "Executa consultas periodicamente para disparar alertas de limite.", + "alert.setting.type.periodic.log": "Logs Periódicos", + "alert.setting.type.periodic.metric": "Métrica Periódicas", + "alert.setting.type.realtime": "Limite em Tempo Real", + "alert.setting.type.realtime.desc": "Calcula dados em tempo real com alerta instantâneo ao ultrapassar o limite.", + "alert.setting.type.realtime.log": "Logs em Tempo Real", + "alert.setting.type.realtime.metric": "Métrica em Tempo Real", "alert.setting.name.tip": "O nome da regra de limite precisa ser exclusivo", - "alert.setting.new.metrics.periodic": "Adicionar novo limite do plano", - "alert.setting.new.metrics.realtime": "Adicionado limite em tempo real", - "alert.setting.new.log.periodic": "Adicionar novo limite do plano", - "alert.setting.new.log.realtime": "Adicionado limite em tempo real", "alert.setting.period": "Ciclo de execução", "alert.setting.period.placeholder": "Insira o período de execução, mínimo de 60 segundos", - "alert.setting.edit.metrics.periodic": "Editar Limite de Métricas Periódicas", - "alert.setting.edit.metrics.realtime": "Editar Limite de Métricas em Tempo Real", - "alert.setting.edit.log.periodic": "Editar Limite de Logs Periódicos", - "alert.setting.edit.log.realtime": "Editar Limite de Logs em Tempo Real", "alert.setting.bind.available": "Monitoramento opcional", "alert.setting.bind.manage": "Monitoramento relacionado", "alert.setting.bind.monitors": "Monitoramento relacionado", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 44c5ac69bd9..491e63ae1b6 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -248,16 +248,18 @@ "alert.setting.connect.label": "标签关联", "alert.setting.connect.label.input": "输入标签", "alert.setting.connect.label.empty": "暂无匹配的监控对象,请添加标签", + "alert.setting.datatype": "数据类型", + "alert.setting.datatype.tip": "选择此阈值规则的数据类型", + "alert.setting.datatype.metric": "指标", + "alert.setting.datatype.log": "日志", "alert.setting.filter.name": "输入名称进行过滤...", "alert.setting.filter.labels": "输入标签进行过滤...", "alert.setting.default": "应用全局", "alert.setting.default.tip": "此告警阈值配置是否应用于全局所有此类型监控", "alert.setting.delete": "删除阈值规则", "alert.setting.edit": "编辑阈值规则", - "alert.setting.edit.metrics.periodic": "编辑指标周期阈值", - "alert.setting.edit.metrics.realtime": "编辑指标实时阈值", - "alert.setting.edit.log.periodic": "编辑日志周期阈值", - "alert.setting.edit.log.realtime": "编辑日志实时阈值", + "alert.setting.edit.periodic": "编辑周期阈值", + "alert.setting.edit.realtime": "编辑实时阈值", "alert.setting.enable": "启用阈值", "alert.setting.enable.tip": "此告警阈值配置开启生效或关闭", "alert.setting.export": "导出规则", @@ -278,10 +280,8 @@ "alert.setting.name": "阈值名称", "alert.setting.name.tip": "阈值规则名称,需要有唯一性", "alert.setting.new": "新增阈值", - "alert.setting.new.metrics.periodic": "新增指标周期阈值", - "alert.setting.new.metrics.realtime": "新增指标实时阈值", - "alert.setting.new.log.periodic": "新增日志周期阈值", - "alert.setting.new.log.realtime": "新增日志实时阈值", + "alert.setting.new.periodic": "新增周期阈值", + "alert.setting.new.realtime": "新增实时阈值", "alert.setting.number": "数值型", "alert.setting.object": "对象", "alert.setting.operator": "支持操作符函数", @@ -341,14 +341,14 @@ "alert.setting.times.tip": "设置触发阈值多少次之后才会发送告警", "alert.setting.trigger": "异常时触发告警", "alert.setting.type": "阈值类型", - "alert.setting.type.metrics.periodic": "指标周期", - "alert.setting.type.metrics.periodic.desc": "周期性执行PromQL查询,触发阈值告警。", - "alert.setting.type.metrics.realtime": "指标实时", - "alert.setting.type.metrics.realtime.desc": "实时指标计算,触发阈值告警。", - "alert.setting.type.log.periodic": "日志周期", - "alert.setting.type.log.periodic.desc": "周期性执行SQL查询,触发阈值告警。", - "alert.setting.type.log.realtime": "日志实时", - "alert.setting.type.log.realtime.desc": "实时日志计算,触发阈值告警。", + "alert.setting.type.periodic": "周期阈值", + "alert.setting.type.periodic.desc": "周期性执行查询,触发阈值告警。", + "alert.setting.type.periodic.log": "日志周期", + "alert.setting.type.periodic.metric": "指标周期", + "alert.setting.type.realtime": "实时阈值", + "alert.setting.type.realtime.desc": "实时计算数据,触发阈值时立即告警", + "alert.setting.type.realtime.log": "日志实时", + "alert.setting.type.realtime.metric": "指标实时", "alert.severity": "告警级别", "alert.severity.0": "紧急告警", "alert.severity.1": "严重告警", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index e6dad456d59..c1833d01d9f 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -245,10 +245,8 @@ "alert.setting.default.tip": "此告警阈值配置是否應用于全局所有此類型監控", "alert.setting.delete": "刪除阈值規則", "alert.setting.edit": "編輯阈值規則", - "alert.setting.edit.metrics.periodic": "編輯指標周期阈值", - "alert.setting.edit.metrics.realtime": "編輯指標實時阈值", - "alert.setting.edit.log.periodic": "編輯日志周期阈值", - "alert.setting.edit.log.realtime": "編輯日志實時阈值", + "alert.setting.edit.periodic": "編輯周期阈值", + "alert.setting.edit.realtime": "編輯實時阈值", "alert.setting.enable": "啓用閾值", "alert.setting.enable.tip": "此告警阈值配置開啓生效或關閉", "alert.setting.export": "導出規則", @@ -269,10 +267,8 @@ "alert.setting.name": "阈值名称", "alert.setting.name.tip": "阈值规则名称,需要有唯一性", "alert.setting.new": "新增阈值規則", - "alert.setting.new.metrics.periodic": "新增指標周期阈值", - "alert.setting.new.metrics.realtime": "新增指標實時阈值", - "alert.setting.new.log.periodic": "新增日志周期阈值", - "alert.setting.new.log.realtime": "新增日志實時阈值", + "alert.setting.new.periodic": "新增周期阈值", + "alert.setting.new.realtime": "新增實時阈值", "alert.setting.number": "數值型", "alert.setting.object": "對象", "alert.setting.operator": "支持操作符函數", @@ -332,14 +328,14 @@ "alert.setting.times.tip": "設置觸發阈值多少次之後才會發送告警", "alert.setting.trigger": "異常時觸發告警", "alert.setting.type": "阈值类型", - "alert.setting.type.metrics.periodic": "指標周期", - "alert.setting.type.metrics.periodic.desc": "週期性執行 PromQL 查詢觸發閾值警報", - "alert.setting.type.metrics.realtime": "指標實時", - "alert.setting.type.metrics.realtime.desc": "即時計算指標數據,觸發閾值時立即告警", - "alert.setting.type.log.periodic": "日志周期", - "alert.setting.type.log.periodic.desc": "週期性執行 SQL 查詢觸發閾值警報", - "alert.setting.type.log.realtime": "日志實時", - "alert.setting.type.log.realtime.desc": "即時計算日志數據,觸發閾值時立即告警", + "alert.setting.type.periodic": "周期阈值", + "alert.setting.type.periodic.desc": "周期性执行查询,触发阈值告警。", + "alert.setting.type.periodic.log": "日志周期", + "alert.setting.type.periodic.metric": "指标周期", + "alert.setting.type.realtime": "实时阈值", + "alert.setting.type.realtime.desc": "即時計算數據,觸發閾值時立即告警", + "alert.setting.type.realtime.log": "日志实时", + "alert.setting.type.realtime.metric": "指标实时", "alert.severity": "告警級別", "alert.severity.0": "緊急告警", "alert.severity.1": "嚴重告警", From 98ca234adcd27f8110a6cf1b1ce2df7018757b27 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 17 Jul 2025 14:41:00 +0800 Subject: [PATCH 14/39] feat: Save log to db --- .../calculate/LogRealTimeAlertCalculator.java | 2 +- .../dispatch/export/NettyDataQueue.java | 12 +++- .../common/queue/CommonDataQueue.java | 16 ++++- .../queue/impl/InMemoryCommonDataQueue.java | 19 +++++- .../queue/impl/KafkaCommonDataQueue.java | 31 +++++++++- .../queue/impl/RedisCommonDataQueue.java | 24 +++++++- .../service/impl/OtlpLogProtocolAdapter.java | 9 +-- .../warehouse/store/DataStorageDispatch.java | 28 +++++++++ .../store/history/tsdb/HistoryDataWriter.java | 33 +++++++++++ .../tsdb/greptime/GreptimeDbDataStorage.java | 59 +++++++++++++++++++ 10 files changed, 216 insertions(+), 17 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java index 7f86473f38b..67f3b0f517a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java @@ -75,7 +75,7 @@ protected LogEntry pollData() throws InterruptedException { @Override protected void processDataAfterCalculation(LogEntry logEntry) { - // todo send log to storage + dataQueue.sendLogEntryToStorage(logEntry); } @Override diff --git a/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java b/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java index a39deeea69c..2328496b916 100644 --- a/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java +++ b/hertzbeat-collector/hertzbeat-collector-common/src/main/java/org/apache/hertzbeat/collector/dispatch/export/NettyDataQueue.java @@ -90,7 +90,7 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } @Override - public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + public void sendLogEntry(LogEntry logEntry) { } @@ -98,4 +98,14 @@ public void sendLogEntry(LogEntry logEntry) throws InterruptedException { public LogEntry pollLogEntry() throws InterruptedException { return null; } + + @Override + public void sendLogEntryToStorage(LogEntry logEntry) { + + } + + @Override + public LogEntry pollLogEntryToStorage() throws InterruptedException { + return null; + } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java index cdc2dae256b..4abbc29b145 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/CommonDataQueue.java @@ -69,7 +69,7 @@ public interface CommonDataQueue { * @param logEntry log entry data based on OpenTelemetry log data model * @throws InterruptedException when sending is interrupted */ - void sendLogEntry(LogEntry logEntry) throws InterruptedException; + void sendLogEntry(LogEntry logEntry); /** * poll log entry from queue @@ -77,4 +77,18 @@ public interface CommonDataQueue { * @throws InterruptedException when poll timeout */ LogEntry pollLogEntry() throws InterruptedException; + + /** + * send log entry to storage queue + * @param logEntry log entry data based on OpenTelemetry log data model + * @throws InterruptedException when sending is interrupted + */ + void sendLogEntryToStorage(LogEntry logEntry); + + /** + * poll log entry from storage queue + * @return log entry data + * @throws InterruptedException when poll timeout + */ + LogEntry pollLogEntryToStorage() throws InterruptedException; } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java index cb238a9fe47..bb8f61a6cfa 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/InMemoryCommonDataQueue.java @@ -48,18 +48,22 @@ public class InMemoryCommonDataQueue implements CommonDataQueue, DisposableBean private final LinkedBlockingQueue metricsDataToStorageQueue; private final LinkedBlockingQueue serviceDiscoveryDataQueue; private final LinkedBlockingQueue logEntryQueue; + private final LinkedBlockingQueue logEntryToStorageQueue; public InMemoryCommonDataQueue() { metricsDataToAlertQueue = new LinkedBlockingQueue<>(); metricsDataToStorageQueue = new LinkedBlockingQueue<>(); serviceDiscoveryDataQueue = new LinkedBlockingQueue<>(); logEntryQueue = new LinkedBlockingQueue<>(); + logEntryToStorageQueue = new LinkedBlockingQueue<>(); } public Map getQueueSizeMetricsInfo() { Map metrics = new HashMap<>(8); metrics.put("metricsDataToAlertQueue", metricsDataToAlertQueue.size()); metrics.put("metricsDataToStorageQueue", metricsDataToStorageQueue.size()); + metrics.put("logEntryQueue", logEntryQueue.size()); + metrics.put("logEntryToStorageQueue", logEntryToStorageQueue.size()); return metrics; } @@ -94,8 +98,8 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } @Override - public void sendLogEntry(LogEntry logEntry) throws InterruptedException { - logEntryQueue.put(logEntry); + public void sendLogEntry(LogEntry logEntry) { + logEntryQueue.offer(logEntry); } @Override @@ -103,11 +107,22 @@ public LogEntry pollLogEntry() throws InterruptedException { return logEntryQueue.take(); } + @Override + public void sendLogEntryToStorage(LogEntry logEntry) { + logEntryToStorageQueue.offer(logEntry); + } + + @Override + public LogEntry pollLogEntryToStorage() throws InterruptedException { + return logEntryToStorageQueue.take(); + } + @Override public void destroy() { metricsDataToAlertQueue.clear(); metricsDataToStorageQueue.clear(); serviceDiscoveryDataQueue.clear(); logEntryQueue.clear(); + logEntryToStorageQueue.clear(); } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java index 9feed8f5657..614fc17c828 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java @@ -63,10 +63,12 @@ public class KafkaCommonDataQueue implements CommonDataQueue, DisposableBean { private final ReentrantLock metricDataToStorageLock = new ReentrantLock(); private final ReentrantLock serviceDiscoveryDataLock = new ReentrantLock(); private final ReentrantLock logEntryLock = new ReentrantLock(); + private final ReentrantLock logEntryToStorageLock = new ReentrantLock(); private final LinkedBlockingQueue metricsDataToAlertQueue; private final LinkedBlockingQueue metricsDataToStorageQueue; private final LinkedBlockingQueue serviceDiscoveryDataQueue; private final LinkedBlockingQueue logEntryQueue; + private final LinkedBlockingQueue logEntryToStorageQueue; private final CommonProperties.KafkaProperties kafka; private KafkaProducer metricsDataProducer; private KafkaProducer logEntryProducer; @@ -85,6 +87,7 @@ public KafkaCommonDataQueue(CommonProperties properties) { metricsDataToStorageQueue = new LinkedBlockingQueue<>(); serviceDiscoveryDataQueue = new LinkedBlockingQueue<>(); logEntryQueue = new LinkedBlockingQueue<>(); + logEntryToStorageQueue = new LinkedBlockingQueue<>(); initDataQueue(); } @@ -207,7 +210,7 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } @Override - public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + public void sendLogEntry(LogEntry logEntry) { if (logEntryProducer != null) { try { ProducerRecord record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry); @@ -215,11 +218,11 @@ public void sendLogEntry(LogEntry logEntry) throws InterruptedException { } catch (Exception e) { log.error("Failed to send LogEntry to Kafka: {}", e.getMessage()); // Fallback to memory queue if Kafka fails - logEntryQueue.put(logEntry); + logEntryQueue.offer(logEntry); } } else { log.warn("logEntryProducer is not enabled, using memory queue"); - logEntryQueue.put(logEntry); + logEntryQueue.offer(logEntry); } } @@ -228,6 +231,28 @@ public LogEntry pollLogEntry() throws InterruptedException { return genericPollDataFunction(logEntryQueue, logEntryConsumer, logEntryLock); } + @Override + public void sendLogEntryToStorage(LogEntry logEntry) { + if (logEntryProducer != null) { + try { + ProducerRecord record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry); + logEntryProducer.send(record); + } catch (Exception e) { + log.error("Failed to send LogEntry to storage via Kafka: {}", e.getMessage()); + // Fallback to memory queue if Kafka fails + logEntryToStorageQueue.offer(logEntry); + } + } else { + log.warn("logEntryProducer is not enabled, using memory queue for storage"); + logEntryToStorageQueue.offer(logEntry); + } + } + + @Override + public LogEntry pollLogEntryToStorage() throws InterruptedException { + return genericPollDataFunction(logEntryToStorageQueue, logEntryConsumer, logEntryToStorageLock); + } + @Override public void destroy() throws Exception { if (metricsDataProducer != null) { diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java index e3851175123..9bf51facd59 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueue.java @@ -54,6 +54,7 @@ public class RedisCommonDataQueue implements CommonDataQueue, DisposableBean { private final String metricsDataQueueNameForServiceDiscovery; private final String metricsDataQueueNameToAlerter; private final String logEntryQueueName; + private final String logEntryToStorageQueueName; private final CommonProperties.RedisProperties redisProperties; public RedisCommonDataQueue(CommonProperties properties) { @@ -81,6 +82,7 @@ public RedisCommonDataQueue(CommonProperties properties) { this.metricsDataQueueNameForServiceDiscovery = redisProperties.getMetricsDataQueueNameForServiceDiscovery(); this.metricsDataQueueNameToAlerter = redisProperties.getMetricsDataQueueNameToAlerter(); this.logEntryQueueName = redisProperties.getLogEntryQueueName(); + this.logEntryToStorageQueueName = redisProperties.getLogEntryQueueName() + "_storage"; } @Override @@ -141,12 +143,11 @@ public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) { } @Override - public void sendLogEntry(LogEntry logEntry) throws InterruptedException { + public void sendLogEntry(LogEntry logEntry) { try { logEntrySyncCommands.lpush(logEntryQueueName, logEntry); } catch (Exception e) { log.error("Failed to send LogEntry to Redis: {}", e.getMessage()); - throw new InterruptedException("Failed to send LogEntry to Redis"); } } @@ -160,6 +161,25 @@ public LogEntry pollLogEntry() throws InterruptedException { } } + @Override + public void sendLogEntryToStorage(LogEntry logEntry) { + try { + logEntrySyncCommands.lpush(logEntryToStorageQueueName, logEntry); + } catch (Exception e) { + log.error("Failed to send LogEntry to storage via Redis: {}", e.getMessage()); + } + } + + @Override + public LogEntry pollLogEntryToStorage() throws InterruptedException { + try { + return logEntrySyncCommands.rpop(logEntryToStorageQueueName); + } catch (Exception e) { + log.error("Failed to poll LogEntry from storage via Redis: {}", e.getMessage()); + throw new InterruptedException("Failed to poll LogEntry from storage via Redis"); + } + } + @Override public void destroy() { connection.close(); diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index 98488085b4a..60bd2cb01ba 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -68,13 +68,8 @@ public void ingest(String content) { log.debug("Successfully extracted {} log entries from OTLP payload {}", logEntries.size(), content); logEntries.forEach(entry -> { - try { - commonDataQueue.sendLogEntry(entry); - log.info("Log entry sent to queue: {}", entry); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Failed to send log entry to queue: {}", e.getMessage()); - } + commonDataQueue.sendLogEntry(entry); + log.info("Log entry sent to queue: {}", entry); }); } catch (InvalidProtocolBufferException e) { diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java index 222b644f07a..802711065e1 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java @@ -22,6 +22,7 @@ import jakarta.persistence.PersistenceContext; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.manager.Monitor; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; @@ -62,6 +63,7 @@ public DataStorageDispatch(CommonDataQueue commonDataQueue, this.historyDataWriter = historyDataWriter; this.pluginRunner = pluginRunner; startPersistentDataStorage(); + startLogDataStorage(); } protected void startPersistentDataStorage() { @@ -89,6 +91,32 @@ protected void startPersistentDataStorage() { }; workerPool.executeJob(runnable); } + + protected void startLogDataStorage() { + Runnable runnable = () -> { + Thread.currentThread().setName("warehouse-log-data-storage"); + while (!Thread.currentThread().isInterrupted()) { + try { + LogEntry logEntry = commonDataQueue.pollLogEntryToStorage(); + if (logEntry == null) { + continue; + } + historyDataWriter.ifPresent(dataWriter -> { + try { + dataWriter.saveLogData(logEntry); + } catch (Exception e) { + log.error("Failed to save log entry: {}", e.getMessage(), e); + } + }); + } catch (InterruptedException interruptedException) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("Error in log data storage thread: {}", e.getMessage(), e); + } + } + }; + workerPool.executeJob(runnable); + } protected void calculateMonitorStatus(CollectRep.MetricsData metricsData) { if (metricsData.getPriority() == 0) { diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java index b9c12ba95d5..a2845575e3b 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java @@ -17,8 +17,12 @@ package org.apache.hertzbeat.warehouse.store.history.tsdb; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; +import java.util.List; +import java.util.Map; + /** * history data writer */ @@ -34,4 +38,33 @@ public interface HistoryDataWriter { * @param metricsData metrics data */ void saveData(CollectRep.MetricsData metricsData); + + /** + * default save log data + * @param logEntry log entry + */ + default void saveLogData(LogEntry logEntry) { + throw new UnsupportedOperationException("save log data is not supported"); + } + + + default List queryLogs(Long startTime, Long endTime, Integer limit){ + return null; + } + + default List queryLogsByResource(Map resource, Long startTime, Long endTime, Integer limit){ + return null; + } + + default List queryLogsBySpanId(String spanId, Integer limit){ + return null; + } + + default List queryLogsBySeverity(Integer severityNumber, Long startTime, Long endTime, Integer limit){ + return null; + } + + default List queryLogsByTraceId(String traceId, Integer limit){ + return null; + } } diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java index 0da96056868..343a3286257 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java @@ -51,6 +51,7 @@ import org.apache.hertzbeat.common.constants.MetricDataConstants; import org.apache.hertzbeat.common.entity.arrow.RowWrapper; import org.apache.hertzbeat.common.entity.dto.Value; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.util.Base64Util; import org.apache.hertzbeat.common.util.JsonUtil; @@ -81,6 +82,7 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage { private static final String LABEL_KEY_NAME = "__name__"; private static final String LABEL_KEY_FIELD = "__field__"; private static final String LABEL_KEY_INSTANCE = "instance"; + private static final String LOG_TABLE_NAME = "hertzbeat_logs"; private GreptimeDB greptimeDb; @@ -292,4 +294,61 @@ public void destroy() { this.greptimeDb = null; } } + + @Override + public void saveLogData(LogEntry logEntry) { + if (!isServerAvailable()) { + return; + } + + try { + // Create table schema + TableSchema.Builder tableSchemaBuilder = TableSchema.newBuilder(LOG_TABLE_NAME); + tableSchemaBuilder.addTimestamp("time_unix_nano", DataType.TimestampNanosecond) + .addField("observed_time_unix_nano", DataType.TimestampNanosecond) + .addField("severity_number", DataType.Int32) + .addField("severity_text", DataType.String) + .addField("body", DataType.Json) + .addField("trace_id", DataType.String) + .addField("span_id", DataType.String) + .addField("trace_flags", DataType.Int32) + .addField("attributes", DataType.Json) + .addField("resource", DataType.Json) + .addField("instrumentation_scope", DataType.Json) + .addField("dropped_attributes_count", DataType.Int32); + + Table table = Table.from(tableSchemaBuilder.build()); + + // Convert LogEntry to table row + Object[] values = new Object[] { + logEntry.getTimeUnixNano() != null ? logEntry.getTimeUnixNano() : System.nanoTime(), + logEntry.getObservedTimeUnixNano() != null ? logEntry.getObservedTimeUnixNano() : System.nanoTime(), + logEntry.getSeverityNumber(), + logEntry.getSeverityText(), + JsonUtil.toJson(logEntry.getBody()), + logEntry.getTraceId(), + logEntry.getSpanId(), + logEntry.getTraceFlags(), + JsonUtil.toJson(logEntry.getAttributes()), + JsonUtil.toJson(logEntry.getResource()), + JsonUtil.toJson(logEntry.getInstrumentationScope()), + logEntry.getDroppedAttributesCount() + }; + + table.addRow(values); + + // Write to GreptimeDB + CompletableFuture> writeFuture = greptimeDb.write(table); + Result result = writeFuture.get(10, TimeUnit.SECONDS); + + if (result.isOk()) { + log.debug("[warehouse greptime-log] Write successful"); + } else { + log.warn("[warehouse greptime-log] Write failed: {}", result.getErr()); + } + } catch (Exception e) { + log.error("[warehouse greptime-log] Error saving log entry", e); + } + } + } From b7811cc396e724854aa180d058f69289ed4405fc Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 20 Jul 2025 11:06:13 +0800 Subject: [PATCH 15/39] feat: log stream show --- .../log/controller/LogSseController.java | 57 +++ .../log/notice/LogSseFilterCriteria.java | 78 ++++ .../hertzbeat/log/notice/LogSseManager.java | 87 ++++ .../service/impl/OtlpLogProtocolAdapter.java | 6 +- .../src/main/resources/sureness.yml | 1 + .../src/app/routes/log/log-routing.module.ts | 4 +- .../log/log-stream/log-stream.component.html | 290 ++++++++++++ .../log/log-stream/log-stream.component.less | 426 +++++++++++++++++ .../log-stream/log-stream.component.spec.ts | 23 + .../log/log-stream/log-stream.component.ts | 439 ++++++++++++++++++ web-app/src/app/routes/log/log.module.ts | 4 +- web-app/src/assets/app-data.json | 6 + web-app/src/assets/i18n/en-US.json | 40 ++ web-app/src/assets/i18n/ja-JP.json | 46 ++ web-app/src/assets/i18n/pt-BR.json | 46 ++ web-app/src/assets/i18n/zh-CN.json | 41 ++ web-app/src/assets/i18n/zh-TW.json | 46 ++ 17 files changed, 1637 insertions(+), 3 deletions(-) create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogSseController.java create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java create mode 100644 web-app/src/app/routes/log/log-stream/log-stream.component.html create mode 100644 web-app/src/app/routes/log/log-stream/log-stream.component.less create mode 100644 web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts create mode 100644 web-app/src/app/routes/log/log-stream/log-stream.component.ts diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogSseController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogSseController.java new file mode 100644 index 00000000000..ba76865ce0c --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogSseController.java @@ -0,0 +1,57 @@ +/* + * 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.hertzbeat.log.controller; + +import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE; +import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator; +import org.apache.hertzbeat.log.notice.LogSseFilterCriteria; +import org.apache.hertzbeat.log.notice.LogSseManager; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import io.swagger.v3.oas.annotations.Operation; + +/** + * SSE controller for log streaming with filtering support + */ +@RestController +@RequestMapping(path = "/api/log/sse", produces = {TEXT_EVENT_STREAM_VALUE}) +public class LogSseController { + + private final LogSseManager emitterManager; + + public LogSseController(LogSseManager emitterManager) { + this.emitterManager = emitterManager; + } + + /** + * Subscribe to log events with optional filtering + * @param filterCriteria Filter criteria for log events (all parameters are optional) + * @return SSE emitter for streaming log events + */ + @GetMapping(path = "/subscribe") + @Operation(summary = "Subscribe to log events with optional filtering", description = "Subscribe to log events with optional filtering") + public SseEmitter subscribe(@ModelAttribute LogSseFilterCriteria filterCriteria) { + Long clientId = SnowFlakeIdGenerator.generateId(); + return emitterManager.createEmitter(clientId, filterCriteria); + } +} \ No newline at end of file diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java new file mode 100644 index 00000000000..3012099beac --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java @@ -0,0 +1,78 @@ +package org.apache.hertzbeat.log.notice; + +import lombok.Data; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.springframework.util.StringUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE; + +/** + * Log filtering criteria for SSE (Server-Sent Events) log streaming + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "Log filtering criteria for SSE (Server-Sent Events) log streaming") +public class LogSseFilterCriteria { + /** + * Numerical value of the severity. + * Smaller numerical values correspond to less severe events (such as debug events), + * larger numerical values correspond to more severe events (such as errors and critical events). + */ + @Schema(description = "Numerical value of the severity.", example = "1", accessMode = READ_WRITE) + private Integer severityNumber; + + /** + * The severity text (also known as log level). + * This is the original string representation of the severity as it is known at the source. + */ + @Schema(description = "The severity text (also known as log level).", example = "INFO", accessMode = READ_WRITE) + private String severityText; + + /** + * A unique identifier for a trace. + * All spans from the same trace share the same trace_id. + * The ID is a 16-byte array represented as a hex string. + */ + @Schema(description = "A unique identifier for a trace.", example = "1234567890", accessMode = READ_WRITE) + private String traceId; + + /** + * A unique identifier for a span within a trace. + * The ID is an 8-byte array represented as a hex string. + */ + @Schema(description = "A unique identifier for a span.", example = "1234567890", accessMode = READ_WRITE) + private String spanId; + + + /** + * Core filtering logic to determine if a log entry matches the criteria + * @param log Log entry to be checked + * @return boolean Whether the log entry matches the filter criteria + */ + public boolean matches(LogEntry log) { + // Check severity text match + if (StringUtils.hasText(severityText) && !severityText.equalsIgnoreCase(log.getSeverityText())) { + return false; + } + + // Check severity number match (if both are present) + if (severityNumber != null && log.getSeverityNumber() != null && + !severityNumber.equals(log.getSeverityNumber())) { + return false; + } + + // Check trace ID match + if (StringUtils.hasText(traceId) && !traceId.equalsIgnoreCase(log.getTraceId())) { + return false; + } + + // Check span ID match + if (StringUtils.hasText(spanId) && !spanId.equalsIgnoreCase(log.getSpanId())) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java new file mode 100644 index 00000000000..127cabc6b21 --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java @@ -0,0 +1,87 @@ +package org.apache.hertzbeat.log.notice; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * SSE manager for log + */ +@Component +@Slf4j +public class LogSseManager { + private final Map emitters = new ConcurrentHashMap<>(); + + /** + * Create a new SSE emitter for a client with specified filters + * @param clientId The unique identifier for the client + * @param filters The filters to apply to the log data + * @return The SSE emitter + */ + public SseEmitter createEmitter(Long clientId, LogSseFilterCriteria filters) { + SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); + emitter.onCompletion(() -> removeEmitter(clientId)); + emitter.onTimeout(() -> removeEmitter(clientId)); + emitter.onError((ex) -> removeEmitter(clientId)); + + SseSubscriber subscriber = new SseSubscriber(emitter, filters); + emitters.put(clientId, subscriber); + return emitter; + } + + /** + * Broadcast log data to all subscribers + * @param logEntry The log data to broadcast + */ + @Async + public void broadcast(LogEntry logEntry) { + emitters.forEach((clientId, subscriber) -> { + try { + // Check if the log entry matches the subscriber's filter criteria + if (subscriber.filters == null || subscriber.filters.matches(logEntry)) { + subscriber.emitter.send(SseEmitter.event() + .id(String.valueOf(System.currentTimeMillis())) + .name("LOG_EVENT") + .data(logEntry)); + } + } catch (IOException | IllegalStateException e) { + subscriber.emitter.complete(); + removeEmitter(clientId); + } catch (Exception exception) { + log.error("Failed to broadcast log to client: {}", exception.getMessage()); + subscriber.emitter.complete(); + removeEmitter(clientId); + } + }); + } + + private void removeEmitter(Long clientId) { + emitters.remove(clientId); + } + + /** + * SSE subscriber + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + private static class SseSubscriber { + /** + * The SSE emitter for streaming log events + */ + SseEmitter emitter; + /** + * The filters for streaming log events + */ + LogSseFilterCriteria filters; + } +} diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java index 60bd2cb01ba..27ca1a24efe 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapter.java @@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.apache.hertzbeat.log.notice.LogSseManager; import org.apache.hertzbeat.log.service.LogProtocolAdapter; import org.springframework.stereotype.Service; @@ -47,9 +48,11 @@ public class OtlpLogProtocolAdapter implements LogProtocolAdapter { private static final String PROTOCOL_NAME = "otlp"; private final CommonDataQueue commonDataQueue; + private final LogSseManager logSseManager; - public OtlpLogProtocolAdapter(CommonDataQueue commonDataQueue) { + public OtlpLogProtocolAdapter(CommonDataQueue commonDataQueue, LogSseManager logSseManager) { this.commonDataQueue = commonDataQueue; + this.logSseManager = logSseManager; } @Override @@ -69,6 +72,7 @@ public void ingest(String content) { logEntries.forEach(entry -> { commonDataQueue.sendLogEntry(entry); + logSseManager.broadcast(entry); log.info("Log entry sent to queue: {}", entry); }); diff --git a/hertzbeat-manager/src/main/resources/sureness.yml b/hertzbeat-manager/src/main/resources/sureness.yml index 337a658a393..f4d70b7f308 100644 --- a/hertzbeat-manager/src/main/resources/sureness.yml +++ b/hertzbeat-manager/src/main/resources/sureness.yml @@ -73,6 +73,7 @@ resourceRole: excludedResource: - /api/alerts/report/**===* - /api/alert/sse/**===* + - /api/log/sse/**===* - /api/account/auth/**===* - /api/i18n/**===get - /api/apps/hierarchy===get diff --git a/web-app/src/app/routes/log/log-routing.module.ts b/web-app/src/app/routes/log/log-routing.module.ts index 73562c633be..04dd31568f6 100644 --- a/web-app/src/app/routes/log/log-routing.module.ts +++ b/web-app/src/app/routes/log/log-routing.module.ts @@ -21,10 +21,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { LogIntegrationComponent } from './log-integration/log-integration.component'; +import { LogStreamComponent } from './log-stream/log-stream.component'; const routes: Routes = [ { path: '', component: LogIntegrationComponent }, - { path: 'integration/:source', component: LogIntegrationComponent } + { path: 'integration/:source', component: LogIntegrationComponent }, + { path: 'stream', component: LogStreamComponent } ]; @NgModule({ diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.html b/web-app/src/app/routes/log/log-stream/log-stream.component.html new file mode 100644 index 00000000000..d1e6d3b7f12 --- /dev/null +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.html @@ -0,0 +1,290 @@ + + + + + + + +
+ + + + +
+ +
+ + + + {{ isConnected ? ('log.stream.connected' | i18n) : (isConnecting ? ('log.stream.connecting' | i18n) : ('log.stream.disconnected' | i18n)) }} + + + {{ logEntries.length }} {{ 'log.stream.logs' | i18n }} +
+ + +
+ + + + + + + +
+
+ + +
+ +
+
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ +
+
+
+ +
+ + +
+ + +
+ + +
+ +
+
+ + {{ logEntry.severityText || ('log.stream.unknown' | i18n) }} + + + + {{ formatTimestamp(logEntry.timestamp!) }} + + + + {{ 'log.stream.trace' | i18n }} {{ logEntry.traceId.substring(0, 8) }}... + + + + {{ 'log.stream.span' | i18n }} {{ logEntry.spanId.substring(0, 8) }}... + + + + + {{ getObjectKeys(logEntry.attributes).length }} {{ 'log.stream.attributes' | i18n }} + + + + {{ getObjectKeys(logEntry.resource).length }} {{ 'log.stream.resource' | i18n }} + +
+ +
+ +
+
+ +
+ {{ logEntry.displayMessage }} +
+
+
+
+
+ + + + + +
+ + +
+
+ {{ 'log.stream.severity' | i18n }} + + {{ selectedLogEntry.severityText || ('log.stream.unknown' | i18n) }} + +
+
+ {{ 'log.stream.timestamp' | i18n }} + {{ formatTimestamp(selectedLogEntry.timestamp!) }} +
+
+ {{ 'log.stream.trace-id-full' | i18n }} + {{ selectedLogEntry.traceId }} +
+
+ {{ 'log.stream.span-id-full' | i18n }} + {{ selectedLogEntry.spanId }} +
+
+
+ + + +
+
{{ selectedLogEntry.displayMessage }}
+
+
+ + + +
+
{{ getLogEntryJson(selectedLogEntry) }}
+
+
+
+
+
diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.less b/web-app/src/app/routes/log/log-stream/log-stream.component.less new file mode 100644 index 00000000000..829902576ce --- /dev/null +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.less @@ -0,0 +1,426 @@ +/* + * 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. + */ + +@import "~src/styles/theme"; + +.log-stream-container { + background: @common-background-color; + + .header-card { + margin-bottom: 16px; + + .header-content { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 16px; + + .connection-status { + display: flex; + align-items: center; + gap: 12px; + + .status-tag { + font-weight: 500; + font-size: 14px; + padding: 4px 12px; + border-radius: 4px; + + i { + margin-right: 6px; + } + } + + .log-count { + color: #666; + font-size: 14px; + font-weight: 500; + background: #f0f0f0; + padding: 4px 12px; + border-radius: 4px; + } + } + + .control-buttons { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + + button { + border-radius: 4px; + + i { + margin-right: 6px; + } + } + + nz-switch { + margin-left: 8px; + } + } + } + + .filter-content { + .filter-controls { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + + .filter-item { + display: flex; + align-items: center; + gap: 8px; + + .filter-label { + font-weight: 500; + color: #666; + font-size: 14px; + white-space: nowrap; + margin-bottom: 0; + } + + .filter-select { + width: 140px; + } + + .filter-input { + width: 180px; + } + } + + .filter-actions { + margin-left: auto; + } + } + } + + .error-alert { + margin-top: 16px; + border-radius: 4px; + } + } + + .log-container { + max-height: 60vh; + overflow-y: auto; + background: @common-background-color; + position: relative; + margin-top: 24px; + + &.paused { + opacity: 0.8; + } + + .loading-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + color: #666; + + .loading-text { + margin-top: 16px; + font-size: 16px; + } + } + + .empty-state { + padding: 60px 20px; + } + + .log-entry { + border-bottom: 1px solid #f0f0f0; + padding: 12px 16px; + background: @common-background-color; + cursor: pointer; + + &.new-entry { + background: #e6f7ff; + border-left: 4px solid #1890ff; + } + + &:hover { + background: #fafafa; + } + + &:last-child { + border-bottom: none; + } + + .log-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + flex-wrap: wrap; + gap: 8px; + + .log-meta { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + + .severity-tag { + font-weight: 500; + font-size: 12px; + border-radius: 4px; + padding: 2px 8px; + min-width: 60px; + text-align: center; + } + + .timestamp { + color: #666; + font-size: 12px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + background: #f5f5f5; + padding: 2px 8px; + border-radius: 4px; + } + + .trace-info, + .span-info { + nz-tag { + font-size: 11px; + border-radius: 4px; + } + } + } + + .log-actions { + button { + border: none; + box-shadow: none; + color: #666; + + &:hover { + color: #1890ff; + background: #e6f7ff; + } + } + } + } + + .log-message { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + line-height: 1.5; + color: #333; + word-break: break-word; + white-space: pre-wrap; + background: #f8f9fa; + padding: 8px 12px; + border-radius: 4px; + border-left: 3px solid #e9ecef; + margin: 8px 0; + } + } + + .pause-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.9); + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + + .pause-message { + text-align: center; + padding: 20px; + background: @common-background-color; + border-radius: 4px; + border: 1px solid #e9ecef; + + .pause-icon { + font-size: 32px; + color: #faad14; + margin-bottom: 12px; + display: block; + } + + span { + display: block; + font-size: 16px; + color: #666; + margin-bottom: 16px; + font-weight: 500; + } + + button { + border-radius: 4px; + height: 36px; + padding: 0 20px; + font-weight: 500; + } + } + } + } +} + +// Responsive design +@media (max-width: 768px) { + .log-stream-container { + padding: 12px; + + .header-card { + .header-content { + flex-direction: column; + align-items: stretch; + + .connection-status { + justify-content: center; + } + + .control-buttons { + justify-content: center; + } + } + + .filter-content .filter-controls { + flex-direction: column; + align-items: stretch; + gap: 12px; + + .filter-item { + flex-direction: column; + align-items: stretch; + gap: 4px; + + .filter-label { + font-size: 12px; + } + + .filter-select, + .filter-input { + width: 100%; + } + } + + .filter-actions { + margin-left: 0; + text-align: center; + } + } + } + + .log-container { + max-height: 60vh; + + .log-entry { + padding: 8px 12px; + + .log-header { + flex-direction: column; + align-items: flex-start; + + .log-meta { + width: 100%; + } + + .log-actions { + width: 100%; + text-align: right; + } + } + + .log-message { + font-size: 12px; + } + } + } + } +} + +// Modal styles +.log-details-modal { + .basic-info { + .info-row { + display: flex; + align-items: center; + margin-bottom: 12px; + + &:last-child { + margin-bottom: 0; + } + + .info-label { + font-weight: 500; + color: #666; + min-width: 100px; + margin-right: 12px; + } + + span { + color: #333; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + } + + nz-tag { + font-size: 12px; + } + } + } + + .message-content { + pre { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 4px; + padding: 12px; + margin: 0; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + line-height: 1.5; + color: #333; + white-space: pre-wrap; + word-break: break-word; + max-height: 200px; + overflow-y: auto; + } + } + + .json-content { + pre { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 4px; + padding: 12px; + margin: 0; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 12px; + line-height: 1.4; + color: #333; + white-space: pre-wrap; + word-break: break-word; + max-height: 400px; + overflow-y: auto; + } + } +} + diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts new file mode 100644 index 00000000000..ece44e5eeca --- /dev/null +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LogStreamComponent } from './log-stream.component'; + +describe('LogStreamComponent', () => { + let component: LogStreamComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LogStreamComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LogStreamComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.ts new file mode 100644 index 00000000000..85e3b352fc3 --- /dev/null +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.ts @@ -0,0 +1,439 @@ +/* + * 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. + */ + +import { Component, Inject, OnDestroy, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { I18NService } from '@core'; +import { ALAIN_I18N_TOKEN } from '@delon/theme'; +import { SharedModule } from '@shared'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzCardModule } from 'ng-zorro-antd/card'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzSelectModule } from 'ng-zorro-antd/select'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { NzSwitchModule } from 'ng-zorro-antd/switch'; +import { NzAlertModule } from 'ng-zorro-antd/alert'; +import { NzEmptyModule } from 'ng-zorro-antd/empty'; +import { NzModalModule } from 'ng-zorro-antd/modal'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NzDividerComponent } from 'ng-zorro-antd/divider'; + +// Define LogEntry interface based on backend structure +interface LogEntry { + timeUnixNano?: number; + observedTimeUnixNano?: number; + severityNumber?: number; + severityText?: string; + body?: any; + attributes?: { [key: string]: any }; + droppedAttributesCount?: number; + traceId?: string; + spanId?: string; + traceFlags?: number; + resource?: { [key: string]: any }; + instrumentationScope?: { + name?: string; + version?: string; + attributes?: { [key: string]: any }; + }; +} + +interface ExtendedLogEntry extends LogEntry { + isNew?: boolean; + timestamp?: Date; + displayMessage?: string; +} + +@Component({ + selector: 'app-log-stream', + standalone: true, + imports: [ + CommonModule, + SharedModule, + FormsModule, + NzCardModule, + NzInputModule, + NzSelectModule, + NzButtonModule, + NzTagModule, + NzToolTipModule, + NzSwitchModule, + NzAlertModule, + NzEmptyModule, + NzModalModule, + NzDividerComponent + ], + templateUrl: './log-stream.component.html', + styleUrl: './log-stream.component.less' +}) +export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { + // SSE connection and state + private eventSource!: EventSource; + isConnected: boolean = false; + isConnecting: boolean = false; + + // Log data + logEntries: ExtendedLogEntry[] = []; + maxLogEntries: number = 1000; + isPaused: boolean = false; + + // Filter properties + filterSeverityNumber: string = ''; + filterSeverityText: string = ''; + filterTraceId: string = ''; + filterSpanId: string = ''; + + // UI state + autoScroll: boolean = true; + showFilters: boolean = true; + + // Modal state + isModalVisible: boolean = false; + selectedLogEntry: ExtendedLogEntry | null = null; + + // Auto scroll state + userScrolled: boolean = false; + private scrollTimeout: any; + private scrollDebounceTimeout: any; + private isNearBottom: boolean = true; + + // ViewChild for log container + @ViewChild('logContainer', { static: false }) logContainerRef!: ElementRef; + + constructor(@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService) { + } + + ngOnInit(): void { + this.connectToLogStream(); + } + + ngAfterViewInit(): void { + this.setupScrollListener(); + } + + ngOnDestroy(): void { + this.disconnectFromLogStream(); + this.cleanupScrollListener(); + } + + onReconnect(): void { + this.logEntries = []; + this.connectToLogStream(); + } + + + private connectToLogStream(): void { + if (this.eventSource) { + this.disconnectFromLogStream(); + } + + this.isConnecting = true; + + // Build filter parameters + const filterParams = this.buildFilterParams(); + const url = '/api/log/sse/subscribe' + (filterParams ? '?' + filterParams : ''); + + try { + this.eventSource = new EventSource(url); + + this.eventSource.onopen = () => { + this.isConnected = true; + this.isConnecting = false; + }; + + this.eventSource.addEventListener('LOG_EVENT', (evt: MessageEvent) => { + if (!this.isPaused) { + try { + const logEntry: LogEntry = JSON.parse(evt.data); + this.addLogEntry(logEntry); + } catch (error) { + console.error('Error parsing log data:', error); + } + } + }); + + this.eventSource.onerror = error => { + console.error('Log stream connection error:', error); + this.isConnected = false; + this.isConnecting = false; + + // Auto-reconnect after 5 seconds + setTimeout(() => { + if (!this.isConnected) { + this.connectToLogStream(); + } + }, 5000); + }; + } catch (error) { + this.isConnecting = false; + console.error('Failed to create EventSource:', error); + } + } + + private disconnectFromLogStream(): void { + if (this.eventSource) { + this.eventSource.close(); + this.isConnected = false; + this.isConnecting = false; + } + } + + private buildFilterParams(): string { + const params = new URLSearchParams(); + + if (this.filterSeverityNumber && this.filterSeverityNumber.trim()) { + params.append('severityNumber', this.filterSeverityNumber); + } + + if (this.filterSeverityText && this.filterSeverityText.trim()) { + params.append('severityText', this.filterSeverityText); + } + + if (this.filterTraceId && this.filterTraceId.trim()) { + params.append('traceId', this.filterTraceId); + } + + if (this.filterSpanId && this.filterSpanId.trim()) { + params.append('spanId', this.filterSpanId); + } + + return params.toString(); + } + + private addLogEntry(logEntry: LogEntry): void { + const extendedEntry: ExtendedLogEntry = { + ...logEntry, + isNew: true, + timestamp: logEntry.timeUnixNano ? new Date(logEntry.timeUnixNano / 1000000) : new Date(), + displayMessage: this.extractDisplayMessage(logEntry) + }; + + this.logEntries.unshift(extendedEntry); + + // Limit the number of log entries + if (this.logEntries.length > this.maxLogEntries) { + this.logEntries = this.logEntries.slice(0, this.maxLogEntries); + } + + // Remove new indicator after animation + setTimeout(() => { + const index = this.logEntries.findIndex(entry => entry === extendedEntry); + if (index !== -1) { + this.logEntries[index].isNew = false; + } + }, 1000); + + // Auto scroll to top if enabled and user hasn't scrolled away + if (!this.userScrolled) { + this.scheduleAutoScroll(); + } + } + + private setupScrollListener(): void { + if (this.logContainerRef?.nativeElement) { + const container = this.logContainerRef.nativeElement; + + container.addEventListener('scroll', () => { + // Debounce scroll events for better performance + if (this.scrollDebounceTimeout) { + clearTimeout(this.scrollDebounceTimeout); + } + + this.scrollDebounceTimeout = setTimeout(() => { + this.handleScroll(); + }, 100); + }); + } + } + + private cleanupScrollListener(): void { + if (this.scrollTimeout) { + clearTimeout(this.scrollTimeout); + } + if (this.scrollDebounceTimeout) { + clearTimeout(this.scrollDebounceTimeout); + } + } + + private handleScroll(): void { + if (!this.logContainerRef?.nativeElement) return; + + const container = this.logContainerRef.nativeElement; + const scrollTop = container.scrollTop; + + // Check if user is near the top (within 20px for more precise detection) + this.isNearBottom = scrollTop <= 20; + + // If user scrolls away from top, mark as user scrolled + if (!this.isNearBottom) { + this.userScrolled = true; + } else { + // If user scrolls back to top, reset the flag + this.userScrolled = false; + } + } + + private scheduleAutoScroll(): void { + // Clear existing timeout + if (this.scrollTimeout) { + clearTimeout(this.scrollTimeout); + } + + // Schedule scroll with longer delay to ensure DOM update + this.scrollTimeout = setTimeout(() => { + this.performAutoScroll(); + }, 100); + } + + private performAutoScroll(): void { + if (!this.logContainerRef?.nativeElement || this.userScrolled) { + return; + } + + const container = this.logContainerRef.nativeElement; + + // Use smooth scroll for better UX + container.scrollTo({ + top: 0, + behavior: 'smooth' + }); + } + + private extractDisplayMessage(logEntry: LogEntry): string { + if (logEntry.body) { + if (typeof logEntry.body === 'string') { + return logEntry.body; + } else { + return JSON.stringify(logEntry.body); + } + } + return 'No message content'; + } + + // Event handlers + onApplyFilters(): void { + this.logEntries = []; // Clear existing logs + this.connectToLogStream(); // Reconnect with new filters + } + + onFilterChange(): void { + this.logEntries = []; // Clear existing logs + this.connectToLogStream(); // Reconnect with new filters + } + + onClearFilters(): void { + this.filterSeverityNumber = ''; + this.filterSeverityText = ''; + this.filterTraceId = ''; + this.filterSpanId = ''; + this.logEntries = []; + this.connectToLogStream(); + } + + onTogglePause(): void { + this.isPaused = !this.isPaused; + } + + onClearLogs(): void { + this.logEntries = []; + this.userScrolled = false; + this.isNearBottom = true; + } + + onToggleFilters(): void { + this.showFilters = !this.showFilters; + } + + // Add method to manually scroll to top + scrollToTop(): void { + this.userScrolled = false; + this.isNearBottom = true; + this.scheduleAutoScroll(); + } + + // Utility methods + getSeverityColor(severityNumber: number | undefined): string { + console.log('severityNumber', severityNumber); + if (!severityNumber) { + return 'default'; + } + + // Based on OpenTelemetry specification: + // 1-4: TRACE, 5-8: DEBUG, 9-12: INFO, 13-16: WARN, 17-20: ERROR, 21-24: FATAL + if (severityNumber >= 1 && severityNumber <= 4) { + return 'default'; // TRACE + } else if (severityNumber >= 5 && severityNumber <= 8) { + return 'blue'; // DEBUG + } else if (severityNumber >= 9 && severityNumber <= 12) { + return 'green'; // INFO + } else if (severityNumber >= 13 && severityNumber <= 16) { + return 'orange'; // WARN + } else if (severityNumber >= 17 && severityNumber <= 20) { + return 'red'; // ERROR + } else if (severityNumber >= 21 && severityNumber <= 24) { + return 'volcano'; // FATAL + } else { + return 'default'; // Unknown + } + } + + formatTimestamp(timestamp: Date): string { + return timestamp.toLocaleString(); + } + + copyToClipboard(text: string): void { + navigator.clipboard + .writeText(text) + .then(() => { + console.log('Copied to clipboard'); + }) + .catch(err => { + console.error('Failed to copy: ', err); + }); + } + + trackByLogEntry(index: number, logEntry: ExtendedLogEntry): any { + return logEntry.timeUnixNano || index; + } + + getObjectKeys(obj: any): string[] { + return Object.keys(obj || {}); + } + + // Modal methods + showLogDetails(logEntry: ExtendedLogEntry): void { + this.selectedLogEntry = logEntry; + this.isModalVisible = true; + } + + handleModalOk(): void { + this.isModalVisible = false; + this.selectedLogEntry = null; + } + + handleModalCancel(): void { + this.isModalVisible = false; + this.selectedLogEntry = null; + } + + getLogEntryJson(logEntry: ExtendedLogEntry): string { + return JSON.stringify(logEntry, null, 2); + } +} diff --git a/web-app/src/app/routes/log/log.module.ts b/web-app/src/app/routes/log/log.module.ts index bce994868e3..eca9852b9bf 100644 --- a/web-app/src/app/routes/log/log.module.ts +++ b/web-app/src/app/routes/log/log.module.ts @@ -28,6 +28,7 @@ import { NzTagModule } from 'ng-zorro-antd/tag'; import { LogIntegrationComponent } from './log-integration/log-integration.component'; import { LogRoutingModule } from './log-routing.module'; +import { LogStreamComponent } from './log-stream/log-stream.component'; const COMPONENTS: Array> = []; @@ -41,7 +42,8 @@ const COMPONENTS: Array> = []; NzPaginationModule, NzEmptyModule, NzBreadCrumbModule, - LogIntegrationComponent + LogIntegrationComponent, + LogStreamComponent ], declarations: COMPONENTS }) diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index 04691b0f115..4ebd13b2d3e 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -147,6 +147,12 @@ "i18n": "menu.log.integration", "icon": "anticon-api", "link": "/log/integration/otlp" + }, + { + "text": "Stream", + "i18n": "menu.log.stream", + "icon": "anticon-api", + "link": "/log/stream" } ] }, diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index e9f63c6f91f..6f7e3618aa5 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -109,10 +109,50 @@ "alert.integration.token.title": "Access Token", "log.help.integration": "Unified management of logs, integrating and receiving log messages from external log sources, and performing actions such as collection, storage, alerting, and analysis.", "log.help.integration.link": "https://hertzbeat.apache.org", + "log.help.stream": "Real-time log streaming and monitoring interface for viewing live log entries with filtering and search capabilities.", + "log.help.stream.link": "https://hertzbeat.apache.org", "log.integration.source": "Integration Source", "log.integration.source.otlp": "OTLP Protocol", "log.integration.token.desc": "Token you generated that can be used to access the HertzBeat API.", "log.integration.token.new": "Click to Generate Token", + "log.stream.title": "Log Stream", + "log.stream.live-logs": "Live Logs", + "log.stream.connected": "Connected", + "log.stream.connecting": "Connecting...", + "log.stream.disconnected": "Disconnected", + "log.stream.logs": "logs", + "log.stream.toggle-filters": "Toggle Filters", + "log.stream.hide-filters": "Hide Filters", + "log.stream.show-filters": "Show Filters", + "log.stream.resume": "Resume", + "log.stream.pause": "Pause", + "log.stream.clear": "Clear", + "log.stream.scroll-to-top": "Scroll to Top", + "log.stream.severity-number": "Severity Number:", + "log.stream.severity-number-placeholder": "Enter severity number", + "log.stream.severity-text": "Severity:", + "log.stream.severity-text-placeholder": "Enter severity text", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-placeholder": "Enter trace ID", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-placeholder": "Enter span ID", + "log.stream.clear-filters": "Clear Filters", + "log.stream.clear-filters-tooltip": "Clear all filters", + "log.stream.no-logs": "No logs available", + "log.stream.unknown": "UNKNOWN", + "log.stream.trace": "Trace:", + "log.stream.span": "Span:", + "log.stream.copy-message": "Copy message", + "log.stream.attributes": "attributes", + "log.stream.resource": "resource", + "log.stream.log-entry-details": "Log Entry Details", + "log.stream.basic-information": "Basic Information", + "log.stream.severity": "Severity:", + "log.stream.timestamp": "Timestamp:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.message": "Message", + "log.stream.complete-json-data": "Complete JSON Data", "alert.notice.receiver": "Notice Receiver", "alert.notice.receiver.delete": "Delete Receiver", "alert.notice.receiver.edit": "Edit Receiver", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index d732b675469..eafba193efe 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -564,6 +564,52 @@ "label.value": "ラベル値", "labels.help": "ラベルは至る所にあります。リソースグループ化、ルール下のタグマッチングなどにラベルを適用できます。[ラベル管理]は、ラベルの統一管理に使用され、新規、削除、編集などが可能です。
ラベルを使用して監視リソースを分類および管理できます。例えば、本番環境とテスト環境にラベルをバインドすることができます。", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", + "log.help.integration": "ログの統一管理、外部ログソースからのログメッセージの統合および受信、収集、保存、アラート、分析などのアクションの実行。", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.integration.source": "統合ソース", + "log.integration.source.otlp": "OTLPプロトコル", + "log.integration.token.desc": "HertzBeat APIにアクセスするために生成したトークン。", + "log.integration.token.new": "トークンを生成するにはクリック", + "log.help.stream": "フィルタリングと検索機能を備えたライブログエントリを表示するためのリアルタイムログストリーミングおよび監視インターフェース。", + "log.help.stream.link": "https://hertzbeat.apache.org", + "log.stream.title": "ログストリーム", + "log.stream.live-logs": "ライブログ", + "log.stream.connected": "接続済み", + "log.stream.connecting": "接続中...", + "log.stream.disconnected": "切断", + "log.stream.logs": "ログ", + "log.stream.toggle-filters": "フィルターを切り替え", + "log.stream.hide-filters": "フィルターを非表示", + "log.stream.show-filters": "フィルターを表示", + "log.stream.resume": "再開", + "log.stream.pause": "一時停止", + "log.stream.clear": "クリア", + "log.stream.scroll-to-top": "トップにスクロール", + "log.stream.severity-number": "重大度番号:", + "log.stream.severity-number-placeholder": "重大度番号を入力してください", + "log.stream.severity-text": "重大度:", + "log.stream.severity-text-placeholder": "重大度テキストを入力してください", + "log.stream.trace-id": "トレースID:", + "log.stream.trace-id-placeholder": "トレースIDを入力してください", + "log.stream.span-id": "スパンID:", + "log.stream.span-id-placeholder": "スパンIDを入力してください", + "log.stream.clear-filters": "フィルターをクリア", + "log.stream.clear-filters-tooltip": "すべてのフィルターをクリア", + "log.stream.no-logs": "利用可能なログがありません", + "log.stream.unknown": "不明", + "log.stream.trace": "トレース:", + "log.stream.span": "スパン:", + "log.stream.copy-message": "メッセージをコピー", + "log.stream.attributes": "属性", + "log.stream.resource": "リソース", + "log.stream.log-entry-details": "ログエントリの詳細", + "log.stream.basic-information": "基本情報", + "log.stream.severity": "重大度:", + "log.stream.timestamp": "タイムスタンプ:", + "log.stream.trace-id-full": "トレースID:", + "log.stream.span-id-full": "スパンID:", + "log.stream.message": "メッセージ", + "log.stream.complete-json-data": "完全なJSONデータ", "menu.account": "個人", "menu.account.binding": "アカウントバインディング", "menu.account.center": "個人センター", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index 4023ba3aee0..0fedeab59d6 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -616,6 +616,52 @@ "dashboard.monitors.sub-title": "A Distribuição dos Monitores", "dashboard.monitors.formatter": " Monitores ", "dashboard.monitors.distribute": "Distribuição do Monitor", + "log.help.integration": "Gerenciamento unificado de logs, integrando e recebendo mensagens de log de fontes de log externas, e executando ações como coleta, armazenamento, alertas e análise.", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.integration.source": "Fonte de Integração", + "log.integration.source.otlp": "Protocolo OTLP", + "log.integration.token.desc": "Token que você gerou que pode ser usado para acessar a API do HertzBeat.", + "log.integration.token.new": "Clique para Gerar Token", + "log.help.stream": "Interface de streaming e monitoramento de logs em tempo real para visualizar entradas de log ao vivo com capacidades de filtragem e pesquisa.", + "log.help.stream.link": "https://hertzbeat.apache.org", + "log.stream.title": "Fluxo de Log", + "log.stream.live-logs": "Logs ao Vivo", + "log.stream.connected": "Conectado", + "log.stream.connecting": "Conectando...", + "log.stream.disconnected": "Desconectado", + "log.stream.logs": "logs", + "log.stream.toggle-filters": "Alternar Filtros", + "log.stream.hide-filters": "Ocultar Filtros", + "log.stream.show-filters": "Mostrar Filtros", + "log.stream.resume": "Retomar", + "log.stream.pause": "Pausar", + "log.stream.clear": "Limpar", + "log.stream.scroll-to-top": "Rolar para o Topo", + "log.stream.severity-number": "Número de Severidade:", + "log.stream.severity-number-placeholder": "Digite o número de severidade", + "log.stream.severity-text": "Severidade:", + "log.stream.severity-text-placeholder": "Digite o texto de severidade", + "log.stream.trace-id": "ID do Rastreamento:", + "log.stream.trace-id-placeholder": "Digite o ID do rastreamento", + "log.stream.span-id": "ID do Span:", + "log.stream.span-id-placeholder": "Digite o ID do span", + "log.stream.clear-filters": "Limpar Filtros", + "log.stream.clear-filters-tooltip": "Limpar todos os filtros", + "log.stream.no-logs": "Nenhum log disponível", + "log.stream.unknown": "DESCONHECIDO", + "log.stream.trace": "Rastreamento:", + "log.stream.span": "Span:", + "log.stream.copy-message": "Copiar mensagem", + "log.stream.attributes": "atributos", + "log.stream.resource": "recurso", + "log.stream.log-entry-details": "Detalhes da Entrada de Log", + "log.stream.basic-information": "Informações Básicas", + "log.stream.severity": "Severidade:", + "log.stream.timestamp": "Timestamp:", + "log.stream.trace-id-full": "ID do Rastreamento:", + "log.stream.span-id-full": "ID do Span:", + "log.stream.message": "Mensagem", + "log.stream.complete-json-data": "Dados JSON Completos", "menu.link.question": "FAQ", "menu.link.guild": "Guia do Usuário", "menu.account": "Página pessoal", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 491e63ae1b6..36d83bb93cc 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -109,12 +109,52 @@ "alert.integration.token.title": "访问认证 Token", "log.help.integration": "统一管理日志,集成接入外部日志源的日志消息,对其进行收集,存储,告警和分析等。", "log.help.integration.link": "https://hertzbeat.apache.org", + "log.help.stream": "实时日志流监控界面,用于查看实时日志条目,具备过滤和搜索功能。", + "log.help.stream.link": "https://hertzbeat.apache.org", "log.integration.source": "集成日志源", "log.integration.source.otlp": "OTLP 协议", "log.integration.token.desc": "生成的 Token 可用于访问 HertzBeat 日志接入API", "log.integration.token.new": "点击生成 Token", "log.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", "log.integration.token.title": "访问认证 Token", + "log.stream.title": "日志流", + "log.stream.live-logs": "实时日志", + "log.stream.connected": "已连接", + "log.stream.connecting": "连接中...", + "log.stream.disconnected": "已断开", + "log.stream.logs": "条日志", + "log.stream.toggle-filters": "切换过滤器", + "log.stream.hide-filters": "隐藏过滤器", + "log.stream.show-filters": "显示过滤器", + "log.stream.resume": "恢复", + "log.stream.pause": "暂停", + "log.stream.clear": "清除", + "log.stream.scroll-to-top": "滚动到顶部", + "log.stream.severity-number": "严重程度编号:", + "log.stream.severity-number-placeholder": "输入严重程度编号", + "log.stream.severity-text": "严重程度:", + "log.stream.severity-text-placeholder": "输入严重程度", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-placeholder": "输入Trace ID", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-placeholder": "输入Span ID", + "log.stream.clear-filters": "清除过滤器", + "log.stream.clear-filters-tooltip": "清除所有过滤器", + "log.stream.no-logs": "暂无可用日志", + "log.stream.unknown": "未知", + "log.stream.trace": "跟踪:", + "log.stream.span": "跨度:", + "log.stream.copy-message": "复制消息", + "log.stream.attributes": "个属性", + "log.stream.resource": "个源", + "log.stream.log-entry-details": "日志条目详情", + "log.stream.basic-information": "基本信息", + "log.stream.severity": "严重程度:", + "log.stream.timestamp": "时间戳:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.message": "消息", + "log.stream.complete-json-data": "完整JSON数据", "alert.notice.receiver": "通知媒介", "alert.notice.receiver.delete": "删除接收对象", "alert.notice.receiver.edit": "编辑接收对象", @@ -605,6 +645,7 @@ "menu.alert.silence": "告警静默", "menu.log": "日志", "menu.log.integration": "集成接入", + "menu.log.stream": "日志流", "menu.clear.local.storage": "清理本地缓存", "menu.dashboard": "仪表盘", "menu.extras": "更多", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index c1833d01d9f..010862320fc 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -568,6 +568,52 @@ "label.value": "標簽值", "labels.help": "標簽無處不在,我們可以應用標簽在資源分組,規則下的標簽匹配等場景。標簽管理用于對標簽的統壹管理維護,包含新增,刪除,編輯等操作。
例如:您可以使用標簽對監控資源進行分類管理,給資源分別綁定生産環境、測試環境的標簽,在告警通知時通過標簽匹配不同的通知人。", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", + "log.help.integration": "統一管理日誌,整合接入外部日誌源的日誌訊息,對其進行收集,儲存,告警和分析等。", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.integration.source": "整合日誌源", + "log.integration.source.otlp": "OTLP 協議", + "log.integration.token.desc": "生成的 Token 可用於存取 HertzBeat 日誌接入API", + "log.integration.token.new": "點擊生成 Token", + "log.help.stream": "即時日誌流監控介面,用於檢視即時日誌條目,具備過濾和搜尋功能。", + "log.help.stream.link": "https://hertzbeat.apache.org", + "log.stream.title": "日誌流", + "log.stream.live-logs": "即時日誌", + "log.stream.connected": "已連線", + "log.stream.connecting": "連線中...", + "log.stream.disconnected": "已斷線", + "log.stream.logs": "條日誌", + "log.stream.toggle-filters": "切換過濾器", + "log.stream.hide-filters": "隱藏過濾器", + "log.stream.show-filters": "顯示過濾器", + "log.stream.resume": "恢復", + "log.stream.pause": "暫停", + "log.stream.clear": "清除", + "log.stream.scroll-to-top": "捲動到頂部", + "log.stream.severity-number": "嚴重程度編號:", + "log.stream.severity-number-placeholder": "輸入嚴重程度编号", + "log.stream.severity-text": "嚴重程度:", + "log.stream.severity-text-placeholder": "輸入嚴重程度", + "log.stream.trace-id": "追蹤ID:", + "log.stream.trace-id-placeholder": "輸入追蹤ID", + "log.stream.span-id": "跨度ID:", + "log.stream.span-id-placeholder": "輸入跨度ID", + "log.stream.clear-filters": "清除過濾器", + "log.stream.clear-filters-tooltip": "清除所有過濾器", + "log.stream.no-logs": "暫無可用日誌", + "log.stream.unknown": "未知", + "log.stream.trace": "追蹤:", + "log.stream.span": "跨度:", + "log.stream.copy-message": "複製訊息", + "log.stream.attributes": "個屬性", + "log.stream.resource": "個資源", + "log.stream.log-entry-details": "日誌條目詳情", + "log.stream.basic-information": "基本資訊", + "log.stream.severity": "嚴重程度:", + "log.stream.timestamp": "時間戳:", + "log.stream.trace-id-full": "追蹤ID:", + "log.stream.span-id-full": "跨度ID:", + "log.stream.message": "訊息", + "log.stream.complete-json-data": "完整JSON資料", "menu.account": "個人頁", "menu.account.binding": "賬號綁定", "menu.account.center": "個人中心", From b4329405fb0c5b8ea142abd4e8c8a5bc9d513238 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 20 Jul 2025 12:11:40 +0800 Subject: [PATCH 16/39] improvement: To make the log display more compact and unify the code style --- .../log/notice/LogSseFilterCriteria.java | 4 +- web-app/src/app/pojo/LogEntry.ts | 19 +++ .../alert-setting.component.html | 2 +- .../alert-setting/alert-setting.component.ts | 3 +- .../log/log-stream/log-stream.component.html | 157 +++++++----------- .../log/log-stream/log-stream.component.less | 74 ++++----- .../log-stream/log-stream.component.spec.ts | 5 +- .../log/log-stream/log-stream.component.ts | 83 +++------ 8 files changed, 144 insertions(+), 203 deletions(-) create mode 100644 web-app/src/app/pojo/LogEntry.ts diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java index 3012099beac..84d63770fb3 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java @@ -59,8 +59,8 @@ public boolean matches(LogEntry log) { } // Check severity number match (if both are present) - if (severityNumber != null && log.getSeverityNumber() != null && - !severityNumber.equals(log.getSeverityNumber())) { + if (severityNumber != null && log.getSeverityNumber() != null + && !severityNumber.equals(log.getSeverityNumber())) { return false; } diff --git a/web-app/src/app/pojo/LogEntry.ts b/web-app/src/app/pojo/LogEntry.ts new file mode 100644 index 00000000000..0faa22a8ac7 --- /dev/null +++ b/web-app/src/app/pojo/LogEntry.ts @@ -0,0 +1,19 @@ +// Define LogEntry interface based on backend structure +export class LogEntry { + timeUnixNano?: number; + observedTimeUnixNano?: number; + severityNumber?: number; + severityText?: string; + body?: any; + attributes?: { [key: string]: any }; + droppedAttributesCount?: number; + traceId?: string; + spanId?: string; + traceFlags?: number; + resource?: { [key: string]: any }; + instrumentationScope?: { + name?: string; + version?: string; + attributes?: { [key: string]: any }; + }; +} diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 6a5a7fe5ed3..9005d3a6768 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -442,7 +442,7 @@ - + {{ 'alert.setting.rule' | i18n }} diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index 31ef53a8cdd..6820f29a4d2 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -338,7 +338,6 @@ export class AlertSettingComponent implements OnInit { this.isManageModalAdd = true; this.isManageModalVisible = true; this.isManageModalOkLoading = false; - } onDataTypeChange() { @@ -358,7 +357,7 @@ export class AlertSettingComponent implements OnInit { this.define.type = 'periodic_log'; this.updateLogQbConfig(); } - + // Reset form state when switching data source type this.userExpr = ''; this.cascadeValues = []; diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.html b/web-app/src/app/routes/log/log-stream/log-stream.component.html index d1e6d3b7f12..e60d8553b6f 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.html +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.html @@ -17,7 +17,6 @@ * under the License. --> -
-
- - - - {{ isConnected ? ('log.stream.connected' | i18n) : (isConnecting ? ('log.stream.connecting' | i18n) : ('log.stream.disconnected' | i18n)) }} + + + {{ + isConnected + ? ('log.stream.connected' | i18n) + : isConnecting + ? ('log.stream.connecting' | i18n) + : ('log.stream.disconnected' | i18n) + }} - + {{ logEntries.length }} {{ 'log.stream.logs' | i18n }}
- - - - - - - @@ -91,7 +84,7 @@
- +
@@ -158,12 +151,13 @@
- @@ -171,68 +165,46 @@
-
- +
- - +
-
- -
+ (click)="showLogDetails(logEntry)" + > +
- - {{ logEntry.severityText || ('log.stream.unknown' | i18n) }} + + {{ logEntry.original.severityText || ('log.stream.unknown' | i18n) }} - + {{ formatTimestamp(logEntry.timestamp!) }} - - - {{ 'log.stream.trace' | i18n }} {{ logEntry.traceId.substring(0, 8) }}... - - - - {{ 'log.stream.span' | i18n }} {{ logEntry.spanId.substring(0, 8) }}... - +
- - - {{ getObjectKeys(logEntry.attributes).length }} {{ 'log.stream.attributes' | i18n }} - - - - {{ getObjectKeys(logEntry.resource).length }} {{ 'log.stream.resource' | i18n }} - +
+ {{ getLogEntryJson(logEntry.original) }}
- +
-
- -
- {{ logEntry.displayMessage }} -
@@ -244,45 +216,38 @@ [nzTitle]="'log.stream.log-entry-details' | i18n" (nzOnCancel)="handleModalCancel()" [nzFooter]="null" - [nzWidth]="800"> - + [nzWidth]="800" +>
- +
{{ 'log.stream.severity' | i18n }} - - {{ selectedLogEntry.severityText || ('log.stream.unknown' | i18n) }} + + {{ selectedLogEntry.original.severityText || ('log.stream.unknown' | i18n) }}
{{ 'log.stream.timestamp' | i18n }} {{ formatTimestamp(selectedLogEntry.timestamp!) }}
-
+
{{ 'log.stream.trace-id-full' | i18n }} - {{ selectedLogEntry.traceId }} + {{ selectedLogEntry.original.traceId }}
-
+
{{ 'log.stream.span-id-full' | i18n }} - {{ selectedLogEntry.spanId }} + {{ selectedLogEntry.original.spanId }}
- - -
-
{{ selectedLogEntry.displayMessage }}
-
-
-
-
{{ getLogEntryJson(selectedLogEntry) }}
+
{{ getLogEntryJson(selectedLogEntry.original) }}
diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.less b/web-app/src/app/routes/log/log-stream/log-stream.component.less index 829902576ce..a74529f24aa 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.less +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.less @@ -150,7 +150,7 @@ .log-entry { border-bottom: 1px solid #f0f0f0; - padding: 12px 16px; + padding: 0px 12px; background: @common-background-color; cursor: pointer; @@ -167,26 +167,24 @@ border-bottom: none; } - .log-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; - flex-wrap: wrap; - gap: 8px; + .log-content { + display: flex; + align-items: center; + gap: 12px; + padding: 4px 0; .log-meta { display: flex; align-items: center; gap: 8px; - flex-wrap: wrap; + flex-shrink: 0; .severity-tag { font-weight: 500; - font-size: 12px; - border-radius: 4px; - padding: 2px 8px; - min-width: 60px; + font-size: 11px; + border-radius: 3px; + padding: 1px 6px; + min-width: 50px; text-align: center; } @@ -198,17 +196,27 @@ padding: 2px 8px; border-radius: 4px; } + } - .trace-info, - .span-info { - nz-tag { - font-size: 11px; - border-radius: 4px; - } - } + .log-message { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + line-height: 1.4; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background: #f8f9fa; + padding: 6px 12px; + border-radius: 4px; + border-left: 3px solid #e9ecef; + flex: 1; + min-width: 0; } .log-actions { + flex-shrink: 0; + button { border: none; box-shadow: none; @@ -221,20 +229,6 @@ } } } - - .log-message { - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; - font-size: 13px; - line-height: 1.5; - color: #333; - word-break: break-word; - white-space: pre-wrap; - background: #f8f9fa; - padding: 8px 12px; - border-radius: 4px; - border-left: 3px solid #e9ecef; - margin: 8px 0; - } } .pause-overlay { @@ -334,23 +328,25 @@ .log-entry { padding: 8px 12px; - .log-header { + .log-content { flex-direction: column; align-items: flex-start; + gap: 8px; .log-meta { width: 100%; } + .log-message { + width: 100%; + font-size: 12px; + } + .log-actions { width: 100%; text-align: right; } } - - .log-message { - font-size: 12px; - } } } } diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts index ece44e5eeca..f169eeee65f 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts @@ -9,9 +9,8 @@ describe('LogStreamComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [LogStreamComponent] - }) - .compileComponents(); - + }).compileComponents(); + fixture = TestBed.createComponent(LogStreamComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.ts index 85e3b352fc3..7ad189cfc7b 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.ts +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.ts @@ -17,48 +17,30 @@ * under the License. */ +import { CommonModule } from '@angular/common'; import { Component, Inject, OnDestroy, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { I18NService } from '@core'; import { ALAIN_I18N_TOKEN } from '@delon/theme'; import { SharedModule } from '@shared'; +import { NzAlertModule } from 'ng-zorro-antd/alert'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzCardModule } from 'ng-zorro-antd/card'; +import { NzDividerComponent } from 'ng-zorro-antd/divider'; +import { NzEmptyModule } from 'ng-zorro-antd/empty'; import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzSelectModule } from 'ng-zorro-antd/select'; +import { NzSwitchModule } from 'ng-zorro-antd/switch'; import { NzTagModule } from 'ng-zorro-antd/tag'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; -import { NzSwitchModule } from 'ng-zorro-antd/switch'; -import { NzAlertModule } from 'ng-zorro-antd/alert'; -import { NzEmptyModule } from 'ng-zorro-antd/empty'; -import { NzModalModule } from 'ng-zorro-antd/modal'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { NzDividerComponent } from 'ng-zorro-antd/divider'; -// Define LogEntry interface based on backend structure -interface LogEntry { - timeUnixNano?: number; - observedTimeUnixNano?: number; - severityNumber?: number; - severityText?: string; - body?: any; - attributes?: { [key: string]: any }; - droppedAttributesCount?: number; - traceId?: string; - spanId?: string; - traceFlags?: number; - resource?: { [key: string]: any }; - instrumentationScope?: { - name?: string; - version?: string; - attributes?: { [key: string]: any }; - }; -} +import { LogEntry } from '../../../pojo/LogEntry'; -interface ExtendedLogEntry extends LogEntry { +interface ExtendedLogEntry { + original: LogEntry; isNew?: boolean; timestamp?: Date; - displayMessage?: string; } @Component({ @@ -101,9 +83,8 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { filterSpanId: string = ''; // UI state - autoScroll: boolean = true; showFilters: boolean = true; - + // Modal state isModalVisible: boolean = false; selectedLogEntry: ExtendedLogEntry | null = null; @@ -117,8 +98,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { // ViewChild for log container @ViewChild('logContainer', { static: false }) logContainerRef!: ElementRef; - constructor(@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService) { - } + constructor(@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService) {} ngOnInit(): void { this.connectToLogStream(); @@ -138,7 +118,6 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { this.connectToLogStream(); } - private connectToLogStream(): void { if (this.eventSource) { this.disconnectFromLogStream(); @@ -148,7 +127,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { // Build filter parameters const filterParams = this.buildFilterParams(); - const url = '/api/log/sse/subscribe' + (filterParams ? '?' + filterParams : ''); + const url = `/api/log/sse/subscribe${filterParams ? `?${filterParams}` : ''}`; try { this.eventSource = new EventSource(url); @@ -219,10 +198,9 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { private addLogEntry(logEntry: LogEntry): void { const extendedEntry: ExtendedLogEntry = { - ...logEntry, + original: logEntry, isNew: true, - timestamp: logEntry.timeUnixNano ? new Date(logEntry.timeUnixNano / 1000000) : new Date(), - displayMessage: this.extractDisplayMessage(logEntry) + timestamp: logEntry.timeUnixNano ? new Date(logEntry.timeUnixNano / 1000000) : new Date() }; this.logEntries.unshift(extendedEntry); @@ -249,13 +227,13 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { private setupScrollListener(): void { if (this.logContainerRef?.nativeElement) { const container = this.logContainerRef.nativeElement; - + container.addEventListener('scroll', () => { // Debounce scroll events for better performance if (this.scrollDebounceTimeout) { clearTimeout(this.scrollDebounceTimeout); } - + this.scrollDebounceTimeout = setTimeout(() => { this.handleScroll(); }, 100); @@ -277,10 +255,10 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { const container = this.logContainerRef.nativeElement; const scrollTop = container.scrollTop; - + // Check if user is near the top (within 20px for more precise detection) this.isNearBottom = scrollTop <= 20; - + // If user scrolls away from top, mark as user scrolled if (!this.isNearBottom) { this.userScrolled = true; @@ -308,7 +286,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { } const container = this.logContainerRef.nativeElement; - + // Use smooth scroll for better UX container.scrollTo({ top: 0, @@ -316,17 +294,6 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { }); } - private extractDisplayMessage(logEntry: LogEntry): string { - if (logEntry.body) { - if (typeof logEntry.body === 'string') { - return logEntry.body; - } else { - return JSON.stringify(logEntry.body); - } - } - return 'No message content'; - } - // Event handlers onApplyFilters(): void { this.logEntries = []; // Clear existing logs @@ -374,7 +341,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { if (!severityNumber) { return 'default'; } - + // Based on OpenTelemetry specification: // 1-4: TRACE, 5-8: DEBUG, 9-12: INFO, 13-16: WARN, 17-20: ERROR, 21-24: FATAL if (severityNumber >= 1 && severityNumber <= 4) { @@ -410,11 +377,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { } trackByLogEntry(index: number, logEntry: ExtendedLogEntry): any { - return logEntry.timeUnixNano || index; - } - - getObjectKeys(obj: any): string[] { - return Object.keys(obj || {}); + return logEntry.original.timeUnixNano || index; } // Modal methods @@ -433,7 +396,7 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { this.selectedLogEntry = null; } - getLogEntryJson(logEntry: ExtendedLogEntry): string { + getLogEntryJson(logEntry: LogEntry): string { return JSON.stringify(logEntry, null, 2); } } From 116b74f0dce587ca2a2de934d0d087bc3543f95a Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Wed, 30 Jul 2025 14:48:55 +0800 Subject: [PATCH 17/39] temp --- CLAUDE.md | 93 + .../gen/expr/SqlExpression.interp | 126 + .../gen/expr/SqlExpression.tokens | 60 + .../gen/expr/SqlExpressionBaseListener.java | 304 +++ .../gen/expr/SqlExpressionBaseVisitor.java | 169 ++ .../gen/expr/SqlExpressionLexer.interp | 158 ++ .../gen/expr/SqlExpressionLexer.java | 348 +++ .../gen/expr/SqlExpressionLexer.tokens | 60 + .../gen/expr/SqlExpressionListener.java | 234 ++ .../gen/expr/SqlExpressionParser.java | 2100 ++++++++++++++++ .../gen/expr/SqlExpressionVisitor.java | 147 ++ .../calculate/PeriodicAlertCalculator.java | 18 +- .../expr/sql/SqlExpressionBaseListener.java | 350 +++ .../expr/sql/SqlExpressionBaseVisitor.java | 191 ++ .../expr/sql/SqlExpressionEvalVisitor.java | 42 + .../alert/expr/sql/SqlExpressionLexer.java | 350 +++ .../alert/expr/sql/SqlExpressionListener.java | 278 +++ .../alert/expr/sql/SqlExpressionParser.java | 2113 +++++++++++++++++ .../alert/expr/sql/SqlExpressionVisitor.java | 169 ++ .../alert/service/DataSourceService.java | 8 + .../service/impl/DataSourceServiceImpl.java | 23 + .../src/main/resources/expr/SqlExpression.g4 | 200 ++ .../src/main/resources/application.yml | 4 +- .../db/GreptimePromqlQueryExecutor.java | 4 +- .../db/GreptimeSqlQueryExecutor.java | 114 + .../warehouse/db/SqlQueryExecutor.java | 17 +- .../greptime/GreptimeSqlQueryContent.java | 94 + .../alert-setting.component.html | 9 +- 28 files changed, 7770 insertions(+), 13 deletions(-) create mode 100644 CLAUDE.md create mode 100644 hertzbeat-alerter/gen/expr/SqlExpression.interp create mode 100644 hertzbeat-alerter/gen/expr/SqlExpression.tokens create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.java create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionListener.java create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionParser.java create mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java create mode 100644 hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 create mode 100644 hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java create mode 100644 hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..c7f21dd8282 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,93 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Backend (Java/Spring Boot) +- **Build entire project**: `mvn clean install -DskipTests` +- **Run tests**: `mvn test` +- **Run specific module tests**: `mvn test -pl hertzbeat-manager` +- **Package application**: `mvn package -DskipTests` +- **Code quality checks**: `mvn checkstyle:check` (automatically runs in validate phase) +- **Test coverage**: `mvn jacoco:report` (coverage reports generated in target/site/jacoco) + +### Frontend (Angular) +- **Install dependencies**: `cd web-app && yarn install` +- **Start development server**: `cd web-app && ng serve --proxy-config proxy.conf.json` +- **Build for production**: `cd web-app && npm run package` +- **Run tests**: `cd web-app && ng test` +- **Test coverage**: `cd web-app && ng test --code-coverage --watch=false` +- **Linting**: `cd web-app && npm run lint` (includes both TypeScript and style linting) + +### Running Locally +1. **Backend**: + - Requires Java 17, Maven 3+, Lombok + - Add VM option: `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED` + - Run main class: `org.apache.hertzbeat.manager.Manager` +2. **Frontend**: + - Requires Node.js >= 18, Yarn + - Start backend first, then run: `ng serve --open` + - Default login: admin/hertzbeat + +## Architecture Overview + +HertzBeat is a real-time monitoring system with agentless architecture, built using: + +### Core Modules +- **hertzbeat-manager**: Main Spring Boot application (port 1157) - handles web UI, API, monitoring configuration +- **hertzbeat-collector**: Multi-module collector system with pluggable monitoring capabilities + - `hertzbeat-collector-basic`: Basic monitoring implementations + - `hertzbeat-collector-collector`: Core collection engine + - `hertzbeat-collector-common`: Shared collector utilities + - `hertzbeat-collector-mongodb/kafka/rocketmq/nebulagraph`: Database-specific collectors +- **hertzbeat-alerter**: Alert processing and notification system +- **hertzbeat-warehouse**: Data storage and querying layer +- **hertzbeat-common**: Shared utilities and constants +- **hertzbeat-remoting**: Communication layer between components +- **hertzbeat-push**: Notification delivery system +- **hertzbeat-plugin**: Plugin architecture for extensibility + +### Frontend +- **web-app**: Angular 17 application with ng-alain framework +- Serves from `classpath:/dist/` (built frontend files) + +### Key Technologies +- **Backend**: Java 17, Spring Boot 3.4.2, EclipseLink JPA, H2 database (default) +- **Frontend**: Angular 17, ng-alain, TypeScript +- **Build**: Maven, Yarn +- **Monitoring**: Prometheus-compatible, custom YML-based monitoring templates +- **Security**: Sureness framework for authentication/authorization + +### Database Support +- Default: H2 (embedded) +- Supported: MySQL, PostgreSQL, Oracle, SQL Server, MongoDB, ClickHouse, IoTDB, TDengine, GreptimeDB + +### Configuration +- Main config: `hertzbeat-manager/src/main/resources/application.yml` +- Database migrations: Flyway (locations: `classpath:db/migration/{vendor}`) +- Monitoring templates: YML files in `hertzbeat-manager/src/main/resources/define/` + +## Development Guidelines + +### Code Style +- Java: Checkstyle enforced (max line length: 200, specific suppression rules) +- Frontend: ESLint + Stylelint configured +- Commit format: `[module name or type name]feature or bugfix or doc: custom message` + +### Testing +- Backend: JUnit 5, Spring Boot Test +- Frontend: Jasmine, Karma +- E2E tests available in `hertzbeat-e2e` module +- Coverage reporting via JaCoCo + +### Key Patterns +- Monitoring types defined as configurable YML templates +- Pluggable collector architecture +- Alert expression evaluation with SQL-based rules +- Agentless monitoring using various protocols (HTTP, JMX, SSH, SNMP, JDBC, etc.) + +### Database Schema +- Uses EclipseLink JPA with MySQL-compatible platform +- Flyway for database migrations +- Supports multiple database vendors through vendor-specific migration scripts \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpression.interp b/hertzbeat-alerter/gen/expr/SqlExpression.interp new file mode 100644 index 00000000000..3ea61a858d3 --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpression.interp @@ -0,0 +1,126 @@ +token literal names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +'*' +null +null +null +null +null +null +'>' +'>=' +'<' +'<=' +null +'!=' +'(' +')' +'[' +']' +',' +'.' +';' +null +null +null +null +null +null +null + +token symbolic names: +null +AND +OR +NOT +SELECT +FROM +WHERE +GROUP +BY +HAVING +ORDER +LIMIT +OFFSET +AS +ASC +DESC +IN +IS +NULL +LIKE +BETWEEN +STAR +COUNT +SUM +AVG +MIN +MAX +SQL_FUNCTION +GT +GE +LT +LE +EQ +NE +LPAREN +RPAREN +LBRACKET +RBRACKET +COMMA +DOT +SEMICOLON +FLOAT +NUMBER +STRING +IDENTIFIER +WS +LINE_COMMENT +BLOCK_COMMENT + +rule names: +expression +sqlExpr +selectSql +selectFieldList +selectField +relList +relation +conditionList +compOp +condition +conditionUnit +functionCall +functionName +parameterList +parameter +groupByList +orderByList +orderByField +limitClause +number +string + + +atn: +[4, 1, 47, 260, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 52, 8, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 60, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 65, 8, 2, 1, 2, 1, 2, 3, 2, 69, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 74, 8, 2, 1, 2, 1, 2, 3, 2, 78, 8, 2, 1, 3, 1, 3, 1, 3, 5, 3, 83, 8, 3, 10, 3, 12, 3, 86, 9, 3, 1, 4, 1, 4, 3, 4, 90, 8, 4, 1, 4, 3, 4, 93, 8, 4, 1, 4, 1, 4, 3, 4, 97, 8, 4, 1, 4, 3, 4, 100, 8, 4, 1, 4, 1, 4, 3, 4, 104, 8, 4, 1, 4, 3, 4, 107, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 113, 8, 4, 1, 4, 3, 4, 116, 8, 4, 3, 4, 118, 8, 4, 1, 5, 1, 5, 1, 5, 5, 5, 123, 8, 5, 10, 5, 12, 5, 126, 9, 5, 1, 6, 1, 6, 3, 6, 130, 8, 6, 1, 6, 3, 6, 133, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 141, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 149, 8, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 157, 8, 7, 10, 7, 12, 7, 160, 9, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 177, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 193, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 207, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 219, 8, 13, 10, 13, 12, 13, 222, 9, 13, 1, 14, 1, 14, 3, 14, 226, 8, 14, 1, 15, 1, 15, 1, 15, 5, 15, 231, 8, 15, 10, 15, 12, 15, 234, 9, 15, 1, 16, 1, 16, 1, 16, 5, 16, 239, 8, 16, 10, 16, 12, 16, 242, 9, 16, 1, 17, 1, 17, 3, 17, 246, 8, 17, 1, 17, 1, 17, 3, 17, 250, 8, 17, 3, 17, 252, 8, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 0, 1, 14, 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 0, 3, 2, 0, 22, 26, 44, 44, 1, 0, 14, 15, 1, 0, 41, 42, 289, 0, 42, 1, 0, 0, 0, 2, 51, 1, 0, 0, 0, 4, 53, 1, 0, 0, 0, 6, 79, 1, 0, 0, 0, 8, 117, 1, 0, 0, 0, 10, 119, 1, 0, 0, 0, 12, 140, 1, 0, 0, 0, 14, 148, 1, 0, 0, 0, 16, 176, 1, 0, 0, 0, 18, 192, 1, 0, 0, 0, 20, 206, 1, 0, 0, 0, 22, 208, 1, 0, 0, 0, 24, 213, 1, 0, 0, 0, 26, 215, 1, 0, 0, 0, 28, 225, 1, 0, 0, 0, 30, 227, 1, 0, 0, 0, 32, 235, 1, 0, 0, 0, 34, 251, 1, 0, 0, 0, 36, 253, 1, 0, 0, 0, 38, 255, 1, 0, 0, 0, 40, 257, 1, 0, 0, 0, 42, 43, 3, 2, 1, 0, 43, 44, 5, 0, 0, 1, 44, 1, 1, 0, 0, 0, 45, 52, 3, 4, 2, 0, 46, 47, 5, 27, 0, 0, 47, 48, 5, 34, 0, 0, 48, 49, 3, 40, 20, 0, 49, 50, 5, 35, 0, 0, 50, 52, 1, 0, 0, 0, 51, 45, 1, 0, 0, 0, 51, 46, 1, 0, 0, 0, 52, 3, 1, 0, 0, 0, 53, 54, 5, 4, 0, 0, 54, 55, 3, 6, 3, 0, 55, 56, 5, 5, 0, 0, 56, 59, 3, 10, 5, 0, 57, 58, 5, 6, 0, 0, 58, 60, 3, 14, 7, 0, 59, 57, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 64, 1, 0, 0, 0, 61, 62, 5, 7, 0, 0, 62, 63, 5, 8, 0, 0, 63, 65, 3, 30, 15, 0, 64, 61, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 68, 1, 0, 0, 0, 66, 67, 5, 9, 0, 0, 67, 69, 3, 14, 7, 0, 68, 66, 1, 0, 0, 0, 68, 69, 1, 0, 0, 0, 69, 73, 1, 0, 0, 0, 70, 71, 5, 10, 0, 0, 71, 72, 5, 8, 0, 0, 72, 74, 3, 32, 16, 0, 73, 70, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 77, 1, 0, 0, 0, 75, 76, 5, 11, 0, 0, 76, 78, 3, 36, 18, 0, 77, 75, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 5, 1, 0, 0, 0, 79, 84, 3, 8, 4, 0, 80, 81, 5, 38, 0, 0, 81, 83, 3, 8, 4, 0, 82, 80, 1, 0, 0, 0, 83, 86, 1, 0, 0, 0, 84, 82, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 84, 1, 0, 0, 0, 87, 92, 3, 22, 11, 0, 88, 90, 5, 13, 0, 0, 89, 88, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 93, 5, 44, 0, 0, 92, 89, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 118, 1, 0, 0, 0, 94, 99, 5, 44, 0, 0, 95, 97, 5, 13, 0, 0, 96, 95, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 100, 5, 44, 0, 0, 99, 96, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 118, 1, 0, 0, 0, 101, 106, 5, 21, 0, 0, 102, 104, 5, 13, 0, 0, 103, 102, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 107, 5, 44, 0, 0, 106, 103, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 118, 1, 0, 0, 0, 108, 109, 5, 44, 0, 0, 109, 110, 5, 39, 0, 0, 110, 115, 5, 44, 0, 0, 111, 113, 5, 13, 0, 0, 112, 111, 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 1, 0, 0, 0, 114, 116, 5, 44, 0, 0, 115, 112, 1, 0, 0, 0, 115, 116, 1, 0, 0, 0, 116, 118, 1, 0, 0, 0, 117, 87, 1, 0, 0, 0, 117, 94, 1, 0, 0, 0, 117, 101, 1, 0, 0, 0, 117, 108, 1, 0, 0, 0, 118, 9, 1, 0, 0, 0, 119, 124, 3, 12, 6, 0, 120, 121, 5, 38, 0, 0, 121, 123, 3, 12, 6, 0, 122, 120, 1, 0, 0, 0, 123, 126, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 124, 125, 1, 0, 0, 0, 125, 11, 1, 0, 0, 0, 126, 124, 1, 0, 0, 0, 127, 132, 5, 44, 0, 0, 128, 130, 5, 13, 0, 0, 129, 128, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 131, 1, 0, 0, 0, 131, 133, 5, 44, 0, 0, 132, 129, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 141, 1, 0, 0, 0, 134, 135, 5, 34, 0, 0, 135, 136, 3, 4, 2, 0, 136, 137, 5, 35, 0, 0, 137, 138, 5, 13, 0, 0, 138, 139, 5, 44, 0, 0, 139, 141, 1, 0, 0, 0, 140, 127, 1, 0, 0, 0, 140, 134, 1, 0, 0, 0, 141, 13, 1, 0, 0, 0, 142, 143, 6, 7, -1, 0, 143, 149, 3, 18, 9, 0, 144, 145, 5, 34, 0, 0, 145, 146, 3, 14, 7, 0, 146, 147, 5, 35, 0, 0, 147, 149, 1, 0, 0, 0, 148, 142, 1, 0, 0, 0, 148, 144, 1, 0, 0, 0, 149, 158, 1, 0, 0, 0, 150, 151, 10, 3, 0, 0, 151, 152, 5, 1, 0, 0, 152, 157, 3, 14, 7, 4, 153, 154, 10, 2, 0, 0, 154, 155, 5, 2, 0, 0, 155, 157, 3, 14, 7, 3, 156, 150, 1, 0, 0, 0, 156, 153, 1, 0, 0, 0, 157, 160, 1, 0, 0, 0, 158, 156, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 15, 1, 0, 0, 0, 160, 158, 1, 0, 0, 0, 161, 177, 5, 32, 0, 0, 162, 177, 5, 30, 0, 0, 163, 177, 5, 28, 0, 0, 164, 177, 5, 31, 0, 0, 165, 177, 5, 29, 0, 0, 166, 177, 5, 33, 0, 0, 167, 177, 5, 19, 0, 0, 168, 169, 5, 3, 0, 0, 169, 177, 5, 19, 0, 0, 170, 177, 5, 16, 0, 0, 171, 172, 5, 3, 0, 0, 172, 177, 5, 16, 0, 0, 173, 177, 5, 17, 0, 0, 174, 175, 5, 17, 0, 0, 175, 177, 5, 3, 0, 0, 176, 161, 1, 0, 0, 0, 176, 162, 1, 0, 0, 0, 176, 163, 1, 0, 0, 0, 176, 164, 1, 0, 0, 0, 176, 165, 1, 0, 0, 0, 176, 166, 1, 0, 0, 0, 176, 167, 1, 0, 0, 0, 176, 168, 1, 0, 0, 0, 176, 170, 1, 0, 0, 0, 176, 171, 1, 0, 0, 0, 176, 173, 1, 0, 0, 0, 176, 174, 1, 0, 0, 0, 177, 17, 1, 0, 0, 0, 178, 179, 3, 20, 10, 0, 179, 180, 3, 16, 8, 0, 180, 181, 3, 20, 10, 0, 181, 193, 1, 0, 0, 0, 182, 183, 5, 34, 0, 0, 183, 184, 3, 18, 9, 0, 184, 185, 5, 35, 0, 0, 185, 193, 1, 0, 0, 0, 186, 187, 5, 44, 0, 0, 187, 188, 5, 20, 0, 0, 188, 189, 3, 38, 19, 0, 189, 190, 5, 1, 0, 0, 190, 191, 3, 38, 19, 0, 191, 193, 1, 0, 0, 0, 192, 178, 1, 0, 0, 0, 192, 182, 1, 0, 0, 0, 192, 186, 1, 0, 0, 0, 193, 19, 1, 0, 0, 0, 194, 207, 3, 38, 19, 0, 195, 207, 3, 40, 20, 0, 196, 207, 5, 44, 0, 0, 197, 198, 5, 44, 0, 0, 198, 199, 5, 39, 0, 0, 199, 207, 5, 44, 0, 0, 200, 207, 5, 18, 0, 0, 201, 202, 5, 34, 0, 0, 202, 203, 3, 4, 2, 0, 203, 204, 5, 35, 0, 0, 204, 207, 1, 0, 0, 0, 205, 207, 3, 22, 11, 0, 206, 194, 1, 0, 0, 0, 206, 195, 1, 0, 0, 0, 206, 196, 1, 0, 0, 0, 206, 197, 1, 0, 0, 0, 206, 200, 1, 0, 0, 0, 206, 201, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 21, 1, 0, 0, 0, 208, 209, 3, 24, 12, 0, 209, 210, 5, 34, 0, 0, 210, 211, 3, 26, 13, 0, 211, 212, 5, 35, 0, 0, 212, 23, 1, 0, 0, 0, 213, 214, 7, 0, 0, 0, 214, 25, 1, 0, 0, 0, 215, 220, 3, 28, 14, 0, 216, 217, 5, 38, 0, 0, 217, 219, 3, 28, 14, 0, 218, 216, 1, 0, 0, 0, 219, 222, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 27, 1, 0, 0, 0, 222, 220, 1, 0, 0, 0, 223, 226, 5, 21, 0, 0, 224, 226, 3, 40, 20, 0, 225, 223, 1, 0, 0, 0, 225, 224, 1, 0, 0, 0, 226, 29, 1, 0, 0, 0, 227, 232, 5, 44, 0, 0, 228, 229, 5, 38, 0, 0, 229, 231, 5, 44, 0, 0, 230, 228, 1, 0, 0, 0, 231, 234, 1, 0, 0, 0, 232, 230, 1, 0, 0, 0, 232, 233, 1, 0, 0, 0, 233, 31, 1, 0, 0, 0, 234, 232, 1, 0, 0, 0, 235, 240, 3, 34, 17, 0, 236, 237, 5, 38, 0, 0, 237, 239, 3, 34, 17, 0, 238, 236, 1, 0, 0, 0, 239, 242, 1, 0, 0, 0, 240, 238, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 33, 1, 0, 0, 0, 242, 240, 1, 0, 0, 0, 243, 245, 5, 44, 0, 0, 244, 246, 7, 1, 0, 0, 245, 244, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 252, 1, 0, 0, 0, 247, 249, 3, 22, 11, 0, 248, 250, 7, 1, 0, 0, 249, 248, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 252, 1, 0, 0, 0, 251, 243, 1, 0, 0, 0, 251, 247, 1, 0, 0, 0, 252, 35, 1, 0, 0, 0, 253, 254, 5, 42, 0, 0, 254, 37, 1, 0, 0, 0, 255, 256, 7, 2, 0, 0, 256, 39, 1, 0, 0, 0, 257, 258, 5, 43, 0, 0, 258, 41, 1, 0, 0, 0, 33, 51, 59, 64, 68, 73, 77, 84, 89, 92, 96, 99, 103, 106, 112, 115, 117, 124, 129, 132, 140, 148, 156, 158, 176, 192, 206, 220, 225, 232, 240, 245, 249, 251] \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpression.tokens b/hertzbeat-alerter/gen/expr/SqlExpression.tokens new file mode 100644 index 00000000000..5d6784e6666 --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpression.tokens @@ -0,0 +1,60 @@ +AND=1 +OR=2 +NOT=3 +SELECT=4 +FROM=5 +WHERE=6 +GROUP=7 +BY=8 +HAVING=9 +ORDER=10 +LIMIT=11 +OFFSET=12 +AS=13 +ASC=14 +DESC=15 +IN=16 +IS=17 +NULL=18 +LIKE=19 +BETWEEN=20 +STAR=21 +COUNT=22 +SUM=23 +AVG=24 +MIN=25 +MAX=26 +SQL_FUNCTION=27 +GT=28 +GE=29 +LT=30 +LE=31 +EQ=32 +NE=33 +LPAREN=34 +RPAREN=35 +LBRACKET=36 +RBRACKET=37 +COMMA=38 +DOT=39 +SEMICOLON=40 +FLOAT=41 +NUMBER=42 +STRING=43 +IDENTIFIER=44 +WS=45 +LINE_COMMENT=46 +BLOCK_COMMENT=47 +'*'=21 +'>'=28 +'>='=29 +'<'=30 +'<='=31 +'!='=33 +'('=34 +')'=35 +'['=36 +']'=37 +','=38 +'.'=39 +';'=40 diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java b/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java new file mode 100644 index 00000000000..025be9e1b7b --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java @@ -0,0 +1,304 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link SqlExpressionListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +@SuppressWarnings("CheckReturnValue") +public class SqlExpressionBaseListener implements SqlExpressionListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpression(SqlExpressionParser.ExpressionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpression(SqlExpressionParser.ExpressionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectField(SqlExpressionParser.SelectFieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectField(SqlExpressionParser.SelectFieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRelList(SqlExpressionParser.RelListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRelList(SqlExpressionParser.RelListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRelation(SqlExpressionParser.RelationContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRelation(SqlExpressionParser.RelationContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterConditionList(SqlExpressionParser.ConditionListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitConditionList(SqlExpressionParser.ConditionListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCompOp(SqlExpressionParser.CompOpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCompOp(SqlExpressionParser.CompOpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCondition(SqlExpressionParser.ConditionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCondition(SqlExpressionParser.ConditionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParameterList(SqlExpressionParser.ParameterListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParameterList(SqlExpressionParser.ParameterListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParameter(SqlExpressionParser.ParameterContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParameter(SqlExpressionParser.ParameterContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterGroupByList(SqlExpressionParser.GroupByListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitGroupByList(SqlExpressionParser.GroupByListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOrderByList(SqlExpressionParser.OrderByListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOrderByList(SqlExpressionParser.OrderByListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNumber(SqlExpressionParser.NumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNumber(SqlExpressionParser.NumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterString(SqlExpressionParser.StringContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitString(SqlExpressionParser.StringContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java b/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java new file mode 100644 index 00000000000..27049707de2 --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java @@ -0,0 +1,169 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link SqlExpressionVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +@SuppressWarnings("CheckReturnValue") +public class SqlExpressionBaseVisitor extends AbstractParseTreeVisitor implements SqlExpressionVisitor { + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpression(SqlExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectField(SqlExpressionParser.SelectFieldContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitRelList(SqlExpressionParser.RelListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitRelation(SqlExpressionParser.RelationContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitConditionList(SqlExpressionParser.ConditionListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitCompOp(SqlExpressionParser.CompOpContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitCondition(SqlExpressionParser.ConditionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParameterList(SqlExpressionParser.ParameterListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParameter(SqlExpressionParser.ParameterContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitGroupByList(SqlExpressionParser.GroupByListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOrderByList(SqlExpressionParser.OrderByListContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNumber(SqlExpressionParser.NumberContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitString(SqlExpressionParser.StringContext ctx) { return visitChildren(ctx); } +} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp new file mode 100644 index 00000000000..b5fc335057f --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp @@ -0,0 +1,158 @@ +token literal names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +'*' +null +null +null +null +null +null +'>' +'>=' +'<' +'<=' +null +'!=' +'(' +')' +'[' +']' +',' +'.' +';' +null +null +null +null +null +null +null + +token symbolic names: +null +AND +OR +NOT +SELECT +FROM +WHERE +GROUP +BY +HAVING +ORDER +LIMIT +OFFSET +AS +ASC +DESC +IN +IS +NULL +LIKE +BETWEEN +STAR +COUNT +SUM +AVG +MIN +MAX +SQL_FUNCTION +GT +GE +LT +LE +EQ +NE +LPAREN +RPAREN +LBRACKET +RBRACKET +COMMA +DOT +SEMICOLON +FLOAT +NUMBER +STRING +IDENTIFIER +WS +LINE_COMMENT +BLOCK_COMMENT + +rule names: +AND +OR +NOT +SELECT +FROM +WHERE +GROUP +BY +HAVING +ORDER +LIMIT +OFFSET +AS +ASC +DESC +IN +IS +NULL +LIKE +BETWEEN +STAR +COUNT +SUM +AVG +MIN +MAX +SQL_FUNCTION +GT +GE +LT +LE +EQ +NE +LPAREN +RPAREN +LBRACKET +RBRACKET +COMMA +DOT +SEMICOLON +FLOAT +NUMBER +STRING +IDENTIFIER +WS +LINE_COMMENT +BLOCK_COMMENT + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 47, 331, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 3, 31, 236, 8, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 39, 1, 39, 1, 40, 4, 40, 256, 8, 40, 11, 40, 12, 40, 257, 1, 40, 1, 40, 4, 40, 262, 8, 40, 11, 40, 12, 40, 263, 1, 41, 4, 41, 267, 8, 41, 11, 41, 12, 41, 268, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 275, 8, 42, 10, 42, 12, 42, 278, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 285, 8, 42, 10, 42, 12, 42, 288, 9, 42, 1, 42, 3, 42, 291, 8, 42, 1, 43, 1, 43, 5, 43, 295, 8, 43, 10, 43, 12, 43, 298, 9, 43, 1, 44, 4, 44, 301, 8, 44, 11, 44, 12, 44, 302, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 5, 45, 311, 8, 45, 10, 45, 12, 45, 314, 9, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 5, 46, 322, 8, 46, 10, 46, 12, 46, 325, 9, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 323, 0, 47, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 1, 0, 31, 2, 0, 65, 65, 97, 97, 2, 0, 78, 78, 110, 110, 2, 0, 68, 68, 100, 100, 2, 0, 79, 79, 111, 111, 2, 0, 82, 82, 114, 114, 2, 0, 84, 84, 116, 116, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 76, 76, 108, 108, 2, 0, 67, 67, 99, 99, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 87, 87, 119, 119, 2, 0, 72, 72, 104, 104, 2, 0, 71, 71, 103, 103, 2, 0, 85, 85, 117, 117, 2, 0, 80, 80, 112, 112, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 2, 0, 86, 86, 118, 118, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 88, 88, 120, 120, 2, 0, 81, 81, 113, 113, 1, 0, 48, 57, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, 10, 10, 13, 13, 343, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 3, 99, 1, 0, 0, 0, 5, 102, 1, 0, 0, 0, 7, 106, 1, 0, 0, 0, 9, 113, 1, 0, 0, 0, 11, 118, 1, 0, 0, 0, 13, 124, 1, 0, 0, 0, 15, 130, 1, 0, 0, 0, 17, 133, 1, 0, 0, 0, 19, 140, 1, 0, 0, 0, 21, 146, 1, 0, 0, 0, 23, 152, 1, 0, 0, 0, 25, 159, 1, 0, 0, 0, 27, 162, 1, 0, 0, 0, 29, 166, 1, 0, 0, 0, 31, 171, 1, 0, 0, 0, 33, 174, 1, 0, 0, 0, 35, 177, 1, 0, 0, 0, 37, 182, 1, 0, 0, 0, 39, 187, 1, 0, 0, 0, 41, 195, 1, 0, 0, 0, 43, 197, 1, 0, 0, 0, 45, 203, 1, 0, 0, 0, 47, 207, 1, 0, 0, 0, 49, 211, 1, 0, 0, 0, 51, 215, 1, 0, 0, 0, 53, 218, 1, 0, 0, 0, 55, 222, 1, 0, 0, 0, 57, 224, 1, 0, 0, 0, 59, 227, 1, 0, 0, 0, 61, 229, 1, 0, 0, 0, 63, 235, 1, 0, 0, 0, 65, 237, 1, 0, 0, 0, 67, 240, 1, 0, 0, 0, 69, 242, 1, 0, 0, 0, 71, 244, 1, 0, 0, 0, 73, 246, 1, 0, 0, 0, 75, 248, 1, 0, 0, 0, 77, 250, 1, 0, 0, 0, 79, 252, 1, 0, 0, 0, 81, 255, 1, 0, 0, 0, 83, 266, 1, 0, 0, 0, 85, 290, 1, 0, 0, 0, 87, 292, 1, 0, 0, 0, 89, 300, 1, 0, 0, 0, 91, 306, 1, 0, 0, 0, 93, 317, 1, 0, 0, 0, 95, 96, 7, 0, 0, 0, 96, 97, 7, 1, 0, 0, 97, 98, 7, 2, 0, 0, 98, 2, 1, 0, 0, 0, 99, 100, 7, 3, 0, 0, 100, 101, 7, 4, 0, 0, 101, 4, 1, 0, 0, 0, 102, 103, 7, 1, 0, 0, 103, 104, 7, 3, 0, 0, 104, 105, 7, 5, 0, 0, 105, 6, 1, 0, 0, 0, 106, 107, 7, 6, 0, 0, 107, 108, 7, 7, 0, 0, 108, 109, 7, 8, 0, 0, 109, 110, 7, 7, 0, 0, 110, 111, 7, 9, 0, 0, 111, 112, 7, 5, 0, 0, 112, 8, 1, 0, 0, 0, 113, 114, 7, 10, 0, 0, 114, 115, 7, 4, 0, 0, 115, 116, 7, 3, 0, 0, 116, 117, 7, 11, 0, 0, 117, 10, 1, 0, 0, 0, 118, 119, 7, 12, 0, 0, 119, 120, 7, 13, 0, 0, 120, 121, 7, 7, 0, 0, 121, 122, 7, 4, 0, 0, 122, 123, 7, 7, 0, 0, 123, 12, 1, 0, 0, 0, 124, 125, 7, 14, 0, 0, 125, 126, 7, 4, 0, 0, 126, 127, 7, 3, 0, 0, 127, 128, 7, 15, 0, 0, 128, 129, 7, 16, 0, 0, 129, 14, 1, 0, 0, 0, 130, 131, 7, 17, 0, 0, 131, 132, 7, 18, 0, 0, 132, 16, 1, 0, 0, 0, 133, 134, 7, 13, 0, 0, 134, 135, 7, 0, 0, 0, 135, 136, 7, 19, 0, 0, 136, 137, 7, 20, 0, 0, 137, 138, 7, 1, 0, 0, 138, 139, 7, 14, 0, 0, 139, 18, 1, 0, 0, 0, 140, 141, 7, 3, 0, 0, 141, 142, 7, 4, 0, 0, 142, 143, 7, 2, 0, 0, 143, 144, 7, 7, 0, 0, 144, 145, 7, 4, 0, 0, 145, 20, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 20, 0, 0, 148, 149, 7, 11, 0, 0, 149, 150, 7, 20, 0, 0, 150, 151, 7, 5, 0, 0, 151, 22, 1, 0, 0, 0, 152, 153, 7, 3, 0, 0, 153, 154, 7, 10, 0, 0, 154, 155, 7, 10, 0, 0, 155, 156, 7, 6, 0, 0, 156, 157, 7, 7, 0, 0, 157, 158, 7, 5, 0, 0, 158, 24, 1, 0, 0, 0, 159, 160, 7, 0, 0, 0, 160, 161, 7, 6, 0, 0, 161, 26, 1, 0, 0, 0, 162, 163, 7, 0, 0, 0, 163, 164, 7, 6, 0, 0, 164, 165, 7, 9, 0, 0, 165, 28, 1, 0, 0, 0, 166, 167, 7, 2, 0, 0, 167, 168, 7, 7, 0, 0, 168, 169, 7, 6, 0, 0, 169, 170, 7, 9, 0, 0, 170, 30, 1, 0, 0, 0, 171, 172, 7, 20, 0, 0, 172, 173, 7, 1, 0, 0, 173, 32, 1, 0, 0, 0, 174, 175, 7, 20, 0, 0, 175, 176, 7, 6, 0, 0, 176, 34, 1, 0, 0, 0, 177, 178, 7, 1, 0, 0, 178, 179, 7, 15, 0, 0, 179, 180, 7, 8, 0, 0, 180, 181, 7, 8, 0, 0, 181, 36, 1, 0, 0, 0, 182, 183, 7, 8, 0, 0, 183, 184, 7, 20, 0, 0, 184, 185, 7, 21, 0, 0, 185, 186, 7, 7, 0, 0, 186, 38, 1, 0, 0, 0, 187, 188, 7, 17, 0, 0, 188, 189, 7, 7, 0, 0, 189, 190, 7, 5, 0, 0, 190, 191, 7, 12, 0, 0, 191, 192, 7, 7, 0, 0, 192, 193, 7, 7, 0, 0, 193, 194, 7, 1, 0, 0, 194, 40, 1, 0, 0, 0, 195, 196, 5, 42, 0, 0, 196, 42, 1, 0, 0, 0, 197, 198, 7, 9, 0, 0, 198, 199, 7, 3, 0, 0, 199, 200, 7, 15, 0, 0, 200, 201, 7, 1, 0, 0, 201, 202, 7, 5, 0, 0, 202, 44, 1, 0, 0, 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 15, 0, 0, 205, 206, 7, 11, 0, 0, 206, 46, 1, 0, 0, 0, 207, 208, 7, 0, 0, 0, 208, 209, 7, 19, 0, 0, 209, 210, 7, 14, 0, 0, 210, 48, 1, 0, 0, 0, 211, 212, 7, 11, 0, 0, 212, 213, 7, 20, 0, 0, 213, 214, 7, 1, 0, 0, 214, 50, 1, 0, 0, 0, 215, 216, 7, 0, 0, 0, 216, 217, 7, 22, 0, 0, 217, 52, 1, 0, 0, 0, 218, 219, 7, 6, 0, 0, 219, 220, 7, 23, 0, 0, 220, 221, 7, 8, 0, 0, 221, 54, 1, 0, 0, 0, 222, 223, 5, 62, 0, 0, 223, 56, 1, 0, 0, 0, 224, 225, 5, 62, 0, 0, 225, 226, 5, 61, 0, 0, 226, 58, 1, 0, 0, 0, 227, 228, 5, 60, 0, 0, 228, 60, 1, 0, 0, 0, 229, 230, 5, 60, 0, 0, 230, 231, 5, 61, 0, 0, 231, 62, 1, 0, 0, 0, 232, 233, 5, 61, 0, 0, 233, 236, 5, 61, 0, 0, 234, 236, 5, 61, 0, 0, 235, 232, 1, 0, 0, 0, 235, 234, 1, 0, 0, 0, 236, 64, 1, 0, 0, 0, 237, 238, 5, 33, 0, 0, 238, 239, 5, 61, 0, 0, 239, 66, 1, 0, 0, 0, 240, 241, 5, 40, 0, 0, 241, 68, 1, 0, 0, 0, 242, 243, 5, 41, 0, 0, 243, 70, 1, 0, 0, 0, 244, 245, 5, 91, 0, 0, 245, 72, 1, 0, 0, 0, 246, 247, 5, 93, 0, 0, 247, 74, 1, 0, 0, 0, 248, 249, 5, 44, 0, 0, 249, 76, 1, 0, 0, 0, 250, 251, 5, 46, 0, 0, 251, 78, 1, 0, 0, 0, 252, 253, 5, 59, 0, 0, 253, 80, 1, 0, 0, 0, 254, 256, 7, 24, 0, 0, 255, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 261, 5, 46, 0, 0, 260, 262, 7, 24, 0, 0, 261, 260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 82, 1, 0, 0, 0, 265, 267, 7, 24, 0, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 84, 1, 0, 0, 0, 270, 276, 5, 34, 0, 0, 271, 275, 8, 25, 0, 0, 272, 273, 5, 92, 0, 0, 273, 275, 9, 0, 0, 0, 274, 271, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 279, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 291, 5, 34, 0, 0, 280, 286, 5, 39, 0, 0, 281, 285, 8, 26, 0, 0, 282, 283, 5, 92, 0, 0, 283, 285, 9, 0, 0, 0, 284, 281, 1, 0, 0, 0, 284, 282, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 289, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 291, 5, 39, 0, 0, 290, 270, 1, 0, 0, 0, 290, 280, 1, 0, 0, 0, 291, 86, 1, 0, 0, 0, 292, 296, 7, 27, 0, 0, 293, 295, 7, 28, 0, 0, 294, 293, 1, 0, 0, 0, 295, 298, 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 88, 1, 0, 0, 0, 298, 296, 1, 0, 0, 0, 299, 301, 7, 29, 0, 0, 300, 299, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 302, 303, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 305, 6, 44, 0, 0, 305, 90, 1, 0, 0, 0, 306, 307, 5, 47, 0, 0, 307, 308, 5, 47, 0, 0, 308, 312, 1, 0, 0, 0, 309, 311, 8, 30, 0, 0, 310, 309, 1, 0, 0, 0, 311, 314, 1, 0, 0, 0, 312, 310, 1, 0, 0, 0, 312, 313, 1, 0, 0, 0, 313, 315, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 315, 316, 6, 45, 1, 0, 316, 92, 1, 0, 0, 0, 317, 318, 5, 47, 0, 0, 318, 319, 5, 42, 0, 0, 319, 323, 1, 0, 0, 0, 320, 322, 9, 0, 0, 0, 321, 320, 1, 0, 0, 0, 322, 325, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 323, 321, 1, 0, 0, 0, 324, 326, 1, 0, 0, 0, 325, 323, 1, 0, 0, 0, 326, 327, 5, 42, 0, 0, 327, 328, 5, 47, 0, 0, 328, 329, 1, 0, 0, 0, 329, 330, 6, 46, 1, 0, 330, 94, 1, 0, 0, 0, 14, 0, 235, 257, 263, 268, 274, 276, 284, 286, 290, 296, 302, 312, 323, 2, 0, 1, 0, 6, 0, 0] \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java new file mode 100644 index 00000000000..c2cd5f6fa8f --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java @@ -0,0 +1,348 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class SqlExpressionLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, + ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, + LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, + SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, + RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, + NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "'*'", null, null, + null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", + "')'", "'['", "']'", "','", "'.'", "';'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public SqlExpressionLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "SqlExpression.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\u0004\u0000/\u014b\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ + "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ + "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ + "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ + "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ + "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ + "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ + "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ + "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ + "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ + "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ + "!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+ + "&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007"+ + "+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0001\u0000\u0001\u0000\u0001"+ + "\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001"+ + "\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+ + "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001"+ + "\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005\u0001"+ + "\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001"+ + "\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ + "\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b"+ + "\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0001"+ + "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+ + "\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001"+ + "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001"+ + "\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+ + "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001"+ + "\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001"+ + "\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ + "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0001"+ + "\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001"+ + "\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+ + "\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e\u0001"+ + "\u001f\u0001\u001f\u0001\u001f\u0003\u001f\u00ec\b\u001f\u0001 \u0001"+ + " \u0001 \u0001!\u0001!\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001"+ + "%\u0001%\u0001&\u0001&\u0001\'\u0001\'\u0001(\u0004(\u0100\b(\u000b(\f"+ + "(\u0101\u0001(\u0001(\u0004(\u0106\b(\u000b(\f(\u0107\u0001)\u0004)\u010b"+ + "\b)\u000b)\f)\u010c\u0001*\u0001*\u0001*\u0001*\u0005*\u0113\b*\n*\f*"+ + "\u0116\t*\u0001*\u0001*\u0001*\u0001*\u0001*\u0005*\u011d\b*\n*\f*\u0120"+ + "\t*\u0001*\u0003*\u0123\b*\u0001+\u0001+\u0005+\u0127\b+\n+\f+\u012a\t"+ + "+\u0001,\u0004,\u012d\b,\u000b,\f,\u012e\u0001,\u0001,\u0001-\u0001-\u0001"+ + "-\u0001-\u0005-\u0137\b-\n-\f-\u013a\t-\u0001-\u0001-\u0001.\u0001.\u0001"+ + ".\u0001.\u0005.\u0142\b.\n.\f.\u0145\t.\u0001.\u0001.\u0001.\u0001.\u0001"+ + ".\u0001\u0143\u0000/\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t"+ + "\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f"+ + "\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014"+ + ")\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d;\u001e"+ + "=\u001f? A!C\"E#G$I%K&M\'O(Q)S*U+W,Y-[.]/\u0001\u0000\u001f\u0002\u0000"+ + "AAaa\u0002\u0000NNnn\u0002\u0000DDdd\u0002\u0000OOoo\u0002\u0000RRrr\u0002"+ + "\u0000TTtt\u0002\u0000SSss\u0002\u0000EEee\u0002\u0000LLll\u0002\u0000"+ + "CCcc\u0002\u0000FFff\u0002\u0000MMmm\u0002\u0000WWww\u0002\u0000HHhh\u0002"+ + "\u0000GGgg\u0002\u0000UUuu\u0002\u0000PPpp\u0002\u0000BBbb\u0002\u0000"+ + "YYyy\u0002\u0000VVvv\u0002\u0000IIii\u0002\u0000KKkk\u0002\u0000XXxx\u0002"+ + "\u0000QQqq\u0001\u000009\u0004\u0000\n\n\r\r\"\"\\\\\u0004\u0000\n\n\r"+ + "\r\'\'\\\\\u0003\u0000AZ__az\u0004\u000009AZ__az\u0003\u0000\t\n\r\r "+ + " \u0002\u0000\n\n\r\r\u0157\u0000\u0001\u0001\u0000\u0000\u0000\u0000"+ + "\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000"+ + "\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b"+ + "\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001"+ + "\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001"+ + "\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001"+ + "\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001"+ + "\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001"+ + "\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000"+ + "\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000"+ + "\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-"+ + "\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000"+ + "\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000"+ + "\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000\u0000\u0000\u0000;"+ + "\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0000?\u0001\u0000"+ + "\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C\u0001\u0000\u0000\u0000"+ + "\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000\u0000\u0000\u0000I"+ + "\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000\u0000M\u0001\u0000"+ + "\u0000\u0000\u0000O\u0001\u0000\u0000\u0000\u0000Q\u0001\u0000\u0000\u0000"+ + "\u0000S\u0001\u0000\u0000\u0000\u0000U\u0001\u0000\u0000\u0000\u0000W"+ + "\u0001\u0000\u0000\u0000\u0000Y\u0001\u0000\u0000\u0000\u0000[\u0001\u0000"+ + "\u0000\u0000\u0000]\u0001\u0000\u0000\u0000\u0001_\u0001\u0000\u0000\u0000"+ + "\u0003c\u0001\u0000\u0000\u0000\u0005f\u0001\u0000\u0000\u0000\u0007j"+ + "\u0001\u0000\u0000\u0000\tq\u0001\u0000\u0000\u0000\u000bv\u0001\u0000"+ + "\u0000\u0000\r|\u0001\u0000\u0000\u0000\u000f\u0082\u0001\u0000\u0000"+ + "\u0000\u0011\u0085\u0001\u0000\u0000\u0000\u0013\u008c\u0001\u0000\u0000"+ + "\u0000\u0015\u0092\u0001\u0000\u0000\u0000\u0017\u0098\u0001\u0000\u0000"+ + "\u0000\u0019\u009f\u0001\u0000\u0000\u0000\u001b\u00a2\u0001\u0000\u0000"+ + "\u0000\u001d\u00a6\u0001\u0000\u0000\u0000\u001f\u00ab\u0001\u0000\u0000"+ + "\u0000!\u00ae\u0001\u0000\u0000\u0000#\u00b1\u0001\u0000\u0000\u0000%"+ + "\u00b6\u0001\u0000\u0000\u0000\'\u00bb\u0001\u0000\u0000\u0000)\u00c3"+ + "\u0001\u0000\u0000\u0000+\u00c5\u0001\u0000\u0000\u0000-\u00cb\u0001\u0000"+ + "\u0000\u0000/\u00cf\u0001\u0000\u0000\u00001\u00d3\u0001\u0000\u0000\u0000"+ + "3\u00d7\u0001\u0000\u0000\u00005\u00da\u0001\u0000\u0000\u00007\u00de"+ + "\u0001\u0000\u0000\u00009\u00e0\u0001\u0000\u0000\u0000;\u00e3\u0001\u0000"+ + "\u0000\u0000=\u00e5\u0001\u0000\u0000\u0000?\u00eb\u0001\u0000\u0000\u0000"+ + "A\u00ed\u0001\u0000\u0000\u0000C\u00f0\u0001\u0000\u0000\u0000E\u00f2"+ + "\u0001\u0000\u0000\u0000G\u00f4\u0001\u0000\u0000\u0000I\u00f6\u0001\u0000"+ + "\u0000\u0000K\u00f8\u0001\u0000\u0000\u0000M\u00fa\u0001\u0000\u0000\u0000"+ + "O\u00fc\u0001\u0000\u0000\u0000Q\u00ff\u0001\u0000\u0000\u0000S\u010a"+ + "\u0001\u0000\u0000\u0000U\u0122\u0001\u0000\u0000\u0000W\u0124\u0001\u0000"+ + "\u0000\u0000Y\u012c\u0001\u0000\u0000\u0000[\u0132\u0001\u0000\u0000\u0000"+ + "]\u013d\u0001\u0000\u0000\u0000_`\u0007\u0000\u0000\u0000`a\u0007\u0001"+ + "\u0000\u0000ab\u0007\u0002\u0000\u0000b\u0002\u0001\u0000\u0000\u0000"+ + "cd\u0007\u0003\u0000\u0000de\u0007\u0004\u0000\u0000e\u0004\u0001\u0000"+ + "\u0000\u0000fg\u0007\u0001\u0000\u0000gh\u0007\u0003\u0000\u0000hi\u0007"+ + "\u0005\u0000\u0000i\u0006\u0001\u0000\u0000\u0000jk\u0007\u0006\u0000"+ + "\u0000kl\u0007\u0007\u0000\u0000lm\u0007\b\u0000\u0000mn\u0007\u0007\u0000"+ + "\u0000no\u0007\t\u0000\u0000op\u0007\u0005\u0000\u0000p\b\u0001\u0000"+ + "\u0000\u0000qr\u0007\n\u0000\u0000rs\u0007\u0004\u0000\u0000st\u0007\u0003"+ + "\u0000\u0000tu\u0007\u000b\u0000\u0000u\n\u0001\u0000\u0000\u0000vw\u0007"+ + "\f\u0000\u0000wx\u0007\r\u0000\u0000xy\u0007\u0007\u0000\u0000yz\u0007"+ + "\u0004\u0000\u0000z{\u0007\u0007\u0000\u0000{\f\u0001\u0000\u0000\u0000"+ + "|}\u0007\u000e\u0000\u0000}~\u0007\u0004\u0000\u0000~\u007f\u0007\u0003"+ + "\u0000\u0000\u007f\u0080\u0007\u000f\u0000\u0000\u0080\u0081\u0007\u0010"+ + "\u0000\u0000\u0081\u000e\u0001\u0000\u0000\u0000\u0082\u0083\u0007\u0011"+ + "\u0000\u0000\u0083\u0084\u0007\u0012\u0000\u0000\u0084\u0010\u0001\u0000"+ + "\u0000\u0000\u0085\u0086\u0007\r\u0000\u0000\u0086\u0087\u0007\u0000\u0000"+ + "\u0000\u0087\u0088\u0007\u0013\u0000\u0000\u0088\u0089\u0007\u0014\u0000"+ + "\u0000\u0089\u008a\u0007\u0001\u0000\u0000\u008a\u008b\u0007\u000e\u0000"+ + "\u0000\u008b\u0012\u0001\u0000\u0000\u0000\u008c\u008d\u0007\u0003\u0000"+ + "\u0000\u008d\u008e\u0007\u0004\u0000\u0000\u008e\u008f\u0007\u0002\u0000"+ + "\u0000\u008f\u0090\u0007\u0007\u0000\u0000\u0090\u0091\u0007\u0004\u0000"+ + "\u0000\u0091\u0014\u0001\u0000\u0000\u0000\u0092\u0093\u0007\b\u0000\u0000"+ + "\u0093\u0094\u0007\u0014\u0000\u0000\u0094\u0095\u0007\u000b\u0000\u0000"+ + "\u0095\u0096\u0007\u0014\u0000\u0000\u0096\u0097\u0007\u0005\u0000\u0000"+ + "\u0097\u0016\u0001\u0000\u0000\u0000\u0098\u0099\u0007\u0003\u0000\u0000"+ + "\u0099\u009a\u0007\n\u0000\u0000\u009a\u009b\u0007\n\u0000\u0000\u009b"+ + "\u009c\u0007\u0006\u0000\u0000\u009c\u009d\u0007\u0007\u0000\u0000\u009d"+ + "\u009e\u0007\u0005\u0000\u0000\u009e\u0018\u0001\u0000\u0000\u0000\u009f"+ + "\u00a0\u0007\u0000\u0000\u0000\u00a0\u00a1\u0007\u0006\u0000\u0000\u00a1"+ + "\u001a\u0001\u0000\u0000\u0000\u00a2\u00a3\u0007\u0000\u0000\u0000\u00a3"+ + "\u00a4\u0007\u0006\u0000\u0000\u00a4\u00a5\u0007\t\u0000\u0000\u00a5\u001c"+ + "\u0001\u0000\u0000\u0000\u00a6\u00a7\u0007\u0002\u0000\u0000\u00a7\u00a8"+ + "\u0007\u0007\u0000\u0000\u00a8\u00a9\u0007\u0006\u0000\u0000\u00a9\u00aa"+ + "\u0007\t\u0000\u0000\u00aa\u001e\u0001\u0000\u0000\u0000\u00ab\u00ac\u0007"+ + "\u0014\u0000\u0000\u00ac\u00ad\u0007\u0001\u0000\u0000\u00ad \u0001\u0000"+ + "\u0000\u0000\u00ae\u00af\u0007\u0014\u0000\u0000\u00af\u00b0\u0007\u0006"+ + "\u0000\u0000\u00b0\"\u0001\u0000\u0000\u0000\u00b1\u00b2\u0007\u0001\u0000"+ + "\u0000\u00b2\u00b3\u0007\u000f\u0000\u0000\u00b3\u00b4\u0007\b\u0000\u0000"+ + "\u00b4\u00b5\u0007\b\u0000\u0000\u00b5$\u0001\u0000\u0000\u0000\u00b6"+ + "\u00b7\u0007\b\u0000\u0000\u00b7\u00b8\u0007\u0014\u0000\u0000\u00b8\u00b9"+ + "\u0007\u0015\u0000\u0000\u00b9\u00ba\u0007\u0007\u0000\u0000\u00ba&\u0001"+ + "\u0000\u0000\u0000\u00bb\u00bc\u0007\u0011\u0000\u0000\u00bc\u00bd\u0007"+ + "\u0007\u0000\u0000\u00bd\u00be\u0007\u0005\u0000\u0000\u00be\u00bf\u0007"+ + "\f\u0000\u0000\u00bf\u00c0\u0007\u0007\u0000\u0000\u00c0\u00c1\u0007\u0007"+ + "\u0000\u0000\u00c1\u00c2\u0007\u0001\u0000\u0000\u00c2(\u0001\u0000\u0000"+ + "\u0000\u00c3\u00c4\u0005*\u0000\u0000\u00c4*\u0001\u0000\u0000\u0000\u00c5"+ + "\u00c6\u0007\t\u0000\u0000\u00c6\u00c7\u0007\u0003\u0000\u0000\u00c7\u00c8"+ + "\u0007\u000f\u0000\u0000\u00c8\u00c9\u0007\u0001\u0000\u0000\u00c9\u00ca"+ + "\u0007\u0005\u0000\u0000\u00ca,\u0001\u0000\u0000\u0000\u00cb\u00cc\u0007"+ + "\u0006\u0000\u0000\u00cc\u00cd\u0007\u000f\u0000\u0000\u00cd\u00ce\u0007"+ + "\u000b\u0000\u0000\u00ce.\u0001\u0000\u0000\u0000\u00cf\u00d0\u0007\u0000"+ + "\u0000\u0000\u00d0\u00d1\u0007\u0013\u0000\u0000\u00d1\u00d2\u0007\u000e"+ + "\u0000\u0000\u00d20\u0001\u0000\u0000\u0000\u00d3\u00d4\u0007\u000b\u0000"+ + "\u0000\u00d4\u00d5\u0007\u0014\u0000\u0000\u00d5\u00d6\u0007\u0001\u0000"+ + "\u0000\u00d62\u0001\u0000\u0000\u0000\u00d7\u00d8\u0007\u0000\u0000\u0000"+ + "\u00d8\u00d9\u0007\u0016\u0000\u0000\u00d94\u0001\u0000\u0000\u0000\u00da"+ + "\u00db\u0007\u0006\u0000\u0000\u00db\u00dc\u0007\u0017\u0000\u0000\u00dc"+ + "\u00dd\u0007\b\u0000\u0000\u00dd6\u0001\u0000\u0000\u0000\u00de\u00df"+ + "\u0005>\u0000\u0000\u00df8\u0001\u0000\u0000\u0000\u00e0\u00e1\u0005>"+ + "\u0000\u0000\u00e1\u00e2\u0005=\u0000\u0000\u00e2:\u0001\u0000\u0000\u0000"+ + "\u00e3\u00e4\u0005<\u0000\u0000\u00e4<\u0001\u0000\u0000\u0000\u00e5\u00e6"+ + "\u0005<\u0000\u0000\u00e6\u00e7\u0005=\u0000\u0000\u00e7>\u0001\u0000"+ + "\u0000\u0000\u00e8\u00e9\u0005=\u0000\u0000\u00e9\u00ec\u0005=\u0000\u0000"+ + "\u00ea\u00ec\u0005=\u0000\u0000\u00eb\u00e8\u0001\u0000\u0000\u0000\u00eb"+ + "\u00ea\u0001\u0000\u0000\u0000\u00ec@\u0001\u0000\u0000\u0000\u00ed\u00ee"+ + "\u0005!\u0000\u0000\u00ee\u00ef\u0005=\u0000\u0000\u00efB\u0001\u0000"+ + "\u0000\u0000\u00f0\u00f1\u0005(\u0000\u0000\u00f1D\u0001\u0000\u0000\u0000"+ + "\u00f2\u00f3\u0005)\u0000\u0000\u00f3F\u0001\u0000\u0000\u0000\u00f4\u00f5"+ + "\u0005[\u0000\u0000\u00f5H\u0001\u0000\u0000\u0000\u00f6\u00f7\u0005]"+ + "\u0000\u0000\u00f7J\u0001\u0000\u0000\u0000\u00f8\u00f9\u0005,\u0000\u0000"+ + "\u00f9L\u0001\u0000\u0000\u0000\u00fa\u00fb\u0005.\u0000\u0000\u00fbN"+ + "\u0001\u0000\u0000\u0000\u00fc\u00fd\u0005;\u0000\u0000\u00fdP\u0001\u0000"+ + "\u0000\u0000\u00fe\u0100\u0007\u0018\u0000\u0000\u00ff\u00fe\u0001\u0000"+ + "\u0000\u0000\u0100\u0101\u0001\u0000\u0000\u0000\u0101\u00ff\u0001\u0000"+ + "\u0000\u0000\u0101\u0102\u0001\u0000\u0000\u0000\u0102\u0103\u0001\u0000"+ + "\u0000\u0000\u0103\u0105\u0005.\u0000\u0000\u0104\u0106\u0007\u0018\u0000"+ + "\u0000\u0105\u0104\u0001\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000"+ + "\u0000\u0107\u0105\u0001\u0000\u0000\u0000\u0107\u0108\u0001\u0000\u0000"+ + "\u0000\u0108R\u0001\u0000\u0000\u0000\u0109\u010b\u0007\u0018\u0000\u0000"+ + "\u010a\u0109\u0001\u0000\u0000\u0000\u010b\u010c\u0001\u0000\u0000\u0000"+ + "\u010c\u010a\u0001\u0000\u0000\u0000\u010c\u010d\u0001\u0000\u0000\u0000"+ + "\u010dT\u0001\u0000\u0000\u0000\u010e\u0114\u0005\"\u0000\u0000\u010f"+ + "\u0113\b\u0019\u0000\u0000\u0110\u0111\u0005\\\u0000\u0000\u0111\u0113"+ + "\t\u0000\u0000\u0000\u0112\u010f\u0001\u0000\u0000\u0000\u0112\u0110\u0001"+ + "\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000\u0114\u0112\u0001"+ + "\u0000\u0000\u0000\u0114\u0115\u0001\u0000\u0000\u0000\u0115\u0117\u0001"+ + "\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u0123\u0005"+ + "\"\u0000\u0000\u0118\u011e\u0005\'\u0000\u0000\u0119\u011d\b\u001a\u0000"+ + "\u0000\u011a\u011b\u0005\\\u0000\u0000\u011b\u011d\t\u0000\u0000\u0000"+ + "\u011c\u0119\u0001\u0000\u0000\u0000\u011c\u011a\u0001\u0000\u0000\u0000"+ + "\u011d\u0120\u0001\u0000\u0000\u0000\u011e\u011c\u0001\u0000\u0000\u0000"+ + "\u011e\u011f\u0001\u0000\u0000\u0000\u011f\u0121\u0001\u0000\u0000\u0000"+ + "\u0120\u011e\u0001\u0000\u0000\u0000\u0121\u0123\u0005\'\u0000\u0000\u0122"+ + "\u010e\u0001\u0000\u0000\u0000\u0122\u0118\u0001\u0000\u0000\u0000\u0123"+ + "V\u0001\u0000\u0000\u0000\u0124\u0128\u0007\u001b\u0000\u0000\u0125\u0127"+ + "\u0007\u001c\u0000\u0000\u0126\u0125\u0001\u0000\u0000\u0000\u0127\u012a"+ + "\u0001\u0000\u0000\u0000\u0128\u0126\u0001\u0000\u0000\u0000\u0128\u0129"+ + "\u0001\u0000\u0000\u0000\u0129X\u0001\u0000\u0000\u0000\u012a\u0128\u0001"+ + "\u0000\u0000\u0000\u012b\u012d\u0007\u001d\u0000\u0000\u012c\u012b\u0001"+ + "\u0000\u0000\u0000\u012d\u012e\u0001\u0000\u0000\u0000\u012e\u012c\u0001"+ + "\u0000\u0000\u0000\u012e\u012f\u0001\u0000\u0000\u0000\u012f\u0130\u0001"+ + "\u0000\u0000\u0000\u0130\u0131\u0006,\u0000\u0000\u0131Z\u0001\u0000\u0000"+ + "\u0000\u0132\u0133\u0005/\u0000\u0000\u0133\u0134\u0005/\u0000\u0000\u0134"+ + "\u0138\u0001\u0000\u0000\u0000\u0135\u0137\b\u001e\u0000\u0000\u0136\u0135"+ + "\u0001\u0000\u0000\u0000\u0137\u013a\u0001\u0000\u0000\u0000\u0138\u0136"+ + "\u0001\u0000\u0000\u0000\u0138\u0139\u0001\u0000\u0000\u0000\u0139\u013b"+ + "\u0001\u0000\u0000\u0000\u013a\u0138\u0001\u0000\u0000\u0000\u013b\u013c"+ + "\u0006-\u0001\u0000\u013c\\\u0001\u0000\u0000\u0000\u013d\u013e\u0005"+ + "/\u0000\u0000\u013e\u013f\u0005*\u0000\u0000\u013f\u0143\u0001\u0000\u0000"+ + "\u0000\u0140\u0142\t\u0000\u0000\u0000\u0141\u0140\u0001\u0000\u0000\u0000"+ + "\u0142\u0145\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+ + "\u0143\u0141\u0001\u0000\u0000\u0000\u0144\u0146\u0001\u0000\u0000\u0000"+ + "\u0145\u0143\u0001\u0000\u0000\u0000\u0146\u0147\u0005*\u0000\u0000\u0147"+ + "\u0148\u0005/\u0000\u0000\u0148\u0149\u0001\u0000\u0000\u0000\u0149\u014a"+ + "\u0006.\u0001\u0000\u014a^\u0001\u0000\u0000\u0000\u000e\u0000\u00eb\u0101"+ + "\u0107\u010c\u0112\u0114\u011c\u011e\u0122\u0128\u012e\u0138\u0143\u0002"+ + "\u0000\u0001\u0000\u0006\u0000\u0000"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens new file mode 100644 index 00000000000..5d6784e6666 --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens @@ -0,0 +1,60 @@ +AND=1 +OR=2 +NOT=3 +SELECT=4 +FROM=5 +WHERE=6 +GROUP=7 +BY=8 +HAVING=9 +ORDER=10 +LIMIT=11 +OFFSET=12 +AS=13 +ASC=14 +DESC=15 +IN=16 +IS=17 +NULL=18 +LIKE=19 +BETWEEN=20 +STAR=21 +COUNT=22 +SUM=23 +AVG=24 +MIN=25 +MAX=26 +SQL_FUNCTION=27 +GT=28 +GE=29 +LT=30 +LE=31 +EQ=32 +NE=33 +LPAREN=34 +RPAREN=35 +LBRACKET=36 +RBRACKET=37 +COMMA=38 +DOT=39 +SEMICOLON=40 +FLOAT=41 +NUMBER=42 +STRING=43 +IDENTIFIER=44 +WS=45 +LINE_COMMENT=46 +BLOCK_COMMENT=47 +'*'=21 +'>'=28 +'>='=29 +'<'=30 +'<='=31 +'!='=33 +'('=34 +')'=35 +'['=36 +']'=37 +','=38 +'.'=39 +';'=40 diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionListener.java b/hertzbeat-alerter/gen/expr/SqlExpressionListener.java new file mode 100644 index 00000000000..638b3aae90f --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionListener.java @@ -0,0 +1,234 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link SqlExpressionParser}. + */ +public interface SqlExpressionListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterExpression(SqlExpressionParser.ExpressionContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitExpression(SqlExpressionParser.ExpressionContext ctx); + /** + * Enter a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + /** + * Exit a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + /** + * Enter a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + /** + * Exit a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + */ + void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + */ + void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + */ + void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + */ + void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + */ + void enterSelectField(SqlExpressionParser.SelectFieldContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + */ + void exitSelectField(SqlExpressionParser.SelectFieldContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + */ + void enterRelList(SqlExpressionParser.RelListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + */ + void exitRelList(SqlExpressionParser.RelListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + */ + void enterRelation(SqlExpressionParser.RelationContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + */ + void exitRelation(SqlExpressionParser.RelationContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + */ + void enterConditionList(SqlExpressionParser.ConditionListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + */ + void exitConditionList(SqlExpressionParser.ConditionListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + */ + void enterCompOp(SqlExpressionParser.CompOpContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + */ + void exitCompOp(SqlExpressionParser.CompOpContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + */ + void enterCondition(SqlExpressionParser.ConditionContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + */ + void exitCondition(SqlExpressionParser.ConditionContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + */ + void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + */ + void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + */ + void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + */ + void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + */ + void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + */ + void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + */ + void enterParameterList(SqlExpressionParser.ParameterListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + */ + void exitParameterList(SqlExpressionParser.ParameterListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + */ + void enterParameter(SqlExpressionParser.ParameterContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + */ + void exitParameter(SqlExpressionParser.ParameterContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + */ + void enterGroupByList(SqlExpressionParser.GroupByListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + */ + void exitGroupByList(SqlExpressionParser.GroupByListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + */ + void enterOrderByList(SqlExpressionParser.OrderByListContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + */ + void exitOrderByList(SqlExpressionParser.OrderByListContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + */ + void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + */ + void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + */ + void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + */ + void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + */ + void enterNumber(SqlExpressionParser.NumberContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + */ + void exitNumber(SqlExpressionParser.NumberContext ctx); + /** + * Enter a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + */ + void enterString(SqlExpressionParser.StringContext ctx); + /** + * Exit a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + */ + void exitString(SqlExpressionParser.StringContext ctx); +} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionParser.java b/hertzbeat-alerter/gen/expr/SqlExpressionParser.java new file mode 100644 index 00000000000..23fed448bf7 --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionParser.java @@ -0,0 +1,2100 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class SqlExpressionParser extends Parser { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, + ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, + LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, + SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, + RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, + NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; + public static final int + RULE_expression = 0, RULE_sqlExpr = 1, RULE_selectSql = 2, RULE_selectFieldList = 3, + RULE_selectField = 4, RULE_relList = 5, RULE_relation = 6, RULE_conditionList = 7, + RULE_compOp = 8, RULE_condition = 9, RULE_conditionUnit = 10, RULE_functionCall = 11, + RULE_functionName = 12, RULE_parameterList = 13, RULE_parameter = 14, + RULE_groupByList = 15, RULE_orderByList = 16, RULE_orderByField = 17, + RULE_limitClause = 18, RULE_number = 19, RULE_string = 20; + private static String[] makeRuleNames() { + return new String[] { + "expression", "sqlExpr", "selectSql", "selectFieldList", "selectField", + "relList", "relation", "conditionList", "compOp", "condition", "conditionUnit", + "functionCall", "functionName", "parameterList", "parameter", "groupByList", + "orderByList", "orderByField", "limitClause", "number", "string" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "'*'", null, null, + null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", + "')'", "'['", "']'", "','", "'.'", "';'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "SqlExpression.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public SqlExpressionParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @SuppressWarnings("CheckReturnValue") + public static class ExpressionContext extends ParserRuleContext { + public SqlExprContext sqlExpr() { + return getRuleContext(SqlExprContext.class,0); + } + public TerminalNode EOF() { return getToken(SqlExpressionParser.EOF, 0); } + public ExpressionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_expression; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterExpression(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitExpression(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitExpression(this); + else return visitor.visitChildren(this); + } + } + + public final ExpressionContext expression() throws RecognitionException { + ExpressionContext _localctx = new ExpressionContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_expression); + try { + enterOuterAlt(_localctx, 1); + { + setState(42); + sqlExpr(); + setState(43); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SqlExprContext extends ParserRuleContext { + public SqlExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_sqlExpr; } + + public SqlExprContext() { } + public void copyFrom(SqlExprContext ctx) { + super.copyFrom(ctx); + } + } + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlCallExprContext extends SqlExprContext { + public TerminalNode SQL_FUNCTION() { return getToken(SqlExpressionParser.SQL_FUNCTION, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public SelectSqlCallExprContext(SqlExprContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlCallExpr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlCallExpr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlCallExpr(this); + else return visitor.visitChildren(this); + } + } + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlExprContext extends SqlExprContext { + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public SelectSqlExprContext(SqlExprContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlExpr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlExpr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlExpr(this); + else return visitor.visitChildren(this); + } + } + + public final SqlExprContext sqlExpr() throws RecognitionException { + SqlExprContext _localctx = new SqlExprContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_sqlExpr); + try { + setState(51); + _errHandler.sync(this); + switch (_input.LA(1)) { + case SELECT: + _localctx = new SelectSqlExprContext(_localctx); + enterOuterAlt(_localctx, 1); + { + setState(45); + selectSql(); + } + break; + case SQL_FUNCTION: + _localctx = new SelectSqlCallExprContext(_localctx); + enterOuterAlt(_localctx, 2); + { + setState(46); + match(SQL_FUNCTION); + setState(47); + match(LPAREN); + setState(48); + string(); + setState(49); + match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlContext extends ParserRuleContext { + public TerminalNode SELECT() { return getToken(SqlExpressionParser.SELECT, 0); } + public SelectFieldListContext selectFieldList() { + return getRuleContext(SelectFieldListContext.class,0); + } + public TerminalNode FROM() { return getToken(SqlExpressionParser.FROM, 0); } + public RelListContext relList() { + return getRuleContext(RelListContext.class,0); + } + public TerminalNode WHERE() { return getToken(SqlExpressionParser.WHERE, 0); } + public List conditionList() { + return getRuleContexts(ConditionListContext.class); + } + public ConditionListContext conditionList(int i) { + return getRuleContext(ConditionListContext.class,i); + } + public TerminalNode GROUP() { return getToken(SqlExpressionParser.GROUP, 0); } + public List BY() { return getTokens(SqlExpressionParser.BY); } + public TerminalNode BY(int i) { + return getToken(SqlExpressionParser.BY, i); + } + public GroupByListContext groupByList() { + return getRuleContext(GroupByListContext.class,0); + } + public TerminalNode HAVING() { return getToken(SqlExpressionParser.HAVING, 0); } + public TerminalNode ORDER() { return getToken(SqlExpressionParser.ORDER, 0); } + public OrderByListContext orderByList() { + return getRuleContext(OrderByListContext.class,0); + } + public TerminalNode LIMIT() { return getToken(SqlExpressionParser.LIMIT, 0); } + public LimitClauseContext limitClause() { + return getRuleContext(LimitClauseContext.class,0); + } + public SelectSqlContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectSql; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSql(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSql(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSql(this); + else return visitor.visitChildren(this); + } + } + + public final SelectSqlContext selectSql() throws RecognitionException { + SelectSqlContext _localctx = new SelectSqlContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_selectSql); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(53); + match(SELECT); + setState(54); + selectFieldList(); + setState(55); + match(FROM); + setState(56); + relList(); + setState(59); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==WHERE) { + { + setState(57); + match(WHERE); + setState(58); + conditionList(0); + } + } + + setState(64); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==GROUP) { + { + setState(61); + match(GROUP); + setState(62); + match(BY); + setState(63); + groupByList(); + } + } + + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==HAVING) { + { + setState(66); + match(HAVING); + setState(67); + conditionList(0); + } + } + + setState(73); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ORDER) { + { + setState(70); + match(ORDER); + setState(71); + match(BY); + setState(72); + orderByList(); + } + } + + setState(77); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==LIMIT) { + { + setState(75); + match(LIMIT); + setState(76); + limitClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectFieldListContext extends ParserRuleContext { + public List selectField() { + return getRuleContexts(SelectFieldContext.class); + } + public SelectFieldContext selectField(int i) { + return getRuleContext(SelectFieldContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public SelectFieldListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectFieldList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectFieldList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectFieldList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectFieldList(this); + else return visitor.visitChildren(this); + } + } + + public final SelectFieldListContext selectFieldList() throws RecognitionException { + SelectFieldListContext _localctx = new SelectFieldListContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_selectFieldList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(79); + selectField(); + setState(84); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(80); + match(COMMA); + setState(81); + selectField(); + } + } + setState(86); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectFieldContext extends ParserRuleContext { + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } + public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } + public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } + public SelectFieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectField; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectField(this); + else return visitor.visitChildren(this); + } + } + + public final SelectFieldContext selectField() throws RecognitionException { + SelectFieldContext _localctx = new SelectFieldContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_selectField); + int _la; + try { + setState(117); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(87); + functionCall(); + setState(92); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(89); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(88); + match(AS); + } + } + + setState(91); + match(IDENTIFIER); + } + } + + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(94); + match(IDENTIFIER); + setState(99); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(96); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(95); + match(AS); + } + } + + setState(98); + match(IDENTIFIER); + } + } + + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(101); + match(STAR); + setState(106); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(103); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(102); + match(AS); + } + } + + setState(105); + match(IDENTIFIER); + } + } + + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(108); + match(IDENTIFIER); + setState(109); + match(DOT); + setState(110); + match(IDENTIFIER); + setState(115); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(112); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(111); + match(AS); + } + } + + setState(114); + match(IDENTIFIER); + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RelListContext extends ParserRuleContext { + public List relation() { + return getRuleContexts(RelationContext.class); + } + public RelationContext relation(int i) { + return getRuleContext(RelationContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public RelListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_relList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelList(this); + else return visitor.visitChildren(this); + } + } + + public final RelListContext relList() throws RecognitionException { + RelListContext _localctx = new RelListContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_relList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(119); + relation(); + setState(124); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(120); + match(COMMA); + setState(121); + relation(); + } + } + setState(126); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RelationContext extends ParserRuleContext { + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public RelationContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_relation; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelation(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelation(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelation(this); + else return visitor.visitChildren(this); + } + } + + public final RelationContext relation() throws RecognitionException { + RelationContext _localctx = new RelationContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_relation); + int _la; + try { + setState(140); + _errHandler.sync(this); + switch (_input.LA(1)) { + case IDENTIFIER: + enterOuterAlt(_localctx, 1); + { + setState(127); + match(IDENTIFIER); + setState(132); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(129); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(128); + match(AS); + } + } + + setState(131); + match(IDENTIFIER); + } + } + + } + break; + case LPAREN: + enterOuterAlt(_localctx, 2); + { + setState(134); + match(LPAREN); + setState(135); + selectSql(); + setState(136); + match(RPAREN); + setState(137); + match(AS); + setState(138); + match(IDENTIFIER); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionListContext extends ParserRuleContext { + public ConditionContext condition() { + return getRuleContext(ConditionContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public List conditionList() { + return getRuleContexts(ConditionListContext.class); + } + public ConditionListContext conditionList(int i) { + return getRuleContext(ConditionListContext.class,i); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } + public TerminalNode OR() { return getToken(SqlExpressionParser.OR, 0); } + public ConditionListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_conditionList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionList(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionListContext conditionList() throws RecognitionException { + return conditionList(0); + } + + private ConditionListContext conditionList(int _p) throws RecognitionException { + ParserRuleContext _parentctx = _ctx; + int _parentState = getState(); + ConditionListContext _localctx = new ConditionListContext(_ctx, _parentState); + ConditionListContext _prevctx = _localctx; + int _startState = 14; + enterRecursionRule(_localctx, 14, RULE_conditionList, _p); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(148); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,20,_ctx) ) { + case 1: + { + setState(143); + condition(); + } + break; + case 2: + { + setState(144); + match(LPAREN); + setState(145); + conditionList(0); + setState(146); + match(RPAREN); + } + break; + } + _ctx.stop = _input.LT(-1); + setState(158); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + if ( _parseListeners!=null ) triggerExitRuleEvent(); + _prevctx = _localctx; + { + setState(156); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { + case 1: + { + _localctx = new ConditionListContext(_parentctx, _parentState); + pushNewRecursionContext(_localctx, _startState, RULE_conditionList); + setState(150); + if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); + setState(151); + match(AND); + setState(152); + conditionList(4); + } + break; + case 2: + { + _localctx = new ConditionListContext(_parentctx, _parentState); + pushNewRecursionContext(_localctx, _startState, RULE_conditionList); + setState(153); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(154); + match(OR); + setState(155); + conditionList(3); + } + break; + } + } + } + setState(160); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + unrollRecursionContexts(_parentctx); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class CompOpContext extends ParserRuleContext { + public TerminalNode EQ() { return getToken(SqlExpressionParser.EQ, 0); } + public TerminalNode LT() { return getToken(SqlExpressionParser.LT, 0); } + public TerminalNode GT() { return getToken(SqlExpressionParser.GT, 0); } + public TerminalNode LE() { return getToken(SqlExpressionParser.LE, 0); } + public TerminalNode GE() { return getToken(SqlExpressionParser.GE, 0); } + public TerminalNode NE() { return getToken(SqlExpressionParser.NE, 0); } + public TerminalNode LIKE() { return getToken(SqlExpressionParser.LIKE, 0); } + public TerminalNode NOT() { return getToken(SqlExpressionParser.NOT, 0); } + public TerminalNode IN() { return getToken(SqlExpressionParser.IN, 0); } + public TerminalNode IS() { return getToken(SqlExpressionParser.IS, 0); } + public CompOpContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_compOp; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCompOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCompOp(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCompOp(this); + else return visitor.visitChildren(this); + } + } + + public final CompOpContext compOp() throws RecognitionException { + CompOpContext _localctx = new CompOpContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_compOp); + try { + setState(176); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(161); + match(EQ); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(162); + match(LT); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(163); + match(GT); + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(164); + match(LE); + } + break; + case 5: + enterOuterAlt(_localctx, 5); + { + setState(165); + match(GE); + } + break; + case 6: + enterOuterAlt(_localctx, 6); + { + setState(166); + match(NE); + } + break; + case 7: + enterOuterAlt(_localctx, 7); + { + setState(167); + match(LIKE); + } + break; + case 8: + enterOuterAlt(_localctx, 8); + { + setState(168); + match(NOT); + setState(169); + match(LIKE); + } + break; + case 9: + enterOuterAlt(_localctx, 9); + { + setState(170); + match(IN); + } + break; + case 10: + enterOuterAlt(_localctx, 10); + { + setState(171); + match(NOT); + setState(172); + match(IN); + } + break; + case 11: + enterOuterAlt(_localctx, 11); + { + setState(173); + match(IS); + } + break; + case 12: + enterOuterAlt(_localctx, 12); + { + setState(174); + match(IS); + setState(175); + match(NOT); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionContext extends ParserRuleContext { + public List conditionUnit() { + return getRuleContexts(ConditionUnitContext.class); + } + public ConditionUnitContext conditionUnit(int i) { + return getRuleContext(ConditionUnitContext.class,i); + } + public CompOpContext compOp() { + return getRuleContext(CompOpContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public ConditionContext condition() { + return getRuleContext(ConditionContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public TerminalNode BETWEEN() { return getToken(SqlExpressionParser.BETWEEN, 0); } + public List number() { + return getRuleContexts(NumberContext.class); + } + public NumberContext number(int i) { + return getRuleContext(NumberContext.class,i); + } + public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } + public ConditionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_condition; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCondition(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCondition(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCondition(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionContext condition() throws RecognitionException { + ConditionContext _localctx = new ConditionContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_condition); + try { + setState(192); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(178); + conditionUnit(); + setState(179); + compOp(); + setState(180); + conditionUnit(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(182); + match(LPAREN); + setState(183); + condition(); + setState(184); + match(RPAREN); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(186); + match(IDENTIFIER); + setState(187); + match(BETWEEN); + setState(188); + number(); + setState(189); + match(AND); + setState(190); + number(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionUnitContext extends ParserRuleContext { + public NumberContext number() { + return getRuleContext(NumberContext.class,0); + } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } + public TerminalNode NULL() { return getToken(SqlExpressionParser.NULL, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public ConditionUnitContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_conditionUnit; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionUnit(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionUnit(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionUnit(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionUnitContext conditionUnit() throws RecognitionException { + ConditionUnitContext _localctx = new ConditionUnitContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_conditionUnit); + try { + setState(206); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(194); + number(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(195); + string(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(196); + match(IDENTIFIER); + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(197); + match(IDENTIFIER); + setState(198); + match(DOT); + setState(199); + match(IDENTIFIER); + } + break; + case 5: + enterOuterAlt(_localctx, 5); + { + setState(200); + match(NULL); + } + break; + case 6: + enterOuterAlt(_localctx, 6); + { + setState(201); + match(LPAREN); + setState(202); + selectSql(); + setState(203); + match(RPAREN); + } + break; + case 7: + enterOuterAlt(_localctx, 7); + { + setState(205); + functionCall(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class FunctionCallContext extends ParserRuleContext { + public FunctionNameContext functionName() { + return getRuleContext(FunctionNameContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public ParameterListContext parameterList() { + return getRuleContext(ParameterListContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public FunctionCallContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functionCall; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionCall(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionCall(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionCall(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionCallContext functionCall() throws RecognitionException { + FunctionCallContext _localctx = new FunctionCallContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_functionCall); + try { + enterOuterAlt(_localctx, 1); + { + setState(208); + functionName(); + setState(209); + match(LPAREN); + setState(210); + parameterList(); + setState(211); + match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class FunctionNameContext extends ParserRuleContext { + public TerminalNode COUNT() { return getToken(SqlExpressionParser.COUNT, 0); } + public TerminalNode AVG() { return getToken(SqlExpressionParser.AVG, 0); } + public TerminalNode SUM() { return getToken(SqlExpressionParser.SUM, 0); } + public TerminalNode MIN() { return getToken(SqlExpressionParser.MIN, 0); } + public TerminalNode MAX() { return getToken(SqlExpressionParser.MAX, 0); } + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public FunctionNameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functionName; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionName(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionName(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionName(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionNameContext functionName() throws RecognitionException { + FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_functionName); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(213); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 17592316067840L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParameterListContext extends ParserRuleContext { + public List parameter() { + return getRuleContexts(ParameterContext.class); + } + public ParameterContext parameter(int i) { + return getRuleContext(ParameterContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public ParameterListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parameterList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameterList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameterList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameterList(this); + else return visitor.visitChildren(this); + } + } + + public final ParameterListContext parameterList() throws RecognitionException { + ParameterListContext _localctx = new ParameterListContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_parameterList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(215); + parameter(); + setState(220); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(216); + match(COMMA); + setState(217); + parameter(); + } + } + setState(222); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParameterContext extends ParserRuleContext { + public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public ParameterContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parameter; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameter(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameter(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameter(this); + else return visitor.visitChildren(this); + } + } + + public final ParameterContext parameter() throws RecognitionException { + ParameterContext _localctx = new ParameterContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_parameter); + try { + setState(225); + _errHandler.sync(this); + switch (_input.LA(1)) { + case STAR: + enterOuterAlt(_localctx, 1); + { + setState(223); + match(STAR); + } + break; + case STRING: + enterOuterAlt(_localctx, 2); + { + setState(224); + string(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class GroupByListContext extends ParserRuleContext { + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public GroupByListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_groupByList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterGroupByList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitGroupByList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitGroupByList(this); + else return visitor.visitChildren(this); + } + } + + public final GroupByListContext groupByList() throws RecognitionException { + GroupByListContext _localctx = new GroupByListContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_groupByList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(227); + match(IDENTIFIER); + setState(232); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(228); + match(COMMA); + setState(229); + match(IDENTIFIER); + } + } + setState(234); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class OrderByListContext extends ParserRuleContext { + public List orderByField() { + return getRuleContexts(OrderByFieldContext.class); + } + public OrderByFieldContext orderByField(int i) { + return getRuleContext(OrderByFieldContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public OrderByListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_orderByList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByList(this); + else return visitor.visitChildren(this); + } + } + + public final OrderByListContext orderByList() throws RecognitionException { + OrderByListContext _localctx = new OrderByListContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_orderByList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(235); + orderByField(); + setState(240); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(236); + match(COMMA); + setState(237); + orderByField(); + } + } + setState(242); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class OrderByFieldContext extends ParserRuleContext { + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public TerminalNode ASC() { return getToken(SqlExpressionParser.ASC, 0); } + public TerminalNode DESC() { return getToken(SqlExpressionParser.DESC, 0); } + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public OrderByFieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_orderByField; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByField(this); + else return visitor.visitChildren(this); + } + } + + public final OrderByFieldContext orderByField() throws RecognitionException { + OrderByFieldContext _localctx = new OrderByFieldContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_orderByField); + int _la; + try { + setState(251); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(243); + match(IDENTIFIER); + setState(245); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ASC || _la==DESC) { + { + setState(244); + _la = _input.LA(1); + if ( !(_la==ASC || _la==DESC) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(247); + functionCall(); + setState(249); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ASC || _la==DESC) { + { + setState(248); + _la = _input.LA(1); + if ( !(_la==ASC || _la==DESC) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class LimitClauseContext extends ParserRuleContext { + public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } + public LimitClauseContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_limitClause; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterLimitClause(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitLimitClause(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitLimitClause(this); + else return visitor.visitChildren(this); + } + } + + public final LimitClauseContext limitClause() throws RecognitionException { + LimitClauseContext _localctx = new LimitClauseContext(_ctx, getState()); + enterRule(_localctx, 36, RULE_limitClause); + try { + enterOuterAlt(_localctx, 1); + { + setState(253); + match(NUMBER); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class NumberContext extends ParserRuleContext { + public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } + public TerminalNode FLOAT() { return getToken(SqlExpressionParser.FLOAT, 0); } + public NumberContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_number; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterNumber(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitNumber(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitNumber(this); + else return visitor.visitChildren(this); + } + } + + public final NumberContext number() throws RecognitionException { + NumberContext _localctx = new NumberContext(_ctx, getState()); + enterRule(_localctx, 38, RULE_number); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(255); + _la = _input.LA(1); + if ( !(_la==FLOAT || _la==NUMBER) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class StringContext extends ParserRuleContext { + public TerminalNode STRING() { return getToken(SqlExpressionParser.STRING, 0); } + public StringContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_string; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterString(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitString(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitString(this); + else return visitor.visitChildren(this); + } + } + + public final StringContext string() throws RecognitionException { + StringContext _localctx = new StringContext(_ctx, getState()); + enterRule(_localctx, 40, RULE_string); + try { + enterOuterAlt(_localctx, 1); + { + setState(257); + match(STRING); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 7: + return conditionList_sempred((ConditionListContext)_localctx, predIndex); + } + return true; + } + private boolean conditionList_sempred(ConditionListContext _localctx, int predIndex) { + switch (predIndex) { + case 0: + return precpred(_ctx, 3); + case 1: + return precpred(_ctx, 2); + } + return true; + } + + public static final String _serializedATN = + "\u0004\u0001/\u0104\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ + "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ + "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ + "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ + "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012"+ + "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0001\u0000\u0001\u0000"+ + "\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+ + "\u0001\u0001\u0003\u00014\b\u0001\u0001\u0002\u0001\u0002\u0001\u0002"+ + "\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002<\b\u0002\u0001\u0002"+ + "\u0001\u0002\u0001\u0002\u0003\u0002A\b\u0002\u0001\u0002\u0001\u0002"+ + "\u0003\u0002E\b\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002"+ + "J\b\u0002\u0001\u0002\u0001\u0002\u0003\u0002N\b\u0002\u0001\u0003\u0001"+ + "\u0003\u0001\u0003\u0005\u0003S\b\u0003\n\u0003\f\u0003V\t\u0003\u0001"+ + "\u0004\u0001\u0004\u0003\u0004Z\b\u0004\u0001\u0004\u0003\u0004]\b\u0004"+ + "\u0001\u0004\u0001\u0004\u0003\u0004a\b\u0004\u0001\u0004\u0003\u0004"+ + "d\b\u0004\u0001\u0004\u0001\u0004\u0003\u0004h\b\u0004\u0001\u0004\u0003"+ + "\u0004k\b\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003"+ + "\u0004q\b\u0004\u0001\u0004\u0003\u0004t\b\u0004\u0003\u0004v\b\u0004"+ + "\u0001\u0005\u0001\u0005\u0001\u0005\u0005\u0005{\b\u0005\n\u0005\f\u0005"+ + "~\t\u0005\u0001\u0006\u0001\u0006\u0003\u0006\u0082\b\u0006\u0001\u0006"+ + "\u0003\u0006\u0085\b\u0006\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ + "\u0001\u0006\u0001\u0006\u0003\u0006\u008d\b\u0006\u0001\u0007\u0001\u0007"+ + "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007\u0095\b\u0007"+ + "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007"+ + "\u0005\u0007\u009d\b\u0007\n\u0007\f\u0007\u00a0\t\u0007\u0001\b\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0003\b\u00b1\b\b\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0003\t\u00c1\b\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ + "\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003\n\u00cf"+ + "\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001"+ + "\f\u0001\f\u0001\r\u0001\r\u0001\r\u0005\r\u00db\b\r\n\r\f\r\u00de\t\r"+ + "\u0001\u000e\u0001\u000e\u0003\u000e\u00e2\b\u000e\u0001\u000f\u0001\u000f"+ + "\u0001\u000f\u0005\u000f\u00e7\b\u000f\n\u000f\f\u000f\u00ea\t\u000f\u0001"+ + "\u0010\u0001\u0010\u0001\u0010\u0005\u0010\u00ef\b\u0010\n\u0010\f\u0010"+ + "\u00f2\t\u0010\u0001\u0011\u0001\u0011\u0003\u0011\u00f6\b\u0011\u0001"+ + "\u0011\u0001\u0011\u0003\u0011\u00fa\b\u0011\u0003\u0011\u00fc\b\u0011"+ + "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014"+ + "\u0001\u0014\u0000\u0001\u000e\u0015\u0000\u0002\u0004\u0006\b\n\f\u000e"+ + "\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(\u0000\u0003\u0002"+ + "\u0000\u0016\u001a,,\u0001\u0000\u000e\u000f\u0001\u0000)*\u0121\u0000"+ + "*\u0001\u0000\u0000\u0000\u00023\u0001\u0000\u0000\u0000\u00045\u0001"+ + "\u0000\u0000\u0000\u0006O\u0001\u0000\u0000\u0000\bu\u0001\u0000\u0000"+ + "\u0000\nw\u0001\u0000\u0000\u0000\f\u008c\u0001\u0000\u0000\u0000\u000e"+ + "\u0094\u0001\u0000\u0000\u0000\u0010\u00b0\u0001\u0000\u0000\u0000\u0012"+ + "\u00c0\u0001\u0000\u0000\u0000\u0014\u00ce\u0001\u0000\u0000\u0000\u0016"+ + "\u00d0\u0001\u0000\u0000\u0000\u0018\u00d5\u0001\u0000\u0000\u0000\u001a"+ + "\u00d7\u0001\u0000\u0000\u0000\u001c\u00e1\u0001\u0000\u0000\u0000\u001e"+ + "\u00e3\u0001\u0000\u0000\u0000 \u00eb\u0001\u0000\u0000\u0000\"\u00fb"+ + "\u0001\u0000\u0000\u0000$\u00fd\u0001\u0000\u0000\u0000&\u00ff\u0001\u0000"+ + "\u0000\u0000(\u0101\u0001\u0000\u0000\u0000*+\u0003\u0002\u0001\u0000"+ + "+,\u0005\u0000\u0000\u0001,\u0001\u0001\u0000\u0000\u0000-4\u0003\u0004"+ + "\u0002\u0000./\u0005\u001b\u0000\u0000/0\u0005\"\u0000\u000001\u0003("+ + "\u0014\u000012\u0005#\u0000\u000024\u0001\u0000\u0000\u00003-\u0001\u0000"+ + "\u0000\u00003.\u0001\u0000\u0000\u00004\u0003\u0001\u0000\u0000\u0000"+ + "56\u0005\u0004\u0000\u000067\u0003\u0006\u0003\u000078\u0005\u0005\u0000"+ + "\u00008;\u0003\n\u0005\u00009:\u0005\u0006\u0000\u0000:<\u0003\u000e\u0007"+ + "\u0000;9\u0001\u0000\u0000\u0000;<\u0001\u0000\u0000\u0000<@\u0001\u0000"+ + "\u0000\u0000=>\u0005\u0007\u0000\u0000>?\u0005\b\u0000\u0000?A\u0003\u001e"+ + "\u000f\u0000@=\u0001\u0000\u0000\u0000@A\u0001\u0000\u0000\u0000AD\u0001"+ + "\u0000\u0000\u0000BC\u0005\t\u0000\u0000CE\u0003\u000e\u0007\u0000DB\u0001"+ + "\u0000\u0000\u0000DE\u0001\u0000\u0000\u0000EI\u0001\u0000\u0000\u0000"+ + "FG\u0005\n\u0000\u0000GH\u0005\b\u0000\u0000HJ\u0003 \u0010\u0000IF\u0001"+ + "\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JM\u0001\u0000\u0000\u0000"+ + "KL\u0005\u000b\u0000\u0000LN\u0003$\u0012\u0000MK\u0001\u0000\u0000\u0000"+ + "MN\u0001\u0000\u0000\u0000N\u0005\u0001\u0000\u0000\u0000OT\u0003\b\u0004"+ + "\u0000PQ\u0005&\u0000\u0000QS\u0003\b\u0004\u0000RP\u0001\u0000\u0000"+ + "\u0000SV\u0001\u0000\u0000\u0000TR\u0001\u0000\u0000\u0000TU\u0001\u0000"+ + "\u0000\u0000U\u0007\u0001\u0000\u0000\u0000VT\u0001\u0000\u0000\u0000"+ + "W\\\u0003\u0016\u000b\u0000XZ\u0005\r\u0000\u0000YX\u0001\u0000\u0000"+ + "\u0000YZ\u0001\u0000\u0000\u0000Z[\u0001\u0000\u0000\u0000[]\u0005,\u0000"+ + "\u0000\\Y\u0001\u0000\u0000\u0000\\]\u0001\u0000\u0000\u0000]v\u0001\u0000"+ + "\u0000\u0000^c\u0005,\u0000\u0000_a\u0005\r\u0000\u0000`_\u0001\u0000"+ + "\u0000\u0000`a\u0001\u0000\u0000\u0000ab\u0001\u0000\u0000\u0000bd\u0005"+ + ",\u0000\u0000c`\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000dv\u0001"+ + "\u0000\u0000\u0000ej\u0005\u0015\u0000\u0000fh\u0005\r\u0000\u0000gf\u0001"+ + "\u0000\u0000\u0000gh\u0001\u0000\u0000\u0000hi\u0001\u0000\u0000\u0000"+ + "ik\u0005,\u0000\u0000jg\u0001\u0000\u0000\u0000jk\u0001\u0000\u0000\u0000"+ + "kv\u0001\u0000\u0000\u0000lm\u0005,\u0000\u0000mn\u0005\'\u0000\u0000"+ + "ns\u0005,\u0000\u0000oq\u0005\r\u0000\u0000po\u0001\u0000\u0000\u0000"+ + "pq\u0001\u0000\u0000\u0000qr\u0001\u0000\u0000\u0000rt\u0005,\u0000\u0000"+ + "sp\u0001\u0000\u0000\u0000st\u0001\u0000\u0000\u0000tv\u0001\u0000\u0000"+ + "\u0000uW\u0001\u0000\u0000\u0000u^\u0001\u0000\u0000\u0000ue\u0001\u0000"+ + "\u0000\u0000ul\u0001\u0000\u0000\u0000v\t\u0001\u0000\u0000\u0000w|\u0003"+ + "\f\u0006\u0000xy\u0005&\u0000\u0000y{\u0003\f\u0006\u0000zx\u0001\u0000"+ + "\u0000\u0000{~\u0001\u0000\u0000\u0000|z\u0001\u0000\u0000\u0000|}\u0001"+ + "\u0000\u0000\u0000}\u000b\u0001\u0000\u0000\u0000~|\u0001\u0000\u0000"+ + "\u0000\u007f\u0084\u0005,\u0000\u0000\u0080\u0082\u0005\r\u0000\u0000"+ + "\u0081\u0080\u0001\u0000\u0000\u0000\u0081\u0082\u0001\u0000\u0000\u0000"+ + "\u0082\u0083\u0001\u0000\u0000\u0000\u0083\u0085\u0005,\u0000\u0000\u0084"+ + "\u0081\u0001\u0000\u0000\u0000\u0084\u0085\u0001\u0000\u0000\u0000\u0085"+ + "\u008d\u0001\u0000\u0000\u0000\u0086\u0087\u0005\"\u0000\u0000\u0087\u0088"+ + "\u0003\u0004\u0002\u0000\u0088\u0089\u0005#\u0000\u0000\u0089\u008a\u0005"+ + "\r\u0000\u0000\u008a\u008b\u0005,\u0000\u0000\u008b\u008d\u0001\u0000"+ + "\u0000\u0000\u008c\u007f\u0001\u0000\u0000\u0000\u008c\u0086\u0001\u0000"+ + "\u0000\u0000\u008d\r\u0001\u0000\u0000\u0000\u008e\u008f\u0006\u0007\uffff"+ + "\uffff\u0000\u008f\u0095\u0003\u0012\t\u0000\u0090\u0091\u0005\"\u0000"+ + "\u0000\u0091\u0092\u0003\u000e\u0007\u0000\u0092\u0093\u0005#\u0000\u0000"+ + "\u0093\u0095\u0001\u0000\u0000\u0000\u0094\u008e\u0001\u0000\u0000\u0000"+ + "\u0094\u0090\u0001\u0000\u0000\u0000\u0095\u009e\u0001\u0000\u0000\u0000"+ + "\u0096\u0097\n\u0003\u0000\u0000\u0097\u0098\u0005\u0001\u0000\u0000\u0098"+ + "\u009d\u0003\u000e\u0007\u0004\u0099\u009a\n\u0002\u0000\u0000\u009a\u009b"+ + "\u0005\u0002\u0000\u0000\u009b\u009d\u0003\u000e\u0007\u0003\u009c\u0096"+ + "\u0001\u0000\u0000\u0000\u009c\u0099\u0001\u0000\u0000\u0000\u009d\u00a0"+ + "\u0001\u0000\u0000\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009e\u009f"+ + "\u0001\u0000\u0000\u0000\u009f\u000f\u0001\u0000\u0000\u0000\u00a0\u009e"+ + "\u0001\u0000\u0000\u0000\u00a1\u00b1\u0005 \u0000\u0000\u00a2\u00b1\u0005"+ + "\u001e\u0000\u0000\u00a3\u00b1\u0005\u001c\u0000\u0000\u00a4\u00b1\u0005"+ + "\u001f\u0000\u0000\u00a5\u00b1\u0005\u001d\u0000\u0000\u00a6\u00b1\u0005"+ + "!\u0000\u0000\u00a7\u00b1\u0005\u0013\u0000\u0000\u00a8\u00a9\u0005\u0003"+ + "\u0000\u0000\u00a9\u00b1\u0005\u0013\u0000\u0000\u00aa\u00b1\u0005\u0010"+ + "\u0000\u0000\u00ab\u00ac\u0005\u0003\u0000\u0000\u00ac\u00b1\u0005\u0010"+ + "\u0000\u0000\u00ad\u00b1\u0005\u0011\u0000\u0000\u00ae\u00af\u0005\u0011"+ + "\u0000\u0000\u00af\u00b1\u0005\u0003\u0000\u0000\u00b0\u00a1\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a2\u0001\u0000\u0000\u0000\u00b0\u00a3\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a4\u0001\u0000\u0000\u0000\u00b0\u00a5\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a6\u0001\u0000\u0000\u0000\u00b0\u00a7\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a8\u0001\u0000\u0000\u0000\u00b0\u00aa\u0001\u0000"+ + "\u0000\u0000\u00b0\u00ab\u0001\u0000\u0000\u0000\u00b0\u00ad\u0001\u0000"+ + "\u0000\u0000\u00b0\u00ae\u0001\u0000\u0000\u0000\u00b1\u0011\u0001\u0000"+ + "\u0000\u0000\u00b2\u00b3\u0003\u0014\n\u0000\u00b3\u00b4\u0003\u0010\b"+ + "\u0000\u00b4\u00b5\u0003\u0014\n\u0000\u00b5\u00c1\u0001\u0000\u0000\u0000"+ + "\u00b6\u00b7\u0005\"\u0000\u0000\u00b7\u00b8\u0003\u0012\t\u0000\u00b8"+ + "\u00b9\u0005#\u0000\u0000\u00b9\u00c1\u0001\u0000\u0000\u0000\u00ba\u00bb"+ + "\u0005,\u0000\u0000\u00bb\u00bc\u0005\u0014\u0000\u0000\u00bc\u00bd\u0003"+ + "&\u0013\u0000\u00bd\u00be\u0005\u0001\u0000\u0000\u00be\u00bf\u0003&\u0013"+ + "\u0000\u00bf\u00c1\u0001\u0000\u0000\u0000\u00c0\u00b2\u0001\u0000\u0000"+ + "\u0000\u00c0\u00b6\u0001\u0000\u0000\u0000\u00c0\u00ba\u0001\u0000\u0000"+ + "\u0000\u00c1\u0013\u0001\u0000\u0000\u0000\u00c2\u00cf\u0003&\u0013\u0000"+ + "\u00c3\u00cf\u0003(\u0014\u0000\u00c4\u00cf\u0005,\u0000\u0000\u00c5\u00c6"+ + "\u0005,\u0000\u0000\u00c6\u00c7\u0005\'\u0000\u0000\u00c7\u00cf\u0005"+ + ",\u0000\u0000\u00c8\u00cf\u0005\u0012\u0000\u0000\u00c9\u00ca\u0005\""+ + "\u0000\u0000\u00ca\u00cb\u0003\u0004\u0002\u0000\u00cb\u00cc\u0005#\u0000"+ + "\u0000\u00cc\u00cf\u0001\u0000\u0000\u0000\u00cd\u00cf\u0003\u0016\u000b"+ + "\u0000\u00ce\u00c2\u0001\u0000\u0000\u0000\u00ce\u00c3\u0001\u0000\u0000"+ + "\u0000\u00ce\u00c4\u0001\u0000\u0000\u0000\u00ce\u00c5\u0001\u0000\u0000"+ + "\u0000\u00ce\u00c8\u0001\u0000\u0000\u0000\u00ce\u00c9\u0001\u0000\u0000"+ + "\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000\u00cf\u0015\u0001\u0000\u0000"+ + "\u0000\u00d0\u00d1\u0003\u0018\f\u0000\u00d1\u00d2\u0005\"\u0000\u0000"+ + "\u00d2\u00d3\u0003\u001a\r\u0000\u00d3\u00d4\u0005#\u0000\u0000\u00d4"+ + "\u0017\u0001\u0000\u0000\u0000\u00d5\u00d6\u0007\u0000\u0000\u0000\u00d6"+ + "\u0019\u0001\u0000\u0000\u0000\u00d7\u00dc\u0003\u001c\u000e\u0000\u00d8"+ + "\u00d9\u0005&\u0000\u0000\u00d9\u00db\u0003\u001c\u000e\u0000\u00da\u00d8"+ + "\u0001\u0000\u0000\u0000\u00db\u00de\u0001\u0000\u0000\u0000\u00dc\u00da"+ + "\u0001\u0000\u0000\u0000\u00dc\u00dd\u0001\u0000\u0000\u0000\u00dd\u001b"+ + "\u0001\u0000\u0000\u0000\u00de\u00dc\u0001\u0000\u0000\u0000\u00df\u00e2"+ + "\u0005\u0015\u0000\u0000\u00e0\u00e2\u0003(\u0014\u0000\u00e1\u00df\u0001"+ + "\u0000\u0000\u0000\u00e1\u00e0\u0001\u0000\u0000\u0000\u00e2\u001d\u0001"+ + "\u0000\u0000\u0000\u00e3\u00e8\u0005,\u0000\u0000\u00e4\u00e5\u0005&\u0000"+ + "\u0000\u00e5\u00e7\u0005,\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000"+ + "\u00e7\u00ea\u0001\u0000\u0000\u0000\u00e8\u00e6\u0001\u0000\u0000\u0000"+ + "\u00e8\u00e9\u0001\u0000\u0000\u0000\u00e9\u001f\u0001\u0000\u0000\u0000"+ + "\u00ea\u00e8\u0001\u0000\u0000\u0000\u00eb\u00f0\u0003\"\u0011\u0000\u00ec"+ + "\u00ed\u0005&\u0000\u0000\u00ed\u00ef\u0003\"\u0011\u0000\u00ee\u00ec"+ + "\u0001\u0000\u0000\u0000\u00ef\u00f2\u0001\u0000\u0000\u0000\u00f0\u00ee"+ + "\u0001\u0000\u0000\u0000\u00f0\u00f1\u0001\u0000\u0000\u0000\u00f1!\u0001"+ + "\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000\u0000\u0000\u00f3\u00f5\u0005"+ + ",\u0000\u0000\u00f4\u00f6\u0007\u0001\u0000\u0000\u00f5\u00f4\u0001\u0000"+ + "\u0000\u0000\u00f5\u00f6\u0001\u0000\u0000\u0000\u00f6\u00fc\u0001\u0000"+ + "\u0000\u0000\u00f7\u00f9\u0003\u0016\u000b\u0000\u00f8\u00fa\u0007\u0001"+ + "\u0000\u0000\u00f9\u00f8\u0001\u0000\u0000\u0000\u00f9\u00fa\u0001\u0000"+ + "\u0000\u0000\u00fa\u00fc\u0001\u0000\u0000\u0000\u00fb\u00f3\u0001\u0000"+ + "\u0000\u0000\u00fb\u00f7\u0001\u0000\u0000\u0000\u00fc#\u0001\u0000\u0000"+ + "\u0000\u00fd\u00fe\u0005*\u0000\u0000\u00fe%\u0001\u0000\u0000\u0000\u00ff"+ + "\u0100\u0007\u0002\u0000\u0000\u0100\'\u0001\u0000\u0000\u0000\u0101\u0102"+ + "\u0005+\u0000\u0000\u0102)\u0001\u0000\u0000\u0000!3;@DIMTY\\`cgjpsu|"+ + "\u0081\u0084\u008c\u0094\u009c\u009e\u00b0\u00c0\u00ce\u00dc\u00e1\u00e8"+ + "\u00f0\u00f5\u00f9\u00fb"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java b/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java new file mode 100644 index 00000000000..571d0d2ffcc --- /dev/null +++ b/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java @@ -0,0 +1,147 @@ +// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 +package expr; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link SqlExpressionParser}. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface SqlExpressionVisitor extends ParseTreeVisitor { + /** + * Visit a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpression(SqlExpressionParser.ExpressionContext ctx); + /** + * Visit a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + /** + * Visit a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectField(SqlExpressionParser.SelectFieldContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelList(SqlExpressionParser.RelListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelation(SqlExpressionParser.RelationContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitConditionList(SqlExpressionParser.ConditionListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitCompOp(SqlExpressionParser.CompOpContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitCondition(SqlExpressionParser.ConditionContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParameterList(SqlExpressionParser.ParameterListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParameter(SqlExpressionParser.ParameterContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitGroupByList(SqlExpressionParser.GroupByListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOrderByList(SqlExpressionParser.OrderByListContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNumber(SqlExpressionParser.NumberContext ctx); + /** + * Visit a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitString(SqlExpressionParser.StringContext ctx); +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java index aab937667c1..4feb9eb026d 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java @@ -27,6 +27,7 @@ import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; import org.apache.hertzbeat.common.entity.alerter.SingleAlert; +import org.apache.hertzbeat.warehouse.constants.WarehouseConstants; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -66,10 +67,19 @@ public void calculate(AlertDefine define) { // result: [{'value': 100, 'timestamp': 1343554, 'instance': 'node1'},{'value': 200, 'timestamp': 1343555, 'instance': 'node2'}] // the return result should be matched with threshold try { - List> results = dataSourceService.calculate( - define.getDatasource(), - define.getExpr() - ); + List> results; + String sqlOrPromql = define.getDatasource(); + if (WarehouseConstants.SQL.equals(sqlOrPromql)) { + // sql + results = dataSourceService.query(sqlOrPromql, define.getExpr()); + // this.doCaculate(); + } else { + // promql + results = dataSourceService.calculate( + define.getDatasource(), + define.getExpr() + ); + } // if no match the expr threshold, the results item map {'value': null} should be null and others field keep // if results has multi list, should trigger multi alert if (CollectionUtils.isEmpty(results)) { diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java new file mode 100644 index 00000000000..fe7fa44f6c7 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java @@ -0,0 +1,350 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link SqlExpressionListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +@SuppressWarnings("CheckReturnValue") +public class SqlExpressionBaseListener implements SqlExpressionListener { + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpression(SqlExpressionParser.ExpressionContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpression(SqlExpressionParser.ExpressionContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSelectField(SqlExpressionParser.SelectFieldContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSelectField(SqlExpressionParser.SelectFieldContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRelList(SqlExpressionParser.RelListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRelList(SqlExpressionParser.RelListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRelation(SqlExpressionParser.RelationContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRelation(SqlExpressionParser.RelationContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterConditionList(SqlExpressionParser.ConditionListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitConditionList(SqlExpressionParser.ConditionListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCompOp(SqlExpressionParser.CompOpContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCompOp(SqlExpressionParser.CompOpContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCondition(SqlExpressionParser.ConditionContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCondition(SqlExpressionParser.ConditionContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParameterList(SqlExpressionParser.ParameterListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParameterList(SqlExpressionParser.ParameterListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParameter(SqlExpressionParser.ParameterContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParameter(SqlExpressionParser.ParameterContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterGroupByList(SqlExpressionParser.GroupByListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitGroupByList(SqlExpressionParser.GroupByListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOrderByList(SqlExpressionParser.OrderByListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOrderByList(SqlExpressionParser.OrderByListContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNumber(SqlExpressionParser.NumberContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNumber(SqlExpressionParser.NumberContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterString(SqlExpressionParser.StringContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitString(SqlExpressionParser.StringContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java new file mode 100644 index 00000000000..6879f154c2b --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java @@ -0,0 +1,191 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link SqlExpressionVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +@SuppressWarnings("CheckReturnValue") +public class SqlExpressionBaseVisitor extends AbstractParseTreeVisitor implements SqlExpressionVisitor { + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpression(SqlExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSelectField(SqlExpressionParser.SelectFieldContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitRelList(SqlExpressionParser.RelListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitRelation(SqlExpressionParser.RelationContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitConditionList(SqlExpressionParser.ConditionListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitCompOp(SqlExpressionParser.CompOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitCondition(SqlExpressionParser.ConditionContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParameterList(SqlExpressionParser.ParameterListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParameter(SqlExpressionParser.ParameterContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitGroupByList(SqlExpressionParser.GroupByListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOrderByList(SqlExpressionParser.OrderByListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNumber(SqlExpressionParser.NumberContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitString(SqlExpressionParser.StringContext ctx) { return visitChildren(ctx); } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java new file mode 100644 index 00000000000..e0ad51847e5 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java @@ -0,0 +1,42 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.CommonTokenStream; +import org.apache.hertzbeat.warehouse.db.QueryExecutor; + +import java.util.List; +import java.util.Map; + +/** + * Sql expression evaluation visitor. + */ +public class SqlExpressionEvalVisitor extends SqlExpressionBaseVisitor>> { + + private final QueryExecutor executor; + private final CommonTokenStream tokens; + + public SqlExpressionEvalVisitor(QueryExecutor executor, CommonTokenStream tokens) { + this.executor = executor; + this.tokens = tokens; + } + + @Override + public List> visitExpression(SqlExpressionParser.ExpressionContext ctx) { + return super.visit(ctx.sqlExpr()); + } + + @Override + public List> visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { + String rawSql = tokens.getText(ctx.selectSql()); + return executor.execute(rawSql); + } + + @Override + public List> visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { + return callSql(tokens.getText(ctx.string())); + } + + private List> callSql(String text) { + String script = text.substring(1, text.length() - 1); + return executor.execute(script); + } +} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java new file mode 100644 index 00000000000..d344919786d --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java @@ -0,0 +1,350 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; +import org.antlr.v4.runtime.dfa.DFA; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class SqlExpressionLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, + ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, + LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, + SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, + RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, + NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "'*'", null, null, + null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", + "')'", "'['", "']'", "','", "'.'", "';'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public SqlExpressionLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "SqlExpression.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\u0004\u0000/\u014b\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ + "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ + "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ + "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ + "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ + "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ + "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ + "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ + "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ + "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ + "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ + "!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+ + "&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007"+ + "+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0001\u0000\u0001\u0000\u0001"+ + "\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001"+ + "\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+ + "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001"+ + "\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005\u0001"+ + "\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001"+ + "\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ + "\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b"+ + "\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0001"+ + "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+ + "\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001"+ + "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001"+ + "\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+ + "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001"+ + "\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001"+ + "\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ + "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0001"+ + "\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001"+ + "\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+ + "\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e\u0001"+ + "\u001f\u0001\u001f\u0001\u001f\u0003\u001f\u00ec\b\u001f\u0001 \u0001"+ + " \u0001 \u0001!\u0001!\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001"+ + "%\u0001%\u0001&\u0001&\u0001\'\u0001\'\u0001(\u0004(\u0100\b(\u000b(\f"+ + "(\u0101\u0001(\u0001(\u0004(\u0106\b(\u000b(\f(\u0107\u0001)\u0004)\u010b"+ + "\b)\u000b)\f)\u010c\u0001*\u0001*\u0001*\u0001*\u0005*\u0113\b*\n*\f*"+ + "\u0116\t*\u0001*\u0001*\u0001*\u0001*\u0001*\u0005*\u011d\b*\n*\f*\u0120"+ + "\t*\u0001*\u0003*\u0123\b*\u0001+\u0001+\u0005+\u0127\b+\n+\f+\u012a\t"+ + "+\u0001,\u0004,\u012d\b,\u000b,\f,\u012e\u0001,\u0001,\u0001-\u0001-\u0001"+ + "-\u0001-\u0005-\u0137\b-\n-\f-\u013a\t-\u0001-\u0001-\u0001.\u0001.\u0001"+ + ".\u0001.\u0005.\u0142\b.\n.\f.\u0145\t.\u0001.\u0001.\u0001.\u0001.\u0001"+ + ".\u0001\u0143\u0000/\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t"+ + "\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f"+ + "\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014"+ + ")\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d;\u001e"+ + "=\u001f? A!C\"E#G$I%K&M\'O(Q)S*U+W,Y-[.]/\u0001\u0000\u001f\u0002\u0000"+ + "AAaa\u0002\u0000NNnn\u0002\u0000DDdd\u0002\u0000OOoo\u0002\u0000RRrr\u0002"+ + "\u0000TTtt\u0002\u0000SSss\u0002\u0000EEee\u0002\u0000LLll\u0002\u0000"+ + "CCcc\u0002\u0000FFff\u0002\u0000MMmm\u0002\u0000WWww\u0002\u0000HHhh\u0002"+ + "\u0000GGgg\u0002\u0000UUuu\u0002\u0000PPpp\u0002\u0000BBbb\u0002\u0000"+ + "YYyy\u0002\u0000VVvv\u0002\u0000IIii\u0002\u0000KKkk\u0002\u0000XXxx\u0002"+ + "\u0000QQqq\u0001\u000009\u0004\u0000\n\n\r\r\"\"\\\\\u0004\u0000\n\n\r"+ + "\r\'\'\\\\\u0003\u0000AZ__az\u0004\u000009AZ__az\u0003\u0000\t\n\r\r "+ + " \u0002\u0000\n\n\r\r\u0157\u0000\u0001\u0001\u0000\u0000\u0000\u0000"+ + "\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000"+ + "\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b"+ + "\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001"+ + "\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001"+ + "\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001"+ + "\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001"+ + "\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001"+ + "\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000"+ + "\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000"+ + "\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-"+ + "\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000"+ + "\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000"+ + "\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000\u0000\u0000\u0000;"+ + "\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0000?\u0001\u0000"+ + "\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C\u0001\u0000\u0000\u0000"+ + "\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000\u0000\u0000\u0000I"+ + "\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000\u0000M\u0001\u0000"+ + "\u0000\u0000\u0000O\u0001\u0000\u0000\u0000\u0000Q\u0001\u0000\u0000\u0000"+ + "\u0000S\u0001\u0000\u0000\u0000\u0000U\u0001\u0000\u0000\u0000\u0000W"+ + "\u0001\u0000\u0000\u0000\u0000Y\u0001\u0000\u0000\u0000\u0000[\u0001\u0000"+ + "\u0000\u0000\u0000]\u0001\u0000\u0000\u0000\u0001_\u0001\u0000\u0000\u0000"+ + "\u0003c\u0001\u0000\u0000\u0000\u0005f\u0001\u0000\u0000\u0000\u0007j"+ + "\u0001\u0000\u0000\u0000\tq\u0001\u0000\u0000\u0000\u000bv\u0001\u0000"+ + "\u0000\u0000\r|\u0001\u0000\u0000\u0000\u000f\u0082\u0001\u0000\u0000"+ + "\u0000\u0011\u0085\u0001\u0000\u0000\u0000\u0013\u008c\u0001\u0000\u0000"+ + "\u0000\u0015\u0092\u0001\u0000\u0000\u0000\u0017\u0098\u0001\u0000\u0000"+ + "\u0000\u0019\u009f\u0001\u0000\u0000\u0000\u001b\u00a2\u0001\u0000\u0000"+ + "\u0000\u001d\u00a6\u0001\u0000\u0000\u0000\u001f\u00ab\u0001\u0000\u0000"+ + "\u0000!\u00ae\u0001\u0000\u0000\u0000#\u00b1\u0001\u0000\u0000\u0000%"+ + "\u00b6\u0001\u0000\u0000\u0000\'\u00bb\u0001\u0000\u0000\u0000)\u00c3"+ + "\u0001\u0000\u0000\u0000+\u00c5\u0001\u0000\u0000\u0000-\u00cb\u0001\u0000"+ + "\u0000\u0000/\u00cf\u0001\u0000\u0000\u00001\u00d3\u0001\u0000\u0000\u0000"+ + "3\u00d7\u0001\u0000\u0000\u00005\u00da\u0001\u0000\u0000\u00007\u00de"+ + "\u0001\u0000\u0000\u00009\u00e0\u0001\u0000\u0000\u0000;\u00e3\u0001\u0000"+ + "\u0000\u0000=\u00e5\u0001\u0000\u0000\u0000?\u00eb\u0001\u0000\u0000\u0000"+ + "A\u00ed\u0001\u0000\u0000\u0000C\u00f0\u0001\u0000\u0000\u0000E\u00f2"+ + "\u0001\u0000\u0000\u0000G\u00f4\u0001\u0000\u0000\u0000I\u00f6\u0001\u0000"+ + "\u0000\u0000K\u00f8\u0001\u0000\u0000\u0000M\u00fa\u0001\u0000\u0000\u0000"+ + "O\u00fc\u0001\u0000\u0000\u0000Q\u00ff\u0001\u0000\u0000\u0000S\u010a"+ + "\u0001\u0000\u0000\u0000U\u0122\u0001\u0000\u0000\u0000W\u0124\u0001\u0000"+ + "\u0000\u0000Y\u012c\u0001\u0000\u0000\u0000[\u0132\u0001\u0000\u0000\u0000"+ + "]\u013d\u0001\u0000\u0000\u0000_`\u0007\u0000\u0000\u0000`a\u0007\u0001"+ + "\u0000\u0000ab\u0007\u0002\u0000\u0000b\u0002\u0001\u0000\u0000\u0000"+ + "cd\u0007\u0003\u0000\u0000de\u0007\u0004\u0000\u0000e\u0004\u0001\u0000"+ + "\u0000\u0000fg\u0007\u0001\u0000\u0000gh\u0007\u0003\u0000\u0000hi\u0007"+ + "\u0005\u0000\u0000i\u0006\u0001\u0000\u0000\u0000jk\u0007\u0006\u0000"+ + "\u0000kl\u0007\u0007\u0000\u0000lm\u0007\b\u0000\u0000mn\u0007\u0007\u0000"+ + "\u0000no\u0007\t\u0000\u0000op\u0007\u0005\u0000\u0000p\b\u0001\u0000"+ + "\u0000\u0000qr\u0007\n\u0000\u0000rs\u0007\u0004\u0000\u0000st\u0007\u0003"+ + "\u0000\u0000tu\u0007\u000b\u0000\u0000u\n\u0001\u0000\u0000\u0000vw\u0007"+ + "\f\u0000\u0000wx\u0007\r\u0000\u0000xy\u0007\u0007\u0000\u0000yz\u0007"+ + "\u0004\u0000\u0000z{\u0007\u0007\u0000\u0000{\f\u0001\u0000\u0000\u0000"+ + "|}\u0007\u000e\u0000\u0000}~\u0007\u0004\u0000\u0000~\u007f\u0007\u0003"+ + "\u0000\u0000\u007f\u0080\u0007\u000f\u0000\u0000\u0080\u0081\u0007\u0010"+ + "\u0000\u0000\u0081\u000e\u0001\u0000\u0000\u0000\u0082\u0083\u0007\u0011"+ + "\u0000\u0000\u0083\u0084\u0007\u0012\u0000\u0000\u0084\u0010\u0001\u0000"+ + "\u0000\u0000\u0085\u0086\u0007\r\u0000\u0000\u0086\u0087\u0007\u0000\u0000"+ + "\u0000\u0087\u0088\u0007\u0013\u0000\u0000\u0088\u0089\u0007\u0014\u0000"+ + "\u0000\u0089\u008a\u0007\u0001\u0000\u0000\u008a\u008b\u0007\u000e\u0000"+ + "\u0000\u008b\u0012\u0001\u0000\u0000\u0000\u008c\u008d\u0007\u0003\u0000"+ + "\u0000\u008d\u008e\u0007\u0004\u0000\u0000\u008e\u008f\u0007\u0002\u0000"+ + "\u0000\u008f\u0090\u0007\u0007\u0000\u0000\u0090\u0091\u0007\u0004\u0000"+ + "\u0000\u0091\u0014\u0001\u0000\u0000\u0000\u0092\u0093\u0007\b\u0000\u0000"+ + "\u0093\u0094\u0007\u0014\u0000\u0000\u0094\u0095\u0007\u000b\u0000\u0000"+ + "\u0095\u0096\u0007\u0014\u0000\u0000\u0096\u0097\u0007\u0005\u0000\u0000"+ + "\u0097\u0016\u0001\u0000\u0000\u0000\u0098\u0099\u0007\u0003\u0000\u0000"+ + "\u0099\u009a\u0007\n\u0000\u0000\u009a\u009b\u0007\n\u0000\u0000\u009b"+ + "\u009c\u0007\u0006\u0000\u0000\u009c\u009d\u0007\u0007\u0000\u0000\u009d"+ + "\u009e\u0007\u0005\u0000\u0000\u009e\u0018\u0001\u0000\u0000\u0000\u009f"+ + "\u00a0\u0007\u0000\u0000\u0000\u00a0\u00a1\u0007\u0006\u0000\u0000\u00a1"+ + "\u001a\u0001\u0000\u0000\u0000\u00a2\u00a3\u0007\u0000\u0000\u0000\u00a3"+ + "\u00a4\u0007\u0006\u0000\u0000\u00a4\u00a5\u0007\t\u0000\u0000\u00a5\u001c"+ + "\u0001\u0000\u0000\u0000\u00a6\u00a7\u0007\u0002\u0000\u0000\u00a7\u00a8"+ + "\u0007\u0007\u0000\u0000\u00a8\u00a9\u0007\u0006\u0000\u0000\u00a9\u00aa"+ + "\u0007\t\u0000\u0000\u00aa\u001e\u0001\u0000\u0000\u0000\u00ab\u00ac\u0007"+ + "\u0014\u0000\u0000\u00ac\u00ad\u0007\u0001\u0000\u0000\u00ad \u0001\u0000"+ + "\u0000\u0000\u00ae\u00af\u0007\u0014\u0000\u0000\u00af\u00b0\u0007\u0006"+ + "\u0000\u0000\u00b0\"\u0001\u0000\u0000\u0000\u00b1\u00b2\u0007\u0001\u0000"+ + "\u0000\u00b2\u00b3\u0007\u000f\u0000\u0000\u00b3\u00b4\u0007\b\u0000\u0000"+ + "\u00b4\u00b5\u0007\b\u0000\u0000\u00b5$\u0001\u0000\u0000\u0000\u00b6"+ + "\u00b7\u0007\b\u0000\u0000\u00b7\u00b8\u0007\u0014\u0000\u0000\u00b8\u00b9"+ + "\u0007\u0015\u0000\u0000\u00b9\u00ba\u0007\u0007\u0000\u0000\u00ba&\u0001"+ + "\u0000\u0000\u0000\u00bb\u00bc\u0007\u0011\u0000\u0000\u00bc\u00bd\u0007"+ + "\u0007\u0000\u0000\u00bd\u00be\u0007\u0005\u0000\u0000\u00be\u00bf\u0007"+ + "\f\u0000\u0000\u00bf\u00c0\u0007\u0007\u0000\u0000\u00c0\u00c1\u0007\u0007"+ + "\u0000\u0000\u00c1\u00c2\u0007\u0001\u0000\u0000\u00c2(\u0001\u0000\u0000"+ + "\u0000\u00c3\u00c4\u0005*\u0000\u0000\u00c4*\u0001\u0000\u0000\u0000\u00c5"+ + "\u00c6\u0007\t\u0000\u0000\u00c6\u00c7\u0007\u0003\u0000\u0000\u00c7\u00c8"+ + "\u0007\u000f\u0000\u0000\u00c8\u00c9\u0007\u0001\u0000\u0000\u00c9\u00ca"+ + "\u0007\u0005\u0000\u0000\u00ca,\u0001\u0000\u0000\u0000\u00cb\u00cc\u0007"+ + "\u0006\u0000\u0000\u00cc\u00cd\u0007\u000f\u0000\u0000\u00cd\u00ce\u0007"+ + "\u000b\u0000\u0000\u00ce.\u0001\u0000\u0000\u0000\u00cf\u00d0\u0007\u0000"+ + "\u0000\u0000\u00d0\u00d1\u0007\u0013\u0000\u0000\u00d1\u00d2\u0007\u000e"+ + "\u0000\u0000\u00d20\u0001\u0000\u0000\u0000\u00d3\u00d4\u0007\u000b\u0000"+ + "\u0000\u00d4\u00d5\u0007\u0014\u0000\u0000\u00d5\u00d6\u0007\u0001\u0000"+ + "\u0000\u00d62\u0001\u0000\u0000\u0000\u00d7\u00d8\u0007\u0000\u0000\u0000"+ + "\u00d8\u00d9\u0007\u0016\u0000\u0000\u00d94\u0001\u0000\u0000\u0000\u00da"+ + "\u00db\u0007\u0006\u0000\u0000\u00db\u00dc\u0007\u0017\u0000\u0000\u00dc"+ + "\u00dd\u0007\b\u0000\u0000\u00dd6\u0001\u0000\u0000\u0000\u00de\u00df"+ + "\u0005>\u0000\u0000\u00df8\u0001\u0000\u0000\u0000\u00e0\u00e1\u0005>"+ + "\u0000\u0000\u00e1\u00e2\u0005=\u0000\u0000\u00e2:\u0001\u0000\u0000\u0000"+ + "\u00e3\u00e4\u0005<\u0000\u0000\u00e4<\u0001\u0000\u0000\u0000\u00e5\u00e6"+ + "\u0005<\u0000\u0000\u00e6\u00e7\u0005=\u0000\u0000\u00e7>\u0001\u0000"+ + "\u0000\u0000\u00e8\u00e9\u0005=\u0000\u0000\u00e9\u00ec\u0005=\u0000\u0000"+ + "\u00ea\u00ec\u0005=\u0000\u0000\u00eb\u00e8\u0001\u0000\u0000\u0000\u00eb"+ + "\u00ea\u0001\u0000\u0000\u0000\u00ec@\u0001\u0000\u0000\u0000\u00ed\u00ee"+ + "\u0005!\u0000\u0000\u00ee\u00ef\u0005=\u0000\u0000\u00efB\u0001\u0000"+ + "\u0000\u0000\u00f0\u00f1\u0005(\u0000\u0000\u00f1D\u0001\u0000\u0000\u0000"+ + "\u00f2\u00f3\u0005)\u0000\u0000\u00f3F\u0001\u0000\u0000\u0000\u00f4\u00f5"+ + "\u0005[\u0000\u0000\u00f5H\u0001\u0000\u0000\u0000\u00f6\u00f7\u0005]"+ + "\u0000\u0000\u00f7J\u0001\u0000\u0000\u0000\u00f8\u00f9\u0005,\u0000\u0000"+ + "\u00f9L\u0001\u0000\u0000\u0000\u00fa\u00fb\u0005.\u0000\u0000\u00fbN"+ + "\u0001\u0000\u0000\u0000\u00fc\u00fd\u0005;\u0000\u0000\u00fdP\u0001\u0000"+ + "\u0000\u0000\u00fe\u0100\u0007\u0018\u0000\u0000\u00ff\u00fe\u0001\u0000"+ + "\u0000\u0000\u0100\u0101\u0001\u0000\u0000\u0000\u0101\u00ff\u0001\u0000"+ + "\u0000\u0000\u0101\u0102\u0001\u0000\u0000\u0000\u0102\u0103\u0001\u0000"+ + "\u0000\u0000\u0103\u0105\u0005.\u0000\u0000\u0104\u0106\u0007\u0018\u0000"+ + "\u0000\u0105\u0104\u0001\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000"+ + "\u0000\u0107\u0105\u0001\u0000\u0000\u0000\u0107\u0108\u0001\u0000\u0000"+ + "\u0000\u0108R\u0001\u0000\u0000\u0000\u0109\u010b\u0007\u0018\u0000\u0000"+ + "\u010a\u0109\u0001\u0000\u0000\u0000\u010b\u010c\u0001\u0000\u0000\u0000"+ + "\u010c\u010a\u0001\u0000\u0000\u0000\u010c\u010d\u0001\u0000\u0000\u0000"+ + "\u010dT\u0001\u0000\u0000\u0000\u010e\u0114\u0005\"\u0000\u0000\u010f"+ + "\u0113\b\u0019\u0000\u0000\u0110\u0111\u0005\\\u0000\u0000\u0111\u0113"+ + "\t\u0000\u0000\u0000\u0112\u010f\u0001\u0000\u0000\u0000\u0112\u0110\u0001"+ + "\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000\u0114\u0112\u0001"+ + "\u0000\u0000\u0000\u0114\u0115\u0001\u0000\u0000\u0000\u0115\u0117\u0001"+ + "\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u0123\u0005"+ + "\"\u0000\u0000\u0118\u011e\u0005\'\u0000\u0000\u0119\u011d\b\u001a\u0000"+ + "\u0000\u011a\u011b\u0005\\\u0000\u0000\u011b\u011d\t\u0000\u0000\u0000"+ + "\u011c\u0119\u0001\u0000\u0000\u0000\u011c\u011a\u0001\u0000\u0000\u0000"+ + "\u011d\u0120\u0001\u0000\u0000\u0000\u011e\u011c\u0001\u0000\u0000\u0000"+ + "\u011e\u011f\u0001\u0000\u0000\u0000\u011f\u0121\u0001\u0000\u0000\u0000"+ + "\u0120\u011e\u0001\u0000\u0000\u0000\u0121\u0123\u0005\'\u0000\u0000\u0122"+ + "\u010e\u0001\u0000\u0000\u0000\u0122\u0118\u0001\u0000\u0000\u0000\u0123"+ + "V\u0001\u0000\u0000\u0000\u0124\u0128\u0007\u001b\u0000\u0000\u0125\u0127"+ + "\u0007\u001c\u0000\u0000\u0126\u0125\u0001\u0000\u0000\u0000\u0127\u012a"+ + "\u0001\u0000\u0000\u0000\u0128\u0126\u0001\u0000\u0000\u0000\u0128\u0129"+ + "\u0001\u0000\u0000\u0000\u0129X\u0001\u0000\u0000\u0000\u012a\u0128\u0001"+ + "\u0000\u0000\u0000\u012b\u012d\u0007\u001d\u0000\u0000\u012c\u012b\u0001"+ + "\u0000\u0000\u0000\u012d\u012e\u0001\u0000\u0000\u0000\u012e\u012c\u0001"+ + "\u0000\u0000\u0000\u012e\u012f\u0001\u0000\u0000\u0000\u012f\u0130\u0001"+ + "\u0000\u0000\u0000\u0130\u0131\u0006,\u0000\u0000\u0131Z\u0001\u0000\u0000"+ + "\u0000\u0132\u0133\u0005/\u0000\u0000\u0133\u0134\u0005/\u0000\u0000\u0134"+ + "\u0138\u0001\u0000\u0000\u0000\u0135\u0137\b\u001e\u0000\u0000\u0136\u0135"+ + "\u0001\u0000\u0000\u0000\u0137\u013a\u0001\u0000\u0000\u0000\u0138\u0136"+ + "\u0001\u0000\u0000\u0000\u0138\u0139\u0001\u0000\u0000\u0000\u0139\u013b"+ + "\u0001\u0000\u0000\u0000\u013a\u0138\u0001\u0000\u0000\u0000\u013b\u013c"+ + "\u0006-\u0001\u0000\u013c\\\u0001\u0000\u0000\u0000\u013d\u013e\u0005"+ + "/\u0000\u0000\u013e\u013f\u0005*\u0000\u0000\u013f\u0143\u0001\u0000\u0000"+ + "\u0000\u0140\u0142\t\u0000\u0000\u0000\u0141\u0140\u0001\u0000\u0000\u0000"+ + "\u0142\u0145\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+ + "\u0143\u0141\u0001\u0000\u0000\u0000\u0144\u0146\u0001\u0000\u0000\u0000"+ + "\u0145\u0143\u0001\u0000\u0000\u0000\u0146\u0147\u0005*\u0000\u0000\u0147"+ + "\u0148\u0005/\u0000\u0000\u0148\u0149\u0001\u0000\u0000\u0000\u0149\u014a"+ + "\u0006.\u0001\u0000\u014a^\u0001\u0000\u0000\u0000\u000e\u0000\u00eb\u0101"+ + "\u0107\u010c\u0112\u0114\u011c\u011e\u0122\u0128\u012e\u0138\u0143\u0002"+ + "\u0000\u0001\u0000\u0006\u0000\u0000"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java new file mode 100644 index 00000000000..dc55f5d8cab --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java @@ -0,0 +1,278 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link SqlExpressionParser}. + */ +public interface SqlExpressionListener extends ParseTreeListener { + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterExpression(SqlExpressionParser.ExpressionContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitExpression(SqlExpressionParser.ExpressionContext ctx); + + /** + * Enter a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + + /** + * Exit a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + + /** + * Enter a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + + /** + * Exit a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + */ + void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + */ + void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + */ + void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + */ + void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + */ + void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + */ + void enterSelectField(SqlExpressionParser.SelectFieldContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + */ + void exitSelectField(SqlExpressionParser.SelectFieldContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + */ + void enterRelList(SqlExpressionParser.RelListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + */ + void exitRelList(SqlExpressionParser.RelListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + */ + void enterRelation(SqlExpressionParser.RelationContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + */ + void exitRelation(SqlExpressionParser.RelationContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + */ + void enterConditionList(SqlExpressionParser.ConditionListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + */ + void exitConditionList(SqlExpressionParser.ConditionListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + */ + void enterCompOp(SqlExpressionParser.CompOpContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + */ + void exitCompOp(SqlExpressionParser.CompOpContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + */ + void enterCondition(SqlExpressionParser.ConditionContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + */ + void exitCondition(SqlExpressionParser.ConditionContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + */ + void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + */ + void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + */ + void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + */ + void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + */ + void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + */ + void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + */ + void enterParameterList(SqlExpressionParser.ParameterListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + */ + void exitParameterList(SqlExpressionParser.ParameterListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + */ + void enterParameter(SqlExpressionParser.ParameterContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + */ + void exitParameter(SqlExpressionParser.ParameterContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + */ + void enterGroupByList(SqlExpressionParser.GroupByListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + */ + void exitGroupByList(SqlExpressionParser.GroupByListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + */ + void enterOrderByList(SqlExpressionParser.OrderByListContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + */ + void exitOrderByList(SqlExpressionParser.OrderByListContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + */ + void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + */ + void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + */ + void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + */ + void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + */ + void enterNumber(SqlExpressionParser.NumberContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + */ + void exitNumber(SqlExpressionParser.NumberContext ctx); + + /** + * Enter a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + */ + void enterString(SqlExpressionParser.StringContext ctx); + + /** + * Exit a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + */ + void exitString(SqlExpressionParser.StringContext ctx); +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java new file mode 100644 index 00000000000..0c4e900a242 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java @@ -0,0 +1,2113 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.FailedPredicateException; +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; + +import java.util.List; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class SqlExpressionParser extends Parser { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, + ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, + LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, + SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, + RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, + NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; + public static final int + RULE_expression = 0, RULE_sqlExpr = 1, RULE_selectSql = 2, RULE_selectFieldList = 3, + RULE_selectField = 4, RULE_relList = 5, RULE_relation = 6, RULE_conditionList = 7, + RULE_compOp = 8, RULE_condition = 9, RULE_conditionUnit = 10, RULE_functionCall = 11, + RULE_functionName = 12, RULE_parameterList = 13, RULE_parameter = 14, + RULE_groupByList = 15, RULE_orderByList = 16, RULE_orderByField = 17, + RULE_limitClause = 18, RULE_number = 19, RULE_string = 20; + private static String[] makeRuleNames() { + return new String[] { + "expression", "sqlExpr", "selectSql", "selectFieldList", "selectField", + "relList", "relation", "conditionList", "compOp", "condition", "conditionUnit", + "functionCall", "functionName", "parameterList", "parameter", "groupByList", + "orderByList", "orderByField", "limitClause", "number", "string" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "'*'", null, null, + null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", + "')'", "'['", "']'", "','", "'.'", "';'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", + "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", + "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", + "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", + "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", + "WS", "LINE_COMMENT", "BLOCK_COMMENT" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "SqlExpression.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public SqlExpressionParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @SuppressWarnings("CheckReturnValue") + public static class ExpressionContext extends ParserRuleContext { + public SqlExprContext sqlExpr() { + return getRuleContext(SqlExprContext.class,0); + } + public TerminalNode EOF() { return getToken(SqlExpressionParser.EOF, 0); } + public ExpressionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_expression; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterExpression(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitExpression(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitExpression(this); + else return visitor.visitChildren(this); + } + } + + public final ExpressionContext expression() throws RecognitionException { + ExpressionContext _localctx = new ExpressionContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_expression); + try { + enterOuterAlt(_localctx, 1); + { + setState(42); + sqlExpr(); + setState(43); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SqlExprContext extends ParserRuleContext { + public SqlExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_sqlExpr; } + + public SqlExprContext() { } + public void copyFrom(SqlExprContext ctx) { + super.copyFrom(ctx); + } + } + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlCallExprContext extends SqlExprContext { + public TerminalNode SQL_FUNCTION() { return getToken(SqlExpressionParser.SQL_FUNCTION, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public SelectSqlCallExprContext(SqlExprContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlCallExpr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlCallExpr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlCallExpr(this); + else return visitor.visitChildren(this); + } + } + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlExprContext extends SqlExprContext { + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public SelectSqlExprContext(SqlExprContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlExpr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlExpr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlExpr(this); + else return visitor.visitChildren(this); + } + } + + public final SqlExprContext sqlExpr() throws RecognitionException { + SqlExprContext _localctx = new SqlExprContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_sqlExpr); + try { + setState(51); + _errHandler.sync(this); + switch (_input.LA(1)) { + case SELECT: + _localctx = new SelectSqlExprContext(_localctx); + enterOuterAlt(_localctx, 1); + { + setState(45); + selectSql(); + } + break; + case SQL_FUNCTION: + _localctx = new SelectSqlCallExprContext(_localctx); + enterOuterAlt(_localctx, 2); + { + setState(46); + match(SQL_FUNCTION); + setState(47); + match(LPAREN); + setState(48); + string(); + setState(49); + match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectSqlContext extends ParserRuleContext { + public TerminalNode SELECT() { return getToken(SqlExpressionParser.SELECT, 0); } + public SelectFieldListContext selectFieldList() { + return getRuleContext(SelectFieldListContext.class,0); + } + public TerminalNode FROM() { return getToken(SqlExpressionParser.FROM, 0); } + public RelListContext relList() { + return getRuleContext(RelListContext.class,0); + } + public TerminalNode WHERE() { return getToken(SqlExpressionParser.WHERE, 0); } + public List conditionList() { + return getRuleContexts(ConditionListContext.class); + } + public ConditionListContext conditionList(int i) { + return getRuleContext(ConditionListContext.class,i); + } + public TerminalNode GROUP() { return getToken(SqlExpressionParser.GROUP, 0); } + public List BY() { return getTokens(SqlExpressionParser.BY); } + public TerminalNode BY(int i) { + return getToken(SqlExpressionParser.BY, i); + } + public GroupByListContext groupByList() { + return getRuleContext(GroupByListContext.class,0); + } + public TerminalNode HAVING() { return getToken(SqlExpressionParser.HAVING, 0); } + public TerminalNode ORDER() { return getToken(SqlExpressionParser.ORDER, 0); } + public OrderByListContext orderByList() { + return getRuleContext(OrderByListContext.class,0); + } + public TerminalNode LIMIT() { return getToken(SqlExpressionParser.LIMIT, 0); } + public LimitClauseContext limitClause() { + return getRuleContext(LimitClauseContext.class,0); + } + public SelectSqlContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectSql; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSql(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSql(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSql(this); + else return visitor.visitChildren(this); + } + } + + public final SelectSqlContext selectSql() throws RecognitionException { + SelectSqlContext _localctx = new SelectSqlContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_selectSql); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(53); + match(SELECT); + setState(54); + selectFieldList(); + setState(55); + match(FROM); + setState(56); + relList(); + setState(59); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==WHERE) { + { + setState(57); + match(WHERE); + setState(58); + conditionList(0); + } + } + + setState(64); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==GROUP) { + { + setState(61); + match(GROUP); + setState(62); + match(BY); + setState(63); + groupByList(); + } + } + + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==HAVING) { + { + setState(66); + match(HAVING); + setState(67); + conditionList(0); + } + } + + setState(73); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ORDER) { + { + setState(70); + match(ORDER); + setState(71); + match(BY); + setState(72); + orderByList(); + } + } + + setState(77); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==LIMIT) { + { + setState(75); + match(LIMIT); + setState(76); + limitClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectFieldListContext extends ParserRuleContext { + public List selectField() { + return getRuleContexts(SelectFieldContext.class); + } + public SelectFieldContext selectField(int i) { + return getRuleContext(SelectFieldContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public SelectFieldListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectFieldList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectFieldList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectFieldList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectFieldList(this); + else return visitor.visitChildren(this); + } + } + + public final SelectFieldListContext selectFieldList() throws RecognitionException { + SelectFieldListContext _localctx = new SelectFieldListContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_selectFieldList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(79); + selectField(); + setState(84); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(80); + match(COMMA); + setState(81); + selectField(); + } + } + setState(86); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelectFieldContext extends ParserRuleContext { + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } + public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } + public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } + public SelectFieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selectField; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectField(this); + else return visitor.visitChildren(this); + } + } + + public final SelectFieldContext selectField() throws RecognitionException { + SelectFieldContext _localctx = new SelectFieldContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_selectField); + int _la; + try { + setState(117); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(87); + functionCall(); + setState(92); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(89); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(88); + match(AS); + } + } + + setState(91); + match(IDENTIFIER); + } + } + + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(94); + match(IDENTIFIER); + setState(99); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(96); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(95); + match(AS); + } + } + + setState(98); + match(IDENTIFIER); + } + } + + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(101); + match(STAR); + setState(106); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(103); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(102); + match(AS); + } + } + + setState(105); + match(IDENTIFIER); + } + } + + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(108); + match(IDENTIFIER); + setState(109); + match(DOT); + setState(110); + match(IDENTIFIER); + setState(115); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(112); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(111); + match(AS); + } + } + + setState(114); + match(IDENTIFIER); + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RelListContext extends ParserRuleContext { + public List relation() { + return getRuleContexts(RelationContext.class); + } + public RelationContext relation(int i) { + return getRuleContext(RelationContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public RelListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_relList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelList(this); + else return visitor.visitChildren(this); + } + } + + public final RelListContext relList() throws RecognitionException { + RelListContext _localctx = new RelListContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_relList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(119); + relation(); + setState(124); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(120); + match(COMMA); + setState(121); + relation(); + } + } + setState(126); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RelationContext extends ParserRuleContext { + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public RelationContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_relation; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelation(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelation(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelation(this); + else return visitor.visitChildren(this); + } + } + + public final RelationContext relation() throws RecognitionException { + RelationContext _localctx = new RelationContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_relation); + int _la; + try { + setState(140); + _errHandler.sync(this); + switch (_input.LA(1)) { + case IDENTIFIER: + enterOuterAlt(_localctx, 1); + { + setState(127); + match(IDENTIFIER); + setState(132); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS || _la==IDENTIFIER) { + { + setState(129); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==AS) { + { + setState(128); + match(AS); + } + } + + setState(131); + match(IDENTIFIER); + } + } + + } + break; + case LPAREN: + enterOuterAlt(_localctx, 2); + { + setState(134); + match(LPAREN); + setState(135); + selectSql(); + setState(136); + match(RPAREN); + setState(137); + match(AS); + setState(138); + match(IDENTIFIER); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionListContext extends ParserRuleContext { + public ConditionContext condition() { + return getRuleContext(ConditionContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public List conditionList() { + return getRuleContexts(ConditionListContext.class); + } + public ConditionListContext conditionList(int i) { + return getRuleContext(ConditionListContext.class,i); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } + public TerminalNode OR() { return getToken(SqlExpressionParser.OR, 0); } + public ConditionListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_conditionList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionList(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionListContext conditionList() throws RecognitionException { + return conditionList(0); + } + + private ConditionListContext conditionList(int _p) throws RecognitionException { + ParserRuleContext _parentctx = _ctx; + int _parentState = getState(); + ConditionListContext _localctx = new ConditionListContext(_ctx, _parentState); + ConditionListContext _prevctx = _localctx; + int _startState = 14; + enterRecursionRule(_localctx, 14, RULE_conditionList, _p); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(148); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,20,_ctx) ) { + case 1: + { + setState(143); + condition(); + } + break; + case 2: + { + setState(144); + match(LPAREN); + setState(145); + conditionList(0); + setState(146); + match(RPAREN); + } + break; + } + _ctx.stop = _input.LT(-1); + setState(158); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + while ( _alt!=2 && _alt!= ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + if ( _parseListeners!=null ) triggerExitRuleEvent(); + _prevctx = _localctx; + { + setState(156); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { + case 1: + { + _localctx = new ConditionListContext(_parentctx, _parentState); + pushNewRecursionContext(_localctx, _startState, RULE_conditionList); + setState(150); + if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); + setState(151); + match(AND); + setState(152); + conditionList(4); + } + break; + case 2: + { + _localctx = new ConditionListContext(_parentctx, _parentState); + pushNewRecursionContext(_localctx, _startState, RULE_conditionList); + setState(153); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(154); + match(OR); + setState(155); + conditionList(3); + } + break; + } + } + } + setState(160); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + unrollRecursionContexts(_parentctx); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class CompOpContext extends ParserRuleContext { + public TerminalNode EQ() { return getToken(SqlExpressionParser.EQ, 0); } + public TerminalNode LT() { return getToken(SqlExpressionParser.LT, 0); } + public TerminalNode GT() { return getToken(SqlExpressionParser.GT, 0); } + public TerminalNode LE() { return getToken(SqlExpressionParser.LE, 0); } + public TerminalNode GE() { return getToken(SqlExpressionParser.GE, 0); } + public TerminalNode NE() { return getToken(SqlExpressionParser.NE, 0); } + public TerminalNode LIKE() { return getToken(SqlExpressionParser.LIKE, 0); } + public TerminalNode NOT() { return getToken(SqlExpressionParser.NOT, 0); } + public TerminalNode IN() { return getToken(SqlExpressionParser.IN, 0); } + public TerminalNode IS() { return getToken(SqlExpressionParser.IS, 0); } + public CompOpContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_compOp; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCompOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCompOp(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCompOp(this); + else return visitor.visitChildren(this); + } + } + + public final CompOpContext compOp() throws RecognitionException { + CompOpContext _localctx = new CompOpContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_compOp); + try { + setState(176); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(161); + match(EQ); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(162); + match(LT); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(163); + match(GT); + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(164); + match(LE); + } + break; + case 5: + enterOuterAlt(_localctx, 5); + { + setState(165); + match(GE); + } + break; + case 6: + enterOuterAlt(_localctx, 6); + { + setState(166); + match(NE); + } + break; + case 7: + enterOuterAlt(_localctx, 7); + { + setState(167); + match(LIKE); + } + break; + case 8: + enterOuterAlt(_localctx, 8); + { + setState(168); + match(NOT); + setState(169); + match(LIKE); + } + break; + case 9: + enterOuterAlt(_localctx, 9); + { + setState(170); + match(IN); + } + break; + case 10: + enterOuterAlt(_localctx, 10); + { + setState(171); + match(NOT); + setState(172); + match(IN); + } + break; + case 11: + enterOuterAlt(_localctx, 11); + { + setState(173); + match(IS); + } + break; + case 12: + enterOuterAlt(_localctx, 12); + { + setState(174); + match(IS); + setState(175); + match(NOT); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionContext extends ParserRuleContext { + public List conditionUnit() { + return getRuleContexts(ConditionUnitContext.class); + } + public ConditionUnitContext conditionUnit(int i) { + return getRuleContext(ConditionUnitContext.class,i); + } + public CompOpContext compOp() { + return getRuleContext(CompOpContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public ConditionContext condition() { + return getRuleContext(ConditionContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public TerminalNode BETWEEN() { return getToken(SqlExpressionParser.BETWEEN, 0); } + public List number() { + return getRuleContexts(NumberContext.class); + } + public NumberContext number(int i) { + return getRuleContext(NumberContext.class,i); + } + public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } + public ConditionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_condition; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCondition(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCondition(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCondition(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionContext condition() throws RecognitionException { + ConditionContext _localctx = new ConditionContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_condition); + try { + setState(192); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(178); + conditionUnit(); + setState(179); + compOp(); + setState(180); + conditionUnit(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(182); + match(LPAREN); + setState(183); + condition(); + setState(184); + match(RPAREN); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(186); + match(IDENTIFIER); + setState(187); + match(BETWEEN); + setState(188); + number(); + setState(189); + match(AND); + setState(190); + number(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ConditionUnitContext extends ParserRuleContext { + public NumberContext number() { + return getRuleContext(NumberContext.class,0); + } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } + public TerminalNode NULL() { return getToken(SqlExpressionParser.NULL, 0); } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public SelectSqlContext selectSql() { + return getRuleContext(SelectSqlContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public ConditionUnitContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_conditionUnit; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionUnit(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionUnit(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionUnit(this); + else return visitor.visitChildren(this); + } + } + + public final ConditionUnitContext conditionUnit() throws RecognitionException { + ConditionUnitContext _localctx = new ConditionUnitContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_conditionUnit); + try { + setState(206); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(194); + number(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(195); + string(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(196); + match(IDENTIFIER); + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(197); + match(IDENTIFIER); + setState(198); + match(DOT); + setState(199); + match(IDENTIFIER); + } + break; + case 5: + enterOuterAlt(_localctx, 5); + { + setState(200); + match(NULL); + } + break; + case 6: + enterOuterAlt(_localctx, 6); + { + setState(201); + match(LPAREN); + setState(202); + selectSql(); + setState(203); + match(RPAREN); + } + break; + case 7: + enterOuterAlt(_localctx, 7); + { + setState(205); + functionCall(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class FunctionCallContext extends ParserRuleContext { + public FunctionNameContext functionName() { + return getRuleContext(FunctionNameContext.class,0); + } + public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } + public ParameterListContext parameterList() { + return getRuleContext(ParameterListContext.class,0); + } + public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } + public FunctionCallContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functionCall; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionCall(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionCall(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionCall(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionCallContext functionCall() throws RecognitionException { + FunctionCallContext _localctx = new FunctionCallContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_functionCall); + try { + enterOuterAlt(_localctx, 1); + { + setState(208); + functionName(); + setState(209); + match(LPAREN); + setState(210); + parameterList(); + setState(211); + match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class FunctionNameContext extends ParserRuleContext { + public TerminalNode COUNT() { return getToken(SqlExpressionParser.COUNT, 0); } + public TerminalNode AVG() { return getToken(SqlExpressionParser.AVG, 0); } + public TerminalNode SUM() { return getToken(SqlExpressionParser.SUM, 0); } + public TerminalNode MIN() { return getToken(SqlExpressionParser.MIN, 0); } + public TerminalNode MAX() { return getToken(SqlExpressionParser.MAX, 0); } + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public FunctionNameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functionName; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionName(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionName(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionName(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionNameContext functionName() throws RecognitionException { + FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_functionName); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(213); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 17592316067840L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParameterListContext extends ParserRuleContext { + public List parameter() { + return getRuleContexts(ParameterContext.class); + } + public ParameterContext parameter(int i) { + return getRuleContext(ParameterContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public ParameterListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parameterList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameterList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameterList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameterList(this); + else return visitor.visitChildren(this); + } + } + + public final ParameterListContext parameterList() throws RecognitionException { + ParameterListContext _localctx = new ParameterListContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_parameterList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(215); + parameter(); + setState(220); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(216); + match(COMMA); + setState(217); + parameter(); + } + } + setState(222); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParameterContext extends ParserRuleContext { + public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public ParameterContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parameter; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameter(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameter(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameter(this); + else return visitor.visitChildren(this); + } + } + + public final ParameterContext parameter() throws RecognitionException { + ParameterContext _localctx = new ParameterContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_parameter); + try { + setState(225); + _errHandler.sync(this); + switch (_input.LA(1)) { + case STAR: + enterOuterAlt(_localctx, 1); + { + setState(223); + match(STAR); + } + break; + case STRING: + enterOuterAlt(_localctx, 2); + { + setState(224); + string(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class GroupByListContext extends ParserRuleContext { + public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } + public TerminalNode IDENTIFIER(int i) { + return getToken(SqlExpressionParser.IDENTIFIER, i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public GroupByListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_groupByList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterGroupByList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitGroupByList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitGroupByList(this); + else return visitor.visitChildren(this); + } + } + + public final GroupByListContext groupByList() throws RecognitionException { + GroupByListContext _localctx = new GroupByListContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_groupByList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(227); + match(IDENTIFIER); + setState(232); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(228); + match(COMMA); + setState(229); + match(IDENTIFIER); + } + } + setState(234); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class OrderByListContext extends ParserRuleContext { + public List orderByField() { + return getRuleContexts(OrderByFieldContext.class); + } + public OrderByFieldContext orderByField(int i) { + return getRuleContext(OrderByFieldContext.class,i); + } + public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(SqlExpressionParser.COMMA, i); + } + public OrderByListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_orderByList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByList(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByList(this); + else return visitor.visitChildren(this); + } + } + + public final OrderByListContext orderByList() throws RecognitionException { + OrderByListContext _localctx = new OrderByListContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_orderByList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(235); + orderByField(); + setState(240); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(236); + match(COMMA); + setState(237); + orderByField(); + } + } + setState(242); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class OrderByFieldContext extends ParserRuleContext { + public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } + public TerminalNode ASC() { return getToken(SqlExpressionParser.ASC, 0); } + public TerminalNode DESC() { return getToken(SqlExpressionParser.DESC, 0); } + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class,0); + } + public OrderByFieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_orderByField; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByField(this); + else return visitor.visitChildren(this); + } + } + + public final OrderByFieldContext orderByField() throws RecognitionException { + OrderByFieldContext _localctx = new OrderByFieldContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_orderByField); + int _la; + try { + setState(251); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(243); + match(IDENTIFIER); + setState(245); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ASC || _la==DESC) { + { + setState(244); + _la = _input.LA(1); + if ( !(_la==ASC || _la==DESC) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(247); + functionCall(); + setState(249); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==ASC || _la==DESC) { + { + setState(248); + _la = _input.LA(1); + if ( !(_la==ASC || _la==DESC) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class LimitClauseContext extends ParserRuleContext { + public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } + public LimitClauseContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_limitClause; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterLimitClause(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitLimitClause(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitLimitClause(this); + else return visitor.visitChildren(this); + } + } + + public final LimitClauseContext limitClause() throws RecognitionException { + LimitClauseContext _localctx = new LimitClauseContext(_ctx, getState()); + enterRule(_localctx, 36, RULE_limitClause); + try { + enterOuterAlt(_localctx, 1); + { + setState(253); + match(NUMBER); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class NumberContext extends ParserRuleContext { + public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } + public TerminalNode FLOAT() { return getToken(SqlExpressionParser.FLOAT, 0); } + public NumberContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_number; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterNumber(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitNumber(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitNumber(this); + else return visitor.visitChildren(this); + } + } + + public final NumberContext number() throws RecognitionException { + NumberContext _localctx = new NumberContext(_ctx, getState()); + enterRule(_localctx, 38, RULE_number); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(255); + _la = _input.LA(1); + if ( !(_la==FLOAT || _la==NUMBER) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class StringContext extends ParserRuleContext { + public TerminalNode STRING() { return getToken(SqlExpressionParser.STRING, 0); } + public StringContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_string; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterString(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitString(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitString(this); + else return visitor.visitChildren(this); + } + } + + public final StringContext string() throws RecognitionException { + StringContext _localctx = new StringContext(_ctx, getState()); + enterRule(_localctx, 40, RULE_string); + try { + enterOuterAlt(_localctx, 1); + { + setState(257); + match(STRING); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 7: + return conditionList_sempred((ConditionListContext)_localctx, predIndex); + } + return true; + } + private boolean conditionList_sempred(ConditionListContext _localctx, int predIndex) { + switch (predIndex) { + case 0: + return precpred(_ctx, 3); + case 1: + return precpred(_ctx, 2); + } + return true; + } + + public static final String _serializedATN = + "\u0004\u0001/\u0104\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ + "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ + "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ + "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ + "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012"+ + "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0001\u0000\u0001\u0000"+ + "\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+ + "\u0001\u0001\u0003\u00014\b\u0001\u0001\u0002\u0001\u0002\u0001\u0002"+ + "\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002<\b\u0002\u0001\u0002"+ + "\u0001\u0002\u0001\u0002\u0003\u0002A\b\u0002\u0001\u0002\u0001\u0002"+ + "\u0003\u0002E\b\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002"+ + "J\b\u0002\u0001\u0002\u0001\u0002\u0003\u0002N\b\u0002\u0001\u0003\u0001"+ + "\u0003\u0001\u0003\u0005\u0003S\b\u0003\n\u0003\f\u0003V\t\u0003\u0001"+ + "\u0004\u0001\u0004\u0003\u0004Z\b\u0004\u0001\u0004\u0003\u0004]\b\u0004"+ + "\u0001\u0004\u0001\u0004\u0003\u0004a\b\u0004\u0001\u0004\u0003\u0004"+ + "d\b\u0004\u0001\u0004\u0001\u0004\u0003\u0004h\b\u0004\u0001\u0004\u0003"+ + "\u0004k\b\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003"+ + "\u0004q\b\u0004\u0001\u0004\u0003\u0004t\b\u0004\u0003\u0004v\b\u0004"+ + "\u0001\u0005\u0001\u0005\u0001\u0005\u0005\u0005{\b\u0005\n\u0005\f\u0005"+ + "~\t\u0005\u0001\u0006\u0001\u0006\u0003\u0006\u0082\b\u0006\u0001\u0006"+ + "\u0003\u0006\u0085\b\u0006\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ + "\u0001\u0006\u0001\u0006\u0003\u0006\u008d\b\u0006\u0001\u0007\u0001\u0007"+ + "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007\u0095\b\u0007"+ + "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007"+ + "\u0005\u0007\u009d\b\u0007\n\u0007\f\u0007\u00a0\t\u0007\u0001\b\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0003\b\u00b1\b\b\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001"+ + "\t\u0001\t\u0001\t\u0003\t\u00c1\b\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ + "\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003\n\u00cf"+ + "\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001"+ + "\f\u0001\f\u0001\r\u0001\r\u0001\r\u0005\r\u00db\b\r\n\r\f\r\u00de\t\r"+ + "\u0001\u000e\u0001\u000e\u0003\u000e\u00e2\b\u000e\u0001\u000f\u0001\u000f"+ + "\u0001\u000f\u0005\u000f\u00e7\b\u000f\n\u000f\f\u000f\u00ea\t\u000f\u0001"+ + "\u0010\u0001\u0010\u0001\u0010\u0005\u0010\u00ef\b\u0010\n\u0010\f\u0010"+ + "\u00f2\t\u0010\u0001\u0011\u0001\u0011\u0003\u0011\u00f6\b\u0011\u0001"+ + "\u0011\u0001\u0011\u0003\u0011\u00fa\b\u0011\u0003\u0011\u00fc\b\u0011"+ + "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014"+ + "\u0001\u0014\u0000\u0001\u000e\u0015\u0000\u0002\u0004\u0006\b\n\f\u000e"+ + "\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(\u0000\u0003\u0002"+ + "\u0000\u0016\u001a,,\u0001\u0000\u000e\u000f\u0001\u0000)*\u0121\u0000"+ + "*\u0001\u0000\u0000\u0000\u00023\u0001\u0000\u0000\u0000\u00045\u0001"+ + "\u0000\u0000\u0000\u0006O\u0001\u0000\u0000\u0000\bu\u0001\u0000\u0000"+ + "\u0000\nw\u0001\u0000\u0000\u0000\f\u008c\u0001\u0000\u0000\u0000\u000e"+ + "\u0094\u0001\u0000\u0000\u0000\u0010\u00b0\u0001\u0000\u0000\u0000\u0012"+ + "\u00c0\u0001\u0000\u0000\u0000\u0014\u00ce\u0001\u0000\u0000\u0000\u0016"+ + "\u00d0\u0001\u0000\u0000\u0000\u0018\u00d5\u0001\u0000\u0000\u0000\u001a"+ + "\u00d7\u0001\u0000\u0000\u0000\u001c\u00e1\u0001\u0000\u0000\u0000\u001e"+ + "\u00e3\u0001\u0000\u0000\u0000 \u00eb\u0001\u0000\u0000\u0000\"\u00fb"+ + "\u0001\u0000\u0000\u0000$\u00fd\u0001\u0000\u0000\u0000&\u00ff\u0001\u0000"+ + "\u0000\u0000(\u0101\u0001\u0000\u0000\u0000*+\u0003\u0002\u0001\u0000"+ + "+,\u0005\u0000\u0000\u0001,\u0001\u0001\u0000\u0000\u0000-4\u0003\u0004"+ + "\u0002\u0000./\u0005\u001b\u0000\u0000/0\u0005\"\u0000\u000001\u0003("+ + "\u0014\u000012\u0005#\u0000\u000024\u0001\u0000\u0000\u00003-\u0001\u0000"+ + "\u0000\u00003.\u0001\u0000\u0000\u00004\u0003\u0001\u0000\u0000\u0000"+ + "56\u0005\u0004\u0000\u000067\u0003\u0006\u0003\u000078\u0005\u0005\u0000"+ + "\u00008;\u0003\n\u0005\u00009:\u0005\u0006\u0000\u0000:<\u0003\u000e\u0007"+ + "\u0000;9\u0001\u0000\u0000\u0000;<\u0001\u0000\u0000\u0000<@\u0001\u0000"+ + "\u0000\u0000=>\u0005\u0007\u0000\u0000>?\u0005\b\u0000\u0000?A\u0003\u001e"+ + "\u000f\u0000@=\u0001\u0000\u0000\u0000@A\u0001\u0000\u0000\u0000AD\u0001"+ + "\u0000\u0000\u0000BC\u0005\t\u0000\u0000CE\u0003\u000e\u0007\u0000DB\u0001"+ + "\u0000\u0000\u0000DE\u0001\u0000\u0000\u0000EI\u0001\u0000\u0000\u0000"+ + "FG\u0005\n\u0000\u0000GH\u0005\b\u0000\u0000HJ\u0003 \u0010\u0000IF\u0001"+ + "\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JM\u0001\u0000\u0000\u0000"+ + "KL\u0005\u000b\u0000\u0000LN\u0003$\u0012\u0000MK\u0001\u0000\u0000\u0000"+ + "MN\u0001\u0000\u0000\u0000N\u0005\u0001\u0000\u0000\u0000OT\u0003\b\u0004"+ + "\u0000PQ\u0005&\u0000\u0000QS\u0003\b\u0004\u0000RP\u0001\u0000\u0000"+ + "\u0000SV\u0001\u0000\u0000\u0000TR\u0001\u0000\u0000\u0000TU\u0001\u0000"+ + "\u0000\u0000U\u0007\u0001\u0000\u0000\u0000VT\u0001\u0000\u0000\u0000"+ + "W\\\u0003\u0016\u000b\u0000XZ\u0005\r\u0000\u0000YX\u0001\u0000\u0000"+ + "\u0000YZ\u0001\u0000\u0000\u0000Z[\u0001\u0000\u0000\u0000[]\u0005,\u0000"+ + "\u0000\\Y\u0001\u0000\u0000\u0000\\]\u0001\u0000\u0000\u0000]v\u0001\u0000"+ + "\u0000\u0000^c\u0005,\u0000\u0000_a\u0005\r\u0000\u0000`_\u0001\u0000"+ + "\u0000\u0000`a\u0001\u0000\u0000\u0000ab\u0001\u0000\u0000\u0000bd\u0005"+ + ",\u0000\u0000c`\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000dv\u0001"+ + "\u0000\u0000\u0000ej\u0005\u0015\u0000\u0000fh\u0005\r\u0000\u0000gf\u0001"+ + "\u0000\u0000\u0000gh\u0001\u0000\u0000\u0000hi\u0001\u0000\u0000\u0000"+ + "ik\u0005,\u0000\u0000jg\u0001\u0000\u0000\u0000jk\u0001\u0000\u0000\u0000"+ + "kv\u0001\u0000\u0000\u0000lm\u0005,\u0000\u0000mn\u0005\'\u0000\u0000"+ + "ns\u0005,\u0000\u0000oq\u0005\r\u0000\u0000po\u0001\u0000\u0000\u0000"+ + "pq\u0001\u0000\u0000\u0000qr\u0001\u0000\u0000\u0000rt\u0005,\u0000\u0000"+ + "sp\u0001\u0000\u0000\u0000st\u0001\u0000\u0000\u0000tv\u0001\u0000\u0000"+ + "\u0000uW\u0001\u0000\u0000\u0000u^\u0001\u0000\u0000\u0000ue\u0001\u0000"+ + "\u0000\u0000ul\u0001\u0000\u0000\u0000v\t\u0001\u0000\u0000\u0000w|\u0003"+ + "\f\u0006\u0000xy\u0005&\u0000\u0000y{\u0003\f\u0006\u0000zx\u0001\u0000"+ + "\u0000\u0000{~\u0001\u0000\u0000\u0000|z\u0001\u0000\u0000\u0000|}\u0001"+ + "\u0000\u0000\u0000}\u000b\u0001\u0000\u0000\u0000~|\u0001\u0000\u0000"+ + "\u0000\u007f\u0084\u0005,\u0000\u0000\u0080\u0082\u0005\r\u0000\u0000"+ + "\u0081\u0080\u0001\u0000\u0000\u0000\u0081\u0082\u0001\u0000\u0000\u0000"+ + "\u0082\u0083\u0001\u0000\u0000\u0000\u0083\u0085\u0005,\u0000\u0000\u0084"+ + "\u0081\u0001\u0000\u0000\u0000\u0084\u0085\u0001\u0000\u0000\u0000\u0085"+ + "\u008d\u0001\u0000\u0000\u0000\u0086\u0087\u0005\"\u0000\u0000\u0087\u0088"+ + "\u0003\u0004\u0002\u0000\u0088\u0089\u0005#\u0000\u0000\u0089\u008a\u0005"+ + "\r\u0000\u0000\u008a\u008b\u0005,\u0000\u0000\u008b\u008d\u0001\u0000"+ + "\u0000\u0000\u008c\u007f\u0001\u0000\u0000\u0000\u008c\u0086\u0001\u0000"+ + "\u0000\u0000\u008d\r\u0001\u0000\u0000\u0000\u008e\u008f\u0006\u0007\uffff"+ + "\uffff\u0000\u008f\u0095\u0003\u0012\t\u0000\u0090\u0091\u0005\"\u0000"+ + "\u0000\u0091\u0092\u0003\u000e\u0007\u0000\u0092\u0093\u0005#\u0000\u0000"+ + "\u0093\u0095\u0001\u0000\u0000\u0000\u0094\u008e\u0001\u0000\u0000\u0000"+ + "\u0094\u0090\u0001\u0000\u0000\u0000\u0095\u009e\u0001\u0000\u0000\u0000"+ + "\u0096\u0097\n\u0003\u0000\u0000\u0097\u0098\u0005\u0001\u0000\u0000\u0098"+ + "\u009d\u0003\u000e\u0007\u0004\u0099\u009a\n\u0002\u0000\u0000\u009a\u009b"+ + "\u0005\u0002\u0000\u0000\u009b\u009d\u0003\u000e\u0007\u0003\u009c\u0096"+ + "\u0001\u0000\u0000\u0000\u009c\u0099\u0001\u0000\u0000\u0000\u009d\u00a0"+ + "\u0001\u0000\u0000\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009e\u009f"+ + "\u0001\u0000\u0000\u0000\u009f\u000f\u0001\u0000\u0000\u0000\u00a0\u009e"+ + "\u0001\u0000\u0000\u0000\u00a1\u00b1\u0005 \u0000\u0000\u00a2\u00b1\u0005"+ + "\u001e\u0000\u0000\u00a3\u00b1\u0005\u001c\u0000\u0000\u00a4\u00b1\u0005"+ + "\u001f\u0000\u0000\u00a5\u00b1\u0005\u001d\u0000\u0000\u00a6\u00b1\u0005"+ + "!\u0000\u0000\u00a7\u00b1\u0005\u0013\u0000\u0000\u00a8\u00a9\u0005\u0003"+ + "\u0000\u0000\u00a9\u00b1\u0005\u0013\u0000\u0000\u00aa\u00b1\u0005\u0010"+ + "\u0000\u0000\u00ab\u00ac\u0005\u0003\u0000\u0000\u00ac\u00b1\u0005\u0010"+ + "\u0000\u0000\u00ad\u00b1\u0005\u0011\u0000\u0000\u00ae\u00af\u0005\u0011"+ + "\u0000\u0000\u00af\u00b1\u0005\u0003\u0000\u0000\u00b0\u00a1\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a2\u0001\u0000\u0000\u0000\u00b0\u00a3\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a4\u0001\u0000\u0000\u0000\u00b0\u00a5\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a6\u0001\u0000\u0000\u0000\u00b0\u00a7\u0001\u0000"+ + "\u0000\u0000\u00b0\u00a8\u0001\u0000\u0000\u0000\u00b0\u00aa\u0001\u0000"+ + "\u0000\u0000\u00b0\u00ab\u0001\u0000\u0000\u0000\u00b0\u00ad\u0001\u0000"+ + "\u0000\u0000\u00b0\u00ae\u0001\u0000\u0000\u0000\u00b1\u0011\u0001\u0000"+ + "\u0000\u0000\u00b2\u00b3\u0003\u0014\n\u0000\u00b3\u00b4\u0003\u0010\b"+ + "\u0000\u00b4\u00b5\u0003\u0014\n\u0000\u00b5\u00c1\u0001\u0000\u0000\u0000"+ + "\u00b6\u00b7\u0005\"\u0000\u0000\u00b7\u00b8\u0003\u0012\t\u0000\u00b8"+ + "\u00b9\u0005#\u0000\u0000\u00b9\u00c1\u0001\u0000\u0000\u0000\u00ba\u00bb"+ + "\u0005,\u0000\u0000\u00bb\u00bc\u0005\u0014\u0000\u0000\u00bc\u00bd\u0003"+ + "&\u0013\u0000\u00bd\u00be\u0005\u0001\u0000\u0000\u00be\u00bf\u0003&\u0013"+ + "\u0000\u00bf\u00c1\u0001\u0000\u0000\u0000\u00c0\u00b2\u0001\u0000\u0000"+ + "\u0000\u00c0\u00b6\u0001\u0000\u0000\u0000\u00c0\u00ba\u0001\u0000\u0000"+ + "\u0000\u00c1\u0013\u0001\u0000\u0000\u0000\u00c2\u00cf\u0003&\u0013\u0000"+ + "\u00c3\u00cf\u0003(\u0014\u0000\u00c4\u00cf\u0005,\u0000\u0000\u00c5\u00c6"+ + "\u0005,\u0000\u0000\u00c6\u00c7\u0005\'\u0000\u0000\u00c7\u00cf\u0005"+ + ",\u0000\u0000\u00c8\u00cf\u0005\u0012\u0000\u0000\u00c9\u00ca\u0005\""+ + "\u0000\u0000\u00ca\u00cb\u0003\u0004\u0002\u0000\u00cb\u00cc\u0005#\u0000"+ + "\u0000\u00cc\u00cf\u0001\u0000\u0000\u0000\u00cd\u00cf\u0003\u0016\u000b"+ + "\u0000\u00ce\u00c2\u0001\u0000\u0000\u0000\u00ce\u00c3\u0001\u0000\u0000"+ + "\u0000\u00ce\u00c4\u0001\u0000\u0000\u0000\u00ce\u00c5\u0001\u0000\u0000"+ + "\u0000\u00ce\u00c8\u0001\u0000\u0000\u0000\u00ce\u00c9\u0001\u0000\u0000"+ + "\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000\u00cf\u0015\u0001\u0000\u0000"+ + "\u0000\u00d0\u00d1\u0003\u0018\f\u0000\u00d1\u00d2\u0005\"\u0000\u0000"+ + "\u00d2\u00d3\u0003\u001a\r\u0000\u00d3\u00d4\u0005#\u0000\u0000\u00d4"+ + "\u0017\u0001\u0000\u0000\u0000\u00d5\u00d6\u0007\u0000\u0000\u0000\u00d6"+ + "\u0019\u0001\u0000\u0000\u0000\u00d7\u00dc\u0003\u001c\u000e\u0000\u00d8"+ + "\u00d9\u0005&\u0000\u0000\u00d9\u00db\u0003\u001c\u000e\u0000\u00da\u00d8"+ + "\u0001\u0000\u0000\u0000\u00db\u00de\u0001\u0000\u0000\u0000\u00dc\u00da"+ + "\u0001\u0000\u0000\u0000\u00dc\u00dd\u0001\u0000\u0000\u0000\u00dd\u001b"+ + "\u0001\u0000\u0000\u0000\u00de\u00dc\u0001\u0000\u0000\u0000\u00df\u00e2"+ + "\u0005\u0015\u0000\u0000\u00e0\u00e2\u0003(\u0014\u0000\u00e1\u00df\u0001"+ + "\u0000\u0000\u0000\u00e1\u00e0\u0001\u0000\u0000\u0000\u00e2\u001d\u0001"+ + "\u0000\u0000\u0000\u00e3\u00e8\u0005,\u0000\u0000\u00e4\u00e5\u0005&\u0000"+ + "\u0000\u00e5\u00e7\u0005,\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000"+ + "\u00e7\u00ea\u0001\u0000\u0000\u0000\u00e8\u00e6\u0001\u0000\u0000\u0000"+ + "\u00e8\u00e9\u0001\u0000\u0000\u0000\u00e9\u001f\u0001\u0000\u0000\u0000"+ + "\u00ea\u00e8\u0001\u0000\u0000\u0000\u00eb\u00f0\u0003\"\u0011\u0000\u00ec"+ + "\u00ed\u0005&\u0000\u0000\u00ed\u00ef\u0003\"\u0011\u0000\u00ee\u00ec"+ + "\u0001\u0000\u0000\u0000\u00ef\u00f2\u0001\u0000\u0000\u0000\u00f0\u00ee"+ + "\u0001\u0000\u0000\u0000\u00f0\u00f1\u0001\u0000\u0000\u0000\u00f1!\u0001"+ + "\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000\u0000\u0000\u00f3\u00f5\u0005"+ + ",\u0000\u0000\u00f4\u00f6\u0007\u0001\u0000\u0000\u00f5\u00f4\u0001\u0000"+ + "\u0000\u0000\u00f5\u00f6\u0001\u0000\u0000\u0000\u00f6\u00fc\u0001\u0000"+ + "\u0000\u0000\u00f7\u00f9\u0003\u0016\u000b\u0000\u00f8\u00fa\u0007\u0001"+ + "\u0000\u0000\u00f9\u00f8\u0001\u0000\u0000\u0000\u00f9\u00fa\u0001\u0000"+ + "\u0000\u0000\u00fa\u00fc\u0001\u0000\u0000\u0000\u00fb\u00f3\u0001\u0000"+ + "\u0000\u0000\u00fb\u00f7\u0001\u0000\u0000\u0000\u00fc#\u0001\u0000\u0000"+ + "\u0000\u00fd\u00fe\u0005*\u0000\u0000\u00fe%\u0001\u0000\u0000\u0000\u00ff"+ + "\u0100\u0007\u0002\u0000\u0000\u0100\'\u0001\u0000\u0000\u0000\u0101\u0102"+ + "\u0005+\u0000\u0000\u0102)\u0001\u0000\u0000\u0000!3;@DIMTY\\`cgjpsu|"+ + "\u0081\u0084\u008c\u0094\u009c\u009e\u00b0\u00c0\u00ce\u00dc\u00e1\u00e8"+ + "\u00f0\u00f5\u00f9\u00fb"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java new file mode 100644 index 00000000000..bd8f0d02874 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java @@ -0,0 +1,169 @@ +package org.apache.hertzbeat.alert.expr.sql; + +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link SqlExpressionParser}. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface SqlExpressionVisitor extends ParseTreeVisitor { + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpression(SqlExpressionParser.ExpressionContext ctx); + + /** + * Visit a parse tree produced by the {@code SelectSqlExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); + + /** + * Visit a parse tree produced by the {@code SelectSqlCallExpr} + * labeled alternative in {@link SqlExpressionParser#sqlExpr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectSql}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#selectField}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSelectField(SqlExpressionParser.SelectFieldContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#relList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelList(SqlExpressionParser.RelListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#relation}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelation(SqlExpressionParser.RelationContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#conditionList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitConditionList(SqlExpressionParser.ConditionListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#compOp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitCompOp(SqlExpressionParser.CompOpContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#condition}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitCondition(SqlExpressionParser.ConditionContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#functionCall}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#functionName}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#parameterList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParameterList(SqlExpressionParser.ParameterListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#parameter}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParameter(SqlExpressionParser.ParameterContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#groupByList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitGroupByList(SqlExpressionParser.GroupByListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#orderByList}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOrderByList(SqlExpressionParser.OrderByListContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#orderByField}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#limitClause}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#number}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNumber(SqlExpressionParser.NumberContext ctx); + + /** + * Visit a parse tree produced by {@link SqlExpressionParser#string}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitString(SqlExpressionParser.StringContext ctx); +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/DataSourceService.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/DataSourceService.java index 96dda0c45a9..8baca0b733b 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/DataSourceService.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/DataSourceService.java @@ -32,4 +32,12 @@ public interface DataSourceService { * @return result */ List> calculate(String datasource, String expr); + + /** + * query result set from db + * @param datasource sql or promql + * @param expr query expr + * @return result + */ + List> query(String datasource, String expr); } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java index d41c4a8671f..d27bd61d695 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java @@ -95,6 +95,29 @@ public List> calculate(String datasource, String expr) { } } + @Override + public List> query(String datasource, String expr) { + if (!StringUtils.hasText(expr)) { + throw new IllegalArgumentException("Empty expression"); + } + if (executors == null || executors.isEmpty()) { + throw new IllegalArgumentException(bundle.getString("alerter.datasource.executor.not.found")); + } + QueryExecutor executor = executors.stream().filter(e -> e.support(datasource)).findFirst().orElse(null); + + if (executor == null) { + throw new IllegalArgumentException("Unsupported datasource: " + datasource); + } + // replace all white space + expr = expr.replaceAll("\\s+", " "); + try { + return executor.execute(expr); + } catch (Exception e) { + log.error("Error executing query on datasource {}: {}", datasource, e.getMessage()); + throw new RuntimeException("Query execution failed", e); + } + } + private List> evaluate(String expr, QueryExecutor executor) { CommonTokenStream tokens = tokenStreamCache.get(expr, this::createTokenStream); AlertExpressionParser parser = new AlertExpressionParser(tokens); diff --git a/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 b/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 new file mode 100644 index 00000000000..2c7c925b3b5 --- /dev/null +++ b/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 @@ -0,0 +1,200 @@ +/* + * 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. + */ + +grammar SqlExpression; + +expression + : sqlExpr EOF + ; + +sqlExpr + : selectSql # SelectSqlExpr + | SQL_FUNCTION LPAREN string RPAREN # SelectSqlCallExpr + ; + +// SQL grammar for complex queries +selectSql + : SELECT selectFieldList FROM relList (WHERE conditionList)? + (GROUP BY groupByList)? (HAVING conditionList)? + (ORDER BY orderByList)? (LIMIT limitClause)? + ; + +selectFieldList + : selectField (COMMA selectField)* + ; + +selectField + : functionCall (AS? IDENTIFIER)? + | IDENTIFIER (AS? IDENTIFIER)? + | STAR (AS? IDENTIFIER)? + | IDENTIFIER DOT IDENTIFIER (AS? IDENTIFIER)? + ; + +relList + : relation (COMMA relation)* + ; + +relation + : IDENTIFIER (AS? IDENTIFIER)? + | LPAREN selectSql RPAREN AS IDENTIFIER + ; + +conditionList + : condition + | conditionList AND conditionList + | conditionList OR conditionList + | LPAREN conditionList RPAREN + ; + +compOp + : EQ | LT | GT | LE | GE | NE | LIKE | NOT LIKE | IN | NOT IN | IS | IS NOT + ; + +condition + : conditionUnit compOp conditionUnit + | LPAREN condition RPAREN + | IDENTIFIER BETWEEN number AND number + ; + +conditionUnit + : number + | string + | IDENTIFIER + | IDENTIFIER DOT IDENTIFIER + | NULL + | LPAREN selectSql RPAREN + | functionCall + ; + +functionCall + : functionName LPAREN parameterList RPAREN + ; + +functionName + : COUNT + | AVG + | SUM + | MIN + | MAX + | IDENTIFIER + ; + +parameterList + : parameter (COMMA parameter)* + ; + +parameter + : STAR + | string + ; + +groupByList + : IDENTIFIER (COMMA IDENTIFIER)* + ; + +orderByList + : orderByField (COMMA orderByField)* + ; + +orderByField + : IDENTIFIER (ASC | DESC)? + | functionCall (ASC | DESC)? + ; + +limitClause + : NUMBER + ; + +number + : NUMBER + | FLOAT + ; + +string + : STRING + ; + + +// Lexer rules + +// Boolean operators +AND : [Aa][Nn][Dd] ; +OR : [Oo][Rr] ; +NOT : [Nn][Oo][Tt] ; + +// SQL keywords +SELECT : [Ss][Ee][Ll][Ee][Cc][Tt] ; +FROM : [Ff][Rr][Oo][Mm] ; +WHERE : [Ww][Hh][Ee][Rr][Ee] ; +GROUP : [Gg][Rr][Oo][Uu][Pp] ; +BY : [Bb][Yy] ; +HAVING : [Hh][Aa][Vv][Ii][Nn][Gg] ; +ORDER : [Oo][Rr][Dd][Ee][Rr] ; +LIMIT : [Ll][Ii][Mm][Ii][Tt] ; +OFFSET : [Oo][Ff][Ff][Ss][Ee][Tt] ; +AS : [Aa][Ss] ; +ASC : [Aa][Ss][Cc] ; +DESC : [Dd][Ee][Ss][Cc] ; +IN : [Ii][Nn] ; +IS : [Ii][Ss] ; +NULL : [Nn][Uu][Ll][Ll] ; +LIKE : [Ll][Ii][Kk][Ee] ; +BETWEEN : [Bb][Ee][Tt][Ww][Ee][Ee][Nn] ; +STAR : '*' ; + +// Aggregate functions +COUNT : [Cc][Oo][Uu][Nn][Tt] ; +SUM : [Ss][Uu][Mm] ; +AVG : [Aa][Vv][Gg] ; +MIN : [Mm][Ii][Nn] ; +MAX : [Aa][Xx] ; + +// Other functions +SQL_FUNCTION: [Ss][Qq][Ll] ; + +// Comparison operators +GT : '>' ; +GE : '>=' ; +LT : '<' ; +LE : '<=' ; +EQ : '==' | '=' ; +NE : '!=' ; + +// Delimiters +LPAREN : '(' ; +RPAREN : ')' ; +LBRACKET: '[' ; +RBRACKET: ']' ; +COMMA : ',' ; +DOT : '.' ; +SEMICOLON: ';' ; + +// number formats +FLOAT : [0-9]+ '.' [0-9]+ ; +NUMBER : [0-9]+ ; + +// String literals +STRING : '"' (~["\r\n\\] | '\\' .)* '"' + | '\'' (~['\r\n\\] | '\\' .)* '\'' ; + +// Identifiers +IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* ; + +// Whitespace and comments +WS : [ \t\r\n]+ -> channel(HIDDEN) ; +LINE_COMMENT : '//' ~[\r\n]* -> skip ; +BLOCK_COMMENT : '/*' .*? '*/' -> skip ; \ No newline at end of file diff --git a/hertzbeat-manager/src/main/resources/application.yml b/hertzbeat-manager/src/main/resources/application.yml index 69027e88a67..682a1d79ec6 100644 --- a/hertzbeat-manager/src/main/resources/application.yml +++ b/hertzbeat-manager/src/main/resources/application.yml @@ -125,7 +125,7 @@ warehouse: store: # store history metrics data, enable only one below jpa: - enabled: true + enabled: false # The maximum retention time for history records, after which records will be deleted expire-time: 1h # The maximum number of history records retained, if this number is exceeded, half of the data in this configuration item will be deleted @@ -161,7 +161,7 @@ warehouse: username: root password: taosdata greptime: - enabled: false + enabled: true grpc-endpoints: localhost:4001 http-endpoint: http://localhost:4000 # if you config other database name, you should create them first diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimePromqlQueryExecutor.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimePromqlQueryExecutor.java index 5be5ae4e2a3..1a877c83766 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimePromqlQueryExecutor.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimePromqlQueryExecutor.java @@ -28,14 +28,14 @@ /** * query executor for victor metrics */ -@Component +@Component("greptimePromqlQueryExecutor") @ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true") @Slf4j public class GreptimePromqlQueryExecutor extends PromqlQueryExecutor { private static final String QUERY_PATH = "/v1/prometheus"; - private static final String Datasource = "Greptime"; + private static final String Datasource = "Greptime-promql"; private final GreptimeProperties greptimeProperties; diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java new file mode 100644 index 00000000000..4ef4708f07f --- /dev/null +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java @@ -0,0 +1,114 @@ +package org.apache.hertzbeat.warehouse.db; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.constants.NetworkConstants; +import org.apache.hertzbeat.common.constants.SignConstants; +import org.apache.hertzbeat.common.util.Base64Util; +import org.apache.hertzbeat.warehouse.store.history.tsdb.greptime.GreptimeProperties; +import org.apache.hertzbeat.warehouse.store.history.tsdb.greptime.GreptimeSqlQueryContent; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpEntity; +import org.springframework.http.MediaType; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * query executor for GreptimeDB SQL + */ +@Slf4j +@Component("greptimeSqlQueryExecutor") +@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true") +public class GreptimeSqlQueryExecutor extends SqlQueryExecutor { + + private static final String QUERY_PATH = "/v1/sql"; + private static final String DATASOURCE = "Greptime-sql"; + + private final GreptimeProperties greptimeProperties; + + + public GreptimeSqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) { + super(restTemplate, new SqlQueryExecutor.HttpSqlProperties(greptimeProperties.httpEndpoint() + QUERY_PATH, + greptimeProperties.username(), greptimeProperties.password())); + this.greptimeProperties = greptimeProperties; + } + + @Override + public List> execute(String queryString) { + List> results = new LinkedList<>(); + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + if (StringUtils.hasText(greptimeProperties.username()) + && StringUtils.hasText(greptimeProperties.password())) { + String authStr = greptimeProperties.username() + ":" + greptimeProperties.password(); + String encodedAuth = Base64Util.encode(authStr); + headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth); + } + + String requestBody = "sql=" + queryString; + HttpEntity httpEntity = new HttpEntity<>(requestBody, headers); + + String url = greptimeProperties.httpEndpoint() + QUERY_PATH; + if (StringUtils.hasText(greptimeProperties.database())) { + url += "?db=" + greptimeProperties.database(); + } + + ResponseEntity responseEntity = restTemplate.exchange(url, + HttpMethod.POST, httpEntity, GreptimeSqlQueryContent.class); + + if (responseEntity.getStatusCode().is2xxSuccessful()) { + GreptimeSqlQueryContent responseBody = responseEntity.getBody(); + if (responseBody != null && responseBody.getCode() == 0 + && responseBody.getOutput() != null && !responseBody.getOutput().isEmpty()) { + + for (GreptimeSqlQueryContent.Output output : responseBody.getOutput()) { + if (output.getRecords() != null && output.getRecords().getRows() != null) { + GreptimeSqlQueryContent.Output.Records.Schema schema = output.getRecords().getSchema(); + List> rows = output.getRecords().getRows(); + + for (List row : rows) { + Map rowMap = new HashMap<>(); + if (schema != null && schema.getColumnSchemas() != null) { + for (int i = 0; i < Math.min(schema.getColumnSchemas().size(), row.size()); i++) { + String columnName = schema.getColumnSchemas().get(i).getName(); + Object value = row.get(i); + rowMap.put(columnName, value); + } + } else { + for (int i = 0; i < row.size(); i++) { + rowMap.put("col_" + i, row.get(i)); + } + } + results.add(rowMap); + } + } + } + } + } else { + log.error("query metrics data from greptime failed. {}", responseEntity); + } + } catch (Exception e) { + log.error("query metrics data from greptime error. {}", e.getMessage(), e); + } + return results; + } + + @Override + public String getDatasource() { + return DATASOURCE; + } +} diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/SqlQueryExecutor.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/SqlQueryExecutor.java index c4a2d8e4bd9..6d5478b88b5 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/SqlQueryExecutor.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/SqlQueryExecutor.java @@ -25,6 +25,7 @@ import static org.apache.hertzbeat.warehouse.constants.WarehouseConstants.SQL; import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; @@ -36,11 +37,23 @@ public abstract class SqlQueryExecutor implements QueryExecutor { private static final String supportQueryLanguage = SQL; + protected final RestTemplate restTemplate; + protected final SqlQueryExecutor.HttpSqlProperties httpSqlProperties; + + SqlQueryExecutor(RestTemplate restTemplate, SqlQueryExecutor.HttpSqlProperties httpSqlProperties) { + this.restTemplate = restTemplate; + this.httpSqlProperties = httpSqlProperties; + } /** - * record class for sql connection + * record class for sql http connection */ - protected record ConnectorSqlProperties () {} + protected record HttpSqlProperties( + String url, + String username, + String password + ) { + } @Override public List> execute(String query) { diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java new file mode 100644 index 00000000000..9419bab80e6 --- /dev/null +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java @@ -0,0 +1,94 @@ +/* + * 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.hertzbeat.warehouse.store.history.tsdb.greptime; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * GreptimeDB SQL query content + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GreptimeSqlQueryContent { + + private int code; + + private List output; + + @JsonProperty("execution_time_ms") + private long executionTimeMs; + + /** + * Represents the output of a GreptimeDB SQL query. + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Output { + + private Records records; + + /** + * Represents the records returned by a GreptimeDB SQL query. + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Records { + + private Schema schema; + + private List> rows; + + /** + * Represents the schema of the records returned by a GreptimeDB SQL query. + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Schema { + + @JsonProperty("column_schemas") + private List columnSchemas; + + /** + * Represents a column schema in the records returned by a GreptimeDB SQL query. + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class ColumnSchema { + + private String name; + + @JsonProperty("data_type") + private String dataType; + } + } + } + } +} \ No newline at end of file diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 9005d3a6768..3b33a63e9a7 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -654,7 +654,7 @@ - + {{ 'alert.setting.rule' | i18n }} @@ -664,11 +664,14 @@ + - + @@ -679,7 +682,7 @@ nz-input name="periodic_expr" id="periodic_expr" - [placeholder]="'alert.setting.promql.tip' | i18n" + [placeholder]="define.datasource == 'promql' ? ('alert.setting.promql.tip' | i18n) : ('alert.setting.sql.tip' | i18n)" > From 59796457922a2f7566b20d48e7ec4f83cbe72c10 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 31 Jul 2025 19:39:54 +0800 Subject: [PATCH 18/39] feat: Modify the alert_define table definition to support log querying, log-based alert triggering, and previewing of SQL query results. --- CLAUDE.md | 93 - .../gen/expr/SqlExpression.interp | 126 - .../gen/expr/SqlExpression.tokens | 60 - .../gen/expr/SqlExpressionBaseListener.java | 304 --- .../gen/expr/SqlExpressionBaseVisitor.java | 169 -- .../gen/expr/SqlExpressionLexer.interp | 158 -- .../gen/expr/SqlExpressionLexer.java | 348 --- .../gen/expr/SqlExpressionLexer.tokens | 60 - .../gen/expr/SqlExpressionListener.java | 234 -- .../gen/expr/SqlExpressionParser.java | 2100 ---------------- .../gen/expr/SqlExpressionVisitor.java | 147 -- .../AbstractRealTimeAlertCalculator.java | 50 +- .../alert/calculate/JexlExprCalculator.java | 50 + .../calculate/LogRealTimeAlertCalculator.java | 16 +- .../MetricsRealTimeAlertCalculator.java | 14 +- .../calculate/PeriodicAlertCalculator.java | 30 +- .../expr/sql/SqlExpressionBaseListener.java | 350 --- .../expr/sql/SqlExpressionBaseVisitor.java | 191 -- .../expr/sql/SqlExpressionEvalVisitor.java | 42 - .../alert/expr/sql/SqlExpressionLexer.java | 350 --- .../alert/expr/sql/SqlExpressionListener.java | 278 --- .../alert/expr/sql/SqlExpressionParser.java | 2113 ----------------- .../alert/expr/sql/SqlExpressionVisitor.java | 169 -- .../service/impl/AlertDefineServiceImpl.java | 6 +- .../src/main/resources/expr/SqlExpression.g4 | 200 -- .../LogRealTimeAlertCalculatorTest.java | 29 +- ...tricsRealTimeAlertCalculatorMatchTest.java | 4 + .../MetricsRealTimeAlertCalculatorTest.java | 3 +- .../common/entity/alerter/AlertDefine.java | 7 +- .../src/main/resources/application.yml | 4 +- .../db/migration/h2/V173__update_column.sql | 8 + .../migration/mysql/V173__update_column.sql | 23 +- .../postgresql/V173__update_column.sql | 8 + web-app/src/app/pojo/AlertDefine.ts | 1 + .../alert-setting.component.html | 93 +- .../alert-setting/alert-setting.component.ts | 69 + web-app/src/assets/i18n/en-US.json | 6 + web-app/src/assets/i18n/ja-JP.json | 6 + web-app/src/assets/i18n/pt-BR.json | 6 + web-app/src/assets/i18n/zh-CN.json | 6 + web-app/src/assets/i18n/zh-TW.json | 6 + 41 files changed, 358 insertions(+), 7579 deletions(-) delete mode 100644 CLAUDE.md delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpression.interp delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpression.tokens delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.java delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionListener.java delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionParser.java delete mode 100644 hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java delete mode 100644 hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index c7f21dd8282..00000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,93 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Development Commands - -### Backend (Java/Spring Boot) -- **Build entire project**: `mvn clean install -DskipTests` -- **Run tests**: `mvn test` -- **Run specific module tests**: `mvn test -pl hertzbeat-manager` -- **Package application**: `mvn package -DskipTests` -- **Code quality checks**: `mvn checkstyle:check` (automatically runs in validate phase) -- **Test coverage**: `mvn jacoco:report` (coverage reports generated in target/site/jacoco) - -### Frontend (Angular) -- **Install dependencies**: `cd web-app && yarn install` -- **Start development server**: `cd web-app && ng serve --proxy-config proxy.conf.json` -- **Build for production**: `cd web-app && npm run package` -- **Run tests**: `cd web-app && ng test` -- **Test coverage**: `cd web-app && ng test --code-coverage --watch=false` -- **Linting**: `cd web-app && npm run lint` (includes both TypeScript and style linting) - -### Running Locally -1. **Backend**: - - Requires Java 17, Maven 3+, Lombok - - Add VM option: `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED` - - Run main class: `org.apache.hertzbeat.manager.Manager` -2. **Frontend**: - - Requires Node.js >= 18, Yarn - - Start backend first, then run: `ng serve --open` - - Default login: admin/hertzbeat - -## Architecture Overview - -HertzBeat is a real-time monitoring system with agentless architecture, built using: - -### Core Modules -- **hertzbeat-manager**: Main Spring Boot application (port 1157) - handles web UI, API, monitoring configuration -- **hertzbeat-collector**: Multi-module collector system with pluggable monitoring capabilities - - `hertzbeat-collector-basic`: Basic monitoring implementations - - `hertzbeat-collector-collector`: Core collection engine - - `hertzbeat-collector-common`: Shared collector utilities - - `hertzbeat-collector-mongodb/kafka/rocketmq/nebulagraph`: Database-specific collectors -- **hertzbeat-alerter**: Alert processing and notification system -- **hertzbeat-warehouse**: Data storage and querying layer -- **hertzbeat-common**: Shared utilities and constants -- **hertzbeat-remoting**: Communication layer between components -- **hertzbeat-push**: Notification delivery system -- **hertzbeat-plugin**: Plugin architecture for extensibility - -### Frontend -- **web-app**: Angular 17 application with ng-alain framework -- Serves from `classpath:/dist/` (built frontend files) - -### Key Technologies -- **Backend**: Java 17, Spring Boot 3.4.2, EclipseLink JPA, H2 database (default) -- **Frontend**: Angular 17, ng-alain, TypeScript -- **Build**: Maven, Yarn -- **Monitoring**: Prometheus-compatible, custom YML-based monitoring templates -- **Security**: Sureness framework for authentication/authorization - -### Database Support -- Default: H2 (embedded) -- Supported: MySQL, PostgreSQL, Oracle, SQL Server, MongoDB, ClickHouse, IoTDB, TDengine, GreptimeDB - -### Configuration -- Main config: `hertzbeat-manager/src/main/resources/application.yml` -- Database migrations: Flyway (locations: `classpath:db/migration/{vendor}`) -- Monitoring templates: YML files in `hertzbeat-manager/src/main/resources/define/` - -## Development Guidelines - -### Code Style -- Java: Checkstyle enforced (max line length: 200, specific suppression rules) -- Frontend: ESLint + Stylelint configured -- Commit format: `[module name or type name]feature or bugfix or doc: custom message` - -### Testing -- Backend: JUnit 5, Spring Boot Test -- Frontend: Jasmine, Karma -- E2E tests available in `hertzbeat-e2e` module -- Coverage reporting via JaCoCo - -### Key Patterns -- Monitoring types defined as configurable YML templates -- Pluggable collector architecture -- Alert expression evaluation with SQL-based rules -- Agentless monitoring using various protocols (HTTP, JMX, SSH, SNMP, JDBC, etc.) - -### Database Schema -- Uses EclipseLink JPA with MySQL-compatible platform -- Flyway for database migrations -- Supports multiple database vendors through vendor-specific migration scripts \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpression.interp b/hertzbeat-alerter/gen/expr/SqlExpression.interp deleted file mode 100644 index 3ea61a858d3..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpression.interp +++ /dev/null @@ -1,126 +0,0 @@ -token literal names: -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -'*' -null -null -null -null -null -null -'>' -'>=' -'<' -'<=' -null -'!=' -'(' -')' -'[' -']' -',' -'.' -';' -null -null -null -null -null -null -null - -token symbolic names: -null -AND -OR -NOT -SELECT -FROM -WHERE -GROUP -BY -HAVING -ORDER -LIMIT -OFFSET -AS -ASC -DESC -IN -IS -NULL -LIKE -BETWEEN -STAR -COUNT -SUM -AVG -MIN -MAX -SQL_FUNCTION -GT -GE -LT -LE -EQ -NE -LPAREN -RPAREN -LBRACKET -RBRACKET -COMMA -DOT -SEMICOLON -FLOAT -NUMBER -STRING -IDENTIFIER -WS -LINE_COMMENT -BLOCK_COMMENT - -rule names: -expression -sqlExpr -selectSql -selectFieldList -selectField -relList -relation -conditionList -compOp -condition -conditionUnit -functionCall -functionName -parameterList -parameter -groupByList -orderByList -orderByField -limitClause -number -string - - -atn: -[4, 1, 47, 260, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 52, 8, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 60, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 65, 8, 2, 1, 2, 1, 2, 3, 2, 69, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 74, 8, 2, 1, 2, 1, 2, 3, 2, 78, 8, 2, 1, 3, 1, 3, 1, 3, 5, 3, 83, 8, 3, 10, 3, 12, 3, 86, 9, 3, 1, 4, 1, 4, 3, 4, 90, 8, 4, 1, 4, 3, 4, 93, 8, 4, 1, 4, 1, 4, 3, 4, 97, 8, 4, 1, 4, 3, 4, 100, 8, 4, 1, 4, 1, 4, 3, 4, 104, 8, 4, 1, 4, 3, 4, 107, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 113, 8, 4, 1, 4, 3, 4, 116, 8, 4, 3, 4, 118, 8, 4, 1, 5, 1, 5, 1, 5, 5, 5, 123, 8, 5, 10, 5, 12, 5, 126, 9, 5, 1, 6, 1, 6, 3, 6, 130, 8, 6, 1, 6, 3, 6, 133, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 141, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 149, 8, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 157, 8, 7, 10, 7, 12, 7, 160, 9, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 177, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 193, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 207, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 219, 8, 13, 10, 13, 12, 13, 222, 9, 13, 1, 14, 1, 14, 3, 14, 226, 8, 14, 1, 15, 1, 15, 1, 15, 5, 15, 231, 8, 15, 10, 15, 12, 15, 234, 9, 15, 1, 16, 1, 16, 1, 16, 5, 16, 239, 8, 16, 10, 16, 12, 16, 242, 9, 16, 1, 17, 1, 17, 3, 17, 246, 8, 17, 1, 17, 1, 17, 3, 17, 250, 8, 17, 3, 17, 252, 8, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 0, 1, 14, 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 0, 3, 2, 0, 22, 26, 44, 44, 1, 0, 14, 15, 1, 0, 41, 42, 289, 0, 42, 1, 0, 0, 0, 2, 51, 1, 0, 0, 0, 4, 53, 1, 0, 0, 0, 6, 79, 1, 0, 0, 0, 8, 117, 1, 0, 0, 0, 10, 119, 1, 0, 0, 0, 12, 140, 1, 0, 0, 0, 14, 148, 1, 0, 0, 0, 16, 176, 1, 0, 0, 0, 18, 192, 1, 0, 0, 0, 20, 206, 1, 0, 0, 0, 22, 208, 1, 0, 0, 0, 24, 213, 1, 0, 0, 0, 26, 215, 1, 0, 0, 0, 28, 225, 1, 0, 0, 0, 30, 227, 1, 0, 0, 0, 32, 235, 1, 0, 0, 0, 34, 251, 1, 0, 0, 0, 36, 253, 1, 0, 0, 0, 38, 255, 1, 0, 0, 0, 40, 257, 1, 0, 0, 0, 42, 43, 3, 2, 1, 0, 43, 44, 5, 0, 0, 1, 44, 1, 1, 0, 0, 0, 45, 52, 3, 4, 2, 0, 46, 47, 5, 27, 0, 0, 47, 48, 5, 34, 0, 0, 48, 49, 3, 40, 20, 0, 49, 50, 5, 35, 0, 0, 50, 52, 1, 0, 0, 0, 51, 45, 1, 0, 0, 0, 51, 46, 1, 0, 0, 0, 52, 3, 1, 0, 0, 0, 53, 54, 5, 4, 0, 0, 54, 55, 3, 6, 3, 0, 55, 56, 5, 5, 0, 0, 56, 59, 3, 10, 5, 0, 57, 58, 5, 6, 0, 0, 58, 60, 3, 14, 7, 0, 59, 57, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 64, 1, 0, 0, 0, 61, 62, 5, 7, 0, 0, 62, 63, 5, 8, 0, 0, 63, 65, 3, 30, 15, 0, 64, 61, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 68, 1, 0, 0, 0, 66, 67, 5, 9, 0, 0, 67, 69, 3, 14, 7, 0, 68, 66, 1, 0, 0, 0, 68, 69, 1, 0, 0, 0, 69, 73, 1, 0, 0, 0, 70, 71, 5, 10, 0, 0, 71, 72, 5, 8, 0, 0, 72, 74, 3, 32, 16, 0, 73, 70, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 77, 1, 0, 0, 0, 75, 76, 5, 11, 0, 0, 76, 78, 3, 36, 18, 0, 77, 75, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 5, 1, 0, 0, 0, 79, 84, 3, 8, 4, 0, 80, 81, 5, 38, 0, 0, 81, 83, 3, 8, 4, 0, 82, 80, 1, 0, 0, 0, 83, 86, 1, 0, 0, 0, 84, 82, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 84, 1, 0, 0, 0, 87, 92, 3, 22, 11, 0, 88, 90, 5, 13, 0, 0, 89, 88, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 93, 5, 44, 0, 0, 92, 89, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 118, 1, 0, 0, 0, 94, 99, 5, 44, 0, 0, 95, 97, 5, 13, 0, 0, 96, 95, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 100, 5, 44, 0, 0, 99, 96, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 118, 1, 0, 0, 0, 101, 106, 5, 21, 0, 0, 102, 104, 5, 13, 0, 0, 103, 102, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 107, 5, 44, 0, 0, 106, 103, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 118, 1, 0, 0, 0, 108, 109, 5, 44, 0, 0, 109, 110, 5, 39, 0, 0, 110, 115, 5, 44, 0, 0, 111, 113, 5, 13, 0, 0, 112, 111, 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 1, 0, 0, 0, 114, 116, 5, 44, 0, 0, 115, 112, 1, 0, 0, 0, 115, 116, 1, 0, 0, 0, 116, 118, 1, 0, 0, 0, 117, 87, 1, 0, 0, 0, 117, 94, 1, 0, 0, 0, 117, 101, 1, 0, 0, 0, 117, 108, 1, 0, 0, 0, 118, 9, 1, 0, 0, 0, 119, 124, 3, 12, 6, 0, 120, 121, 5, 38, 0, 0, 121, 123, 3, 12, 6, 0, 122, 120, 1, 0, 0, 0, 123, 126, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 124, 125, 1, 0, 0, 0, 125, 11, 1, 0, 0, 0, 126, 124, 1, 0, 0, 0, 127, 132, 5, 44, 0, 0, 128, 130, 5, 13, 0, 0, 129, 128, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 131, 1, 0, 0, 0, 131, 133, 5, 44, 0, 0, 132, 129, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 141, 1, 0, 0, 0, 134, 135, 5, 34, 0, 0, 135, 136, 3, 4, 2, 0, 136, 137, 5, 35, 0, 0, 137, 138, 5, 13, 0, 0, 138, 139, 5, 44, 0, 0, 139, 141, 1, 0, 0, 0, 140, 127, 1, 0, 0, 0, 140, 134, 1, 0, 0, 0, 141, 13, 1, 0, 0, 0, 142, 143, 6, 7, -1, 0, 143, 149, 3, 18, 9, 0, 144, 145, 5, 34, 0, 0, 145, 146, 3, 14, 7, 0, 146, 147, 5, 35, 0, 0, 147, 149, 1, 0, 0, 0, 148, 142, 1, 0, 0, 0, 148, 144, 1, 0, 0, 0, 149, 158, 1, 0, 0, 0, 150, 151, 10, 3, 0, 0, 151, 152, 5, 1, 0, 0, 152, 157, 3, 14, 7, 4, 153, 154, 10, 2, 0, 0, 154, 155, 5, 2, 0, 0, 155, 157, 3, 14, 7, 3, 156, 150, 1, 0, 0, 0, 156, 153, 1, 0, 0, 0, 157, 160, 1, 0, 0, 0, 158, 156, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 15, 1, 0, 0, 0, 160, 158, 1, 0, 0, 0, 161, 177, 5, 32, 0, 0, 162, 177, 5, 30, 0, 0, 163, 177, 5, 28, 0, 0, 164, 177, 5, 31, 0, 0, 165, 177, 5, 29, 0, 0, 166, 177, 5, 33, 0, 0, 167, 177, 5, 19, 0, 0, 168, 169, 5, 3, 0, 0, 169, 177, 5, 19, 0, 0, 170, 177, 5, 16, 0, 0, 171, 172, 5, 3, 0, 0, 172, 177, 5, 16, 0, 0, 173, 177, 5, 17, 0, 0, 174, 175, 5, 17, 0, 0, 175, 177, 5, 3, 0, 0, 176, 161, 1, 0, 0, 0, 176, 162, 1, 0, 0, 0, 176, 163, 1, 0, 0, 0, 176, 164, 1, 0, 0, 0, 176, 165, 1, 0, 0, 0, 176, 166, 1, 0, 0, 0, 176, 167, 1, 0, 0, 0, 176, 168, 1, 0, 0, 0, 176, 170, 1, 0, 0, 0, 176, 171, 1, 0, 0, 0, 176, 173, 1, 0, 0, 0, 176, 174, 1, 0, 0, 0, 177, 17, 1, 0, 0, 0, 178, 179, 3, 20, 10, 0, 179, 180, 3, 16, 8, 0, 180, 181, 3, 20, 10, 0, 181, 193, 1, 0, 0, 0, 182, 183, 5, 34, 0, 0, 183, 184, 3, 18, 9, 0, 184, 185, 5, 35, 0, 0, 185, 193, 1, 0, 0, 0, 186, 187, 5, 44, 0, 0, 187, 188, 5, 20, 0, 0, 188, 189, 3, 38, 19, 0, 189, 190, 5, 1, 0, 0, 190, 191, 3, 38, 19, 0, 191, 193, 1, 0, 0, 0, 192, 178, 1, 0, 0, 0, 192, 182, 1, 0, 0, 0, 192, 186, 1, 0, 0, 0, 193, 19, 1, 0, 0, 0, 194, 207, 3, 38, 19, 0, 195, 207, 3, 40, 20, 0, 196, 207, 5, 44, 0, 0, 197, 198, 5, 44, 0, 0, 198, 199, 5, 39, 0, 0, 199, 207, 5, 44, 0, 0, 200, 207, 5, 18, 0, 0, 201, 202, 5, 34, 0, 0, 202, 203, 3, 4, 2, 0, 203, 204, 5, 35, 0, 0, 204, 207, 1, 0, 0, 0, 205, 207, 3, 22, 11, 0, 206, 194, 1, 0, 0, 0, 206, 195, 1, 0, 0, 0, 206, 196, 1, 0, 0, 0, 206, 197, 1, 0, 0, 0, 206, 200, 1, 0, 0, 0, 206, 201, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 21, 1, 0, 0, 0, 208, 209, 3, 24, 12, 0, 209, 210, 5, 34, 0, 0, 210, 211, 3, 26, 13, 0, 211, 212, 5, 35, 0, 0, 212, 23, 1, 0, 0, 0, 213, 214, 7, 0, 0, 0, 214, 25, 1, 0, 0, 0, 215, 220, 3, 28, 14, 0, 216, 217, 5, 38, 0, 0, 217, 219, 3, 28, 14, 0, 218, 216, 1, 0, 0, 0, 219, 222, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 27, 1, 0, 0, 0, 222, 220, 1, 0, 0, 0, 223, 226, 5, 21, 0, 0, 224, 226, 3, 40, 20, 0, 225, 223, 1, 0, 0, 0, 225, 224, 1, 0, 0, 0, 226, 29, 1, 0, 0, 0, 227, 232, 5, 44, 0, 0, 228, 229, 5, 38, 0, 0, 229, 231, 5, 44, 0, 0, 230, 228, 1, 0, 0, 0, 231, 234, 1, 0, 0, 0, 232, 230, 1, 0, 0, 0, 232, 233, 1, 0, 0, 0, 233, 31, 1, 0, 0, 0, 234, 232, 1, 0, 0, 0, 235, 240, 3, 34, 17, 0, 236, 237, 5, 38, 0, 0, 237, 239, 3, 34, 17, 0, 238, 236, 1, 0, 0, 0, 239, 242, 1, 0, 0, 0, 240, 238, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 33, 1, 0, 0, 0, 242, 240, 1, 0, 0, 0, 243, 245, 5, 44, 0, 0, 244, 246, 7, 1, 0, 0, 245, 244, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 252, 1, 0, 0, 0, 247, 249, 3, 22, 11, 0, 248, 250, 7, 1, 0, 0, 249, 248, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 252, 1, 0, 0, 0, 251, 243, 1, 0, 0, 0, 251, 247, 1, 0, 0, 0, 252, 35, 1, 0, 0, 0, 253, 254, 5, 42, 0, 0, 254, 37, 1, 0, 0, 0, 255, 256, 7, 2, 0, 0, 256, 39, 1, 0, 0, 0, 257, 258, 5, 43, 0, 0, 258, 41, 1, 0, 0, 0, 33, 51, 59, 64, 68, 73, 77, 84, 89, 92, 96, 99, 103, 106, 112, 115, 117, 124, 129, 132, 140, 148, 156, 158, 176, 192, 206, 220, 225, 232, 240, 245, 249, 251] \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpression.tokens b/hertzbeat-alerter/gen/expr/SqlExpression.tokens deleted file mode 100644 index 5d6784e6666..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpression.tokens +++ /dev/null @@ -1,60 +0,0 @@ -AND=1 -OR=2 -NOT=3 -SELECT=4 -FROM=5 -WHERE=6 -GROUP=7 -BY=8 -HAVING=9 -ORDER=10 -LIMIT=11 -OFFSET=12 -AS=13 -ASC=14 -DESC=15 -IN=16 -IS=17 -NULL=18 -LIKE=19 -BETWEEN=20 -STAR=21 -COUNT=22 -SUM=23 -AVG=24 -MIN=25 -MAX=26 -SQL_FUNCTION=27 -GT=28 -GE=29 -LT=30 -LE=31 -EQ=32 -NE=33 -LPAREN=34 -RPAREN=35 -LBRACKET=36 -RBRACKET=37 -COMMA=38 -DOT=39 -SEMICOLON=40 -FLOAT=41 -NUMBER=42 -STRING=43 -IDENTIFIER=44 -WS=45 -LINE_COMMENT=46 -BLOCK_COMMENT=47 -'*'=21 -'>'=28 -'>='=29 -'<'=30 -'<='=31 -'!='=33 -'('=34 -')'=35 -'['=36 -']'=37 -','=38 -'.'=39 -';'=40 diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java b/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java deleted file mode 100644 index 025be9e1b7b..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionBaseListener.java +++ /dev/null @@ -1,304 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ErrorNode; -import org.antlr.v4.runtime.tree.TerminalNode; - -/** - * This class provides an empty implementation of {@link SqlExpressionListener}, - * which can be extended to create a listener which only needs to handle a subset - * of the available methods. - */ -@SuppressWarnings("CheckReturnValue") -public class SqlExpressionBaseListener implements SqlExpressionListener { - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterExpression(SqlExpressionParser.ExpressionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitExpression(SqlExpressionParser.ExpressionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectField(SqlExpressionParser.SelectFieldContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectField(SqlExpressionParser.SelectFieldContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterRelList(SqlExpressionParser.RelListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitRelList(SqlExpressionParser.RelListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterRelation(SqlExpressionParser.RelationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitRelation(SqlExpressionParser.RelationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterConditionList(SqlExpressionParser.ConditionListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitConditionList(SqlExpressionParser.ConditionListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterCompOp(SqlExpressionParser.CompOpContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitCompOp(SqlExpressionParser.CompOpContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterCondition(SqlExpressionParser.ConditionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitCondition(SqlExpressionParser.ConditionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameterList(SqlExpressionParser.ParameterListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameterList(SqlExpressionParser.ParameterListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameter(SqlExpressionParser.ParameterContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameter(SqlExpressionParser.ParameterContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterGroupByList(SqlExpressionParser.GroupByListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitGroupByList(SqlExpressionParser.GroupByListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOrderByList(SqlExpressionParser.OrderByListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOrderByList(SqlExpressionParser.OrderByListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterNumber(SqlExpressionParser.NumberContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitNumber(SqlExpressionParser.NumberContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterString(SqlExpressionParser.StringContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitString(SqlExpressionParser.StringContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterEveryRule(ParserRuleContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitEveryRule(ParserRuleContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitTerminal(TerminalNode node) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitErrorNode(ErrorNode node) { } -} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java b/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java deleted file mode 100644 index 27049707de2..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionBaseVisitor.java +++ /dev/null @@ -1,169 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; -import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; - -/** - * This class provides an empty implementation of {@link SqlExpressionVisitor}, - * which can be extended to create a visitor which only needs to handle a subset - * of the available methods. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -@SuppressWarnings("CheckReturnValue") -public class SqlExpressionBaseVisitor extends AbstractParseTreeVisitor implements SqlExpressionVisitor { - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitExpression(SqlExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectField(SqlExpressionParser.SelectFieldContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitRelList(SqlExpressionParser.RelListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitRelation(SqlExpressionParser.RelationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitConditionList(SqlExpressionParser.ConditionListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitCompOp(SqlExpressionParser.CompOpContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitCondition(SqlExpressionParser.ConditionContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameterList(SqlExpressionParser.ParameterListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameter(SqlExpressionParser.ParameterContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitGroupByList(SqlExpressionParser.GroupByListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOrderByList(SqlExpressionParser.OrderByListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitNumber(SqlExpressionParser.NumberContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitString(SqlExpressionParser.StringContext ctx) { return visitChildren(ctx); } -} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp deleted file mode 100644 index b5fc335057f..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.interp +++ /dev/null @@ -1,158 +0,0 @@ -token literal names: -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -null -'*' -null -null -null -null -null -null -'>' -'>=' -'<' -'<=' -null -'!=' -'(' -')' -'[' -']' -',' -'.' -';' -null -null -null -null -null -null -null - -token symbolic names: -null -AND -OR -NOT -SELECT -FROM -WHERE -GROUP -BY -HAVING -ORDER -LIMIT -OFFSET -AS -ASC -DESC -IN -IS -NULL -LIKE -BETWEEN -STAR -COUNT -SUM -AVG -MIN -MAX -SQL_FUNCTION -GT -GE -LT -LE -EQ -NE -LPAREN -RPAREN -LBRACKET -RBRACKET -COMMA -DOT -SEMICOLON -FLOAT -NUMBER -STRING -IDENTIFIER -WS -LINE_COMMENT -BLOCK_COMMENT - -rule names: -AND -OR -NOT -SELECT -FROM -WHERE -GROUP -BY -HAVING -ORDER -LIMIT -OFFSET -AS -ASC -DESC -IN -IS -NULL -LIKE -BETWEEN -STAR -COUNT -SUM -AVG -MIN -MAX -SQL_FUNCTION -GT -GE -LT -LE -EQ -NE -LPAREN -RPAREN -LBRACKET -RBRACKET -COMMA -DOT -SEMICOLON -FLOAT -NUMBER -STRING -IDENTIFIER -WS -LINE_COMMENT -BLOCK_COMMENT - -channel names: -DEFAULT_TOKEN_CHANNEL -HIDDEN - -mode names: -DEFAULT_MODE - -atn: -[4, 0, 47, 331, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 3, 31, 236, 8, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 39, 1, 39, 1, 40, 4, 40, 256, 8, 40, 11, 40, 12, 40, 257, 1, 40, 1, 40, 4, 40, 262, 8, 40, 11, 40, 12, 40, 263, 1, 41, 4, 41, 267, 8, 41, 11, 41, 12, 41, 268, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 275, 8, 42, 10, 42, 12, 42, 278, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 285, 8, 42, 10, 42, 12, 42, 288, 9, 42, 1, 42, 3, 42, 291, 8, 42, 1, 43, 1, 43, 5, 43, 295, 8, 43, 10, 43, 12, 43, 298, 9, 43, 1, 44, 4, 44, 301, 8, 44, 11, 44, 12, 44, 302, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 5, 45, 311, 8, 45, 10, 45, 12, 45, 314, 9, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 5, 46, 322, 8, 46, 10, 46, 12, 46, 325, 9, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 323, 0, 47, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 1, 0, 31, 2, 0, 65, 65, 97, 97, 2, 0, 78, 78, 110, 110, 2, 0, 68, 68, 100, 100, 2, 0, 79, 79, 111, 111, 2, 0, 82, 82, 114, 114, 2, 0, 84, 84, 116, 116, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 76, 76, 108, 108, 2, 0, 67, 67, 99, 99, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 87, 87, 119, 119, 2, 0, 72, 72, 104, 104, 2, 0, 71, 71, 103, 103, 2, 0, 85, 85, 117, 117, 2, 0, 80, 80, 112, 112, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 2, 0, 86, 86, 118, 118, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 88, 88, 120, 120, 2, 0, 81, 81, 113, 113, 1, 0, 48, 57, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, 10, 10, 13, 13, 343, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 3, 99, 1, 0, 0, 0, 5, 102, 1, 0, 0, 0, 7, 106, 1, 0, 0, 0, 9, 113, 1, 0, 0, 0, 11, 118, 1, 0, 0, 0, 13, 124, 1, 0, 0, 0, 15, 130, 1, 0, 0, 0, 17, 133, 1, 0, 0, 0, 19, 140, 1, 0, 0, 0, 21, 146, 1, 0, 0, 0, 23, 152, 1, 0, 0, 0, 25, 159, 1, 0, 0, 0, 27, 162, 1, 0, 0, 0, 29, 166, 1, 0, 0, 0, 31, 171, 1, 0, 0, 0, 33, 174, 1, 0, 0, 0, 35, 177, 1, 0, 0, 0, 37, 182, 1, 0, 0, 0, 39, 187, 1, 0, 0, 0, 41, 195, 1, 0, 0, 0, 43, 197, 1, 0, 0, 0, 45, 203, 1, 0, 0, 0, 47, 207, 1, 0, 0, 0, 49, 211, 1, 0, 0, 0, 51, 215, 1, 0, 0, 0, 53, 218, 1, 0, 0, 0, 55, 222, 1, 0, 0, 0, 57, 224, 1, 0, 0, 0, 59, 227, 1, 0, 0, 0, 61, 229, 1, 0, 0, 0, 63, 235, 1, 0, 0, 0, 65, 237, 1, 0, 0, 0, 67, 240, 1, 0, 0, 0, 69, 242, 1, 0, 0, 0, 71, 244, 1, 0, 0, 0, 73, 246, 1, 0, 0, 0, 75, 248, 1, 0, 0, 0, 77, 250, 1, 0, 0, 0, 79, 252, 1, 0, 0, 0, 81, 255, 1, 0, 0, 0, 83, 266, 1, 0, 0, 0, 85, 290, 1, 0, 0, 0, 87, 292, 1, 0, 0, 0, 89, 300, 1, 0, 0, 0, 91, 306, 1, 0, 0, 0, 93, 317, 1, 0, 0, 0, 95, 96, 7, 0, 0, 0, 96, 97, 7, 1, 0, 0, 97, 98, 7, 2, 0, 0, 98, 2, 1, 0, 0, 0, 99, 100, 7, 3, 0, 0, 100, 101, 7, 4, 0, 0, 101, 4, 1, 0, 0, 0, 102, 103, 7, 1, 0, 0, 103, 104, 7, 3, 0, 0, 104, 105, 7, 5, 0, 0, 105, 6, 1, 0, 0, 0, 106, 107, 7, 6, 0, 0, 107, 108, 7, 7, 0, 0, 108, 109, 7, 8, 0, 0, 109, 110, 7, 7, 0, 0, 110, 111, 7, 9, 0, 0, 111, 112, 7, 5, 0, 0, 112, 8, 1, 0, 0, 0, 113, 114, 7, 10, 0, 0, 114, 115, 7, 4, 0, 0, 115, 116, 7, 3, 0, 0, 116, 117, 7, 11, 0, 0, 117, 10, 1, 0, 0, 0, 118, 119, 7, 12, 0, 0, 119, 120, 7, 13, 0, 0, 120, 121, 7, 7, 0, 0, 121, 122, 7, 4, 0, 0, 122, 123, 7, 7, 0, 0, 123, 12, 1, 0, 0, 0, 124, 125, 7, 14, 0, 0, 125, 126, 7, 4, 0, 0, 126, 127, 7, 3, 0, 0, 127, 128, 7, 15, 0, 0, 128, 129, 7, 16, 0, 0, 129, 14, 1, 0, 0, 0, 130, 131, 7, 17, 0, 0, 131, 132, 7, 18, 0, 0, 132, 16, 1, 0, 0, 0, 133, 134, 7, 13, 0, 0, 134, 135, 7, 0, 0, 0, 135, 136, 7, 19, 0, 0, 136, 137, 7, 20, 0, 0, 137, 138, 7, 1, 0, 0, 138, 139, 7, 14, 0, 0, 139, 18, 1, 0, 0, 0, 140, 141, 7, 3, 0, 0, 141, 142, 7, 4, 0, 0, 142, 143, 7, 2, 0, 0, 143, 144, 7, 7, 0, 0, 144, 145, 7, 4, 0, 0, 145, 20, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 20, 0, 0, 148, 149, 7, 11, 0, 0, 149, 150, 7, 20, 0, 0, 150, 151, 7, 5, 0, 0, 151, 22, 1, 0, 0, 0, 152, 153, 7, 3, 0, 0, 153, 154, 7, 10, 0, 0, 154, 155, 7, 10, 0, 0, 155, 156, 7, 6, 0, 0, 156, 157, 7, 7, 0, 0, 157, 158, 7, 5, 0, 0, 158, 24, 1, 0, 0, 0, 159, 160, 7, 0, 0, 0, 160, 161, 7, 6, 0, 0, 161, 26, 1, 0, 0, 0, 162, 163, 7, 0, 0, 0, 163, 164, 7, 6, 0, 0, 164, 165, 7, 9, 0, 0, 165, 28, 1, 0, 0, 0, 166, 167, 7, 2, 0, 0, 167, 168, 7, 7, 0, 0, 168, 169, 7, 6, 0, 0, 169, 170, 7, 9, 0, 0, 170, 30, 1, 0, 0, 0, 171, 172, 7, 20, 0, 0, 172, 173, 7, 1, 0, 0, 173, 32, 1, 0, 0, 0, 174, 175, 7, 20, 0, 0, 175, 176, 7, 6, 0, 0, 176, 34, 1, 0, 0, 0, 177, 178, 7, 1, 0, 0, 178, 179, 7, 15, 0, 0, 179, 180, 7, 8, 0, 0, 180, 181, 7, 8, 0, 0, 181, 36, 1, 0, 0, 0, 182, 183, 7, 8, 0, 0, 183, 184, 7, 20, 0, 0, 184, 185, 7, 21, 0, 0, 185, 186, 7, 7, 0, 0, 186, 38, 1, 0, 0, 0, 187, 188, 7, 17, 0, 0, 188, 189, 7, 7, 0, 0, 189, 190, 7, 5, 0, 0, 190, 191, 7, 12, 0, 0, 191, 192, 7, 7, 0, 0, 192, 193, 7, 7, 0, 0, 193, 194, 7, 1, 0, 0, 194, 40, 1, 0, 0, 0, 195, 196, 5, 42, 0, 0, 196, 42, 1, 0, 0, 0, 197, 198, 7, 9, 0, 0, 198, 199, 7, 3, 0, 0, 199, 200, 7, 15, 0, 0, 200, 201, 7, 1, 0, 0, 201, 202, 7, 5, 0, 0, 202, 44, 1, 0, 0, 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 15, 0, 0, 205, 206, 7, 11, 0, 0, 206, 46, 1, 0, 0, 0, 207, 208, 7, 0, 0, 0, 208, 209, 7, 19, 0, 0, 209, 210, 7, 14, 0, 0, 210, 48, 1, 0, 0, 0, 211, 212, 7, 11, 0, 0, 212, 213, 7, 20, 0, 0, 213, 214, 7, 1, 0, 0, 214, 50, 1, 0, 0, 0, 215, 216, 7, 0, 0, 0, 216, 217, 7, 22, 0, 0, 217, 52, 1, 0, 0, 0, 218, 219, 7, 6, 0, 0, 219, 220, 7, 23, 0, 0, 220, 221, 7, 8, 0, 0, 221, 54, 1, 0, 0, 0, 222, 223, 5, 62, 0, 0, 223, 56, 1, 0, 0, 0, 224, 225, 5, 62, 0, 0, 225, 226, 5, 61, 0, 0, 226, 58, 1, 0, 0, 0, 227, 228, 5, 60, 0, 0, 228, 60, 1, 0, 0, 0, 229, 230, 5, 60, 0, 0, 230, 231, 5, 61, 0, 0, 231, 62, 1, 0, 0, 0, 232, 233, 5, 61, 0, 0, 233, 236, 5, 61, 0, 0, 234, 236, 5, 61, 0, 0, 235, 232, 1, 0, 0, 0, 235, 234, 1, 0, 0, 0, 236, 64, 1, 0, 0, 0, 237, 238, 5, 33, 0, 0, 238, 239, 5, 61, 0, 0, 239, 66, 1, 0, 0, 0, 240, 241, 5, 40, 0, 0, 241, 68, 1, 0, 0, 0, 242, 243, 5, 41, 0, 0, 243, 70, 1, 0, 0, 0, 244, 245, 5, 91, 0, 0, 245, 72, 1, 0, 0, 0, 246, 247, 5, 93, 0, 0, 247, 74, 1, 0, 0, 0, 248, 249, 5, 44, 0, 0, 249, 76, 1, 0, 0, 0, 250, 251, 5, 46, 0, 0, 251, 78, 1, 0, 0, 0, 252, 253, 5, 59, 0, 0, 253, 80, 1, 0, 0, 0, 254, 256, 7, 24, 0, 0, 255, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 261, 5, 46, 0, 0, 260, 262, 7, 24, 0, 0, 261, 260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 82, 1, 0, 0, 0, 265, 267, 7, 24, 0, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 84, 1, 0, 0, 0, 270, 276, 5, 34, 0, 0, 271, 275, 8, 25, 0, 0, 272, 273, 5, 92, 0, 0, 273, 275, 9, 0, 0, 0, 274, 271, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 279, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 291, 5, 34, 0, 0, 280, 286, 5, 39, 0, 0, 281, 285, 8, 26, 0, 0, 282, 283, 5, 92, 0, 0, 283, 285, 9, 0, 0, 0, 284, 281, 1, 0, 0, 0, 284, 282, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 289, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 291, 5, 39, 0, 0, 290, 270, 1, 0, 0, 0, 290, 280, 1, 0, 0, 0, 291, 86, 1, 0, 0, 0, 292, 296, 7, 27, 0, 0, 293, 295, 7, 28, 0, 0, 294, 293, 1, 0, 0, 0, 295, 298, 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 88, 1, 0, 0, 0, 298, 296, 1, 0, 0, 0, 299, 301, 7, 29, 0, 0, 300, 299, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 302, 303, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 305, 6, 44, 0, 0, 305, 90, 1, 0, 0, 0, 306, 307, 5, 47, 0, 0, 307, 308, 5, 47, 0, 0, 308, 312, 1, 0, 0, 0, 309, 311, 8, 30, 0, 0, 310, 309, 1, 0, 0, 0, 311, 314, 1, 0, 0, 0, 312, 310, 1, 0, 0, 0, 312, 313, 1, 0, 0, 0, 313, 315, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 315, 316, 6, 45, 1, 0, 316, 92, 1, 0, 0, 0, 317, 318, 5, 47, 0, 0, 318, 319, 5, 42, 0, 0, 319, 323, 1, 0, 0, 0, 320, 322, 9, 0, 0, 0, 321, 320, 1, 0, 0, 0, 322, 325, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 323, 321, 1, 0, 0, 0, 324, 326, 1, 0, 0, 0, 325, 323, 1, 0, 0, 0, 326, 327, 5, 42, 0, 0, 327, 328, 5, 47, 0, 0, 328, 329, 1, 0, 0, 0, 329, 330, 6, 46, 1, 0, 330, 94, 1, 0, 0, 0, 14, 0, 235, 257, 263, 268, 274, 276, 284, 286, 290, 296, 302, 312, 323, 2, 0, 1, 0, 6, 0, 0] \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java deleted file mode 100644 index c2cd5f6fa8f..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.java +++ /dev/null @@ -1,348 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class SqlExpressionLexer extends Lexer { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, - ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, - LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, - SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, - RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, - NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; - public static String[] channelNames = { - "DEFAULT_TOKEN_CHANNEL", "HIDDEN" - }; - - public static String[] modeNames = { - "DEFAULT_MODE" - }; - - private static String[] makeRuleNames() { - return new String[] { - "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, "'*'", null, null, - null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", - "')'", "'['", "']'", "','", "'.'", "';'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - - public SqlExpressionLexer(CharStream input) { - super(input); - _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @Override - public String getGrammarFileName() { return "SqlExpression.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public String[] getChannelNames() { return channelNames; } - - @Override - public String[] getModeNames() { return modeNames; } - - @Override - public ATN getATN() { return _ATN; } - - public static final String _serializedATN = - "\u0004\u0000/\u014b\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ - "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ - "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ - "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ - "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ - "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ - "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ - "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ - "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ - "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ - "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ - "!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+ - "&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007"+ - "+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0001\u0000\u0001\u0000\u0001"+ - "\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001"+ - "\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+ - "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001"+ - "\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005\u0001"+ - "\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001"+ - "\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ - "\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b"+ - "\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0001"+ - "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+ - "\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001"+ - "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001"+ - "\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+ - "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001"+ - "\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001"+ - "\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ - "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0001"+ - "\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001"+ - "\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+ - "\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e\u0001"+ - "\u001f\u0001\u001f\u0001\u001f\u0003\u001f\u00ec\b\u001f\u0001 \u0001"+ - " \u0001 \u0001!\u0001!\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001"+ - "%\u0001%\u0001&\u0001&\u0001\'\u0001\'\u0001(\u0004(\u0100\b(\u000b(\f"+ - "(\u0101\u0001(\u0001(\u0004(\u0106\b(\u000b(\f(\u0107\u0001)\u0004)\u010b"+ - "\b)\u000b)\f)\u010c\u0001*\u0001*\u0001*\u0001*\u0005*\u0113\b*\n*\f*"+ - "\u0116\t*\u0001*\u0001*\u0001*\u0001*\u0001*\u0005*\u011d\b*\n*\f*\u0120"+ - "\t*\u0001*\u0003*\u0123\b*\u0001+\u0001+\u0005+\u0127\b+\n+\f+\u012a\t"+ - "+\u0001,\u0004,\u012d\b,\u000b,\f,\u012e\u0001,\u0001,\u0001-\u0001-\u0001"+ - "-\u0001-\u0005-\u0137\b-\n-\f-\u013a\t-\u0001-\u0001-\u0001.\u0001.\u0001"+ - ".\u0001.\u0005.\u0142\b.\n.\f.\u0145\t.\u0001.\u0001.\u0001.\u0001.\u0001"+ - ".\u0001\u0143\u0000/\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t"+ - "\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f"+ - "\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014"+ - ")\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d;\u001e"+ - "=\u001f? A!C\"E#G$I%K&M\'O(Q)S*U+W,Y-[.]/\u0001\u0000\u001f\u0002\u0000"+ - "AAaa\u0002\u0000NNnn\u0002\u0000DDdd\u0002\u0000OOoo\u0002\u0000RRrr\u0002"+ - "\u0000TTtt\u0002\u0000SSss\u0002\u0000EEee\u0002\u0000LLll\u0002\u0000"+ - "CCcc\u0002\u0000FFff\u0002\u0000MMmm\u0002\u0000WWww\u0002\u0000HHhh\u0002"+ - "\u0000GGgg\u0002\u0000UUuu\u0002\u0000PPpp\u0002\u0000BBbb\u0002\u0000"+ - "YYyy\u0002\u0000VVvv\u0002\u0000IIii\u0002\u0000KKkk\u0002\u0000XXxx\u0002"+ - "\u0000QQqq\u0001\u000009\u0004\u0000\n\n\r\r\"\"\\\\\u0004\u0000\n\n\r"+ - "\r\'\'\\\\\u0003\u0000AZ__az\u0004\u000009AZ__az\u0003\u0000\t\n\r\r "+ - " \u0002\u0000\n\n\r\r\u0157\u0000\u0001\u0001\u0000\u0000\u0000\u0000"+ - "\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000"+ - "\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b"+ - "\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001"+ - "\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001"+ - "\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001"+ - "\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001"+ - "\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001"+ - "\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000"+ - "\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000"+ - "\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-"+ - "\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000"+ - "\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000"+ - "\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000\u0000\u0000\u0000;"+ - "\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0000?\u0001\u0000"+ - "\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C\u0001\u0000\u0000\u0000"+ - "\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000\u0000\u0000\u0000I"+ - "\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000\u0000M\u0001\u0000"+ - "\u0000\u0000\u0000O\u0001\u0000\u0000\u0000\u0000Q\u0001\u0000\u0000\u0000"+ - "\u0000S\u0001\u0000\u0000\u0000\u0000U\u0001\u0000\u0000\u0000\u0000W"+ - "\u0001\u0000\u0000\u0000\u0000Y\u0001\u0000\u0000\u0000\u0000[\u0001\u0000"+ - "\u0000\u0000\u0000]\u0001\u0000\u0000\u0000\u0001_\u0001\u0000\u0000\u0000"+ - "\u0003c\u0001\u0000\u0000\u0000\u0005f\u0001\u0000\u0000\u0000\u0007j"+ - "\u0001\u0000\u0000\u0000\tq\u0001\u0000\u0000\u0000\u000bv\u0001\u0000"+ - "\u0000\u0000\r|\u0001\u0000\u0000\u0000\u000f\u0082\u0001\u0000\u0000"+ - "\u0000\u0011\u0085\u0001\u0000\u0000\u0000\u0013\u008c\u0001\u0000\u0000"+ - "\u0000\u0015\u0092\u0001\u0000\u0000\u0000\u0017\u0098\u0001\u0000\u0000"+ - "\u0000\u0019\u009f\u0001\u0000\u0000\u0000\u001b\u00a2\u0001\u0000\u0000"+ - "\u0000\u001d\u00a6\u0001\u0000\u0000\u0000\u001f\u00ab\u0001\u0000\u0000"+ - "\u0000!\u00ae\u0001\u0000\u0000\u0000#\u00b1\u0001\u0000\u0000\u0000%"+ - "\u00b6\u0001\u0000\u0000\u0000\'\u00bb\u0001\u0000\u0000\u0000)\u00c3"+ - "\u0001\u0000\u0000\u0000+\u00c5\u0001\u0000\u0000\u0000-\u00cb\u0001\u0000"+ - "\u0000\u0000/\u00cf\u0001\u0000\u0000\u00001\u00d3\u0001\u0000\u0000\u0000"+ - "3\u00d7\u0001\u0000\u0000\u00005\u00da\u0001\u0000\u0000\u00007\u00de"+ - "\u0001\u0000\u0000\u00009\u00e0\u0001\u0000\u0000\u0000;\u00e3\u0001\u0000"+ - "\u0000\u0000=\u00e5\u0001\u0000\u0000\u0000?\u00eb\u0001\u0000\u0000\u0000"+ - "A\u00ed\u0001\u0000\u0000\u0000C\u00f0\u0001\u0000\u0000\u0000E\u00f2"+ - "\u0001\u0000\u0000\u0000G\u00f4\u0001\u0000\u0000\u0000I\u00f6\u0001\u0000"+ - "\u0000\u0000K\u00f8\u0001\u0000\u0000\u0000M\u00fa\u0001\u0000\u0000\u0000"+ - "O\u00fc\u0001\u0000\u0000\u0000Q\u00ff\u0001\u0000\u0000\u0000S\u010a"+ - "\u0001\u0000\u0000\u0000U\u0122\u0001\u0000\u0000\u0000W\u0124\u0001\u0000"+ - "\u0000\u0000Y\u012c\u0001\u0000\u0000\u0000[\u0132\u0001\u0000\u0000\u0000"+ - "]\u013d\u0001\u0000\u0000\u0000_`\u0007\u0000\u0000\u0000`a\u0007\u0001"+ - "\u0000\u0000ab\u0007\u0002\u0000\u0000b\u0002\u0001\u0000\u0000\u0000"+ - "cd\u0007\u0003\u0000\u0000de\u0007\u0004\u0000\u0000e\u0004\u0001\u0000"+ - "\u0000\u0000fg\u0007\u0001\u0000\u0000gh\u0007\u0003\u0000\u0000hi\u0007"+ - "\u0005\u0000\u0000i\u0006\u0001\u0000\u0000\u0000jk\u0007\u0006\u0000"+ - "\u0000kl\u0007\u0007\u0000\u0000lm\u0007\b\u0000\u0000mn\u0007\u0007\u0000"+ - "\u0000no\u0007\t\u0000\u0000op\u0007\u0005\u0000\u0000p\b\u0001\u0000"+ - "\u0000\u0000qr\u0007\n\u0000\u0000rs\u0007\u0004\u0000\u0000st\u0007\u0003"+ - "\u0000\u0000tu\u0007\u000b\u0000\u0000u\n\u0001\u0000\u0000\u0000vw\u0007"+ - "\f\u0000\u0000wx\u0007\r\u0000\u0000xy\u0007\u0007\u0000\u0000yz\u0007"+ - "\u0004\u0000\u0000z{\u0007\u0007\u0000\u0000{\f\u0001\u0000\u0000\u0000"+ - "|}\u0007\u000e\u0000\u0000}~\u0007\u0004\u0000\u0000~\u007f\u0007\u0003"+ - "\u0000\u0000\u007f\u0080\u0007\u000f\u0000\u0000\u0080\u0081\u0007\u0010"+ - "\u0000\u0000\u0081\u000e\u0001\u0000\u0000\u0000\u0082\u0083\u0007\u0011"+ - "\u0000\u0000\u0083\u0084\u0007\u0012\u0000\u0000\u0084\u0010\u0001\u0000"+ - "\u0000\u0000\u0085\u0086\u0007\r\u0000\u0000\u0086\u0087\u0007\u0000\u0000"+ - "\u0000\u0087\u0088\u0007\u0013\u0000\u0000\u0088\u0089\u0007\u0014\u0000"+ - "\u0000\u0089\u008a\u0007\u0001\u0000\u0000\u008a\u008b\u0007\u000e\u0000"+ - "\u0000\u008b\u0012\u0001\u0000\u0000\u0000\u008c\u008d\u0007\u0003\u0000"+ - "\u0000\u008d\u008e\u0007\u0004\u0000\u0000\u008e\u008f\u0007\u0002\u0000"+ - "\u0000\u008f\u0090\u0007\u0007\u0000\u0000\u0090\u0091\u0007\u0004\u0000"+ - "\u0000\u0091\u0014\u0001\u0000\u0000\u0000\u0092\u0093\u0007\b\u0000\u0000"+ - "\u0093\u0094\u0007\u0014\u0000\u0000\u0094\u0095\u0007\u000b\u0000\u0000"+ - "\u0095\u0096\u0007\u0014\u0000\u0000\u0096\u0097\u0007\u0005\u0000\u0000"+ - "\u0097\u0016\u0001\u0000\u0000\u0000\u0098\u0099\u0007\u0003\u0000\u0000"+ - "\u0099\u009a\u0007\n\u0000\u0000\u009a\u009b\u0007\n\u0000\u0000\u009b"+ - "\u009c\u0007\u0006\u0000\u0000\u009c\u009d\u0007\u0007\u0000\u0000\u009d"+ - "\u009e\u0007\u0005\u0000\u0000\u009e\u0018\u0001\u0000\u0000\u0000\u009f"+ - "\u00a0\u0007\u0000\u0000\u0000\u00a0\u00a1\u0007\u0006\u0000\u0000\u00a1"+ - "\u001a\u0001\u0000\u0000\u0000\u00a2\u00a3\u0007\u0000\u0000\u0000\u00a3"+ - "\u00a4\u0007\u0006\u0000\u0000\u00a4\u00a5\u0007\t\u0000\u0000\u00a5\u001c"+ - "\u0001\u0000\u0000\u0000\u00a6\u00a7\u0007\u0002\u0000\u0000\u00a7\u00a8"+ - "\u0007\u0007\u0000\u0000\u00a8\u00a9\u0007\u0006\u0000\u0000\u00a9\u00aa"+ - "\u0007\t\u0000\u0000\u00aa\u001e\u0001\u0000\u0000\u0000\u00ab\u00ac\u0007"+ - "\u0014\u0000\u0000\u00ac\u00ad\u0007\u0001\u0000\u0000\u00ad \u0001\u0000"+ - "\u0000\u0000\u00ae\u00af\u0007\u0014\u0000\u0000\u00af\u00b0\u0007\u0006"+ - "\u0000\u0000\u00b0\"\u0001\u0000\u0000\u0000\u00b1\u00b2\u0007\u0001\u0000"+ - "\u0000\u00b2\u00b3\u0007\u000f\u0000\u0000\u00b3\u00b4\u0007\b\u0000\u0000"+ - "\u00b4\u00b5\u0007\b\u0000\u0000\u00b5$\u0001\u0000\u0000\u0000\u00b6"+ - "\u00b7\u0007\b\u0000\u0000\u00b7\u00b8\u0007\u0014\u0000\u0000\u00b8\u00b9"+ - "\u0007\u0015\u0000\u0000\u00b9\u00ba\u0007\u0007\u0000\u0000\u00ba&\u0001"+ - "\u0000\u0000\u0000\u00bb\u00bc\u0007\u0011\u0000\u0000\u00bc\u00bd\u0007"+ - "\u0007\u0000\u0000\u00bd\u00be\u0007\u0005\u0000\u0000\u00be\u00bf\u0007"+ - "\f\u0000\u0000\u00bf\u00c0\u0007\u0007\u0000\u0000\u00c0\u00c1\u0007\u0007"+ - "\u0000\u0000\u00c1\u00c2\u0007\u0001\u0000\u0000\u00c2(\u0001\u0000\u0000"+ - "\u0000\u00c3\u00c4\u0005*\u0000\u0000\u00c4*\u0001\u0000\u0000\u0000\u00c5"+ - "\u00c6\u0007\t\u0000\u0000\u00c6\u00c7\u0007\u0003\u0000\u0000\u00c7\u00c8"+ - "\u0007\u000f\u0000\u0000\u00c8\u00c9\u0007\u0001\u0000\u0000\u00c9\u00ca"+ - "\u0007\u0005\u0000\u0000\u00ca,\u0001\u0000\u0000\u0000\u00cb\u00cc\u0007"+ - "\u0006\u0000\u0000\u00cc\u00cd\u0007\u000f\u0000\u0000\u00cd\u00ce\u0007"+ - "\u000b\u0000\u0000\u00ce.\u0001\u0000\u0000\u0000\u00cf\u00d0\u0007\u0000"+ - "\u0000\u0000\u00d0\u00d1\u0007\u0013\u0000\u0000\u00d1\u00d2\u0007\u000e"+ - "\u0000\u0000\u00d20\u0001\u0000\u0000\u0000\u00d3\u00d4\u0007\u000b\u0000"+ - "\u0000\u00d4\u00d5\u0007\u0014\u0000\u0000\u00d5\u00d6\u0007\u0001\u0000"+ - "\u0000\u00d62\u0001\u0000\u0000\u0000\u00d7\u00d8\u0007\u0000\u0000\u0000"+ - "\u00d8\u00d9\u0007\u0016\u0000\u0000\u00d94\u0001\u0000\u0000\u0000\u00da"+ - "\u00db\u0007\u0006\u0000\u0000\u00db\u00dc\u0007\u0017\u0000\u0000\u00dc"+ - "\u00dd\u0007\b\u0000\u0000\u00dd6\u0001\u0000\u0000\u0000\u00de\u00df"+ - "\u0005>\u0000\u0000\u00df8\u0001\u0000\u0000\u0000\u00e0\u00e1\u0005>"+ - "\u0000\u0000\u00e1\u00e2\u0005=\u0000\u0000\u00e2:\u0001\u0000\u0000\u0000"+ - "\u00e3\u00e4\u0005<\u0000\u0000\u00e4<\u0001\u0000\u0000\u0000\u00e5\u00e6"+ - "\u0005<\u0000\u0000\u00e6\u00e7\u0005=\u0000\u0000\u00e7>\u0001\u0000"+ - "\u0000\u0000\u00e8\u00e9\u0005=\u0000\u0000\u00e9\u00ec\u0005=\u0000\u0000"+ - "\u00ea\u00ec\u0005=\u0000\u0000\u00eb\u00e8\u0001\u0000\u0000\u0000\u00eb"+ - "\u00ea\u0001\u0000\u0000\u0000\u00ec@\u0001\u0000\u0000\u0000\u00ed\u00ee"+ - "\u0005!\u0000\u0000\u00ee\u00ef\u0005=\u0000\u0000\u00efB\u0001\u0000"+ - "\u0000\u0000\u00f0\u00f1\u0005(\u0000\u0000\u00f1D\u0001\u0000\u0000\u0000"+ - "\u00f2\u00f3\u0005)\u0000\u0000\u00f3F\u0001\u0000\u0000\u0000\u00f4\u00f5"+ - "\u0005[\u0000\u0000\u00f5H\u0001\u0000\u0000\u0000\u00f6\u00f7\u0005]"+ - "\u0000\u0000\u00f7J\u0001\u0000\u0000\u0000\u00f8\u00f9\u0005,\u0000\u0000"+ - "\u00f9L\u0001\u0000\u0000\u0000\u00fa\u00fb\u0005.\u0000\u0000\u00fbN"+ - "\u0001\u0000\u0000\u0000\u00fc\u00fd\u0005;\u0000\u0000\u00fdP\u0001\u0000"+ - "\u0000\u0000\u00fe\u0100\u0007\u0018\u0000\u0000\u00ff\u00fe\u0001\u0000"+ - "\u0000\u0000\u0100\u0101\u0001\u0000\u0000\u0000\u0101\u00ff\u0001\u0000"+ - "\u0000\u0000\u0101\u0102\u0001\u0000\u0000\u0000\u0102\u0103\u0001\u0000"+ - "\u0000\u0000\u0103\u0105\u0005.\u0000\u0000\u0104\u0106\u0007\u0018\u0000"+ - "\u0000\u0105\u0104\u0001\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000"+ - "\u0000\u0107\u0105\u0001\u0000\u0000\u0000\u0107\u0108\u0001\u0000\u0000"+ - "\u0000\u0108R\u0001\u0000\u0000\u0000\u0109\u010b\u0007\u0018\u0000\u0000"+ - "\u010a\u0109\u0001\u0000\u0000\u0000\u010b\u010c\u0001\u0000\u0000\u0000"+ - "\u010c\u010a\u0001\u0000\u0000\u0000\u010c\u010d\u0001\u0000\u0000\u0000"+ - "\u010dT\u0001\u0000\u0000\u0000\u010e\u0114\u0005\"\u0000\u0000\u010f"+ - "\u0113\b\u0019\u0000\u0000\u0110\u0111\u0005\\\u0000\u0000\u0111\u0113"+ - "\t\u0000\u0000\u0000\u0112\u010f\u0001\u0000\u0000\u0000\u0112\u0110\u0001"+ - "\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000\u0114\u0112\u0001"+ - "\u0000\u0000\u0000\u0114\u0115\u0001\u0000\u0000\u0000\u0115\u0117\u0001"+ - "\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u0123\u0005"+ - "\"\u0000\u0000\u0118\u011e\u0005\'\u0000\u0000\u0119\u011d\b\u001a\u0000"+ - "\u0000\u011a\u011b\u0005\\\u0000\u0000\u011b\u011d\t\u0000\u0000\u0000"+ - "\u011c\u0119\u0001\u0000\u0000\u0000\u011c\u011a\u0001\u0000\u0000\u0000"+ - "\u011d\u0120\u0001\u0000\u0000\u0000\u011e\u011c\u0001\u0000\u0000\u0000"+ - "\u011e\u011f\u0001\u0000\u0000\u0000\u011f\u0121\u0001\u0000\u0000\u0000"+ - "\u0120\u011e\u0001\u0000\u0000\u0000\u0121\u0123\u0005\'\u0000\u0000\u0122"+ - "\u010e\u0001\u0000\u0000\u0000\u0122\u0118\u0001\u0000\u0000\u0000\u0123"+ - "V\u0001\u0000\u0000\u0000\u0124\u0128\u0007\u001b\u0000\u0000\u0125\u0127"+ - "\u0007\u001c\u0000\u0000\u0126\u0125\u0001\u0000\u0000\u0000\u0127\u012a"+ - "\u0001\u0000\u0000\u0000\u0128\u0126\u0001\u0000\u0000\u0000\u0128\u0129"+ - "\u0001\u0000\u0000\u0000\u0129X\u0001\u0000\u0000\u0000\u012a\u0128\u0001"+ - "\u0000\u0000\u0000\u012b\u012d\u0007\u001d\u0000\u0000\u012c\u012b\u0001"+ - "\u0000\u0000\u0000\u012d\u012e\u0001\u0000\u0000\u0000\u012e\u012c\u0001"+ - "\u0000\u0000\u0000\u012e\u012f\u0001\u0000\u0000\u0000\u012f\u0130\u0001"+ - "\u0000\u0000\u0000\u0130\u0131\u0006,\u0000\u0000\u0131Z\u0001\u0000\u0000"+ - "\u0000\u0132\u0133\u0005/\u0000\u0000\u0133\u0134\u0005/\u0000\u0000\u0134"+ - "\u0138\u0001\u0000\u0000\u0000\u0135\u0137\b\u001e\u0000\u0000\u0136\u0135"+ - "\u0001\u0000\u0000\u0000\u0137\u013a\u0001\u0000\u0000\u0000\u0138\u0136"+ - "\u0001\u0000\u0000\u0000\u0138\u0139\u0001\u0000\u0000\u0000\u0139\u013b"+ - "\u0001\u0000\u0000\u0000\u013a\u0138\u0001\u0000\u0000\u0000\u013b\u013c"+ - "\u0006-\u0001\u0000\u013c\\\u0001\u0000\u0000\u0000\u013d\u013e\u0005"+ - "/\u0000\u0000\u013e\u013f\u0005*\u0000\u0000\u013f\u0143\u0001\u0000\u0000"+ - "\u0000\u0140\u0142\t\u0000\u0000\u0000\u0141\u0140\u0001\u0000\u0000\u0000"+ - "\u0142\u0145\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+ - "\u0143\u0141\u0001\u0000\u0000\u0000\u0144\u0146\u0001\u0000\u0000\u0000"+ - "\u0145\u0143\u0001\u0000\u0000\u0000\u0146\u0147\u0005*\u0000\u0000\u0147"+ - "\u0148\u0005/\u0000\u0000\u0148\u0149\u0001\u0000\u0000\u0000\u0149\u014a"+ - "\u0006.\u0001\u0000\u014a^\u0001\u0000\u0000\u0000\u000e\u0000\u00eb\u0101"+ - "\u0107\u010c\u0112\u0114\u011c\u011e\u0122\u0128\u012e\u0138\u0143\u0002"+ - "\u0000\u0001\u0000\u0006\u0000\u0000"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens b/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens deleted file mode 100644 index 5d6784e6666..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionLexer.tokens +++ /dev/null @@ -1,60 +0,0 @@ -AND=1 -OR=2 -NOT=3 -SELECT=4 -FROM=5 -WHERE=6 -GROUP=7 -BY=8 -HAVING=9 -ORDER=10 -LIMIT=11 -OFFSET=12 -AS=13 -ASC=14 -DESC=15 -IN=16 -IS=17 -NULL=18 -LIKE=19 -BETWEEN=20 -STAR=21 -COUNT=22 -SUM=23 -AVG=24 -MIN=25 -MAX=26 -SQL_FUNCTION=27 -GT=28 -GE=29 -LT=30 -LE=31 -EQ=32 -NE=33 -LPAREN=34 -RPAREN=35 -LBRACKET=36 -RBRACKET=37 -COMMA=38 -DOT=39 -SEMICOLON=40 -FLOAT=41 -NUMBER=42 -STRING=43 -IDENTIFIER=44 -WS=45 -LINE_COMMENT=46 -BLOCK_COMMENT=47 -'*'=21 -'>'=28 -'>='=29 -'<'=30 -'<='=31 -'!='=33 -'('=34 -')'=35 -'['=36 -']'=37 -','=38 -'.'=39 -';'=40 diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionListener.java b/hertzbeat-alerter/gen/expr/SqlExpressionListener.java deleted file mode 100644 index 638b3aae90f..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionListener.java +++ /dev/null @@ -1,234 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; -import org.antlr.v4.runtime.tree.ParseTreeListener; - -/** - * This interface defines a complete listener for a parse tree produced by - * {@link SqlExpressionParser}. - */ -public interface SqlExpressionListener extends ParseTreeListener { - /** - * Enter a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - */ - void enterExpression(SqlExpressionParser.ExpressionContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - */ - void exitExpression(SqlExpressionParser.ExpressionContext ctx); - /** - * Enter a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - /** - * Exit a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - /** - * Enter a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - /** - * Exit a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - */ - void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - */ - void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - */ - void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - */ - void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - */ - void enterSelectField(SqlExpressionParser.SelectFieldContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - */ - void exitSelectField(SqlExpressionParser.SelectFieldContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - */ - void enterRelList(SqlExpressionParser.RelListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - */ - void exitRelList(SqlExpressionParser.RelListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - */ - void enterRelation(SqlExpressionParser.RelationContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - */ - void exitRelation(SqlExpressionParser.RelationContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - */ - void enterConditionList(SqlExpressionParser.ConditionListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - */ - void exitConditionList(SqlExpressionParser.ConditionListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - */ - void enterCompOp(SqlExpressionParser.CompOpContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - */ - void exitCompOp(SqlExpressionParser.CompOpContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - */ - void enterCondition(SqlExpressionParser.ConditionContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - */ - void exitCondition(SqlExpressionParser.ConditionContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - */ - void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - */ - void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - */ - void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - */ - void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - */ - void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - */ - void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - */ - void enterParameterList(SqlExpressionParser.ParameterListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - */ - void exitParameterList(SqlExpressionParser.ParameterListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - */ - void enterParameter(SqlExpressionParser.ParameterContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - */ - void exitParameter(SqlExpressionParser.ParameterContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - */ - void enterGroupByList(SqlExpressionParser.GroupByListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - */ - void exitGroupByList(SqlExpressionParser.GroupByListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - */ - void enterOrderByList(SqlExpressionParser.OrderByListContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - */ - void exitOrderByList(SqlExpressionParser.OrderByListContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - */ - void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - */ - void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - */ - void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - */ - void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - */ - void enterNumber(SqlExpressionParser.NumberContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - */ - void exitNumber(SqlExpressionParser.NumberContext ctx); - /** - * Enter a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - */ - void enterString(SqlExpressionParser.StringContext ctx); - /** - * Exit a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - */ - void exitString(SqlExpressionParser.StringContext ctx); -} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionParser.java b/hertzbeat-alerter/gen/expr/SqlExpressionParser.java deleted file mode 100644 index 23fed448bf7..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionParser.java +++ /dev/null @@ -1,2100 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; -import org.antlr.v4.runtime.atn.*; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; -import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class SqlExpressionParser extends Parser { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, - ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, - LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, - SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, - RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, - NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; - public static final int - RULE_expression = 0, RULE_sqlExpr = 1, RULE_selectSql = 2, RULE_selectFieldList = 3, - RULE_selectField = 4, RULE_relList = 5, RULE_relation = 6, RULE_conditionList = 7, - RULE_compOp = 8, RULE_condition = 9, RULE_conditionUnit = 10, RULE_functionCall = 11, - RULE_functionName = 12, RULE_parameterList = 13, RULE_parameter = 14, - RULE_groupByList = 15, RULE_orderByList = 16, RULE_orderByField = 17, - RULE_limitClause = 18, RULE_number = 19, RULE_string = 20; - private static String[] makeRuleNames() { - return new String[] { - "expression", "sqlExpr", "selectSql", "selectFieldList", "selectField", - "relList", "relation", "conditionList", "compOp", "condition", "conditionUnit", - "functionCall", "functionName", "parameterList", "parameter", "groupByList", - "orderByList", "orderByField", "limitClause", "number", "string" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, "'*'", null, null, - null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", - "')'", "'['", "']'", "','", "'.'", "';'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - @Override - public String getGrammarFileName() { return "SqlExpression.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public ATN getATN() { return _ATN; } - - public SqlExpressionParser(TokenStream input) { - super(input); - _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @SuppressWarnings("CheckReturnValue") - public static class ExpressionContext extends ParserRuleContext { - public SqlExprContext sqlExpr() { - return getRuleContext(SqlExprContext.class,0); - } - public TerminalNode EOF() { return getToken(SqlExpressionParser.EOF, 0); } - public ExpressionContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_expression; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterExpression(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitExpression(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitExpression(this); - else return visitor.visitChildren(this); - } - } - - public final ExpressionContext expression() throws RecognitionException { - ExpressionContext _localctx = new ExpressionContext(_ctx, getState()); - enterRule(_localctx, 0, RULE_expression); - try { - enterOuterAlt(_localctx, 1); - { - setState(42); - sqlExpr(); - setState(43); - match(EOF); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SqlExprContext extends ParserRuleContext { - public SqlExprContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_sqlExpr; } - - public SqlExprContext() { } - public void copyFrom(SqlExprContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlCallExprContext extends SqlExprContext { - public TerminalNode SQL_FUNCTION() { return getToken(SqlExpressionParser.SQL_FUNCTION, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public SelectSqlCallExprContext(SqlExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlCallExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlCallExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlCallExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlExprContext extends SqlExprContext { - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public SelectSqlExprContext(SqlExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlExpr(this); - else return visitor.visitChildren(this); - } - } - - public final SqlExprContext sqlExpr() throws RecognitionException { - SqlExprContext _localctx = new SqlExprContext(_ctx, getState()); - enterRule(_localctx, 2, RULE_sqlExpr); - try { - setState(51); - _errHandler.sync(this); - switch (_input.LA(1)) { - case SELECT: - _localctx = new SelectSqlExprContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(45); - selectSql(); - } - break; - case SQL_FUNCTION: - _localctx = new SelectSqlCallExprContext(_localctx); - enterOuterAlt(_localctx, 2); - { - setState(46); - match(SQL_FUNCTION); - setState(47); - match(LPAREN); - setState(48); - string(); - setState(49); - match(RPAREN); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlContext extends ParserRuleContext { - public TerminalNode SELECT() { return getToken(SqlExpressionParser.SELECT, 0); } - public SelectFieldListContext selectFieldList() { - return getRuleContext(SelectFieldListContext.class,0); - } - public TerminalNode FROM() { return getToken(SqlExpressionParser.FROM, 0); } - public RelListContext relList() { - return getRuleContext(RelListContext.class,0); - } - public TerminalNode WHERE() { return getToken(SqlExpressionParser.WHERE, 0); } - public List conditionList() { - return getRuleContexts(ConditionListContext.class); - } - public ConditionListContext conditionList(int i) { - return getRuleContext(ConditionListContext.class,i); - } - public TerminalNode GROUP() { return getToken(SqlExpressionParser.GROUP, 0); } - public List BY() { return getTokens(SqlExpressionParser.BY); } - public TerminalNode BY(int i) { - return getToken(SqlExpressionParser.BY, i); - } - public GroupByListContext groupByList() { - return getRuleContext(GroupByListContext.class,0); - } - public TerminalNode HAVING() { return getToken(SqlExpressionParser.HAVING, 0); } - public TerminalNode ORDER() { return getToken(SqlExpressionParser.ORDER, 0); } - public OrderByListContext orderByList() { - return getRuleContext(OrderByListContext.class,0); - } - public TerminalNode LIMIT() { return getToken(SqlExpressionParser.LIMIT, 0); } - public LimitClauseContext limitClause() { - return getRuleContext(LimitClauseContext.class,0); - } - public SelectSqlContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectSql; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSql(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSql(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSql(this); - else return visitor.visitChildren(this); - } - } - - public final SelectSqlContext selectSql() throws RecognitionException { - SelectSqlContext _localctx = new SelectSqlContext(_ctx, getState()); - enterRule(_localctx, 4, RULE_selectSql); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(53); - match(SELECT); - setState(54); - selectFieldList(); - setState(55); - match(FROM); - setState(56); - relList(); - setState(59); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==WHERE) { - { - setState(57); - match(WHERE); - setState(58); - conditionList(0); - } - } - - setState(64); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==GROUP) { - { - setState(61); - match(GROUP); - setState(62); - match(BY); - setState(63); - groupByList(); - } - } - - setState(68); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==HAVING) { - { - setState(66); - match(HAVING); - setState(67); - conditionList(0); - } - } - - setState(73); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ORDER) { - { - setState(70); - match(ORDER); - setState(71); - match(BY); - setState(72); - orderByList(); - } - } - - setState(77); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==LIMIT) { - { - setState(75); - match(LIMIT); - setState(76); - limitClause(); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectFieldListContext extends ParserRuleContext { - public List selectField() { - return getRuleContexts(SelectFieldContext.class); - } - public SelectFieldContext selectField(int i) { - return getRuleContext(SelectFieldContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public SelectFieldListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectFieldList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectFieldList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectFieldList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectFieldList(this); - else return visitor.visitChildren(this); - } - } - - public final SelectFieldListContext selectFieldList() throws RecognitionException { - SelectFieldListContext _localctx = new SelectFieldListContext(_ctx, getState()); - enterRule(_localctx, 6, RULE_selectFieldList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(79); - selectField(); - setState(84); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(80); - match(COMMA); - setState(81); - selectField(); - } - } - setState(86); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectFieldContext extends ParserRuleContext { - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } - public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } - public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } - public SelectFieldContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectField; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectField(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectField(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectField(this); - else return visitor.visitChildren(this); - } - } - - public final SelectFieldContext selectField() throws RecognitionException { - SelectFieldContext _localctx = new SelectFieldContext(_ctx, getState()); - enterRule(_localctx, 8, RULE_selectField); - int _la; - try { - setState(117); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(87); - functionCall(); - setState(92); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(89); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(88); - match(AS); - } - } - - setState(91); - match(IDENTIFIER); - } - } - - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(94); - match(IDENTIFIER); - setState(99); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(96); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(95); - match(AS); - } - } - - setState(98); - match(IDENTIFIER); - } - } - - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(101); - match(STAR); - setState(106); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(103); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(102); - match(AS); - } - } - - setState(105); - match(IDENTIFIER); - } - } - - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(108); - match(IDENTIFIER); - setState(109); - match(DOT); - setState(110); - match(IDENTIFIER); - setState(115); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(112); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(111); - match(AS); - } - } - - setState(114); - match(IDENTIFIER); - } - } - - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class RelListContext extends ParserRuleContext { - public List relation() { - return getRuleContexts(RelationContext.class); - } - public RelationContext relation(int i) { - return getRuleContext(RelationContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public RelListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_relList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelList(this); - else return visitor.visitChildren(this); - } - } - - public final RelListContext relList() throws RecognitionException { - RelListContext _localctx = new RelListContext(_ctx, getState()); - enterRule(_localctx, 10, RULE_relList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(119); - relation(); - setState(124); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(120); - match(COMMA); - setState(121); - relation(); - } - } - setState(126); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class RelationContext extends ParserRuleContext { - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public RelationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_relation; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelation(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelation(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelation(this); - else return visitor.visitChildren(this); - } - } - - public final RelationContext relation() throws RecognitionException { - RelationContext _localctx = new RelationContext(_ctx, getState()); - enterRule(_localctx, 12, RULE_relation); - int _la; - try { - setState(140); - _errHandler.sync(this); - switch (_input.LA(1)) { - case IDENTIFIER: - enterOuterAlt(_localctx, 1); - { - setState(127); - match(IDENTIFIER); - setState(132); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(129); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(128); - match(AS); - } - } - - setState(131); - match(IDENTIFIER); - } - } - - } - break; - case LPAREN: - enterOuterAlt(_localctx, 2); - { - setState(134); - match(LPAREN); - setState(135); - selectSql(); - setState(136); - match(RPAREN); - setState(137); - match(AS); - setState(138); - match(IDENTIFIER); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionListContext extends ParserRuleContext { - public ConditionContext condition() { - return getRuleContext(ConditionContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public List conditionList() { - return getRuleContexts(ConditionListContext.class); - } - public ConditionListContext conditionList(int i) { - return getRuleContext(ConditionListContext.class,i); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } - public TerminalNode OR() { return getToken(SqlExpressionParser.OR, 0); } - public ConditionListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_conditionList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionList(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionListContext conditionList() throws RecognitionException { - return conditionList(0); - } - - private ConditionListContext conditionList(int _p) throws RecognitionException { - ParserRuleContext _parentctx = _ctx; - int _parentState = getState(); - ConditionListContext _localctx = new ConditionListContext(_ctx, _parentState); - ConditionListContext _prevctx = _localctx; - int _startState = 14; - enterRecursionRule(_localctx, 14, RULE_conditionList, _p); - try { - int _alt; - enterOuterAlt(_localctx, 1); - { - setState(148); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,20,_ctx) ) { - case 1: - { - setState(143); - condition(); - } - break; - case 2: - { - setState(144); - match(LPAREN); - setState(145); - conditionList(0); - setState(146); - match(RPAREN); - } - break; - } - _ctx.stop = _input.LT(-1); - setState(158); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); - while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { - if ( _alt==1 ) { - if ( _parseListeners!=null ) triggerExitRuleEvent(); - _prevctx = _localctx; - { - setState(156); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { - case 1: - { - _localctx = new ConditionListContext(_parentctx, _parentState); - pushNewRecursionContext(_localctx, _startState, RULE_conditionList); - setState(150); - if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(151); - match(AND); - setState(152); - conditionList(4); - } - break; - case 2: - { - _localctx = new ConditionListContext(_parentctx, _parentState); - pushNewRecursionContext(_localctx, _startState, RULE_conditionList); - setState(153); - if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(154); - match(OR); - setState(155); - conditionList(3); - } - break; - } - } - } - setState(160); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - unrollRecursionContexts(_parentctx); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class CompOpContext extends ParserRuleContext { - public TerminalNode EQ() { return getToken(SqlExpressionParser.EQ, 0); } - public TerminalNode LT() { return getToken(SqlExpressionParser.LT, 0); } - public TerminalNode GT() { return getToken(SqlExpressionParser.GT, 0); } - public TerminalNode LE() { return getToken(SqlExpressionParser.LE, 0); } - public TerminalNode GE() { return getToken(SqlExpressionParser.GE, 0); } - public TerminalNode NE() { return getToken(SqlExpressionParser.NE, 0); } - public TerminalNode LIKE() { return getToken(SqlExpressionParser.LIKE, 0); } - public TerminalNode NOT() { return getToken(SqlExpressionParser.NOT, 0); } - public TerminalNode IN() { return getToken(SqlExpressionParser.IN, 0); } - public TerminalNode IS() { return getToken(SqlExpressionParser.IS, 0); } - public CompOpContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_compOp; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCompOp(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCompOp(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCompOp(this); - else return visitor.visitChildren(this); - } - } - - public final CompOpContext compOp() throws RecognitionException { - CompOpContext _localctx = new CompOpContext(_ctx, getState()); - enterRule(_localctx, 16, RULE_compOp); - try { - setState(176); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(161); - match(EQ); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(162); - match(LT); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(163); - match(GT); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(164); - match(LE); - } - break; - case 5: - enterOuterAlt(_localctx, 5); - { - setState(165); - match(GE); - } - break; - case 6: - enterOuterAlt(_localctx, 6); - { - setState(166); - match(NE); - } - break; - case 7: - enterOuterAlt(_localctx, 7); - { - setState(167); - match(LIKE); - } - break; - case 8: - enterOuterAlt(_localctx, 8); - { - setState(168); - match(NOT); - setState(169); - match(LIKE); - } - break; - case 9: - enterOuterAlt(_localctx, 9); - { - setState(170); - match(IN); - } - break; - case 10: - enterOuterAlt(_localctx, 10); - { - setState(171); - match(NOT); - setState(172); - match(IN); - } - break; - case 11: - enterOuterAlt(_localctx, 11); - { - setState(173); - match(IS); - } - break; - case 12: - enterOuterAlt(_localctx, 12); - { - setState(174); - match(IS); - setState(175); - match(NOT); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionContext extends ParserRuleContext { - public List conditionUnit() { - return getRuleContexts(ConditionUnitContext.class); - } - public ConditionUnitContext conditionUnit(int i) { - return getRuleContext(ConditionUnitContext.class,i); - } - public CompOpContext compOp() { - return getRuleContext(CompOpContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public ConditionContext condition() { - return getRuleContext(ConditionContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public TerminalNode BETWEEN() { return getToken(SqlExpressionParser.BETWEEN, 0); } - public List number() { - return getRuleContexts(NumberContext.class); - } - public NumberContext number(int i) { - return getRuleContext(NumberContext.class,i); - } - public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } - public ConditionContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_condition; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCondition(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCondition(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCondition(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionContext condition() throws RecognitionException { - ConditionContext _localctx = new ConditionContext(_ctx, getState()); - enterRule(_localctx, 18, RULE_condition); - try { - setState(192); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(178); - conditionUnit(); - setState(179); - compOp(); - setState(180); - conditionUnit(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(182); - match(LPAREN); - setState(183); - condition(); - setState(184); - match(RPAREN); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(186); - match(IDENTIFIER); - setState(187); - match(BETWEEN); - setState(188); - number(); - setState(189); - match(AND); - setState(190); - number(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionUnitContext extends ParserRuleContext { - public NumberContext number() { - return getRuleContext(NumberContext.class,0); - } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } - public TerminalNode NULL() { return getToken(SqlExpressionParser.NULL, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public ConditionUnitContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_conditionUnit; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionUnit(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionUnit(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionUnit(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionUnitContext conditionUnit() throws RecognitionException { - ConditionUnitContext _localctx = new ConditionUnitContext(_ctx, getState()); - enterRule(_localctx, 20, RULE_conditionUnit); - try { - setState(206); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(194); - number(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(195); - string(); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(196); - match(IDENTIFIER); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(197); - match(IDENTIFIER); - setState(198); - match(DOT); - setState(199); - match(IDENTIFIER); - } - break; - case 5: - enterOuterAlt(_localctx, 5); - { - setState(200); - match(NULL); - } - break; - case 6: - enterOuterAlt(_localctx, 6); - { - setState(201); - match(LPAREN); - setState(202); - selectSql(); - setState(203); - match(RPAREN); - } - break; - case 7: - enterOuterAlt(_localctx, 7); - { - setState(205); - functionCall(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class FunctionCallContext extends ParserRuleContext { - public FunctionNameContext functionName() { - return getRuleContext(FunctionNameContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public ParameterListContext parameterList() { - return getRuleContext(ParameterListContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public FunctionCallContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_functionCall; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionCall(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionCall(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionCall(this); - else return visitor.visitChildren(this); - } - } - - public final FunctionCallContext functionCall() throws RecognitionException { - FunctionCallContext _localctx = new FunctionCallContext(_ctx, getState()); - enterRule(_localctx, 22, RULE_functionCall); - try { - enterOuterAlt(_localctx, 1); - { - setState(208); - functionName(); - setState(209); - match(LPAREN); - setState(210); - parameterList(); - setState(211); - match(RPAREN); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class FunctionNameContext extends ParserRuleContext { - public TerminalNode COUNT() { return getToken(SqlExpressionParser.COUNT, 0); } - public TerminalNode AVG() { return getToken(SqlExpressionParser.AVG, 0); } - public TerminalNode SUM() { return getToken(SqlExpressionParser.SUM, 0); } - public TerminalNode MIN() { return getToken(SqlExpressionParser.MIN, 0); } - public TerminalNode MAX() { return getToken(SqlExpressionParser.MAX, 0); } - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public FunctionNameContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_functionName; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionName(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionName(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionName(this); - else return visitor.visitChildren(this); - } - } - - public final FunctionNameContext functionName() throws RecognitionException { - FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); - enterRule(_localctx, 24, RULE_functionName); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(213); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 17592316067840L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterListContext extends ParserRuleContext { - public List parameter() { - return getRuleContexts(ParameterContext.class); - } - public ParameterContext parameter(int i) { - return getRuleContext(ParameterContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public ParameterListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameterList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameterList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameterList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameterList(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterListContext parameterList() throws RecognitionException { - ParameterListContext _localctx = new ParameterListContext(_ctx, getState()); - enterRule(_localctx, 26, RULE_parameterList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(215); - parameter(); - setState(220); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(216); - match(COMMA); - setState(217); - parameter(); - } - } - setState(222); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterContext extends ParserRuleContext { - public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public ParameterContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameter; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameter(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameter(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameter(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterContext parameter() throws RecognitionException { - ParameterContext _localctx = new ParameterContext(_ctx, getState()); - enterRule(_localctx, 28, RULE_parameter); - try { - setState(225); - _errHandler.sync(this); - switch (_input.LA(1)) { - case STAR: - enterOuterAlt(_localctx, 1); - { - setState(223); - match(STAR); - } - break; - case STRING: - enterOuterAlt(_localctx, 2); - { - setState(224); - string(); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class GroupByListContext extends ParserRuleContext { - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public GroupByListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_groupByList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterGroupByList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitGroupByList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitGroupByList(this); - else return visitor.visitChildren(this); - } - } - - public final GroupByListContext groupByList() throws RecognitionException { - GroupByListContext _localctx = new GroupByListContext(_ctx, getState()); - enterRule(_localctx, 30, RULE_groupByList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(227); - match(IDENTIFIER); - setState(232); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(228); - match(COMMA); - setState(229); - match(IDENTIFIER); - } - } - setState(234); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OrderByListContext extends ParserRuleContext { - public List orderByField() { - return getRuleContexts(OrderByFieldContext.class); - } - public OrderByFieldContext orderByField(int i) { - return getRuleContext(OrderByFieldContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public OrderByListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_orderByList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByList(this); - else return visitor.visitChildren(this); - } - } - - public final OrderByListContext orderByList() throws RecognitionException { - OrderByListContext _localctx = new OrderByListContext(_ctx, getState()); - enterRule(_localctx, 32, RULE_orderByList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(235); - orderByField(); - setState(240); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(236); - match(COMMA); - setState(237); - orderByField(); - } - } - setState(242); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OrderByFieldContext extends ParserRuleContext { - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public TerminalNode ASC() { return getToken(SqlExpressionParser.ASC, 0); } - public TerminalNode DESC() { return getToken(SqlExpressionParser.DESC, 0); } - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public OrderByFieldContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_orderByField; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByField(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByField(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByField(this); - else return visitor.visitChildren(this); - } - } - - public final OrderByFieldContext orderByField() throws RecognitionException { - OrderByFieldContext _localctx = new OrderByFieldContext(_ctx, getState()); - enterRule(_localctx, 34, RULE_orderByField); - int _la; - try { - setState(251); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(243); - match(IDENTIFIER); - setState(245); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASC || _la==DESC) { - { - setState(244); - _la = _input.LA(1); - if ( !(_la==ASC || _la==DESC) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(247); - functionCall(); - setState(249); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASC || _la==DESC) { - { - setState(248); - _la = _input.LA(1); - if ( !(_la==ASC || _la==DESC) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class LimitClauseContext extends ParserRuleContext { - public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } - public LimitClauseContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_limitClause; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterLimitClause(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitLimitClause(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitLimitClause(this); - else return visitor.visitChildren(this); - } - } - - public final LimitClauseContext limitClause() throws RecognitionException { - LimitClauseContext _localctx = new LimitClauseContext(_ctx, getState()); - enterRule(_localctx, 36, RULE_limitClause); - try { - enterOuterAlt(_localctx, 1); - { - setState(253); - match(NUMBER); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class NumberContext extends ParserRuleContext { - public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } - public TerminalNode FLOAT() { return getToken(SqlExpressionParser.FLOAT, 0); } - public NumberContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_number; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterNumber(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitNumber(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitNumber(this); - else return visitor.visitChildren(this); - } - } - - public final NumberContext number() throws RecognitionException { - NumberContext _localctx = new NumberContext(_ctx, getState()); - enterRule(_localctx, 38, RULE_number); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(255); - _la = _input.LA(1); - if ( !(_la==FLOAT || _la==NUMBER) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class StringContext extends ParserRuleContext { - public TerminalNode STRING() { return getToken(SqlExpressionParser.STRING, 0); } - public StringContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_string; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterString(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitString(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitString(this); - else return visitor.visitChildren(this); - } - } - - public final StringContext string() throws RecognitionException { - StringContext _localctx = new StringContext(_ctx, getState()); - enterRule(_localctx, 40, RULE_string); - try { - enterOuterAlt(_localctx, 1); - { - setState(257); - match(STRING); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { - switch (ruleIndex) { - case 7: - return conditionList_sempred((ConditionListContext)_localctx, predIndex); - } - return true; - } - private boolean conditionList_sempred(ConditionListContext _localctx, int predIndex) { - switch (predIndex) { - case 0: - return precpred(_ctx, 3); - case 1: - return precpred(_ctx, 2); - } - return true; - } - - public static final String _serializedATN = - "\u0004\u0001/\u0104\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ - "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ - "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ - "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ - "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ - "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012"+ - "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0001\u0000\u0001\u0000"+ - "\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+ - "\u0001\u0001\u0003\u00014\b\u0001\u0001\u0002\u0001\u0002\u0001\u0002"+ - "\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002<\b\u0002\u0001\u0002"+ - "\u0001\u0002\u0001\u0002\u0003\u0002A\b\u0002\u0001\u0002\u0001\u0002"+ - "\u0003\u0002E\b\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002"+ - "J\b\u0002\u0001\u0002\u0001\u0002\u0003\u0002N\b\u0002\u0001\u0003\u0001"+ - "\u0003\u0001\u0003\u0005\u0003S\b\u0003\n\u0003\f\u0003V\t\u0003\u0001"+ - "\u0004\u0001\u0004\u0003\u0004Z\b\u0004\u0001\u0004\u0003\u0004]\b\u0004"+ - "\u0001\u0004\u0001\u0004\u0003\u0004a\b\u0004\u0001\u0004\u0003\u0004"+ - "d\b\u0004\u0001\u0004\u0001\u0004\u0003\u0004h\b\u0004\u0001\u0004\u0003"+ - "\u0004k\b\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003"+ - "\u0004q\b\u0004\u0001\u0004\u0003\u0004t\b\u0004\u0003\u0004v\b\u0004"+ - "\u0001\u0005\u0001\u0005\u0001\u0005\u0005\u0005{\b\u0005\n\u0005\f\u0005"+ - "~\t\u0005\u0001\u0006\u0001\u0006\u0003\u0006\u0082\b\u0006\u0001\u0006"+ - "\u0003\u0006\u0085\b\u0006\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ - "\u0001\u0006\u0001\u0006\u0003\u0006\u008d\b\u0006\u0001\u0007\u0001\u0007"+ - "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007\u0095\b\u0007"+ - "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007"+ - "\u0005\u0007\u009d\b\u0007\n\u0007\f\u0007\u00a0\t\u0007\u0001\b\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0003\b\u00b1\b\b\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0003\t\u00c1\b\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ - "\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003\n\u00cf"+ - "\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001"+ - "\f\u0001\f\u0001\r\u0001\r\u0001\r\u0005\r\u00db\b\r\n\r\f\r\u00de\t\r"+ - "\u0001\u000e\u0001\u000e\u0003\u000e\u00e2\b\u000e\u0001\u000f\u0001\u000f"+ - "\u0001\u000f\u0005\u000f\u00e7\b\u000f\n\u000f\f\u000f\u00ea\t\u000f\u0001"+ - "\u0010\u0001\u0010\u0001\u0010\u0005\u0010\u00ef\b\u0010\n\u0010\f\u0010"+ - "\u00f2\t\u0010\u0001\u0011\u0001\u0011\u0003\u0011\u00f6\b\u0011\u0001"+ - "\u0011\u0001\u0011\u0003\u0011\u00fa\b\u0011\u0003\u0011\u00fc\b\u0011"+ - "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014"+ - "\u0001\u0014\u0000\u0001\u000e\u0015\u0000\u0002\u0004\u0006\b\n\f\u000e"+ - "\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(\u0000\u0003\u0002"+ - "\u0000\u0016\u001a,,\u0001\u0000\u000e\u000f\u0001\u0000)*\u0121\u0000"+ - "*\u0001\u0000\u0000\u0000\u00023\u0001\u0000\u0000\u0000\u00045\u0001"+ - "\u0000\u0000\u0000\u0006O\u0001\u0000\u0000\u0000\bu\u0001\u0000\u0000"+ - "\u0000\nw\u0001\u0000\u0000\u0000\f\u008c\u0001\u0000\u0000\u0000\u000e"+ - "\u0094\u0001\u0000\u0000\u0000\u0010\u00b0\u0001\u0000\u0000\u0000\u0012"+ - "\u00c0\u0001\u0000\u0000\u0000\u0014\u00ce\u0001\u0000\u0000\u0000\u0016"+ - "\u00d0\u0001\u0000\u0000\u0000\u0018\u00d5\u0001\u0000\u0000\u0000\u001a"+ - "\u00d7\u0001\u0000\u0000\u0000\u001c\u00e1\u0001\u0000\u0000\u0000\u001e"+ - "\u00e3\u0001\u0000\u0000\u0000 \u00eb\u0001\u0000\u0000\u0000\"\u00fb"+ - "\u0001\u0000\u0000\u0000$\u00fd\u0001\u0000\u0000\u0000&\u00ff\u0001\u0000"+ - "\u0000\u0000(\u0101\u0001\u0000\u0000\u0000*+\u0003\u0002\u0001\u0000"+ - "+,\u0005\u0000\u0000\u0001,\u0001\u0001\u0000\u0000\u0000-4\u0003\u0004"+ - "\u0002\u0000./\u0005\u001b\u0000\u0000/0\u0005\"\u0000\u000001\u0003("+ - "\u0014\u000012\u0005#\u0000\u000024\u0001\u0000\u0000\u00003-\u0001\u0000"+ - "\u0000\u00003.\u0001\u0000\u0000\u00004\u0003\u0001\u0000\u0000\u0000"+ - "56\u0005\u0004\u0000\u000067\u0003\u0006\u0003\u000078\u0005\u0005\u0000"+ - "\u00008;\u0003\n\u0005\u00009:\u0005\u0006\u0000\u0000:<\u0003\u000e\u0007"+ - "\u0000;9\u0001\u0000\u0000\u0000;<\u0001\u0000\u0000\u0000<@\u0001\u0000"+ - "\u0000\u0000=>\u0005\u0007\u0000\u0000>?\u0005\b\u0000\u0000?A\u0003\u001e"+ - "\u000f\u0000@=\u0001\u0000\u0000\u0000@A\u0001\u0000\u0000\u0000AD\u0001"+ - "\u0000\u0000\u0000BC\u0005\t\u0000\u0000CE\u0003\u000e\u0007\u0000DB\u0001"+ - "\u0000\u0000\u0000DE\u0001\u0000\u0000\u0000EI\u0001\u0000\u0000\u0000"+ - "FG\u0005\n\u0000\u0000GH\u0005\b\u0000\u0000HJ\u0003 \u0010\u0000IF\u0001"+ - "\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JM\u0001\u0000\u0000\u0000"+ - "KL\u0005\u000b\u0000\u0000LN\u0003$\u0012\u0000MK\u0001\u0000\u0000\u0000"+ - "MN\u0001\u0000\u0000\u0000N\u0005\u0001\u0000\u0000\u0000OT\u0003\b\u0004"+ - "\u0000PQ\u0005&\u0000\u0000QS\u0003\b\u0004\u0000RP\u0001\u0000\u0000"+ - "\u0000SV\u0001\u0000\u0000\u0000TR\u0001\u0000\u0000\u0000TU\u0001\u0000"+ - "\u0000\u0000U\u0007\u0001\u0000\u0000\u0000VT\u0001\u0000\u0000\u0000"+ - "W\\\u0003\u0016\u000b\u0000XZ\u0005\r\u0000\u0000YX\u0001\u0000\u0000"+ - "\u0000YZ\u0001\u0000\u0000\u0000Z[\u0001\u0000\u0000\u0000[]\u0005,\u0000"+ - "\u0000\\Y\u0001\u0000\u0000\u0000\\]\u0001\u0000\u0000\u0000]v\u0001\u0000"+ - "\u0000\u0000^c\u0005,\u0000\u0000_a\u0005\r\u0000\u0000`_\u0001\u0000"+ - "\u0000\u0000`a\u0001\u0000\u0000\u0000ab\u0001\u0000\u0000\u0000bd\u0005"+ - ",\u0000\u0000c`\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000dv\u0001"+ - "\u0000\u0000\u0000ej\u0005\u0015\u0000\u0000fh\u0005\r\u0000\u0000gf\u0001"+ - "\u0000\u0000\u0000gh\u0001\u0000\u0000\u0000hi\u0001\u0000\u0000\u0000"+ - "ik\u0005,\u0000\u0000jg\u0001\u0000\u0000\u0000jk\u0001\u0000\u0000\u0000"+ - "kv\u0001\u0000\u0000\u0000lm\u0005,\u0000\u0000mn\u0005\'\u0000\u0000"+ - "ns\u0005,\u0000\u0000oq\u0005\r\u0000\u0000po\u0001\u0000\u0000\u0000"+ - "pq\u0001\u0000\u0000\u0000qr\u0001\u0000\u0000\u0000rt\u0005,\u0000\u0000"+ - "sp\u0001\u0000\u0000\u0000st\u0001\u0000\u0000\u0000tv\u0001\u0000\u0000"+ - "\u0000uW\u0001\u0000\u0000\u0000u^\u0001\u0000\u0000\u0000ue\u0001\u0000"+ - "\u0000\u0000ul\u0001\u0000\u0000\u0000v\t\u0001\u0000\u0000\u0000w|\u0003"+ - "\f\u0006\u0000xy\u0005&\u0000\u0000y{\u0003\f\u0006\u0000zx\u0001\u0000"+ - "\u0000\u0000{~\u0001\u0000\u0000\u0000|z\u0001\u0000\u0000\u0000|}\u0001"+ - "\u0000\u0000\u0000}\u000b\u0001\u0000\u0000\u0000~|\u0001\u0000\u0000"+ - "\u0000\u007f\u0084\u0005,\u0000\u0000\u0080\u0082\u0005\r\u0000\u0000"+ - "\u0081\u0080\u0001\u0000\u0000\u0000\u0081\u0082\u0001\u0000\u0000\u0000"+ - "\u0082\u0083\u0001\u0000\u0000\u0000\u0083\u0085\u0005,\u0000\u0000\u0084"+ - "\u0081\u0001\u0000\u0000\u0000\u0084\u0085\u0001\u0000\u0000\u0000\u0085"+ - "\u008d\u0001\u0000\u0000\u0000\u0086\u0087\u0005\"\u0000\u0000\u0087\u0088"+ - "\u0003\u0004\u0002\u0000\u0088\u0089\u0005#\u0000\u0000\u0089\u008a\u0005"+ - "\r\u0000\u0000\u008a\u008b\u0005,\u0000\u0000\u008b\u008d\u0001\u0000"+ - "\u0000\u0000\u008c\u007f\u0001\u0000\u0000\u0000\u008c\u0086\u0001\u0000"+ - "\u0000\u0000\u008d\r\u0001\u0000\u0000\u0000\u008e\u008f\u0006\u0007\uffff"+ - "\uffff\u0000\u008f\u0095\u0003\u0012\t\u0000\u0090\u0091\u0005\"\u0000"+ - "\u0000\u0091\u0092\u0003\u000e\u0007\u0000\u0092\u0093\u0005#\u0000\u0000"+ - "\u0093\u0095\u0001\u0000\u0000\u0000\u0094\u008e\u0001\u0000\u0000\u0000"+ - "\u0094\u0090\u0001\u0000\u0000\u0000\u0095\u009e\u0001\u0000\u0000\u0000"+ - "\u0096\u0097\n\u0003\u0000\u0000\u0097\u0098\u0005\u0001\u0000\u0000\u0098"+ - "\u009d\u0003\u000e\u0007\u0004\u0099\u009a\n\u0002\u0000\u0000\u009a\u009b"+ - "\u0005\u0002\u0000\u0000\u009b\u009d\u0003\u000e\u0007\u0003\u009c\u0096"+ - "\u0001\u0000\u0000\u0000\u009c\u0099\u0001\u0000\u0000\u0000\u009d\u00a0"+ - "\u0001\u0000\u0000\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009e\u009f"+ - "\u0001\u0000\u0000\u0000\u009f\u000f\u0001\u0000\u0000\u0000\u00a0\u009e"+ - "\u0001\u0000\u0000\u0000\u00a1\u00b1\u0005 \u0000\u0000\u00a2\u00b1\u0005"+ - "\u001e\u0000\u0000\u00a3\u00b1\u0005\u001c\u0000\u0000\u00a4\u00b1\u0005"+ - "\u001f\u0000\u0000\u00a5\u00b1\u0005\u001d\u0000\u0000\u00a6\u00b1\u0005"+ - "!\u0000\u0000\u00a7\u00b1\u0005\u0013\u0000\u0000\u00a8\u00a9\u0005\u0003"+ - "\u0000\u0000\u00a9\u00b1\u0005\u0013\u0000\u0000\u00aa\u00b1\u0005\u0010"+ - "\u0000\u0000\u00ab\u00ac\u0005\u0003\u0000\u0000\u00ac\u00b1\u0005\u0010"+ - "\u0000\u0000\u00ad\u00b1\u0005\u0011\u0000\u0000\u00ae\u00af\u0005\u0011"+ - "\u0000\u0000\u00af\u00b1\u0005\u0003\u0000\u0000\u00b0\u00a1\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a2\u0001\u0000\u0000\u0000\u00b0\u00a3\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a4\u0001\u0000\u0000\u0000\u00b0\u00a5\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a6\u0001\u0000\u0000\u0000\u00b0\u00a7\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a8\u0001\u0000\u0000\u0000\u00b0\u00aa\u0001\u0000"+ - "\u0000\u0000\u00b0\u00ab\u0001\u0000\u0000\u0000\u00b0\u00ad\u0001\u0000"+ - "\u0000\u0000\u00b0\u00ae\u0001\u0000\u0000\u0000\u00b1\u0011\u0001\u0000"+ - "\u0000\u0000\u00b2\u00b3\u0003\u0014\n\u0000\u00b3\u00b4\u0003\u0010\b"+ - "\u0000\u00b4\u00b5\u0003\u0014\n\u0000\u00b5\u00c1\u0001\u0000\u0000\u0000"+ - "\u00b6\u00b7\u0005\"\u0000\u0000\u00b7\u00b8\u0003\u0012\t\u0000\u00b8"+ - "\u00b9\u0005#\u0000\u0000\u00b9\u00c1\u0001\u0000\u0000\u0000\u00ba\u00bb"+ - "\u0005,\u0000\u0000\u00bb\u00bc\u0005\u0014\u0000\u0000\u00bc\u00bd\u0003"+ - "&\u0013\u0000\u00bd\u00be\u0005\u0001\u0000\u0000\u00be\u00bf\u0003&\u0013"+ - "\u0000\u00bf\u00c1\u0001\u0000\u0000\u0000\u00c0\u00b2\u0001\u0000\u0000"+ - "\u0000\u00c0\u00b6\u0001\u0000\u0000\u0000\u00c0\u00ba\u0001\u0000\u0000"+ - "\u0000\u00c1\u0013\u0001\u0000\u0000\u0000\u00c2\u00cf\u0003&\u0013\u0000"+ - "\u00c3\u00cf\u0003(\u0014\u0000\u00c4\u00cf\u0005,\u0000\u0000\u00c5\u00c6"+ - "\u0005,\u0000\u0000\u00c6\u00c7\u0005\'\u0000\u0000\u00c7\u00cf\u0005"+ - ",\u0000\u0000\u00c8\u00cf\u0005\u0012\u0000\u0000\u00c9\u00ca\u0005\""+ - "\u0000\u0000\u00ca\u00cb\u0003\u0004\u0002\u0000\u00cb\u00cc\u0005#\u0000"+ - "\u0000\u00cc\u00cf\u0001\u0000\u0000\u0000\u00cd\u00cf\u0003\u0016\u000b"+ - "\u0000\u00ce\u00c2\u0001\u0000\u0000\u0000\u00ce\u00c3\u0001\u0000\u0000"+ - "\u0000\u00ce\u00c4\u0001\u0000\u0000\u0000\u00ce\u00c5\u0001\u0000\u0000"+ - "\u0000\u00ce\u00c8\u0001\u0000\u0000\u0000\u00ce\u00c9\u0001\u0000\u0000"+ - "\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000\u00cf\u0015\u0001\u0000\u0000"+ - "\u0000\u00d0\u00d1\u0003\u0018\f\u0000\u00d1\u00d2\u0005\"\u0000\u0000"+ - "\u00d2\u00d3\u0003\u001a\r\u0000\u00d3\u00d4\u0005#\u0000\u0000\u00d4"+ - "\u0017\u0001\u0000\u0000\u0000\u00d5\u00d6\u0007\u0000\u0000\u0000\u00d6"+ - "\u0019\u0001\u0000\u0000\u0000\u00d7\u00dc\u0003\u001c\u000e\u0000\u00d8"+ - "\u00d9\u0005&\u0000\u0000\u00d9\u00db\u0003\u001c\u000e\u0000\u00da\u00d8"+ - "\u0001\u0000\u0000\u0000\u00db\u00de\u0001\u0000\u0000\u0000\u00dc\u00da"+ - "\u0001\u0000\u0000\u0000\u00dc\u00dd\u0001\u0000\u0000\u0000\u00dd\u001b"+ - "\u0001\u0000\u0000\u0000\u00de\u00dc\u0001\u0000\u0000\u0000\u00df\u00e2"+ - "\u0005\u0015\u0000\u0000\u00e0\u00e2\u0003(\u0014\u0000\u00e1\u00df\u0001"+ - "\u0000\u0000\u0000\u00e1\u00e0\u0001\u0000\u0000\u0000\u00e2\u001d\u0001"+ - "\u0000\u0000\u0000\u00e3\u00e8\u0005,\u0000\u0000\u00e4\u00e5\u0005&\u0000"+ - "\u0000\u00e5\u00e7\u0005,\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000"+ - "\u00e7\u00ea\u0001\u0000\u0000\u0000\u00e8\u00e6\u0001\u0000\u0000\u0000"+ - "\u00e8\u00e9\u0001\u0000\u0000\u0000\u00e9\u001f\u0001\u0000\u0000\u0000"+ - "\u00ea\u00e8\u0001\u0000\u0000\u0000\u00eb\u00f0\u0003\"\u0011\u0000\u00ec"+ - "\u00ed\u0005&\u0000\u0000\u00ed\u00ef\u0003\"\u0011\u0000\u00ee\u00ec"+ - "\u0001\u0000\u0000\u0000\u00ef\u00f2\u0001\u0000\u0000\u0000\u00f0\u00ee"+ - "\u0001\u0000\u0000\u0000\u00f0\u00f1\u0001\u0000\u0000\u0000\u00f1!\u0001"+ - "\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000\u0000\u0000\u00f3\u00f5\u0005"+ - ",\u0000\u0000\u00f4\u00f6\u0007\u0001\u0000\u0000\u00f5\u00f4\u0001\u0000"+ - "\u0000\u0000\u00f5\u00f6\u0001\u0000\u0000\u0000\u00f6\u00fc\u0001\u0000"+ - "\u0000\u0000\u00f7\u00f9\u0003\u0016\u000b\u0000\u00f8\u00fa\u0007\u0001"+ - "\u0000\u0000\u00f9\u00f8\u0001\u0000\u0000\u0000\u00f9\u00fa\u0001\u0000"+ - "\u0000\u0000\u00fa\u00fc\u0001\u0000\u0000\u0000\u00fb\u00f3\u0001\u0000"+ - "\u0000\u0000\u00fb\u00f7\u0001\u0000\u0000\u0000\u00fc#\u0001\u0000\u0000"+ - "\u0000\u00fd\u00fe\u0005*\u0000\u0000\u00fe%\u0001\u0000\u0000\u0000\u00ff"+ - "\u0100\u0007\u0002\u0000\u0000\u0100\'\u0001\u0000\u0000\u0000\u0101\u0102"+ - "\u0005+\u0000\u0000\u0102)\u0001\u0000\u0000\u0000!3;@DIMTY\\`cgjpsu|"+ - "\u0081\u0084\u008c\u0094\u009c\u009e\u00b0\u00c0\u00ce\u00dc\u00e1\u00e8"+ - "\u00f0\u00f5\u00f9\u00fb"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java b/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java deleted file mode 100644 index 571d0d2ffcc..00000000000 --- a/hertzbeat-alerter/gen/expr/SqlExpressionVisitor.java +++ /dev/null @@ -1,147 +0,0 @@ -// Generated from D:/Project/hertzbeat/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 by ANTLR 4.13.2 -package expr; -import org.antlr.v4.runtime.tree.ParseTreeVisitor; - -/** - * This interface defines a complete generic visitor for a parse tree produced - * by {@link SqlExpressionParser}. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -public interface SqlExpressionVisitor extends ParseTreeVisitor { - /** - * Visit a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitExpression(SqlExpressionParser.ExpressionContext ctx); - /** - * Visit a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - /** - * Visit a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectField(SqlExpressionParser.SelectFieldContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitRelList(SqlExpressionParser.RelListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitRelation(SqlExpressionParser.RelationContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitConditionList(SqlExpressionParser.ConditionListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitCompOp(SqlExpressionParser.CompOpContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitCondition(SqlExpressionParser.ConditionContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameterList(SqlExpressionParser.ParameterListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameter(SqlExpressionParser.ParameterContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitGroupByList(SqlExpressionParser.GroupByListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOrderByList(SqlExpressionParser.OrderByListContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitNumber(SqlExpressionParser.NumberContext ctx); - /** - * Visit a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitString(SqlExpressionParser.StringContext ctx); -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java index 247a5884fd3..503b4393702 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java @@ -49,14 +49,16 @@ public abstract class AbstractRealTimeAlertCalculator { protected final AlertDefineService alertDefineService; protected final AlarmCommonReduce alarmCommonReduce; protected final AlarmCacheManager alarmCacheManager; + protected final JexlExprCalculator jexlExprCalculator; /** * Constructor with auto-start enabled */ protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { - this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, true); + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator) { + this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, true); } /** @@ -72,13 +74,15 @@ protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDa * set to false to disable thread start (useful for unit testing). */ protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator, boolean start) { this.workerPool = workerPool; this.dataQueue = dataQueue; this.alarmCommonReduce = alarmCommonReduce; this.alertDefineService = alertDefineService; this.alarmCacheManager = alarmCacheManager; + this.jexlExprCalculator = jexlExprCalculator; if (start) { startCalculate(); } @@ -127,42 +131,6 @@ public void startCalculate() { */ protected abstract void calculate(T data); - /** - * Execute an alert expression - * @param fieldValueMap The field value map for expression evaluation - * @param expr The expression to evaluate - * @param ignoreJexlException Whether to ignore JEXL exceptions - * @return true if the expression matches, false otherwise - */ - protected boolean execAlertExpression(Map fieldValueMap, String expr, boolean ignoreJexlException) { - Boolean match; - JexlExpression expression; - try { - expression = JexlExpressionRunner.compile(expr); - } catch (JexlException jexlException) { - log.warn("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage()); - throw jexlException; - } catch (Exception e) { - log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); - throw e; - } - - try { - match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap); - } catch (JexlException jexlException) { - if (ignoreJexlException) { - log.debug("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); - } else { - log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); - } - throw jexlException; - } catch (Exception e) { - log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); - throw e; - } - return match != null && match; - } - /** * Handle alert after threshold rule match */ diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java new file mode 100644 index 00000000000..56d2b14ad43 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java @@ -0,0 +1,50 @@ +package org.apache.hertzbeat.alert.calculate; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlExpression; +import org.apache.hertzbeat.common.util.JexlExpressionRunner; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Slf4j +@Component +public class JexlExprCalculator { + /** + * Execute an alert expression + * @param fieldValueMap The field value map for expression evaluation + * @param expr The expression to evaluate + * @param ignoreJexlException Whether to ignore JEXL exceptions + * @return true if the expression matches, false otherwise + */ + public boolean execAlertExpression(Map fieldValueMap, String expr, boolean ignoreJexlException) { + Boolean match; + JexlExpression expression; + try { + expression = JexlExpressionRunner.compile(expr); + } catch (JexlException jexlException) { + log.warn("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage()); + throw jexlException; + } catch (Exception e) { + log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); + throw e; + } + + try { + match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap); + } catch (JexlException jexlException) { + if (ignoreJexlException) { + log.debug("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); + } else { + log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage()); + } + throw jexlException; + } catch (Exception e) { + log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage()); + throw e; + } + return match != null && match; + } + +} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java index 1ccf9b1884a..fbd95eed99b 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java @@ -45,9 +45,10 @@ public class LogRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator< @Autowired public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager); + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator); } /** @@ -63,9 +64,10 @@ public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue * set to false to disable thread start (useful for unit testing). */ public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, start); + AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator, boolean start) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, start); } @Override @@ -105,7 +107,7 @@ protected void calculate(LogEntry logEntry) { commonFingerPrints.putAll(define.getLabels()); try { - boolean match = execAlertExpression(commonContext, expr, false); + boolean match = jexlExprCalculator.execAlertExpression(commonContext, expr, false); try { if (match) { afterThresholdRuleMatch(define.getId(), currentTimeMilli, commonFingerPrints, commonContext, define, null); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java index a9f29d63205..f70f9ed5548 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java @@ -70,8 +70,9 @@ public class MetricsRealTimeAlertCalculator extends AbstractRealTimeAlertCalcula @Autowired public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager); + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator); } /** @@ -88,8 +89,9 @@ public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQu */ public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, start); + AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator, boolean start) { + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager,jexlExprCalculator, start); } @Override @@ -169,7 +171,7 @@ protected void calculate(CollectRep.MetricsData metricsData) { { // trigger the expr before the metrics data, due the available up down or others try { - boolean match = execAlertExpression(fieldValueMap, expr, true); + boolean match = jexlExprCalculator.execAlertExpression(fieldValueMap, expr, true); try { if (match) { // If the threshold rule matches, the number of times the threshold has been triggered is determined and an alarm is triggered @@ -224,7 +226,7 @@ protected void calculate(CollectRep.MetricsData metricsData) { } } try { - boolean match = execAlertExpression(fieldValueMap, expr, false); + boolean match = jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); try { if (match) { afterThresholdRuleMatch(defineId, currentTimeMilli, fingerPrints, fieldValueMap, define, annotations); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java index 4feb9eb026d..65e643f95f5 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java @@ -30,9 +30,7 @@ import org.apache.hertzbeat.warehouse.constants.WarehouseConstants; import org.springframework.stereotype.Component; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Periodic Alert Calculator @@ -44,16 +42,19 @@ public class PeriodicAlertCalculator { private static final String VALUE = "__value__"; private static final String TIMESTAMP = "__timestamp__"; + private static final String ROWS = "__rows__"; private final DataSourceService dataSourceService; private final AlarmCommonReduce alarmCommonReduce; private final AlarmCacheManager alarmCacheManager; + private final JexlExprCalculator jexlExprCalculator; public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, - AlarmCacheManager alarmCacheManager) { + AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { this.dataSourceService = dataSourceService; this.alarmCommonReduce = alarmCommonReduce; this.alarmCacheManager = alarmCacheManager; + this.jexlExprCalculator = jexlExprCalculator; } public void calculate(AlertDefine define) { @@ -71,8 +72,8 @@ public void calculate(AlertDefine define) { String sqlOrPromql = define.getDatasource(); if (WarehouseConstants.SQL.equals(sqlOrPromql)) { // sql - results = dataSourceService.query(sqlOrPromql, define.getExpr()); - // this.doCaculate(); + results = dataSourceService.query(sqlOrPromql, define.getQueryExpr()); + results = this.doCalculate(results, define.getExpr()); } else { // promql results = dataSourceService.calculate( @@ -121,6 +122,23 @@ public void calculate(AlertDefine define) { } } + private List> doCalculate(List> results, String expression) { + if (CollectionUtils.isEmpty(results)) { + return List.of(); + } + List> newResults = new ArrayList<>(results.size()); + for (Map result : results) { + HashMap fieldMap = new HashMap<>(result); + // todo improvement the rows judge + fieldMap.put(ROWS, results.size()); + boolean match = jexlExprCalculator.execAlertExpression(fieldMap, expression, true); + if (match) { + newResults.add(result); + } + } + return newResults; + } + private void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, Map fieldValueMap, AlertDefine define) { Long defineId = define.getId(); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java deleted file mode 100644 index fe7fa44f6c7..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseListener.java +++ /dev/null @@ -1,350 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ErrorNode; -import org.antlr.v4.runtime.tree.TerminalNode; - -/** - * This class provides an empty implementation of {@link SqlExpressionListener}, - * which can be extended to create a listener which only needs to handle a subset - * of the available methods. - */ -@SuppressWarnings("CheckReturnValue") -public class SqlExpressionBaseListener implements SqlExpressionListener { - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterExpression(SqlExpressionParser.ExpressionContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitExpression(SqlExpressionParser.ExpressionContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSelectField(SqlExpressionParser.SelectFieldContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSelectField(SqlExpressionParser.SelectFieldContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterRelList(SqlExpressionParser.RelListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitRelList(SqlExpressionParser.RelListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterRelation(SqlExpressionParser.RelationContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitRelation(SqlExpressionParser.RelationContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterConditionList(SqlExpressionParser.ConditionListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitConditionList(SqlExpressionParser.ConditionListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterCompOp(SqlExpressionParser.CompOpContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitCompOp(SqlExpressionParser.CompOpContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterCondition(SqlExpressionParser.ConditionContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitCondition(SqlExpressionParser.ConditionContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameterList(SqlExpressionParser.ParameterListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameterList(SqlExpressionParser.ParameterListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameter(SqlExpressionParser.ParameterContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameter(SqlExpressionParser.ParameterContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterGroupByList(SqlExpressionParser.GroupByListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitGroupByList(SqlExpressionParser.GroupByListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOrderByList(SqlExpressionParser.OrderByListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOrderByList(SqlExpressionParser.OrderByListContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterNumber(SqlExpressionParser.NumberContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitNumber(SqlExpressionParser.NumberContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterString(SqlExpressionParser.StringContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitString(SqlExpressionParser.StringContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterEveryRule(ParserRuleContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitEveryRule(ParserRuleContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitTerminal(TerminalNode node) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitErrorNode(ErrorNode node) { } -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java deleted file mode 100644 index 6879f154c2b..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionBaseVisitor.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; - -/** - * This class provides an empty implementation of {@link SqlExpressionVisitor}, - * which can be extended to create a visitor which only needs to handle a subset - * of the available methods. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -@SuppressWarnings("CheckReturnValue") -public class SqlExpressionBaseVisitor extends AbstractParseTreeVisitor implements SqlExpressionVisitor { - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitExpression(SqlExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSelectField(SqlExpressionParser.SelectFieldContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitRelList(SqlExpressionParser.RelListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitRelation(SqlExpressionParser.RelationContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitConditionList(SqlExpressionParser.ConditionListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitCompOp(SqlExpressionParser.CompOpContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitCondition(SqlExpressionParser.ConditionContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameterList(SqlExpressionParser.ParameterListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameter(SqlExpressionParser.ParameterContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitGroupByList(SqlExpressionParser.GroupByListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOrderByList(SqlExpressionParser.OrderByListContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitNumber(SqlExpressionParser.NumberContext ctx) { return visitChildren(ctx); } - - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitString(SqlExpressionParser.StringContext ctx) { return visitChildren(ctx); } -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java deleted file mode 100644 index e0ad51847e5..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionEvalVisitor.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.CommonTokenStream; -import org.apache.hertzbeat.warehouse.db.QueryExecutor; - -import java.util.List; -import java.util.Map; - -/** - * Sql expression evaluation visitor. - */ -public class SqlExpressionEvalVisitor extends SqlExpressionBaseVisitor>> { - - private final QueryExecutor executor; - private final CommonTokenStream tokens; - - public SqlExpressionEvalVisitor(QueryExecutor executor, CommonTokenStream tokens) { - this.executor = executor; - this.tokens = tokens; - } - - @Override - public List> visitExpression(SqlExpressionParser.ExpressionContext ctx) { - return super.visit(ctx.sqlExpr()); - } - - @Override - public List> visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx) { - String rawSql = tokens.getText(ctx.selectSql()); - return executor.execute(rawSql); - } - - @Override - public List> visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx) { - return callSql(tokens.getText(ctx.string())); - } - - private List> callSql(String text) { - String script = text.substring(1, text.length() - 1); - return executor.execute(script); - } -} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java deleted file mode 100644 index d344919786d..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionLexer.java +++ /dev/null @@ -1,350 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.RuntimeMetaData; -import org.antlr.v4.runtime.Vocabulary; -import org.antlr.v4.runtime.VocabularyImpl; -import org.antlr.v4.runtime.atn.ATN; -import org.antlr.v4.runtime.atn.ATNDeserializer; -import org.antlr.v4.runtime.atn.LexerATNSimulator; -import org.antlr.v4.runtime.atn.PredictionContextCache; -import org.antlr.v4.runtime.dfa.DFA; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class SqlExpressionLexer extends Lexer { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, - ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, - LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, - SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, - RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, - NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; - public static String[] channelNames = { - "DEFAULT_TOKEN_CHANNEL", "HIDDEN" - }; - - public static String[] modeNames = { - "DEFAULT_MODE" - }; - - private static String[] makeRuleNames() { - return new String[] { - "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, "'*'", null, null, - null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", - "')'", "'['", "']'", "','", "'.'", "';'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - - public SqlExpressionLexer(CharStream input) { - super(input); - _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @Override - public String getGrammarFileName() { return "SqlExpression.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public String[] getChannelNames() { return channelNames; } - - @Override - public String[] getModeNames() { return modeNames; } - - @Override - public ATN getATN() { return _ATN; } - - public static final String _serializedATN = - "\u0004\u0000/\u014b\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ - "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ - "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ - "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ - "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ - "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ - "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ - "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ - "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ - "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ - "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ - "!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+ - "&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007"+ - "+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0001\u0000\u0001\u0000\u0001"+ - "\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001"+ - "\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+ - "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001"+ - "\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005\u0001"+ - "\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001"+ - "\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ - "\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b"+ - "\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0001"+ - "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+ - "\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001"+ - "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001"+ - "\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+ - "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001"+ - "\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001"+ - "\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ - "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0001"+ - "\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001"+ - "\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+ - "\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e\u0001"+ - "\u001f\u0001\u001f\u0001\u001f\u0003\u001f\u00ec\b\u001f\u0001 \u0001"+ - " \u0001 \u0001!\u0001!\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001"+ - "%\u0001%\u0001&\u0001&\u0001\'\u0001\'\u0001(\u0004(\u0100\b(\u000b(\f"+ - "(\u0101\u0001(\u0001(\u0004(\u0106\b(\u000b(\f(\u0107\u0001)\u0004)\u010b"+ - "\b)\u000b)\f)\u010c\u0001*\u0001*\u0001*\u0001*\u0005*\u0113\b*\n*\f*"+ - "\u0116\t*\u0001*\u0001*\u0001*\u0001*\u0001*\u0005*\u011d\b*\n*\f*\u0120"+ - "\t*\u0001*\u0003*\u0123\b*\u0001+\u0001+\u0005+\u0127\b+\n+\f+\u012a\t"+ - "+\u0001,\u0004,\u012d\b,\u000b,\f,\u012e\u0001,\u0001,\u0001-\u0001-\u0001"+ - "-\u0001-\u0005-\u0137\b-\n-\f-\u013a\t-\u0001-\u0001-\u0001.\u0001.\u0001"+ - ".\u0001.\u0005.\u0142\b.\n.\f.\u0145\t.\u0001.\u0001.\u0001.\u0001.\u0001"+ - ".\u0001\u0143\u0000/\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t"+ - "\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f"+ - "\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014"+ - ")\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d;\u001e"+ - "=\u001f? A!C\"E#G$I%K&M\'O(Q)S*U+W,Y-[.]/\u0001\u0000\u001f\u0002\u0000"+ - "AAaa\u0002\u0000NNnn\u0002\u0000DDdd\u0002\u0000OOoo\u0002\u0000RRrr\u0002"+ - "\u0000TTtt\u0002\u0000SSss\u0002\u0000EEee\u0002\u0000LLll\u0002\u0000"+ - "CCcc\u0002\u0000FFff\u0002\u0000MMmm\u0002\u0000WWww\u0002\u0000HHhh\u0002"+ - "\u0000GGgg\u0002\u0000UUuu\u0002\u0000PPpp\u0002\u0000BBbb\u0002\u0000"+ - "YYyy\u0002\u0000VVvv\u0002\u0000IIii\u0002\u0000KKkk\u0002\u0000XXxx\u0002"+ - "\u0000QQqq\u0001\u000009\u0004\u0000\n\n\r\r\"\"\\\\\u0004\u0000\n\n\r"+ - "\r\'\'\\\\\u0003\u0000AZ__az\u0004\u000009AZ__az\u0003\u0000\t\n\r\r "+ - " \u0002\u0000\n\n\r\r\u0157\u0000\u0001\u0001\u0000\u0000\u0000\u0000"+ - "\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000"+ - "\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b"+ - "\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001"+ - "\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001"+ - "\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001"+ - "\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001"+ - "\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001"+ - "\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000"+ - "\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000"+ - "\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-"+ - "\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000"+ - "\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000"+ - "\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000\u0000\u0000\u0000;"+ - "\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0000?\u0001\u0000"+ - "\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C\u0001\u0000\u0000\u0000"+ - "\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000\u0000\u0000\u0000I"+ - "\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000\u0000M\u0001\u0000"+ - "\u0000\u0000\u0000O\u0001\u0000\u0000\u0000\u0000Q\u0001\u0000\u0000\u0000"+ - "\u0000S\u0001\u0000\u0000\u0000\u0000U\u0001\u0000\u0000\u0000\u0000W"+ - "\u0001\u0000\u0000\u0000\u0000Y\u0001\u0000\u0000\u0000\u0000[\u0001\u0000"+ - "\u0000\u0000\u0000]\u0001\u0000\u0000\u0000\u0001_\u0001\u0000\u0000\u0000"+ - "\u0003c\u0001\u0000\u0000\u0000\u0005f\u0001\u0000\u0000\u0000\u0007j"+ - "\u0001\u0000\u0000\u0000\tq\u0001\u0000\u0000\u0000\u000bv\u0001\u0000"+ - "\u0000\u0000\r|\u0001\u0000\u0000\u0000\u000f\u0082\u0001\u0000\u0000"+ - "\u0000\u0011\u0085\u0001\u0000\u0000\u0000\u0013\u008c\u0001\u0000\u0000"+ - "\u0000\u0015\u0092\u0001\u0000\u0000\u0000\u0017\u0098\u0001\u0000\u0000"+ - "\u0000\u0019\u009f\u0001\u0000\u0000\u0000\u001b\u00a2\u0001\u0000\u0000"+ - "\u0000\u001d\u00a6\u0001\u0000\u0000\u0000\u001f\u00ab\u0001\u0000\u0000"+ - "\u0000!\u00ae\u0001\u0000\u0000\u0000#\u00b1\u0001\u0000\u0000\u0000%"+ - "\u00b6\u0001\u0000\u0000\u0000\'\u00bb\u0001\u0000\u0000\u0000)\u00c3"+ - "\u0001\u0000\u0000\u0000+\u00c5\u0001\u0000\u0000\u0000-\u00cb\u0001\u0000"+ - "\u0000\u0000/\u00cf\u0001\u0000\u0000\u00001\u00d3\u0001\u0000\u0000\u0000"+ - "3\u00d7\u0001\u0000\u0000\u00005\u00da\u0001\u0000\u0000\u00007\u00de"+ - "\u0001\u0000\u0000\u00009\u00e0\u0001\u0000\u0000\u0000;\u00e3\u0001\u0000"+ - "\u0000\u0000=\u00e5\u0001\u0000\u0000\u0000?\u00eb\u0001\u0000\u0000\u0000"+ - "A\u00ed\u0001\u0000\u0000\u0000C\u00f0\u0001\u0000\u0000\u0000E\u00f2"+ - "\u0001\u0000\u0000\u0000G\u00f4\u0001\u0000\u0000\u0000I\u00f6\u0001\u0000"+ - "\u0000\u0000K\u00f8\u0001\u0000\u0000\u0000M\u00fa\u0001\u0000\u0000\u0000"+ - "O\u00fc\u0001\u0000\u0000\u0000Q\u00ff\u0001\u0000\u0000\u0000S\u010a"+ - "\u0001\u0000\u0000\u0000U\u0122\u0001\u0000\u0000\u0000W\u0124\u0001\u0000"+ - "\u0000\u0000Y\u012c\u0001\u0000\u0000\u0000[\u0132\u0001\u0000\u0000\u0000"+ - "]\u013d\u0001\u0000\u0000\u0000_`\u0007\u0000\u0000\u0000`a\u0007\u0001"+ - "\u0000\u0000ab\u0007\u0002\u0000\u0000b\u0002\u0001\u0000\u0000\u0000"+ - "cd\u0007\u0003\u0000\u0000de\u0007\u0004\u0000\u0000e\u0004\u0001\u0000"+ - "\u0000\u0000fg\u0007\u0001\u0000\u0000gh\u0007\u0003\u0000\u0000hi\u0007"+ - "\u0005\u0000\u0000i\u0006\u0001\u0000\u0000\u0000jk\u0007\u0006\u0000"+ - "\u0000kl\u0007\u0007\u0000\u0000lm\u0007\b\u0000\u0000mn\u0007\u0007\u0000"+ - "\u0000no\u0007\t\u0000\u0000op\u0007\u0005\u0000\u0000p\b\u0001\u0000"+ - "\u0000\u0000qr\u0007\n\u0000\u0000rs\u0007\u0004\u0000\u0000st\u0007\u0003"+ - "\u0000\u0000tu\u0007\u000b\u0000\u0000u\n\u0001\u0000\u0000\u0000vw\u0007"+ - "\f\u0000\u0000wx\u0007\r\u0000\u0000xy\u0007\u0007\u0000\u0000yz\u0007"+ - "\u0004\u0000\u0000z{\u0007\u0007\u0000\u0000{\f\u0001\u0000\u0000\u0000"+ - "|}\u0007\u000e\u0000\u0000}~\u0007\u0004\u0000\u0000~\u007f\u0007\u0003"+ - "\u0000\u0000\u007f\u0080\u0007\u000f\u0000\u0000\u0080\u0081\u0007\u0010"+ - "\u0000\u0000\u0081\u000e\u0001\u0000\u0000\u0000\u0082\u0083\u0007\u0011"+ - "\u0000\u0000\u0083\u0084\u0007\u0012\u0000\u0000\u0084\u0010\u0001\u0000"+ - "\u0000\u0000\u0085\u0086\u0007\r\u0000\u0000\u0086\u0087\u0007\u0000\u0000"+ - "\u0000\u0087\u0088\u0007\u0013\u0000\u0000\u0088\u0089\u0007\u0014\u0000"+ - "\u0000\u0089\u008a\u0007\u0001\u0000\u0000\u008a\u008b\u0007\u000e\u0000"+ - "\u0000\u008b\u0012\u0001\u0000\u0000\u0000\u008c\u008d\u0007\u0003\u0000"+ - "\u0000\u008d\u008e\u0007\u0004\u0000\u0000\u008e\u008f\u0007\u0002\u0000"+ - "\u0000\u008f\u0090\u0007\u0007\u0000\u0000\u0090\u0091\u0007\u0004\u0000"+ - "\u0000\u0091\u0014\u0001\u0000\u0000\u0000\u0092\u0093\u0007\b\u0000\u0000"+ - "\u0093\u0094\u0007\u0014\u0000\u0000\u0094\u0095\u0007\u000b\u0000\u0000"+ - "\u0095\u0096\u0007\u0014\u0000\u0000\u0096\u0097\u0007\u0005\u0000\u0000"+ - "\u0097\u0016\u0001\u0000\u0000\u0000\u0098\u0099\u0007\u0003\u0000\u0000"+ - "\u0099\u009a\u0007\n\u0000\u0000\u009a\u009b\u0007\n\u0000\u0000\u009b"+ - "\u009c\u0007\u0006\u0000\u0000\u009c\u009d\u0007\u0007\u0000\u0000\u009d"+ - "\u009e\u0007\u0005\u0000\u0000\u009e\u0018\u0001\u0000\u0000\u0000\u009f"+ - "\u00a0\u0007\u0000\u0000\u0000\u00a0\u00a1\u0007\u0006\u0000\u0000\u00a1"+ - "\u001a\u0001\u0000\u0000\u0000\u00a2\u00a3\u0007\u0000\u0000\u0000\u00a3"+ - "\u00a4\u0007\u0006\u0000\u0000\u00a4\u00a5\u0007\t\u0000\u0000\u00a5\u001c"+ - "\u0001\u0000\u0000\u0000\u00a6\u00a7\u0007\u0002\u0000\u0000\u00a7\u00a8"+ - "\u0007\u0007\u0000\u0000\u00a8\u00a9\u0007\u0006\u0000\u0000\u00a9\u00aa"+ - "\u0007\t\u0000\u0000\u00aa\u001e\u0001\u0000\u0000\u0000\u00ab\u00ac\u0007"+ - "\u0014\u0000\u0000\u00ac\u00ad\u0007\u0001\u0000\u0000\u00ad \u0001\u0000"+ - "\u0000\u0000\u00ae\u00af\u0007\u0014\u0000\u0000\u00af\u00b0\u0007\u0006"+ - "\u0000\u0000\u00b0\"\u0001\u0000\u0000\u0000\u00b1\u00b2\u0007\u0001\u0000"+ - "\u0000\u00b2\u00b3\u0007\u000f\u0000\u0000\u00b3\u00b4\u0007\b\u0000\u0000"+ - "\u00b4\u00b5\u0007\b\u0000\u0000\u00b5$\u0001\u0000\u0000\u0000\u00b6"+ - "\u00b7\u0007\b\u0000\u0000\u00b7\u00b8\u0007\u0014\u0000\u0000\u00b8\u00b9"+ - "\u0007\u0015\u0000\u0000\u00b9\u00ba\u0007\u0007\u0000\u0000\u00ba&\u0001"+ - "\u0000\u0000\u0000\u00bb\u00bc\u0007\u0011\u0000\u0000\u00bc\u00bd\u0007"+ - "\u0007\u0000\u0000\u00bd\u00be\u0007\u0005\u0000\u0000\u00be\u00bf\u0007"+ - "\f\u0000\u0000\u00bf\u00c0\u0007\u0007\u0000\u0000\u00c0\u00c1\u0007\u0007"+ - "\u0000\u0000\u00c1\u00c2\u0007\u0001\u0000\u0000\u00c2(\u0001\u0000\u0000"+ - "\u0000\u00c3\u00c4\u0005*\u0000\u0000\u00c4*\u0001\u0000\u0000\u0000\u00c5"+ - "\u00c6\u0007\t\u0000\u0000\u00c6\u00c7\u0007\u0003\u0000\u0000\u00c7\u00c8"+ - "\u0007\u000f\u0000\u0000\u00c8\u00c9\u0007\u0001\u0000\u0000\u00c9\u00ca"+ - "\u0007\u0005\u0000\u0000\u00ca,\u0001\u0000\u0000\u0000\u00cb\u00cc\u0007"+ - "\u0006\u0000\u0000\u00cc\u00cd\u0007\u000f\u0000\u0000\u00cd\u00ce\u0007"+ - "\u000b\u0000\u0000\u00ce.\u0001\u0000\u0000\u0000\u00cf\u00d0\u0007\u0000"+ - "\u0000\u0000\u00d0\u00d1\u0007\u0013\u0000\u0000\u00d1\u00d2\u0007\u000e"+ - "\u0000\u0000\u00d20\u0001\u0000\u0000\u0000\u00d3\u00d4\u0007\u000b\u0000"+ - "\u0000\u00d4\u00d5\u0007\u0014\u0000\u0000\u00d5\u00d6\u0007\u0001\u0000"+ - "\u0000\u00d62\u0001\u0000\u0000\u0000\u00d7\u00d8\u0007\u0000\u0000\u0000"+ - "\u00d8\u00d9\u0007\u0016\u0000\u0000\u00d94\u0001\u0000\u0000\u0000\u00da"+ - "\u00db\u0007\u0006\u0000\u0000\u00db\u00dc\u0007\u0017\u0000\u0000\u00dc"+ - "\u00dd\u0007\b\u0000\u0000\u00dd6\u0001\u0000\u0000\u0000\u00de\u00df"+ - "\u0005>\u0000\u0000\u00df8\u0001\u0000\u0000\u0000\u00e0\u00e1\u0005>"+ - "\u0000\u0000\u00e1\u00e2\u0005=\u0000\u0000\u00e2:\u0001\u0000\u0000\u0000"+ - "\u00e3\u00e4\u0005<\u0000\u0000\u00e4<\u0001\u0000\u0000\u0000\u00e5\u00e6"+ - "\u0005<\u0000\u0000\u00e6\u00e7\u0005=\u0000\u0000\u00e7>\u0001\u0000"+ - "\u0000\u0000\u00e8\u00e9\u0005=\u0000\u0000\u00e9\u00ec\u0005=\u0000\u0000"+ - "\u00ea\u00ec\u0005=\u0000\u0000\u00eb\u00e8\u0001\u0000\u0000\u0000\u00eb"+ - "\u00ea\u0001\u0000\u0000\u0000\u00ec@\u0001\u0000\u0000\u0000\u00ed\u00ee"+ - "\u0005!\u0000\u0000\u00ee\u00ef\u0005=\u0000\u0000\u00efB\u0001\u0000"+ - "\u0000\u0000\u00f0\u00f1\u0005(\u0000\u0000\u00f1D\u0001\u0000\u0000\u0000"+ - "\u00f2\u00f3\u0005)\u0000\u0000\u00f3F\u0001\u0000\u0000\u0000\u00f4\u00f5"+ - "\u0005[\u0000\u0000\u00f5H\u0001\u0000\u0000\u0000\u00f6\u00f7\u0005]"+ - "\u0000\u0000\u00f7J\u0001\u0000\u0000\u0000\u00f8\u00f9\u0005,\u0000\u0000"+ - "\u00f9L\u0001\u0000\u0000\u0000\u00fa\u00fb\u0005.\u0000\u0000\u00fbN"+ - "\u0001\u0000\u0000\u0000\u00fc\u00fd\u0005;\u0000\u0000\u00fdP\u0001\u0000"+ - "\u0000\u0000\u00fe\u0100\u0007\u0018\u0000\u0000\u00ff\u00fe\u0001\u0000"+ - "\u0000\u0000\u0100\u0101\u0001\u0000\u0000\u0000\u0101\u00ff\u0001\u0000"+ - "\u0000\u0000\u0101\u0102\u0001\u0000\u0000\u0000\u0102\u0103\u0001\u0000"+ - "\u0000\u0000\u0103\u0105\u0005.\u0000\u0000\u0104\u0106\u0007\u0018\u0000"+ - "\u0000\u0105\u0104\u0001\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000"+ - "\u0000\u0107\u0105\u0001\u0000\u0000\u0000\u0107\u0108\u0001\u0000\u0000"+ - "\u0000\u0108R\u0001\u0000\u0000\u0000\u0109\u010b\u0007\u0018\u0000\u0000"+ - "\u010a\u0109\u0001\u0000\u0000\u0000\u010b\u010c\u0001\u0000\u0000\u0000"+ - "\u010c\u010a\u0001\u0000\u0000\u0000\u010c\u010d\u0001\u0000\u0000\u0000"+ - "\u010dT\u0001\u0000\u0000\u0000\u010e\u0114\u0005\"\u0000\u0000\u010f"+ - "\u0113\b\u0019\u0000\u0000\u0110\u0111\u0005\\\u0000\u0000\u0111\u0113"+ - "\t\u0000\u0000\u0000\u0112\u010f\u0001\u0000\u0000\u0000\u0112\u0110\u0001"+ - "\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000\u0114\u0112\u0001"+ - "\u0000\u0000\u0000\u0114\u0115\u0001\u0000\u0000\u0000\u0115\u0117\u0001"+ - "\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u0123\u0005"+ - "\"\u0000\u0000\u0118\u011e\u0005\'\u0000\u0000\u0119\u011d\b\u001a\u0000"+ - "\u0000\u011a\u011b\u0005\\\u0000\u0000\u011b\u011d\t\u0000\u0000\u0000"+ - "\u011c\u0119\u0001\u0000\u0000\u0000\u011c\u011a\u0001\u0000\u0000\u0000"+ - "\u011d\u0120\u0001\u0000\u0000\u0000\u011e\u011c\u0001\u0000\u0000\u0000"+ - "\u011e\u011f\u0001\u0000\u0000\u0000\u011f\u0121\u0001\u0000\u0000\u0000"+ - "\u0120\u011e\u0001\u0000\u0000\u0000\u0121\u0123\u0005\'\u0000\u0000\u0122"+ - "\u010e\u0001\u0000\u0000\u0000\u0122\u0118\u0001\u0000\u0000\u0000\u0123"+ - "V\u0001\u0000\u0000\u0000\u0124\u0128\u0007\u001b\u0000\u0000\u0125\u0127"+ - "\u0007\u001c\u0000\u0000\u0126\u0125\u0001\u0000\u0000\u0000\u0127\u012a"+ - "\u0001\u0000\u0000\u0000\u0128\u0126\u0001\u0000\u0000\u0000\u0128\u0129"+ - "\u0001\u0000\u0000\u0000\u0129X\u0001\u0000\u0000\u0000\u012a\u0128\u0001"+ - "\u0000\u0000\u0000\u012b\u012d\u0007\u001d\u0000\u0000\u012c\u012b\u0001"+ - "\u0000\u0000\u0000\u012d\u012e\u0001\u0000\u0000\u0000\u012e\u012c\u0001"+ - "\u0000\u0000\u0000\u012e\u012f\u0001\u0000\u0000\u0000\u012f\u0130\u0001"+ - "\u0000\u0000\u0000\u0130\u0131\u0006,\u0000\u0000\u0131Z\u0001\u0000\u0000"+ - "\u0000\u0132\u0133\u0005/\u0000\u0000\u0133\u0134\u0005/\u0000\u0000\u0134"+ - "\u0138\u0001\u0000\u0000\u0000\u0135\u0137\b\u001e\u0000\u0000\u0136\u0135"+ - "\u0001\u0000\u0000\u0000\u0137\u013a\u0001\u0000\u0000\u0000\u0138\u0136"+ - "\u0001\u0000\u0000\u0000\u0138\u0139\u0001\u0000\u0000\u0000\u0139\u013b"+ - "\u0001\u0000\u0000\u0000\u013a\u0138\u0001\u0000\u0000\u0000\u013b\u013c"+ - "\u0006-\u0001\u0000\u013c\\\u0001\u0000\u0000\u0000\u013d\u013e\u0005"+ - "/\u0000\u0000\u013e\u013f\u0005*\u0000\u0000\u013f\u0143\u0001\u0000\u0000"+ - "\u0000\u0140\u0142\t\u0000\u0000\u0000\u0141\u0140\u0001\u0000\u0000\u0000"+ - "\u0142\u0145\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+ - "\u0143\u0141\u0001\u0000\u0000\u0000\u0144\u0146\u0001\u0000\u0000\u0000"+ - "\u0145\u0143\u0001\u0000\u0000\u0000\u0146\u0147\u0005*\u0000\u0000\u0147"+ - "\u0148\u0005/\u0000\u0000\u0148\u0149\u0001\u0000\u0000\u0000\u0149\u014a"+ - "\u0006.\u0001\u0000\u014a^\u0001\u0000\u0000\u0000\u000e\u0000\u00eb\u0101"+ - "\u0107\u010c\u0112\u0114\u011c\u011e\u0122\u0128\u012e\u0138\u0143\u0002"+ - "\u0000\u0001\u0000\u0006\u0000\u0000"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java deleted file mode 100644 index dc55f5d8cab..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionListener.java +++ /dev/null @@ -1,278 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.tree.ParseTreeListener; - -/** - * This interface defines a complete listener for a parse tree produced by - * {@link SqlExpressionParser}. - */ -public interface SqlExpressionListener extends ParseTreeListener { - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - */ - void enterExpression(SqlExpressionParser.ExpressionContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - */ - void exitExpression(SqlExpressionParser.ExpressionContext ctx); - - /** - * Enter a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void enterSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - - /** - * Exit a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void exitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - - /** - * Enter a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void enterSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - - /** - * Exit a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - */ - void exitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - */ - void enterSelectSql(SqlExpressionParser.SelectSqlContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - */ - void exitSelectSql(SqlExpressionParser.SelectSqlContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - */ - void enterSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - */ - void exitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - */ - void enterSelectField(SqlExpressionParser.SelectFieldContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - */ - void exitSelectField(SqlExpressionParser.SelectFieldContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - */ - void enterRelList(SqlExpressionParser.RelListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - */ - void exitRelList(SqlExpressionParser.RelListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - */ - void enterRelation(SqlExpressionParser.RelationContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - */ - void exitRelation(SqlExpressionParser.RelationContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - */ - void enterConditionList(SqlExpressionParser.ConditionListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - */ - void exitConditionList(SqlExpressionParser.ConditionListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - */ - void enterCompOp(SqlExpressionParser.CompOpContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - */ - void exitCompOp(SqlExpressionParser.CompOpContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - */ - void enterCondition(SqlExpressionParser.ConditionContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - */ - void exitCondition(SqlExpressionParser.ConditionContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - */ - void enterConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - */ - void exitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - */ - void enterFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - */ - void exitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - */ - void enterFunctionName(SqlExpressionParser.FunctionNameContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - */ - void exitFunctionName(SqlExpressionParser.FunctionNameContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - */ - void enterParameterList(SqlExpressionParser.ParameterListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - */ - void exitParameterList(SqlExpressionParser.ParameterListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - */ - void enterParameter(SqlExpressionParser.ParameterContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - */ - void exitParameter(SqlExpressionParser.ParameterContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - */ - void enterGroupByList(SqlExpressionParser.GroupByListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - */ - void exitGroupByList(SqlExpressionParser.GroupByListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - */ - void enterOrderByList(SqlExpressionParser.OrderByListContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - */ - void exitOrderByList(SqlExpressionParser.OrderByListContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - */ - void enterOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - */ - void exitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - */ - void enterLimitClause(SqlExpressionParser.LimitClauseContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - */ - void exitLimitClause(SqlExpressionParser.LimitClauseContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - */ - void enterNumber(SqlExpressionParser.NumberContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - */ - void exitNumber(SqlExpressionParser.NumberContext ctx); - - /** - * Enter a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - */ - void enterString(SqlExpressionParser.StringContext ctx); - - /** - * Exit a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - */ - void exitString(SqlExpressionParser.StringContext ctx); -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java deleted file mode 100644 index 0c4e900a242..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionParser.java +++ /dev/null @@ -1,2113 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.FailedPredicateException; -import org.antlr.v4.runtime.NoViableAltException; -import org.antlr.v4.runtime.Parser; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.RuleContext; -import org.antlr.v4.runtime.RuntimeMetaData; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.Vocabulary; -import org.antlr.v4.runtime.VocabularyImpl; -import org.antlr.v4.runtime.atn.ATN; -import org.antlr.v4.runtime.atn.ATNDeserializer; -import org.antlr.v4.runtime.atn.ParserATNSimulator; -import org.antlr.v4.runtime.atn.PredictionContextCache; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.tree.ParseTreeListener; -import org.antlr.v4.runtime.tree.ParseTreeVisitor; -import org.antlr.v4.runtime.tree.TerminalNode; - -import java.util.List; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class SqlExpressionParser extends Parser { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - AND=1, OR=2, NOT=3, SELECT=4, FROM=5, WHERE=6, GROUP=7, BY=8, HAVING=9, - ORDER=10, LIMIT=11, OFFSET=12, AS=13, ASC=14, DESC=15, IN=16, IS=17, NULL=18, - LIKE=19, BETWEEN=20, STAR=21, COUNT=22, SUM=23, AVG=24, MIN=25, MAX=26, - SQL_FUNCTION=27, GT=28, GE=29, LT=30, LE=31, EQ=32, NE=33, LPAREN=34, - RPAREN=35, LBRACKET=36, RBRACKET=37, COMMA=38, DOT=39, SEMICOLON=40, FLOAT=41, - NUMBER=42, STRING=43, IDENTIFIER=44, WS=45, LINE_COMMENT=46, BLOCK_COMMENT=47; - public static final int - RULE_expression = 0, RULE_sqlExpr = 1, RULE_selectSql = 2, RULE_selectFieldList = 3, - RULE_selectField = 4, RULE_relList = 5, RULE_relation = 6, RULE_conditionList = 7, - RULE_compOp = 8, RULE_condition = 9, RULE_conditionUnit = 10, RULE_functionCall = 11, - RULE_functionName = 12, RULE_parameterList = 13, RULE_parameter = 14, - RULE_groupByList = 15, RULE_orderByList = 16, RULE_orderByField = 17, - RULE_limitClause = 18, RULE_number = 19, RULE_string = 20; - private static String[] makeRuleNames() { - return new String[] { - "expression", "sqlExpr", "selectSql", "selectFieldList", "selectField", - "relList", "relation", "conditionList", "compOp", "condition", "conditionUnit", - "functionCall", "functionName", "parameterList", "parameter", "groupByList", - "orderByList", "orderByField", "limitClause", "number", "string" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, "'*'", null, null, - null, null, null, null, "'>'", "'>='", "'<'", "'<='", null, "'!='", "'('", - "')'", "'['", "']'", "','", "'.'", "';'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "AND", "OR", "NOT", "SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", - "ORDER", "LIMIT", "OFFSET", "AS", "ASC", "DESC", "IN", "IS", "NULL", - "LIKE", "BETWEEN", "STAR", "COUNT", "SUM", "AVG", "MIN", "MAX", "SQL_FUNCTION", - "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", - "COMMA", "DOT", "SEMICOLON", "FLOAT", "NUMBER", "STRING", "IDENTIFIER", - "WS", "LINE_COMMENT", "BLOCK_COMMENT" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - @Override - public String getGrammarFileName() { return "SqlExpression.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public ATN getATN() { return _ATN; } - - public SqlExpressionParser(TokenStream input) { - super(input); - _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @SuppressWarnings("CheckReturnValue") - public static class ExpressionContext extends ParserRuleContext { - public SqlExprContext sqlExpr() { - return getRuleContext(SqlExprContext.class,0); - } - public TerminalNode EOF() { return getToken(SqlExpressionParser.EOF, 0); } - public ExpressionContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_expression; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterExpression(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitExpression(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitExpression(this); - else return visitor.visitChildren(this); - } - } - - public final ExpressionContext expression() throws RecognitionException { - ExpressionContext _localctx = new ExpressionContext(_ctx, getState()); - enterRule(_localctx, 0, RULE_expression); - try { - enterOuterAlt(_localctx, 1); - { - setState(42); - sqlExpr(); - setState(43); - match(EOF); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SqlExprContext extends ParserRuleContext { - public SqlExprContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_sqlExpr; } - - public SqlExprContext() { } - public void copyFrom(SqlExprContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlCallExprContext extends SqlExprContext { - public TerminalNode SQL_FUNCTION() { return getToken(SqlExpressionParser.SQL_FUNCTION, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public SelectSqlCallExprContext(SqlExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlCallExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlCallExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlCallExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlExprContext extends SqlExprContext { - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public SelectSqlExprContext(SqlExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSqlExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSqlExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSqlExpr(this); - else return visitor.visitChildren(this); - } - } - - public final SqlExprContext sqlExpr() throws RecognitionException { - SqlExprContext _localctx = new SqlExprContext(_ctx, getState()); - enterRule(_localctx, 2, RULE_sqlExpr); - try { - setState(51); - _errHandler.sync(this); - switch (_input.LA(1)) { - case SELECT: - _localctx = new SelectSqlExprContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(45); - selectSql(); - } - break; - case SQL_FUNCTION: - _localctx = new SelectSqlCallExprContext(_localctx); - enterOuterAlt(_localctx, 2); - { - setState(46); - match(SQL_FUNCTION); - setState(47); - match(LPAREN); - setState(48); - string(); - setState(49); - match(RPAREN); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectSqlContext extends ParserRuleContext { - public TerminalNode SELECT() { return getToken(SqlExpressionParser.SELECT, 0); } - public SelectFieldListContext selectFieldList() { - return getRuleContext(SelectFieldListContext.class,0); - } - public TerminalNode FROM() { return getToken(SqlExpressionParser.FROM, 0); } - public RelListContext relList() { - return getRuleContext(RelListContext.class,0); - } - public TerminalNode WHERE() { return getToken(SqlExpressionParser.WHERE, 0); } - public List conditionList() { - return getRuleContexts(ConditionListContext.class); - } - public ConditionListContext conditionList(int i) { - return getRuleContext(ConditionListContext.class,i); - } - public TerminalNode GROUP() { return getToken(SqlExpressionParser.GROUP, 0); } - public List BY() { return getTokens(SqlExpressionParser.BY); } - public TerminalNode BY(int i) { - return getToken(SqlExpressionParser.BY, i); - } - public GroupByListContext groupByList() { - return getRuleContext(GroupByListContext.class,0); - } - public TerminalNode HAVING() { return getToken(SqlExpressionParser.HAVING, 0); } - public TerminalNode ORDER() { return getToken(SqlExpressionParser.ORDER, 0); } - public OrderByListContext orderByList() { - return getRuleContext(OrderByListContext.class,0); - } - public TerminalNode LIMIT() { return getToken(SqlExpressionParser.LIMIT, 0); } - public LimitClauseContext limitClause() { - return getRuleContext(LimitClauseContext.class,0); - } - public SelectSqlContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectSql; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectSql(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectSql(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectSql(this); - else return visitor.visitChildren(this); - } - } - - public final SelectSqlContext selectSql() throws RecognitionException { - SelectSqlContext _localctx = new SelectSqlContext(_ctx, getState()); - enterRule(_localctx, 4, RULE_selectSql); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(53); - match(SELECT); - setState(54); - selectFieldList(); - setState(55); - match(FROM); - setState(56); - relList(); - setState(59); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==WHERE) { - { - setState(57); - match(WHERE); - setState(58); - conditionList(0); - } - } - - setState(64); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==GROUP) { - { - setState(61); - match(GROUP); - setState(62); - match(BY); - setState(63); - groupByList(); - } - } - - setState(68); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==HAVING) { - { - setState(66); - match(HAVING); - setState(67); - conditionList(0); - } - } - - setState(73); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ORDER) { - { - setState(70); - match(ORDER); - setState(71); - match(BY); - setState(72); - orderByList(); - } - } - - setState(77); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==LIMIT) { - { - setState(75); - match(LIMIT); - setState(76); - limitClause(); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectFieldListContext extends ParserRuleContext { - public List selectField() { - return getRuleContexts(SelectFieldContext.class); - } - public SelectFieldContext selectField(int i) { - return getRuleContext(SelectFieldContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public SelectFieldListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectFieldList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectFieldList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectFieldList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectFieldList(this); - else return visitor.visitChildren(this); - } - } - - public final SelectFieldListContext selectFieldList() throws RecognitionException { - SelectFieldListContext _localctx = new SelectFieldListContext(_ctx, getState()); - enterRule(_localctx, 6, RULE_selectFieldList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(79); - selectField(); - setState(84); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(80); - match(COMMA); - setState(81); - selectField(); - } - } - setState(86); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SelectFieldContext extends ParserRuleContext { - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } - public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } - public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } - public SelectFieldContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_selectField; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterSelectField(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitSelectField(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitSelectField(this); - else return visitor.visitChildren(this); - } - } - - public final SelectFieldContext selectField() throws RecognitionException { - SelectFieldContext _localctx = new SelectFieldContext(_ctx, getState()); - enterRule(_localctx, 8, RULE_selectField); - int _la; - try { - setState(117); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(87); - functionCall(); - setState(92); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(89); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(88); - match(AS); - } - } - - setState(91); - match(IDENTIFIER); - } - } - - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(94); - match(IDENTIFIER); - setState(99); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(96); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(95); - match(AS); - } - } - - setState(98); - match(IDENTIFIER); - } - } - - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(101); - match(STAR); - setState(106); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(103); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(102); - match(AS); - } - } - - setState(105); - match(IDENTIFIER); - } - } - - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(108); - match(IDENTIFIER); - setState(109); - match(DOT); - setState(110); - match(IDENTIFIER); - setState(115); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(112); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(111); - match(AS); - } - } - - setState(114); - match(IDENTIFIER); - } - } - - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class RelListContext extends ParserRuleContext { - public List relation() { - return getRuleContexts(RelationContext.class); - } - public RelationContext relation(int i) { - return getRuleContext(RelationContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public RelListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_relList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelList(this); - else return visitor.visitChildren(this); - } - } - - public final RelListContext relList() throws RecognitionException { - RelListContext _localctx = new RelListContext(_ctx, getState()); - enterRule(_localctx, 10, RULE_relList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(119); - relation(); - setState(124); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(120); - match(COMMA); - setState(121); - relation(); - } - } - setState(126); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class RelationContext extends ParserRuleContext { - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode AS() { return getToken(SqlExpressionParser.AS, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public RelationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_relation; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterRelation(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitRelation(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitRelation(this); - else return visitor.visitChildren(this); - } - } - - public final RelationContext relation() throws RecognitionException { - RelationContext _localctx = new RelationContext(_ctx, getState()); - enterRule(_localctx, 12, RULE_relation); - int _la; - try { - setState(140); - _errHandler.sync(this); - switch (_input.LA(1)) { - case IDENTIFIER: - enterOuterAlt(_localctx, 1); - { - setState(127); - match(IDENTIFIER); - setState(132); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS || _la==IDENTIFIER) { - { - setState(129); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==AS) { - { - setState(128); - match(AS); - } - } - - setState(131); - match(IDENTIFIER); - } - } - - } - break; - case LPAREN: - enterOuterAlt(_localctx, 2); - { - setState(134); - match(LPAREN); - setState(135); - selectSql(); - setState(136); - match(RPAREN); - setState(137); - match(AS); - setState(138); - match(IDENTIFIER); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionListContext extends ParserRuleContext { - public ConditionContext condition() { - return getRuleContext(ConditionContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public List conditionList() { - return getRuleContexts(ConditionListContext.class); - } - public ConditionListContext conditionList(int i) { - return getRuleContext(ConditionListContext.class,i); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } - public TerminalNode OR() { return getToken(SqlExpressionParser.OR, 0); } - public ConditionListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_conditionList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionList(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionListContext conditionList() throws RecognitionException { - return conditionList(0); - } - - private ConditionListContext conditionList(int _p) throws RecognitionException { - ParserRuleContext _parentctx = _ctx; - int _parentState = getState(); - ConditionListContext _localctx = new ConditionListContext(_ctx, _parentState); - ConditionListContext _prevctx = _localctx; - int _startState = 14; - enterRecursionRule(_localctx, 14, RULE_conditionList, _p); - try { - int _alt; - enterOuterAlt(_localctx, 1); - { - setState(148); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,20,_ctx) ) { - case 1: - { - setState(143); - condition(); - } - break; - case 2: - { - setState(144); - match(LPAREN); - setState(145); - conditionList(0); - setState(146); - match(RPAREN); - } - break; - } - _ctx.stop = _input.LT(-1); - setState(158); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); - while ( _alt!=2 && _alt!= ATN.INVALID_ALT_NUMBER ) { - if ( _alt==1 ) { - if ( _parseListeners!=null ) triggerExitRuleEvent(); - _prevctx = _localctx; - { - setState(156); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { - case 1: - { - _localctx = new ConditionListContext(_parentctx, _parentState); - pushNewRecursionContext(_localctx, _startState, RULE_conditionList); - setState(150); - if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(151); - match(AND); - setState(152); - conditionList(4); - } - break; - case 2: - { - _localctx = new ConditionListContext(_parentctx, _parentState); - pushNewRecursionContext(_localctx, _startState, RULE_conditionList); - setState(153); - if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(154); - match(OR); - setState(155); - conditionList(3); - } - break; - } - } - } - setState(160); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - unrollRecursionContexts(_parentctx); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class CompOpContext extends ParserRuleContext { - public TerminalNode EQ() { return getToken(SqlExpressionParser.EQ, 0); } - public TerminalNode LT() { return getToken(SqlExpressionParser.LT, 0); } - public TerminalNode GT() { return getToken(SqlExpressionParser.GT, 0); } - public TerminalNode LE() { return getToken(SqlExpressionParser.LE, 0); } - public TerminalNode GE() { return getToken(SqlExpressionParser.GE, 0); } - public TerminalNode NE() { return getToken(SqlExpressionParser.NE, 0); } - public TerminalNode LIKE() { return getToken(SqlExpressionParser.LIKE, 0); } - public TerminalNode NOT() { return getToken(SqlExpressionParser.NOT, 0); } - public TerminalNode IN() { return getToken(SqlExpressionParser.IN, 0); } - public TerminalNode IS() { return getToken(SqlExpressionParser.IS, 0); } - public CompOpContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_compOp; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCompOp(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCompOp(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCompOp(this); - else return visitor.visitChildren(this); - } - } - - public final CompOpContext compOp() throws RecognitionException { - CompOpContext _localctx = new CompOpContext(_ctx, getState()); - enterRule(_localctx, 16, RULE_compOp); - try { - setState(176); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(161); - match(EQ); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(162); - match(LT); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(163); - match(GT); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(164); - match(LE); - } - break; - case 5: - enterOuterAlt(_localctx, 5); - { - setState(165); - match(GE); - } - break; - case 6: - enterOuterAlt(_localctx, 6); - { - setState(166); - match(NE); - } - break; - case 7: - enterOuterAlt(_localctx, 7); - { - setState(167); - match(LIKE); - } - break; - case 8: - enterOuterAlt(_localctx, 8); - { - setState(168); - match(NOT); - setState(169); - match(LIKE); - } - break; - case 9: - enterOuterAlt(_localctx, 9); - { - setState(170); - match(IN); - } - break; - case 10: - enterOuterAlt(_localctx, 10); - { - setState(171); - match(NOT); - setState(172); - match(IN); - } - break; - case 11: - enterOuterAlt(_localctx, 11); - { - setState(173); - match(IS); - } - break; - case 12: - enterOuterAlt(_localctx, 12); - { - setState(174); - match(IS); - setState(175); - match(NOT); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionContext extends ParserRuleContext { - public List conditionUnit() { - return getRuleContexts(ConditionUnitContext.class); - } - public ConditionUnitContext conditionUnit(int i) { - return getRuleContext(ConditionUnitContext.class,i); - } - public CompOpContext compOp() { - return getRuleContext(CompOpContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public ConditionContext condition() { - return getRuleContext(ConditionContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public TerminalNode BETWEEN() { return getToken(SqlExpressionParser.BETWEEN, 0); } - public List number() { - return getRuleContexts(NumberContext.class); - } - public NumberContext number(int i) { - return getRuleContext(NumberContext.class,i); - } - public TerminalNode AND() { return getToken(SqlExpressionParser.AND, 0); } - public ConditionContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_condition; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterCondition(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitCondition(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitCondition(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionContext condition() throws RecognitionException { - ConditionContext _localctx = new ConditionContext(_ctx, getState()); - enterRule(_localctx, 18, RULE_condition); - try { - setState(192); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(178); - conditionUnit(); - setState(179); - compOp(); - setState(180); - conditionUnit(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(182); - match(LPAREN); - setState(183); - condition(); - setState(184); - match(RPAREN); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(186); - match(IDENTIFIER); - setState(187); - match(BETWEEN); - setState(188); - number(); - setState(189); - match(AND); - setState(190); - number(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConditionUnitContext extends ParserRuleContext { - public NumberContext number() { - return getRuleContext(NumberContext.class,0); - } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public TerminalNode DOT() { return getToken(SqlExpressionParser.DOT, 0); } - public TerminalNode NULL() { return getToken(SqlExpressionParser.NULL, 0); } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public SelectSqlContext selectSql() { - return getRuleContext(SelectSqlContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public ConditionUnitContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_conditionUnit; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterConditionUnit(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitConditionUnit(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitConditionUnit(this); - else return visitor.visitChildren(this); - } - } - - public final ConditionUnitContext conditionUnit() throws RecognitionException { - ConditionUnitContext _localctx = new ConditionUnitContext(_ctx, getState()); - enterRule(_localctx, 20, RULE_conditionUnit); - try { - setState(206); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(194); - number(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(195); - string(); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(196); - match(IDENTIFIER); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(197); - match(IDENTIFIER); - setState(198); - match(DOT); - setState(199); - match(IDENTIFIER); - } - break; - case 5: - enterOuterAlt(_localctx, 5); - { - setState(200); - match(NULL); - } - break; - case 6: - enterOuterAlt(_localctx, 6); - { - setState(201); - match(LPAREN); - setState(202); - selectSql(); - setState(203); - match(RPAREN); - } - break; - case 7: - enterOuterAlt(_localctx, 7); - { - setState(205); - functionCall(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class FunctionCallContext extends ParserRuleContext { - public FunctionNameContext functionName() { - return getRuleContext(FunctionNameContext.class,0); - } - public TerminalNode LPAREN() { return getToken(SqlExpressionParser.LPAREN, 0); } - public ParameterListContext parameterList() { - return getRuleContext(ParameterListContext.class,0); - } - public TerminalNode RPAREN() { return getToken(SqlExpressionParser.RPAREN, 0); } - public FunctionCallContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_functionCall; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionCall(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionCall(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionCall(this); - else return visitor.visitChildren(this); - } - } - - public final FunctionCallContext functionCall() throws RecognitionException { - FunctionCallContext _localctx = new FunctionCallContext(_ctx, getState()); - enterRule(_localctx, 22, RULE_functionCall); - try { - enterOuterAlt(_localctx, 1); - { - setState(208); - functionName(); - setState(209); - match(LPAREN); - setState(210); - parameterList(); - setState(211); - match(RPAREN); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class FunctionNameContext extends ParserRuleContext { - public TerminalNode COUNT() { return getToken(SqlExpressionParser.COUNT, 0); } - public TerminalNode AVG() { return getToken(SqlExpressionParser.AVG, 0); } - public TerminalNode SUM() { return getToken(SqlExpressionParser.SUM, 0); } - public TerminalNode MIN() { return getToken(SqlExpressionParser.MIN, 0); } - public TerminalNode MAX() { return getToken(SqlExpressionParser.MAX, 0); } - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public FunctionNameContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_functionName; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterFunctionName(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitFunctionName(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitFunctionName(this); - else return visitor.visitChildren(this); - } - } - - public final FunctionNameContext functionName() throws RecognitionException { - FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); - enterRule(_localctx, 24, RULE_functionName); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(213); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 17592316067840L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterListContext extends ParserRuleContext { - public List parameter() { - return getRuleContexts(ParameterContext.class); - } - public ParameterContext parameter(int i) { - return getRuleContext(ParameterContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public ParameterListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameterList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameterList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameterList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameterList(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterListContext parameterList() throws RecognitionException { - ParameterListContext _localctx = new ParameterListContext(_ctx, getState()); - enterRule(_localctx, 26, RULE_parameterList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(215); - parameter(); - setState(220); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(216); - match(COMMA); - setState(217); - parameter(); - } - } - setState(222); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterContext extends ParserRuleContext { - public TerminalNode STAR() { return getToken(SqlExpressionParser.STAR, 0); } - public StringContext string() { - return getRuleContext(StringContext.class,0); - } - public ParameterContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameter; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterParameter(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitParameter(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitParameter(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterContext parameter() throws RecognitionException { - ParameterContext _localctx = new ParameterContext(_ctx, getState()); - enterRule(_localctx, 28, RULE_parameter); - try { - setState(225); - _errHandler.sync(this); - switch (_input.LA(1)) { - case STAR: - enterOuterAlt(_localctx, 1); - { - setState(223); - match(STAR); - } - break; - case STRING: - enterOuterAlt(_localctx, 2); - { - setState(224); - string(); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class GroupByListContext extends ParserRuleContext { - public List IDENTIFIER() { return getTokens(SqlExpressionParser.IDENTIFIER); } - public TerminalNode IDENTIFIER(int i) { - return getToken(SqlExpressionParser.IDENTIFIER, i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public GroupByListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_groupByList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterGroupByList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitGroupByList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitGroupByList(this); - else return visitor.visitChildren(this); - } - } - - public final GroupByListContext groupByList() throws RecognitionException { - GroupByListContext _localctx = new GroupByListContext(_ctx, getState()); - enterRule(_localctx, 30, RULE_groupByList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(227); - match(IDENTIFIER); - setState(232); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(228); - match(COMMA); - setState(229); - match(IDENTIFIER); - } - } - setState(234); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OrderByListContext extends ParserRuleContext { - public List orderByField() { - return getRuleContexts(OrderByFieldContext.class); - } - public OrderByFieldContext orderByField(int i) { - return getRuleContext(OrderByFieldContext.class,i); - } - public List COMMA() { return getTokens(SqlExpressionParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(SqlExpressionParser.COMMA, i); - } - public OrderByListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_orderByList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByList(this); - else return visitor.visitChildren(this); - } - } - - public final OrderByListContext orderByList() throws RecognitionException { - OrderByListContext _localctx = new OrderByListContext(_ctx, getState()); - enterRule(_localctx, 32, RULE_orderByList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(235); - orderByField(); - setState(240); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(236); - match(COMMA); - setState(237); - orderByField(); - } - } - setState(242); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OrderByFieldContext extends ParserRuleContext { - public TerminalNode IDENTIFIER() { return getToken(SqlExpressionParser.IDENTIFIER, 0); } - public TerminalNode ASC() { return getToken(SqlExpressionParser.ASC, 0); } - public TerminalNode DESC() { return getToken(SqlExpressionParser.DESC, 0); } - public FunctionCallContext functionCall() { - return getRuleContext(FunctionCallContext.class,0); - } - public OrderByFieldContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_orderByField; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterOrderByField(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitOrderByField(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitOrderByField(this); - else return visitor.visitChildren(this); - } - } - - public final OrderByFieldContext orderByField() throws RecognitionException { - OrderByFieldContext _localctx = new OrderByFieldContext(_ctx, getState()); - enterRule(_localctx, 34, RULE_orderByField); - int _la; - try { - setState(251); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(243); - match(IDENTIFIER); - setState(245); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASC || _la==DESC) { - { - setState(244); - _la = _input.LA(1); - if ( !(_la==ASC || _la==DESC) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(247); - functionCall(); - setState(249); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASC || _la==DESC) { - { - setState(248); - _la = _input.LA(1); - if ( !(_la==ASC || _la==DESC) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class LimitClauseContext extends ParserRuleContext { - public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } - public LimitClauseContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_limitClause; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterLimitClause(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitLimitClause(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitLimitClause(this); - else return visitor.visitChildren(this); - } - } - - public final LimitClauseContext limitClause() throws RecognitionException { - LimitClauseContext _localctx = new LimitClauseContext(_ctx, getState()); - enterRule(_localctx, 36, RULE_limitClause); - try { - enterOuterAlt(_localctx, 1); - { - setState(253); - match(NUMBER); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class NumberContext extends ParserRuleContext { - public TerminalNode NUMBER() { return getToken(SqlExpressionParser.NUMBER, 0); } - public TerminalNode FLOAT() { return getToken(SqlExpressionParser.FLOAT, 0); } - public NumberContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_number; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterNumber(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitNumber(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitNumber(this); - else return visitor.visitChildren(this); - } - } - - public final NumberContext number() throws RecognitionException { - NumberContext _localctx = new NumberContext(_ctx, getState()); - enterRule(_localctx, 38, RULE_number); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(255); - _la = _input.LA(1); - if ( !(_la==FLOAT || _la==NUMBER) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class StringContext extends ParserRuleContext { - public TerminalNode STRING() { return getToken(SqlExpressionParser.STRING, 0); } - public StringContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_string; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).enterString(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof SqlExpressionListener ) ((SqlExpressionListener)listener).exitString(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof SqlExpressionVisitor ) return ((SqlExpressionVisitor)visitor).visitString(this); - else return visitor.visitChildren(this); - } - } - - public final StringContext string() throws RecognitionException { - StringContext _localctx = new StringContext(_ctx, getState()); - enterRule(_localctx, 40, RULE_string); - try { - enterOuterAlt(_localctx, 1); - { - setState(257); - match(STRING); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { - switch (ruleIndex) { - case 7: - return conditionList_sempred((ConditionListContext)_localctx, predIndex); - } - return true; - } - private boolean conditionList_sempred(ConditionListContext _localctx, int predIndex) { - switch (predIndex) { - case 0: - return precpred(_ctx, 3); - case 1: - return precpred(_ctx, 2); - } - return true; - } - - public static final String _serializedATN = - "\u0004\u0001/\u0104\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ - "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ - "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ - "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ - "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ - "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012"+ - "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0001\u0000\u0001\u0000"+ - "\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+ - "\u0001\u0001\u0003\u00014\b\u0001\u0001\u0002\u0001\u0002\u0001\u0002"+ - "\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002<\b\u0002\u0001\u0002"+ - "\u0001\u0002\u0001\u0002\u0003\u0002A\b\u0002\u0001\u0002\u0001\u0002"+ - "\u0003\u0002E\b\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002"+ - "J\b\u0002\u0001\u0002\u0001\u0002\u0003\u0002N\b\u0002\u0001\u0003\u0001"+ - "\u0003\u0001\u0003\u0005\u0003S\b\u0003\n\u0003\f\u0003V\t\u0003\u0001"+ - "\u0004\u0001\u0004\u0003\u0004Z\b\u0004\u0001\u0004\u0003\u0004]\b\u0004"+ - "\u0001\u0004\u0001\u0004\u0003\u0004a\b\u0004\u0001\u0004\u0003\u0004"+ - "d\b\u0004\u0001\u0004\u0001\u0004\u0003\u0004h\b\u0004\u0001\u0004\u0003"+ - "\u0004k\b\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003"+ - "\u0004q\b\u0004\u0001\u0004\u0003\u0004t\b\u0004\u0003\u0004v\b\u0004"+ - "\u0001\u0005\u0001\u0005\u0001\u0005\u0005\u0005{\b\u0005\n\u0005\f\u0005"+ - "~\t\u0005\u0001\u0006\u0001\u0006\u0003\u0006\u0082\b\u0006\u0001\u0006"+ - "\u0003\u0006\u0085\b\u0006\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ - "\u0001\u0006\u0001\u0006\u0003\u0006\u008d\b\u0006\u0001\u0007\u0001\u0007"+ - "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007\u0095\b\u0007"+ - "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007"+ - "\u0005\u0007\u009d\b\u0007\n\u0007\f\u0007\u00a0\t\u0007\u0001\b\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001"+ - "\b\u0001\b\u0001\b\u0001\b\u0001\b\u0003\b\u00b1\b\b\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001"+ - "\t\u0001\t\u0001\t\u0003\t\u00c1\b\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ - "\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003\n\u00cf"+ - "\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001"+ - "\f\u0001\f\u0001\r\u0001\r\u0001\r\u0005\r\u00db\b\r\n\r\f\r\u00de\t\r"+ - "\u0001\u000e\u0001\u000e\u0003\u000e\u00e2\b\u000e\u0001\u000f\u0001\u000f"+ - "\u0001\u000f\u0005\u000f\u00e7\b\u000f\n\u000f\f\u000f\u00ea\t\u000f\u0001"+ - "\u0010\u0001\u0010\u0001\u0010\u0005\u0010\u00ef\b\u0010\n\u0010\f\u0010"+ - "\u00f2\t\u0010\u0001\u0011\u0001\u0011\u0003\u0011\u00f6\b\u0011\u0001"+ - "\u0011\u0001\u0011\u0003\u0011\u00fa\b\u0011\u0003\u0011\u00fc\b\u0011"+ - "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014"+ - "\u0001\u0014\u0000\u0001\u000e\u0015\u0000\u0002\u0004\u0006\b\n\f\u000e"+ - "\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(\u0000\u0003\u0002"+ - "\u0000\u0016\u001a,,\u0001\u0000\u000e\u000f\u0001\u0000)*\u0121\u0000"+ - "*\u0001\u0000\u0000\u0000\u00023\u0001\u0000\u0000\u0000\u00045\u0001"+ - "\u0000\u0000\u0000\u0006O\u0001\u0000\u0000\u0000\bu\u0001\u0000\u0000"+ - "\u0000\nw\u0001\u0000\u0000\u0000\f\u008c\u0001\u0000\u0000\u0000\u000e"+ - "\u0094\u0001\u0000\u0000\u0000\u0010\u00b0\u0001\u0000\u0000\u0000\u0012"+ - "\u00c0\u0001\u0000\u0000\u0000\u0014\u00ce\u0001\u0000\u0000\u0000\u0016"+ - "\u00d0\u0001\u0000\u0000\u0000\u0018\u00d5\u0001\u0000\u0000\u0000\u001a"+ - "\u00d7\u0001\u0000\u0000\u0000\u001c\u00e1\u0001\u0000\u0000\u0000\u001e"+ - "\u00e3\u0001\u0000\u0000\u0000 \u00eb\u0001\u0000\u0000\u0000\"\u00fb"+ - "\u0001\u0000\u0000\u0000$\u00fd\u0001\u0000\u0000\u0000&\u00ff\u0001\u0000"+ - "\u0000\u0000(\u0101\u0001\u0000\u0000\u0000*+\u0003\u0002\u0001\u0000"+ - "+,\u0005\u0000\u0000\u0001,\u0001\u0001\u0000\u0000\u0000-4\u0003\u0004"+ - "\u0002\u0000./\u0005\u001b\u0000\u0000/0\u0005\"\u0000\u000001\u0003("+ - "\u0014\u000012\u0005#\u0000\u000024\u0001\u0000\u0000\u00003-\u0001\u0000"+ - "\u0000\u00003.\u0001\u0000\u0000\u00004\u0003\u0001\u0000\u0000\u0000"+ - "56\u0005\u0004\u0000\u000067\u0003\u0006\u0003\u000078\u0005\u0005\u0000"+ - "\u00008;\u0003\n\u0005\u00009:\u0005\u0006\u0000\u0000:<\u0003\u000e\u0007"+ - "\u0000;9\u0001\u0000\u0000\u0000;<\u0001\u0000\u0000\u0000<@\u0001\u0000"+ - "\u0000\u0000=>\u0005\u0007\u0000\u0000>?\u0005\b\u0000\u0000?A\u0003\u001e"+ - "\u000f\u0000@=\u0001\u0000\u0000\u0000@A\u0001\u0000\u0000\u0000AD\u0001"+ - "\u0000\u0000\u0000BC\u0005\t\u0000\u0000CE\u0003\u000e\u0007\u0000DB\u0001"+ - "\u0000\u0000\u0000DE\u0001\u0000\u0000\u0000EI\u0001\u0000\u0000\u0000"+ - "FG\u0005\n\u0000\u0000GH\u0005\b\u0000\u0000HJ\u0003 \u0010\u0000IF\u0001"+ - "\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JM\u0001\u0000\u0000\u0000"+ - "KL\u0005\u000b\u0000\u0000LN\u0003$\u0012\u0000MK\u0001\u0000\u0000\u0000"+ - "MN\u0001\u0000\u0000\u0000N\u0005\u0001\u0000\u0000\u0000OT\u0003\b\u0004"+ - "\u0000PQ\u0005&\u0000\u0000QS\u0003\b\u0004\u0000RP\u0001\u0000\u0000"+ - "\u0000SV\u0001\u0000\u0000\u0000TR\u0001\u0000\u0000\u0000TU\u0001\u0000"+ - "\u0000\u0000U\u0007\u0001\u0000\u0000\u0000VT\u0001\u0000\u0000\u0000"+ - "W\\\u0003\u0016\u000b\u0000XZ\u0005\r\u0000\u0000YX\u0001\u0000\u0000"+ - "\u0000YZ\u0001\u0000\u0000\u0000Z[\u0001\u0000\u0000\u0000[]\u0005,\u0000"+ - "\u0000\\Y\u0001\u0000\u0000\u0000\\]\u0001\u0000\u0000\u0000]v\u0001\u0000"+ - "\u0000\u0000^c\u0005,\u0000\u0000_a\u0005\r\u0000\u0000`_\u0001\u0000"+ - "\u0000\u0000`a\u0001\u0000\u0000\u0000ab\u0001\u0000\u0000\u0000bd\u0005"+ - ",\u0000\u0000c`\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000dv\u0001"+ - "\u0000\u0000\u0000ej\u0005\u0015\u0000\u0000fh\u0005\r\u0000\u0000gf\u0001"+ - "\u0000\u0000\u0000gh\u0001\u0000\u0000\u0000hi\u0001\u0000\u0000\u0000"+ - "ik\u0005,\u0000\u0000jg\u0001\u0000\u0000\u0000jk\u0001\u0000\u0000\u0000"+ - "kv\u0001\u0000\u0000\u0000lm\u0005,\u0000\u0000mn\u0005\'\u0000\u0000"+ - "ns\u0005,\u0000\u0000oq\u0005\r\u0000\u0000po\u0001\u0000\u0000\u0000"+ - "pq\u0001\u0000\u0000\u0000qr\u0001\u0000\u0000\u0000rt\u0005,\u0000\u0000"+ - "sp\u0001\u0000\u0000\u0000st\u0001\u0000\u0000\u0000tv\u0001\u0000\u0000"+ - "\u0000uW\u0001\u0000\u0000\u0000u^\u0001\u0000\u0000\u0000ue\u0001\u0000"+ - "\u0000\u0000ul\u0001\u0000\u0000\u0000v\t\u0001\u0000\u0000\u0000w|\u0003"+ - "\f\u0006\u0000xy\u0005&\u0000\u0000y{\u0003\f\u0006\u0000zx\u0001\u0000"+ - "\u0000\u0000{~\u0001\u0000\u0000\u0000|z\u0001\u0000\u0000\u0000|}\u0001"+ - "\u0000\u0000\u0000}\u000b\u0001\u0000\u0000\u0000~|\u0001\u0000\u0000"+ - "\u0000\u007f\u0084\u0005,\u0000\u0000\u0080\u0082\u0005\r\u0000\u0000"+ - "\u0081\u0080\u0001\u0000\u0000\u0000\u0081\u0082\u0001\u0000\u0000\u0000"+ - "\u0082\u0083\u0001\u0000\u0000\u0000\u0083\u0085\u0005,\u0000\u0000\u0084"+ - "\u0081\u0001\u0000\u0000\u0000\u0084\u0085\u0001\u0000\u0000\u0000\u0085"+ - "\u008d\u0001\u0000\u0000\u0000\u0086\u0087\u0005\"\u0000\u0000\u0087\u0088"+ - "\u0003\u0004\u0002\u0000\u0088\u0089\u0005#\u0000\u0000\u0089\u008a\u0005"+ - "\r\u0000\u0000\u008a\u008b\u0005,\u0000\u0000\u008b\u008d\u0001\u0000"+ - "\u0000\u0000\u008c\u007f\u0001\u0000\u0000\u0000\u008c\u0086\u0001\u0000"+ - "\u0000\u0000\u008d\r\u0001\u0000\u0000\u0000\u008e\u008f\u0006\u0007\uffff"+ - "\uffff\u0000\u008f\u0095\u0003\u0012\t\u0000\u0090\u0091\u0005\"\u0000"+ - "\u0000\u0091\u0092\u0003\u000e\u0007\u0000\u0092\u0093\u0005#\u0000\u0000"+ - "\u0093\u0095\u0001\u0000\u0000\u0000\u0094\u008e\u0001\u0000\u0000\u0000"+ - "\u0094\u0090\u0001\u0000\u0000\u0000\u0095\u009e\u0001\u0000\u0000\u0000"+ - "\u0096\u0097\n\u0003\u0000\u0000\u0097\u0098\u0005\u0001\u0000\u0000\u0098"+ - "\u009d\u0003\u000e\u0007\u0004\u0099\u009a\n\u0002\u0000\u0000\u009a\u009b"+ - "\u0005\u0002\u0000\u0000\u009b\u009d\u0003\u000e\u0007\u0003\u009c\u0096"+ - "\u0001\u0000\u0000\u0000\u009c\u0099\u0001\u0000\u0000\u0000\u009d\u00a0"+ - "\u0001\u0000\u0000\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009e\u009f"+ - "\u0001\u0000\u0000\u0000\u009f\u000f\u0001\u0000\u0000\u0000\u00a0\u009e"+ - "\u0001\u0000\u0000\u0000\u00a1\u00b1\u0005 \u0000\u0000\u00a2\u00b1\u0005"+ - "\u001e\u0000\u0000\u00a3\u00b1\u0005\u001c\u0000\u0000\u00a4\u00b1\u0005"+ - "\u001f\u0000\u0000\u00a5\u00b1\u0005\u001d\u0000\u0000\u00a6\u00b1\u0005"+ - "!\u0000\u0000\u00a7\u00b1\u0005\u0013\u0000\u0000\u00a8\u00a9\u0005\u0003"+ - "\u0000\u0000\u00a9\u00b1\u0005\u0013\u0000\u0000\u00aa\u00b1\u0005\u0010"+ - "\u0000\u0000\u00ab\u00ac\u0005\u0003\u0000\u0000\u00ac\u00b1\u0005\u0010"+ - "\u0000\u0000\u00ad\u00b1\u0005\u0011\u0000\u0000\u00ae\u00af\u0005\u0011"+ - "\u0000\u0000\u00af\u00b1\u0005\u0003\u0000\u0000\u00b0\u00a1\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a2\u0001\u0000\u0000\u0000\u00b0\u00a3\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a4\u0001\u0000\u0000\u0000\u00b0\u00a5\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a6\u0001\u0000\u0000\u0000\u00b0\u00a7\u0001\u0000"+ - "\u0000\u0000\u00b0\u00a8\u0001\u0000\u0000\u0000\u00b0\u00aa\u0001\u0000"+ - "\u0000\u0000\u00b0\u00ab\u0001\u0000\u0000\u0000\u00b0\u00ad\u0001\u0000"+ - "\u0000\u0000\u00b0\u00ae\u0001\u0000\u0000\u0000\u00b1\u0011\u0001\u0000"+ - "\u0000\u0000\u00b2\u00b3\u0003\u0014\n\u0000\u00b3\u00b4\u0003\u0010\b"+ - "\u0000\u00b4\u00b5\u0003\u0014\n\u0000\u00b5\u00c1\u0001\u0000\u0000\u0000"+ - "\u00b6\u00b7\u0005\"\u0000\u0000\u00b7\u00b8\u0003\u0012\t\u0000\u00b8"+ - "\u00b9\u0005#\u0000\u0000\u00b9\u00c1\u0001\u0000\u0000\u0000\u00ba\u00bb"+ - "\u0005,\u0000\u0000\u00bb\u00bc\u0005\u0014\u0000\u0000\u00bc\u00bd\u0003"+ - "&\u0013\u0000\u00bd\u00be\u0005\u0001\u0000\u0000\u00be\u00bf\u0003&\u0013"+ - "\u0000\u00bf\u00c1\u0001\u0000\u0000\u0000\u00c0\u00b2\u0001\u0000\u0000"+ - "\u0000\u00c0\u00b6\u0001\u0000\u0000\u0000\u00c0\u00ba\u0001\u0000\u0000"+ - "\u0000\u00c1\u0013\u0001\u0000\u0000\u0000\u00c2\u00cf\u0003&\u0013\u0000"+ - "\u00c3\u00cf\u0003(\u0014\u0000\u00c4\u00cf\u0005,\u0000\u0000\u00c5\u00c6"+ - "\u0005,\u0000\u0000\u00c6\u00c7\u0005\'\u0000\u0000\u00c7\u00cf\u0005"+ - ",\u0000\u0000\u00c8\u00cf\u0005\u0012\u0000\u0000\u00c9\u00ca\u0005\""+ - "\u0000\u0000\u00ca\u00cb\u0003\u0004\u0002\u0000\u00cb\u00cc\u0005#\u0000"+ - "\u0000\u00cc\u00cf\u0001\u0000\u0000\u0000\u00cd\u00cf\u0003\u0016\u000b"+ - "\u0000\u00ce\u00c2\u0001\u0000\u0000\u0000\u00ce\u00c3\u0001\u0000\u0000"+ - "\u0000\u00ce\u00c4\u0001\u0000\u0000\u0000\u00ce\u00c5\u0001\u0000\u0000"+ - "\u0000\u00ce\u00c8\u0001\u0000\u0000\u0000\u00ce\u00c9\u0001\u0000\u0000"+ - "\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000\u00cf\u0015\u0001\u0000\u0000"+ - "\u0000\u00d0\u00d1\u0003\u0018\f\u0000\u00d1\u00d2\u0005\"\u0000\u0000"+ - "\u00d2\u00d3\u0003\u001a\r\u0000\u00d3\u00d4\u0005#\u0000\u0000\u00d4"+ - "\u0017\u0001\u0000\u0000\u0000\u00d5\u00d6\u0007\u0000\u0000\u0000\u00d6"+ - "\u0019\u0001\u0000\u0000\u0000\u00d7\u00dc\u0003\u001c\u000e\u0000\u00d8"+ - "\u00d9\u0005&\u0000\u0000\u00d9\u00db\u0003\u001c\u000e\u0000\u00da\u00d8"+ - "\u0001\u0000\u0000\u0000\u00db\u00de\u0001\u0000\u0000\u0000\u00dc\u00da"+ - "\u0001\u0000\u0000\u0000\u00dc\u00dd\u0001\u0000\u0000\u0000\u00dd\u001b"+ - "\u0001\u0000\u0000\u0000\u00de\u00dc\u0001\u0000\u0000\u0000\u00df\u00e2"+ - "\u0005\u0015\u0000\u0000\u00e0\u00e2\u0003(\u0014\u0000\u00e1\u00df\u0001"+ - "\u0000\u0000\u0000\u00e1\u00e0\u0001\u0000\u0000\u0000\u00e2\u001d\u0001"+ - "\u0000\u0000\u0000\u00e3\u00e8\u0005,\u0000\u0000\u00e4\u00e5\u0005&\u0000"+ - "\u0000\u00e5\u00e7\u0005,\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000"+ - "\u00e7\u00ea\u0001\u0000\u0000\u0000\u00e8\u00e6\u0001\u0000\u0000\u0000"+ - "\u00e8\u00e9\u0001\u0000\u0000\u0000\u00e9\u001f\u0001\u0000\u0000\u0000"+ - "\u00ea\u00e8\u0001\u0000\u0000\u0000\u00eb\u00f0\u0003\"\u0011\u0000\u00ec"+ - "\u00ed\u0005&\u0000\u0000\u00ed\u00ef\u0003\"\u0011\u0000\u00ee\u00ec"+ - "\u0001\u0000\u0000\u0000\u00ef\u00f2\u0001\u0000\u0000\u0000\u00f0\u00ee"+ - "\u0001\u0000\u0000\u0000\u00f0\u00f1\u0001\u0000\u0000\u0000\u00f1!\u0001"+ - "\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000\u0000\u0000\u00f3\u00f5\u0005"+ - ",\u0000\u0000\u00f4\u00f6\u0007\u0001\u0000\u0000\u00f5\u00f4\u0001\u0000"+ - "\u0000\u0000\u00f5\u00f6\u0001\u0000\u0000\u0000\u00f6\u00fc\u0001\u0000"+ - "\u0000\u0000\u00f7\u00f9\u0003\u0016\u000b\u0000\u00f8\u00fa\u0007\u0001"+ - "\u0000\u0000\u00f9\u00f8\u0001\u0000\u0000\u0000\u00f9\u00fa\u0001\u0000"+ - "\u0000\u0000\u00fa\u00fc\u0001\u0000\u0000\u0000\u00fb\u00f3\u0001\u0000"+ - "\u0000\u0000\u00fb\u00f7\u0001\u0000\u0000\u0000\u00fc#\u0001\u0000\u0000"+ - "\u0000\u00fd\u00fe\u0005*\u0000\u0000\u00fe%\u0001\u0000\u0000\u0000\u00ff"+ - "\u0100\u0007\u0002\u0000\u0000\u0100\'\u0001\u0000\u0000\u0000\u0101\u0102"+ - "\u0005+\u0000\u0000\u0102)\u0001\u0000\u0000\u0000!3;@DIMTY\\`cgjpsu|"+ - "\u0081\u0084\u008c\u0094\u009c\u009e\u00b0\u00c0\u00ce\u00dc\u00e1\u00e8"+ - "\u00f0\u00f5\u00f9\u00fb"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java deleted file mode 100644 index bd8f0d02874..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/expr/sql/SqlExpressionVisitor.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.apache.hertzbeat.alert.expr.sql; - -import org.antlr.v4.runtime.tree.ParseTreeVisitor; - -/** - * This interface defines a complete generic visitor for a parse tree produced - * by {@link SqlExpressionParser}. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -public interface SqlExpressionVisitor extends ParseTreeVisitor { - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#expression}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitExpression(SqlExpressionParser.ExpressionContext ctx); - - /** - * Visit a parse tree produced by the {@code SelectSqlExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSqlExpr(SqlExpressionParser.SelectSqlExprContext ctx); - - /** - * Visit a parse tree produced by the {@code SelectSqlCallExpr} - * labeled alternative in {@link SqlExpressionParser#sqlExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSqlCallExpr(SqlExpressionParser.SelectSqlCallExprContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectSql}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectSql(SqlExpressionParser.SelectSqlContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectFieldList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectFieldList(SqlExpressionParser.SelectFieldListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#selectField}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSelectField(SqlExpressionParser.SelectFieldContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#relList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitRelList(SqlExpressionParser.RelListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#relation}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitRelation(SqlExpressionParser.RelationContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#conditionList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitConditionList(SqlExpressionParser.ConditionListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#compOp}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitCompOp(SqlExpressionParser.CompOpContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#condition}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitCondition(SqlExpressionParser.ConditionContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#conditionUnit}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitConditionUnit(SqlExpressionParser.ConditionUnitContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#functionCall}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFunctionCall(SqlExpressionParser.FunctionCallContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#functionName}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFunctionName(SqlExpressionParser.FunctionNameContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#parameterList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameterList(SqlExpressionParser.ParameterListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#parameter}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameter(SqlExpressionParser.ParameterContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#groupByList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitGroupByList(SqlExpressionParser.GroupByListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#orderByList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOrderByList(SqlExpressionParser.OrderByListContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#orderByField}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOrderByField(SqlExpressionParser.OrderByFieldContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#limitClause}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitLimitClause(SqlExpressionParser.LimitClauseContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#number}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitNumber(SqlExpressionParser.NumberContext ctx); - - /** - * Visit a parse tree produced by {@link SqlExpressionParser#string}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitString(SqlExpressionParser.StringContext ctx); -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index 1c8c2514ed8..92f7de7c147 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -88,7 +88,8 @@ public AlertDefineServiceImpl(List alertDefineImExpo public void validate(AlertDefine alertDefine, boolean isModify) throws IllegalArgumentException { if (StringUtils.hasText(alertDefine.getExpr())) { if (CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType()) - || CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType())) { + || CommonConstants.LOG_ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType()) + || CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC.equals(alertDefine.getType())) { try { JexlExpressionRunner.compile(alertDefine.getExpr()); } catch (Exception e) { @@ -242,6 +243,9 @@ public List> getDefinePreview(String datasource, String type switch (type) { case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC: return dataSourceService.calculate(datasource, expr); + case CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC: + // todo support alert expr preview + return dataSourceService.query(datasource, expr); default: log.error("Get define preview unsupported type: {}", type); return Collections.emptyList(); diff --git a/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 b/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 deleted file mode 100644 index 2c7c925b3b5..00000000000 --- a/hertzbeat-alerter/src/main/resources/expr/SqlExpression.g4 +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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. - */ - -grammar SqlExpression; - -expression - : sqlExpr EOF - ; - -sqlExpr - : selectSql # SelectSqlExpr - | SQL_FUNCTION LPAREN string RPAREN # SelectSqlCallExpr - ; - -// SQL grammar for complex queries -selectSql - : SELECT selectFieldList FROM relList (WHERE conditionList)? - (GROUP BY groupByList)? (HAVING conditionList)? - (ORDER BY orderByList)? (LIMIT limitClause)? - ; - -selectFieldList - : selectField (COMMA selectField)* - ; - -selectField - : functionCall (AS? IDENTIFIER)? - | IDENTIFIER (AS? IDENTIFIER)? - | STAR (AS? IDENTIFIER)? - | IDENTIFIER DOT IDENTIFIER (AS? IDENTIFIER)? - ; - -relList - : relation (COMMA relation)* - ; - -relation - : IDENTIFIER (AS? IDENTIFIER)? - | LPAREN selectSql RPAREN AS IDENTIFIER - ; - -conditionList - : condition - | conditionList AND conditionList - | conditionList OR conditionList - | LPAREN conditionList RPAREN - ; - -compOp - : EQ | LT | GT | LE | GE | NE | LIKE | NOT LIKE | IN | NOT IN | IS | IS NOT - ; - -condition - : conditionUnit compOp conditionUnit - | LPAREN condition RPAREN - | IDENTIFIER BETWEEN number AND number - ; - -conditionUnit - : number - | string - | IDENTIFIER - | IDENTIFIER DOT IDENTIFIER - | NULL - | LPAREN selectSql RPAREN - | functionCall - ; - -functionCall - : functionName LPAREN parameterList RPAREN - ; - -functionName - : COUNT - | AVG - | SUM - | MIN - | MAX - | IDENTIFIER - ; - -parameterList - : parameter (COMMA parameter)* - ; - -parameter - : STAR - | string - ; - -groupByList - : IDENTIFIER (COMMA IDENTIFIER)* - ; - -orderByList - : orderByField (COMMA orderByField)* - ; - -orderByField - : IDENTIFIER (ASC | DESC)? - | functionCall (ASC | DESC)? - ; - -limitClause - : NUMBER - ; - -number - : NUMBER - | FLOAT - ; - -string - : STRING - ; - - -// Lexer rules - -// Boolean operators -AND : [Aa][Nn][Dd] ; -OR : [Oo][Rr] ; -NOT : [Nn][Oo][Tt] ; - -// SQL keywords -SELECT : [Ss][Ee][Ll][Ee][Cc][Tt] ; -FROM : [Ff][Rr][Oo][Mm] ; -WHERE : [Ww][Hh][Ee][Rr][Ee] ; -GROUP : [Gg][Rr][Oo][Uu][Pp] ; -BY : [Bb][Yy] ; -HAVING : [Hh][Aa][Vv][Ii][Nn][Gg] ; -ORDER : [Oo][Rr][Dd][Ee][Rr] ; -LIMIT : [Ll][Ii][Mm][Ii][Tt] ; -OFFSET : [Oo][Ff][Ff][Ss][Ee][Tt] ; -AS : [Aa][Ss] ; -ASC : [Aa][Ss][Cc] ; -DESC : [Dd][Ee][Ss][Cc] ; -IN : [Ii][Nn] ; -IS : [Ii][Ss] ; -NULL : [Nn][Uu][Ll][Ll] ; -LIKE : [Ll][Ii][Kk][Ee] ; -BETWEEN : [Bb][Ee][Tt][Ww][Ee][Ee][Nn] ; -STAR : '*' ; - -// Aggregate functions -COUNT : [Cc][Oo][Uu][Nn][Tt] ; -SUM : [Ss][Uu][Mm] ; -AVG : [Aa][Vv][Gg] ; -MIN : [Mm][Ii][Nn] ; -MAX : [Aa][Xx] ; - -// Other functions -SQL_FUNCTION: [Ss][Qq][Ll] ; - -// Comparison operators -GT : '>' ; -GE : '>=' ; -LT : '<' ; -LE : '<=' ; -EQ : '==' | '=' ; -NE : '!=' ; - -// Delimiters -LPAREN : '(' ; -RPAREN : ')' ; -LBRACKET: '[' ; -RBRACKET: ']' ; -COMMA : ',' ; -DOT : '.' ; -SEMICOLON: ';' ; - -// number formats -FLOAT : [0-9]+ '.' [0-9]+ ; -NUMBER : [0-9]+ ; - -// String literals -STRING : '"' (~["\r\n\\] | '\\' .)* '"' - | '\'' (~['\r\n\\] | '\\' .)* '\'' ; - -// Identifiers -IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* ; - -// Whitespace and comments -WS : [ \t\r\n]+ -> channel(HIDDEN) ; -LINE_COMMENT : '//' ~[\r\n]* -> skip ; -BLOCK_COMMENT : '/*' .*? '*/' -> skip ; \ No newline at end of file diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java index 8989f466240..5492fd3d9ad 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java @@ -45,9 +45,10 @@ void setUp() { SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class); AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class); AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class); + JexlExprCalculator mockExprCalculator = Mockito.mock(JexlExprCalculator.class); calculator = new LogRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, - mockDao, mockReduce, alarmCacheManager, false); + mockDao, mockReduce, alarmCacheManager, mockExprCalculator,false); } @Test @@ -60,7 +61,7 @@ void testExecAlertExpression_SeverityNumberAndSeverityText_ShouldMatch() { String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match when severityNumber is 2 and severityText contains 'ERROR'"); @@ -76,7 +77,7 @@ void testExecAlertExpression_SeverityNumberMatches_SeverityTextNotMatch_ShouldNo String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertFalse(result, "Expression should not match when severityText doesn't contain 'ERROR'"); @@ -92,7 +93,7 @@ void testExecAlertExpression_SeverityNumberNotMatch_SeverityTextMatches_ShouldNo String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertFalse(result, "Expression should not match when severityNumber is not 2"); @@ -108,7 +109,7 @@ void testExecAlertExpression_CaseInsensitiveContains_ShouldMatch() { String expr = "(log.severityNumber == 2) && (contains(log.severityText.toLowerCase(), \"error\"))"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match with case-insensitive comparison"); @@ -124,7 +125,7 @@ void testExecAlertExpression_SeverityGreaterThan_ShouldMatch() { String expr = "log.severityNumber > 2"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match when severityNumber is greater than 2"); @@ -140,7 +141,7 @@ void testExecAlertExpression_BodyContains_ShouldMatch() { String expr = "contains(log.body, \"Database\")"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match when body contains 'Database'"); @@ -160,7 +161,7 @@ void testExecAlertExpression_AttributesCheck_ShouldMatch() { String expr = "log.attributes.service == \"user-service\""; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match when attributes contain expected service"); @@ -180,7 +181,7 @@ void testExecAlertExpression_ComplexExpression_ShouldMatch() { String expr = "(log.severityNumber >= 3) && (contains(log.severityText, \"ERROR\")) && (log.attributes.environment == \"production\")"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Complex expression should match all conditions"); @@ -196,7 +197,7 @@ void testExecAlertExpression_OrExpression_ShouldMatch() { String expr = "(log.severityNumber == 5) || (contains(log.body, \"Debug\"))"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "OR expression should match when at least one condition is true"); @@ -221,7 +222,7 @@ void testExecAlertExpression_TimestampCheck_ShouldMatch() { String expr = "log.timeUnixNano > 0"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Expression should match when timestamp is greater than 0"); @@ -236,7 +237,7 @@ void testExecAlertExpression_NullBody_ShouldNotMatch() { String expr = "contains(log.body, \"error\")"; - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Act & Assert assertFalse(result, "Expression should not match when body is null"); } @@ -251,7 +252,7 @@ void testExecAlertExpression_EmptyAttributes_ShouldNotMatch() { String expr = "log.attributes.service == \"user-service\""; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertFalse(result, "Expression should not match when attributes is empty"); @@ -282,7 +283,7 @@ void testExecAlertExpression_MultipleLogFields_ShouldMatch() { + "(log.traceId == \"abc123\")"; // Act - boolean result = calculator.execAlertExpression(fieldValueMap, expr, false); + boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); // Assert assertTrue(result, "Complex expression with multiple log fields should match"); diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java index fbbbfe683e0..a94b2546b4e 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java @@ -67,6 +67,9 @@ public class MetricsRealTimeAlertCalculatorMatchTest { @Mock private AlarmCacheManager alarmCacheManager; + @Mock + private JexlExprCalculator jexlExprCalculator; + private MetricsRealTimeAlertCalculator metricsRealTimeAlertCalculator; @BeforeEach @@ -80,6 +83,7 @@ public void setUp() { singleAlertDao, alarmCommonReduce, alarmCacheManager, + jexlExprCalculator, false ); } diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java index 558ecd43f52..6d6ca317d3f 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java @@ -47,11 +47,12 @@ void setUp() { SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class); AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class); AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class); + JexlExprCalculator mockExprCalculator = Mockito.mock(JexlExprCalculator.class); Mockito.when(mockDao.querySingleAlertsByStatus(Mockito.anyString())) .thenReturn(Collections.emptyList()); - calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, false); + calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, mockExprCalculator,false); } @Test diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java index 2434df7f017..bc121349bad 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java @@ -88,7 +88,7 @@ public class AlertDefine { @Schema(title = "Annotations", example = "summary: High CPU usage") @Convert(converter = JsonMapAttributeConverter.class) - @Column(length = 4096) + @Column(length = 2048) private Map annotations; @Schema(title = "Alert Content Template", example = "Instance {{ $labels.instance }} CPU usage is {{ $value }}%") @@ -100,6 +100,11 @@ public class AlertDefine { @Size(max = 100) private String datasource; + @Schema(title = "Query Expression", example = "SELECT * FROM metrics WHERE value > 90") + @Size(max = 2048) + @Column(length = 2048) + private String queryExpr; + @Schema(title = "Is Enabled", example = "true") private boolean enable = true; diff --git a/hertzbeat-manager/src/main/resources/application.yml b/hertzbeat-manager/src/main/resources/application.yml index 682a1d79ec6..82e9033e6ce 100644 --- a/hertzbeat-manager/src/main/resources/application.yml +++ b/hertzbeat-manager/src/main/resources/application.yml @@ -162,8 +162,8 @@ warehouse: password: taosdata greptime: enabled: true - grpc-endpoints: localhost:4001 - http-endpoint: http://localhost:4000 + grpc-endpoints: localhost:4101 + http-endpoint: http://localhost:4100 # if you config other database name, you should create them first database: public username: greptime diff --git a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql index 42490384122..6f4e69034aa 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/h2/V173__update_column.sql @@ -28,3 +28,11 @@ WHERE type = 'realtime'; UPDATE HZB_ALERT_DEFINE SET type = 'periodic_metric' WHERE type = 'periodic'; + +-- Modify annotations column length from 4096 to 2048 +ALTER TABLE HZB_ALERT_DEFINE +ALTER COLUMN annotations VARCHAR(2048); + +-- Add query_expr column if not exists +ALTER TABLE HZB_ALERT_DEFINE +ADD COLUMN IF NOT EXISTS query_expr VARCHAR(2048); diff --git a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql index d312038e3c5..3ee63e0dcc0 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql @@ -17,12 +17,13 @@ -- ensure every sql can rerun without error --- Update hzb_alert_define table type column to support log monitoring +-- Update hzb_alert_define table type column to support log monitoring and modify annotations/query_expr columns DELIMITER // -CREATE PROCEDURE UpdateAlertDefineType() +CREATE PROCEDURE UpdateAlertDefineColumns() BEGIN DECLARE table_exists INT; + DECLARE column_exists INT; -- Check if the table exists SELECT COUNT(*) INTO table_exists @@ -39,11 +40,25 @@ BEGIN UPDATE HZB_ALERT_DEFINE SET type = 'periodic_metric' WHERE type = 'periodic'; + + -- Modify annotations column length from 4096 to 2048 + ALTER TABLE HZB_ALERT_DEFINE + MODIFY COLUMN annotations VARCHAR(2048); + + -- Add query_expr column if not exists + SELECT COUNT(*) INTO column_exists + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE' AND COLUMN_NAME = 'query_expr'; + + IF column_exists = 0 THEN + ALTER TABLE HZB_ALERT_DEFINE + ADD COLUMN query_expr VARCHAR(2048); + END IF; END IF; END // DELIMITER ; -CALL UpdateAlertDefineType(); -DROP PROCEDURE IF EXISTS UpdateAlertDefineType; +CALL UpdateAlertDefineColumns(); +DROP PROCEDURE IF EXISTS UpdateAlertDefineColumns; COMMIT; \ No newline at end of file diff --git a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql index 1e5d005b969..53d18fb74b0 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/postgresql/V173__update_column.sql @@ -29,4 +29,12 @@ UPDATE HZB_ALERT_DEFINE SET type = 'periodic_metric' WHERE type = 'periodic'; +-- Modify annotations column length from 4096 to 2048 +ALTER TABLE HZB_ALERT_DEFINE +ALTER COLUMN annotations TYPE VARCHAR(2048); + +-- Add query_expr column +ALTER TABLE HZB_ALERT_DEFINE +ADD COLUMN IF NOT EXISTS query_expr VARCHAR(2048); + commit; diff --git a/web-app/src/app/pojo/AlertDefine.ts b/web-app/src/app/pojo/AlertDefine.ts index 364666a7f9d..25474d2b353 100644 --- a/web-app/src/app/pojo/AlertDefine.ts +++ b/web-app/src/app/pojo/AlertDefine.ts @@ -25,6 +25,7 @@ export class AlertDefine { // datasource when type is periodic, promql | sql datasource: string = 'promql'; expr!: string; + queryExpr!: string; // unit second period: number = 300; times: number = 3; diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 3b33a63e9a7..9cddd4aadaf 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -654,7 +654,7 @@
- + {{ 'alert.setting.rule' | i18n }} @@ -671,7 +671,8 @@ - + + @@ -696,12 +697,12 @@ [nzData]="previewData" [nzSize]="'small'" [nzLoading]="previewTableLoading" - [nzScroll]="previewData.length > 3 ? { x: '1240px', y: '180px' } : { x: '1240px' }" + [nzScroll]="previewData.length > 3 ? { y: '180px' } : {}" [nzShowPagination]="false" > - + {{ column.title }} @@ -717,6 +718,90 @@ + + + {{ 'alert.setting.log.query' | i18n }} + + + + + + + + + + + + + + + + + + +
+ + + + + {{ column.title }} + + + + + + + {{ data[column.key] }} + + + + +
+
+
+ + + + {{ 'alert.setting.log.expr' | i18n }} + + + + + + + {{ 'alert.setting.period' | i18n }} diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index 6820f29a4d2..142a2bc0874 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -1662,6 +1662,75 @@ export class AlertSettingComponent implements OnInit { }); } + onPreviewLogQueryExpr(): void { + if (!this.define.queryExpr) { + this.clearPreview(); + this.previewTableLoading = false; + return; + } + this.previewTableLoading = true; + + this.alertDefineSvc.getMonitorsDefinePreview(this.define.datasource, this.define.type, this.define.queryExpr).subscribe({ + next: res => { + if (res.code === 15 || res.code === 1 || res.code === 4) { + this.message.error(res.msg || 'Expression parsing exception'); + this.clearPreview(); + this.previewTableLoading = false; + return; + } + if (res.code === 0 && Array.isArray(res.data)) { + // Process log data for table display + this.processLogPreviewData(res.data); + } else { + this.clearPreview(); + } + this.previewTableLoading = false; + }, + error: err => { + this.clearPreview(); + this.previewTableLoading = false; + this.message.error('Failed to get preview data.'); + } + }); + } + + private processLogPreviewData(data: any[]): void { + if (!data || data.length === 0) { + this.clearPreview(); + return; + } + + // Get all unique keys from the log data to create columns + const allKeys = new Set(); + data.forEach(item => { + Object.keys(item).forEach(key => allKeys.add(key)); + }); + + // Create columns based on actual query result keys - use the key as both title and key + this.previewColumns = Array.from(allKeys).map(key => ({ + title: key, + key: key + })); + + // Process data for display + this.previewData = data.map(item => { + const processedItem: any = {}; + for (const [key, value] of Object.entries(item)) { + if (value != null) { + // Format complex objects as JSON strings for display + if (typeof value === 'object') { + processedItem[key] = JSON.stringify(value); + } else { + processedItem[key] = String(value); + } + } else { + processedItem[key] = ''; + } + } + return processedItem; + }); + } + private filterEmptyFields(mapData: Record): Record { return Object.entries(mapData).reduce>((acc, [key, value]) => { if (value == null) return acc; diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index e8483422baa..28e3d4e5771 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -346,6 +346,12 @@ "alert.setting.rule.operator.str-no-equals": "not equals", "alert.setting.rule.operator.str-no-matches": "not matches", "alert.setting.rule.string-value.place-holder": "Please input string", + "alert.setting.log.query": "Log Query", + "alert.setting.log.query.tip": "Configure log query conditions to filter logs that need to be monitored", + "alert.setting.log.query.placeholder": "Please enter log query conditions", + "alert.setting.log.expr": "Log Alert Expression", + "alert.setting.log.expr.tip": "Configure log alert trigger conditions based on query results", + "alert.setting.log.expr.placeholder": "Please enter alert expression, e.g.: count > 10", "alert.setting.rule.switch-expr.0": "Template Threshold", "alert.setting.rule.switch-expr.1": "Coding Threshold", "alert.setting.search": "Search Threshold", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index eafba193efe..23481443c74 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -300,6 +300,12 @@ "alert.setting.rule.operator.str-no-equals": "等しくない", "alert.setting.rule.operator.str-no-matches": "一致しない", "alert.setting.rule.string-value.place-holder": "文字列を入力してください", + "alert.setting.log.query": "ログクエリ", + "alert.setting.log.query.tip": "監視するログをフィルタリングするためのログクエリ条件を設定", + "alert.setting.log.query.placeholder": "ログクエリ条件を入力してください", + "alert.setting.log.expr": "ログアラート式", + "alert.setting.log.expr.tip": "クエリ結果に基づいてログアラートトリガー条件を設定", + "alert.setting.log.expr.placeholder": "アラート式を入力してください。例: count > 10", "alert.setting.rule.switch-expr.0": "テンプレート閾値", "alert.setting.rule.switch-expr.1": "コーディング閾値", "alert.setting.search": "閾値を検索", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index 0fedeab59d6..566a4de907a 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -318,6 +318,12 @@ "alert.setting.rule.operator.exists": "valor existe", "alert.setting.rule.operator.no-exists": "valor não existe", "alert.setting.rule.string-value.place-holder": "Digite o texto", + "alert.setting.log.query": "Consulta de Log", + "alert.setting.log.query.tip": "Configure condições de consulta de log para filtrar logs que precisam ser monitorados", + "alert.setting.log.query.placeholder": "Digite as condições de consulta de log", + "alert.setting.log.expr": "Expressão de Alerta de Log", + "alert.setting.log.expr.tip": "Configure condições de disparo de alerta de log baseadas nos resultados da consulta", + "alert.setting.log.expr.placeholder": "Digite a expressão de alerta, ex: count > 10", "alert.setting.rule.numeric-value.place-holder": "Digite o número", "alert.setting.times": "Número de Disparos", "alert.setting.times.tip": "Defina quantas vezes o limite deve ser disparado antes de enviar um alerta", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 18e53fa32c7..633e5e0217a 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -348,6 +348,12 @@ "alert.setting.rule.operator.str-no-equals": "不等于", "alert.setting.rule.operator.str-no-matches": "不匹配", "alert.setting.rule.string-value.place-holder": "请输入匹配字符串", + "alert.setting.log.query": "日志查询", + "alert.setting.log.query.tip": "配置日志查询条件,用于筛选需要监控的日志", + "alert.setting.log.query.placeholder": "请输入日志查询条件", + "alert.setting.log.expr": "日志告警表达式", + "alert.setting.log.expr.tip": "配置日志告警触发条件,基于查询结果进行告警判断", + "alert.setting.log.expr.placeholder": "请输入告警表达式,例如: count > 10", "alert.setting.rule.switch-expr.0": "可视化", "alert.setting.rule.switch-expr.1": "表达式", "alert.setting.search": "搜索阈值", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 010862320fc..1eb107251e0 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -295,6 +295,12 @@ "alert.setting.rule.operator.str-no-equals": "不等於", "alert.setting.rule.operator.str-no-matches": "不匹配", "alert.setting.rule.string-value.place-holder": "請輸入匹配字符串", + "alert.setting.log.query": "日誌查詢", + "alert.setting.log.query.tip": "配置日誌查詢條件,用於篩選需要監控的日誌", + "alert.setting.log.query.placeholder": "請輸入日誌查詢條件", + "alert.setting.log.expr": "日誌告警表達式", + "alert.setting.log.expr.tip": "配置日誌告警觸發條件,基於查詢結果進行告警判斷", + "alert.setting.log.expr.placeholder": "請輸入告警表達式,例如: count > 10", "alert.setting.rule.switch-expr.0": "閾值模版", "alert.setting.rule.switch-expr.1": "閾值表達式", "alert.setting.search": "搜尋閾值", From ec2513c3fbd075afa3224139b52680592790521a Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sat, 2 Aug 2025 16:46:46 +0800 Subject: [PATCH 19/39] improvement: Remove real-time log alert recovery and delete unused import statements. --- .../alert/calculate/AbstractRealTimeAlertCalculator.java | 4 ---- .../hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java | 2 -- .../hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java | 4 ---- 3 files changed, 10 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java index 503b4393702..96139f7cf0a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java @@ -18,8 +18,6 @@ package org.apache.hertzbeat.alert.calculate; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.jexl3.JexlException; -import org.apache.commons.jexl3.JexlExpression; import org.apache.hertzbeat.alert.AlerterWorkerPool; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; @@ -30,8 +28,6 @@ import org.apache.hertzbeat.common.entity.alerter.AlertDefine; import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.apache.hertzbeat.common.queue.CommonDataQueue; -import org.apache.hertzbeat.common.util.JexlExpressionRunner; - import java.util.HashMap; import java.util.Map; diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java index fbd95eed99b..705943b6492 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java @@ -111,8 +111,6 @@ protected void calculate(LogEntry logEntry) { try { if (match) { afterThresholdRuleMatch(define.getId(), currentTimeMilli, commonFingerPrints, commonContext, define, null); - } else { - handleRecoveredAlert(define.getId(), commonFingerPrints); } } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java index 4ef4708f07f..43ac30efc63 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java @@ -1,9 +1,5 @@ package org.apache.hertzbeat.warehouse.db; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.common.constants.NetworkConstants; import org.apache.hertzbeat.common.constants.SignConstants; From 611ea493fb8cbbf2db1e64a29e0094d650cbd136 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 3 Aug 2025 20:38:53 +0800 Subject: [PATCH 20/39] improvement: Split PeriodicAlertCalculator to logxx and metricsxxx --- .../calculate/PeriodicAlertCalculator.java | 202 ------------------ .../AbstractPeriodicAlertCalculator.java | 140 ++++++++++++ .../periodic/LogPeriodicAlertCalculator.java | 103 +++++++++ .../MetricsPeriodicAlertCalculator.java | 99 +++++++++ .../PeriodicAlertRuleScheduler.java | 16 +- .../AbstractRealTimeAlertCalculator.java | 4 +- .../LogRealTimeAlertCalculator.java | 4 +- .../MetricsRealTimeAlertCalculator.java | 4 +- .../service/impl/AlertDefineServiceImpl.java | 2 +- .../MetricsPeriodicAlertCalculatorTest.java} | 9 +- .../LogRealTimeAlertCalculatorTest.java | 4 +- ...tricsRealTimeAlertCalculatorMatchTest.java | 4 +- .../MetricsRealTimeAlertCalculatorTest.java | 4 +- .../alert/service/AlertDefineServiceTest.java | 2 +- .../apache/hertzbeat/manager/ManagerTest.java | 2 +- 15 files changed, 379 insertions(+), 220 deletions(-) delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java rename hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/{ => periodic}/PeriodicAlertRuleScheduler.java (85%) rename hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/AbstractRealTimeAlertCalculator.java (98%) rename hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/LogRealTimeAlertCalculator.java (96%) rename hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/MetricsRealTimeAlertCalculator.java (98%) rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{PeriodicAlertCalculatorTest.java => periodic/MetricsPeriodicAlertCalculatorTest.java} (95%) rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/LogRealTimeAlertCalculatorTest.java (98%) rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/MetricsRealTimeAlertCalculatorMatchTest.java (98%) rename hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/{ => realtime}/MetricsRealTimeAlertCalculatorTest.java (95%) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java deleted file mode 100644 index 65e643f95f5..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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.hertzbeat.alert.calculate; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; -import org.apache.hertzbeat.alert.service.DataSourceService; -import org.apache.hertzbeat.alert.util.AlertTemplateUtil; -import org.apache.hertzbeat.alert.util.AlertUtil; -import org.apache.hertzbeat.common.constants.CommonConstants; -import org.apache.hertzbeat.common.entity.alerter.AlertDefine; -import org.apache.hertzbeat.common.entity.alerter.SingleAlert; -import org.apache.hertzbeat.warehouse.constants.WarehouseConstants; -import org.springframework.stereotype.Component; - -import java.util.*; - -/** - * Periodic Alert Calculator - */ - -@Slf4j -@Component -public class PeriodicAlertCalculator { - - private static final String VALUE = "__value__"; - private static final String TIMESTAMP = "__timestamp__"; - private static final String ROWS = "__rows__"; - - private final DataSourceService dataSourceService; - private final AlarmCommonReduce alarmCommonReduce; - private final AlarmCacheManager alarmCacheManager; - private final JexlExprCalculator jexlExprCalculator; - - public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, - AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { - this.dataSourceService = dataSourceService; - this.alarmCommonReduce = alarmCommonReduce; - this.alarmCacheManager = alarmCacheManager; - this.jexlExprCalculator = jexlExprCalculator; - } - - public void calculate(AlertDefine define) { - if (!define.isEnable() || StringUtils.isEmpty(define.getExpr())) { - log.error("Periodic define {} is disabled or expression is empty", define.getName()); - return; - } - long currentTimeMilli = System.currentTimeMillis(); - try { - // for prometheus is instant promql query, for db is sql query - // result: [{'value': 100, 'timestamp': 1343554, 'instance': 'node1'},{'value': 200, 'timestamp': 1343555, 'instance': 'node2'}] - // the return result should be matched with threshold - try { - List> results; - String sqlOrPromql = define.getDatasource(); - if (WarehouseConstants.SQL.equals(sqlOrPromql)) { - // sql - results = dataSourceService.query(sqlOrPromql, define.getQueryExpr()); - results = this.doCalculate(results, define.getExpr()); - } else { - // promql - results = dataSourceService.calculate( - define.getDatasource(), - define.getExpr() - ); - } - // if no match the expr threshold, the results item map {'value': null} should be null and others field keep - // if results has multi list, should trigger multi alert - if (CollectionUtils.isEmpty(results)) { - return; - } - for (Map result : results) { - Map fingerPrints = new HashMap<>(8); - // here use the alert name as finger, not care the alert name may be changed - fingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); - fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); - fingerPrints.putAll(define.getLabels()); - for (Map.Entry entry : result.entrySet()) { - if (entry.getValue() != null && !VALUE.equals(entry.getKey()) - && !TIMESTAMP.equals(entry.getKey())) { - fingerPrints.put(entry.getKey(), entry.getValue().toString()); - } - } - if (result.get(VALUE) == null) { - // recovery the alert - handleRecoveredAlert(define.getId(), fingerPrints); - continue; - } - Map fieldValueMap = new HashMap<>(8); - fieldValueMap.putAll(define.getLabels()); - fieldValueMap.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); - for (Map.Entry entry : result.entrySet()) { - if (entry.getValue() != null) { - fieldValueMap.put(entry.getKey(), entry.getValue()); - } - } - afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, define); - } - } catch (Exception ignored) { - // ignore the query exception eg: no result, timeout, etc - return; - } - } catch (Exception e) { - log.error("Calculate periodic define {} failed: {}", define.getName(), e.getMessage()); - } - } - - private List> doCalculate(List> results, String expression) { - if (CollectionUtils.isEmpty(results)) { - return List.of(); - } - List> newResults = new ArrayList<>(results.size()); - for (Map result : results) { - HashMap fieldMap = new HashMap<>(result); - // todo improvement the rows judge - fieldMap.put(ROWS, results.size()); - boolean match = jexlExprCalculator.execAlertExpression(fieldMap, expression, true); - if (match) { - newResults.add(result); - } - } - return newResults; - } - - private void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, - Map fieldValueMap, AlertDefine define) { - Long defineId = define.getId(); - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); - Map labels = new HashMap<>(8); - fieldValueMap.putAll(define.getLabels()); - labels.putAll(fingerPrints); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - SingleAlert newAlert = SingleAlert.builder() - .labels(labels) - // todo render var content in annotations - .annotations(define.getAnnotations()) - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, newAlert); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(defineId, fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(defineId, fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, existingAlert); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - - private void handleRecoveredAlert(Long defineId, Map fingerprints) { - String fingerprint = AlertUtil.calculateFingerprint(fingerprints); - SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); - if (firingAlert != null) { - // todo consider multi times to tig for resolved alert - firingAlert.setTriggerTimes(1); - firingAlert.setEndAt(System.currentTimeMillis()); - firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); - alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); - } - alarmCacheManager.removePending(defineId, fingerprint); - } - -} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java new file mode 100644 index 00000000000..364d2ea50a4 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java @@ -0,0 +1,140 @@ +/* + * 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.hertzbeat.alert.calculate.periodic; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.alert.util.AlertUtil; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; + +import java.util.HashMap; +import java.util.Map; + +/** + * Abstract base class for periodic alert calculators + */ +@Slf4j +public abstract class AbstractPeriodicAlertCalculator { + + protected final AlarmCommonReduce alarmCommonReduce; + protected final AlarmCacheManager alarmCacheManager; + protected final JexlExprCalculator jexlExprCalculator; + + protected AbstractPeriodicAlertCalculator(AlarmCommonReduce alarmCommonReduce, + AlarmCacheManager alarmCacheManager, + JexlExprCalculator jexlExprCalculator) { + this.alarmCommonReduce = alarmCommonReduce; + this.alarmCacheManager = alarmCacheManager; + this.jexlExprCalculator = jexlExprCalculator; + } + + /** + * Calculate alerts for the given alert definition + * @param define The alert definition to calculate + */ + public void calculate(AlertDefine define) { + if (!define.isEnable() || StringUtils.isEmpty(define.getExpr())) { + log.error("Periodic define {} is disabled or expression is empty", define.getName()); + return; + } + + long currentTimeMilli = System.currentTimeMillis(); + try { + doCalculate(define, currentTimeMilli); + } catch (Exception e) { + log.error("Calculate periodic define {} failed: {}", define.getName(), e.getMessage()); + } + } + + /** + * Perform the actual calculation for the alert definition + * @param define The alert definition + * @param currentTimeMilli Current timestamp + */ + protected abstract void doCalculate(AlertDefine define, long currentTimeMilli); + + /** + * Handle alert after threshold rule match + */ + protected void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, + Map fieldValueMap, AlertDefine define) { + Long defineId = define.getId(); + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); + Map labels = new HashMap<>(8); + fieldValueMap.putAll(define.getLabels()); + labels.putAll(fingerPrints); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + SingleAlert newAlert = SingleAlert.builder() + .labels(labels) + .annotations(define.getAnnotations()) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(defineId, fingerprint, newAlert); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(defineId, fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(defineId, fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(defineId, fingerprint, existingAlert); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + + /** + * Handle recovered alert + */ + protected void handleRecoveredAlert(Long defineId, Map fingerprints) { + String fingerprint = AlertUtil.calculateFingerprint(fingerprints); + SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); + if (firingAlert != null) { + firingAlert.setTriggerTimes(1); + firingAlert.setEndAt(System.currentTimeMillis()); + firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); + alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); + } + alarmCacheManager.removePending(defineId, fingerprint); + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java new file mode 100644 index 00000000000..e5ef9030425 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java @@ -0,0 +1,103 @@ +/* + * 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.hertzbeat.alert.calculate.periodic; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.service.DataSourceService; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * Log Periodic Alert Calculator + */ +@Slf4j +@Component +public class LogPeriodicAlertCalculator extends AbstractPeriodicAlertCalculator { + + private static final String TIMESTAMP = "__timestamp__"; + private static final String ROWS = "__rows__"; + + private final DataSourceService dataSourceService; + + public LogPeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, + AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { + super(alarmCommonReduce, alarmCacheManager, jexlExprCalculator); + this.dataSourceService = dataSourceService; + } + + @Override + protected void doCalculate(AlertDefine define, long currentTimeMilli) { + try { + // Log-based queries are SQL queries with log-specific expressions + List> results = dataSourceService.query(define.getDatasource(), define.getQueryExpr()); + results = this.calculateLogThreshold(results, define.getExpr()); + + // If no match the expr threshold, the results item map {'value': null} should be null and others field keep + // If results has multi list, should trigger multi alert + if (CollectionUtils.isEmpty(results)) { + return; + } + + for (Map result : results) { + Map fingerPrints = new HashMap<>(8); + // Here use the alert name as finger, not care the alert name may be changed + fingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); + fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + fingerPrints.putAll(define.getLabels()); + for (Map.Entry entry : result.entrySet()) { + if (entry.getValue() != null) { + fingerPrints.put(entry.getKey(), entry.getValue().toString()); + } + } + Map fieldValueMap = new HashMap<>(fingerPrints); + afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, define); + } + } catch (Exception ignored) { + // Ignore the query exception eg: no result, timeout, etc + } + } + + /** + * Calculate log threshold evaluation + * @param results Query results from log datasource + * @param expression Alert expression for log analysis + * @return Filtered results that match the log threshold + */ + private List> calculateLogThreshold(List> results, String expression) { + if (CollectionUtils.isEmpty(results)) { + return List.of(); + } + List> newResults = new ArrayList<>(results.size()); + for (Map result : results) { + HashMap fieldMap = new HashMap<>(result); + fieldMap.put(ROWS, results.size()); + boolean match = jexlExprCalculator.execAlertExpression(fieldMap, expression, true); + if (match) { + newResults.add(result); + } + } + return newResults; + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java new file mode 100644 index 00000000000..1fc61dea4b2 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java @@ -0,0 +1,99 @@ +/* + * 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.hertzbeat.alert.calculate.periodic; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.apache.hertzbeat.alert.service.DataSourceService; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Metrics Periodic Alert Calculator + */ +@Slf4j +@Component +public class MetricsPeriodicAlertCalculator extends AbstractPeriodicAlertCalculator { + + private static final String VALUE = "__value__"; + private static final String TIMESTAMP = "__timestamp__"; + + private final DataSourceService dataSourceService; + + public MetricsPeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, + AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { + super(alarmCommonReduce, alarmCacheManager, jexlExprCalculator); + this.dataSourceService = dataSourceService; + } + + @Override + protected void doCalculate(AlertDefine define, long currentTimeMilli) { + try { + List> results = dataSourceService.calculate( + define.getDatasource(), + define.getExpr() + ); + // If no match the expr threshold, the results item map {'value': null} should be null and others field keep + // If results has multi list, should trigger multi alert + if (CollectionUtils.isEmpty(results)) { + return; + } + + for (Map result : results) { + Map fingerPrints = new HashMap<>(8); + // Here use the alert name as finger, not care the alert name may be changed + fingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); + fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + fingerPrints.putAll(define.getLabels()); + for (Map.Entry entry : result.entrySet()) { + if (entry.getValue() != null && !VALUE.equals(entry.getKey()) + && !TIMESTAMP.equals(entry.getKey())) { + fingerPrints.put(entry.getKey(), entry.getValue().toString()); + } + } + + if (result.get(VALUE) == null) { + // Recovery the alert + handleRecoveredAlert(define.getId(), fingerPrints); + continue; + } + + Map fieldValueMap = new HashMap<>(8); + fieldValueMap.putAll(define.getLabels()); + fieldValueMap.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + for (Map.Entry entry : result.entrySet()) { + if (entry.getValue() != null) { + fieldValueMap.put(entry.getKey(), entry.getValue()); + } + } + afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, define); + } + } catch (Exception ignored) { + // Ignore the query exception eg: no result, timeout, etc + } + } + +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/PeriodicAlertRuleScheduler.java similarity index 85% rename from hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java rename to hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/PeriodicAlertRuleScheduler.java index 5d076542a5a..978fb3d234a 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertRuleScheduler.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/PeriodicAlertRuleScheduler.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.periodic; import static org.apache.hertzbeat.common.constants.CommonConstants.LOG_ALERT_THRESHOLD_TYPE_PERIODIC; import static org.apache.hertzbeat.common.constants.CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC; @@ -42,13 +42,15 @@ @Component public class PeriodicAlertRuleScheduler implements CommandLineRunner { - private final PeriodicAlertCalculator calculator; + private final MetricsPeriodicAlertCalculator metricsCalculator; + private final LogPeriodicAlertCalculator logCalculator; private final AlertDefineDao alertDefineDao; private final ScheduledExecutorService scheduledExecutor; private final Map> scheduledFutures; - public PeriodicAlertRuleScheduler(PeriodicAlertCalculator calculator, AlertDefineDao alertDefineDao) { - this.calculator = calculator; + public PeriodicAlertRuleScheduler(MetricsPeriodicAlertCalculator metricsCalculator, LogPeriodicAlertCalculator logCalculator, AlertDefineDao alertDefineDao) { + this.metricsCalculator = metricsCalculator; + this.logCalculator = logCalculator; this.alertDefineDao = alertDefineDao; ThreadFactory threadFactory = new ThreadFactoryBuilder() .setUncaughtExceptionHandler((thread, throwable) -> { @@ -82,7 +84,11 @@ public void updateSchedule(AlertDefine rule) { if (rule.getType().equals(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC) || rule.getType().equals(LOG_ALERT_THRESHOLD_TYPE_PERIODIC)) { ScheduledFuture future = scheduledExecutor.scheduleAtFixedRate(() -> { - calculator.calculate(rule); + if (rule.getType().equals(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC)) { + metricsCalculator.calculate(rule); + } else if (rule.getType().equals(LOG_ALERT_THRESHOLD_TYPE_PERIODIC)) { + logCalculator.calculate(rule); + } }, 0, rule.getPeriod(), java.util.concurrent.TimeUnit.SECONDS); scheduledFutures.put(rule.getId(), future); } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java similarity index 98% rename from hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java rename to hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java index 96139f7cf0a..159ad940823 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AbstractRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java @@ -15,10 +15,12 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java similarity index 96% rename from hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java rename to hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java index 705943b6492..80be0aa4d72 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java @@ -15,11 +15,13 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java similarity index 98% rename from hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java rename to hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java index f70f9ed5548..fe21749df46 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import java.util.Collections; import java.util.HashMap; @@ -29,6 +29,8 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index 92f7de7c147..679f580c207 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -24,7 +24,7 @@ import jakarta.persistence.criteria.Predicate; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler; +import org.apache.hertzbeat.alert.calculate.periodic.PeriodicAlertRuleScheduler; import org.apache.hertzbeat.alert.dao.AlertDefineDao; import org.apache.hertzbeat.alert.service.AlertDefineImExportService; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculatorTest.java similarity index 95% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculatorTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculatorTest.java index 9acb1be0ece..ea879303def 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculatorTest.java @@ -15,8 +15,9 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.periodic; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.DataSourceService; import org.apache.hertzbeat.common.constants.CommonConstants; @@ -50,10 +51,10 @@ import static org.mockito.Mockito.when; /** - * Test case for {@link PeriodicAlertCalculator} + * Test case for {@link MetricsPeriodicAlertCalculator} */ @ExtendWith(MockitoExtension.class) -class PeriodicAlertCalculatorTest { +class MetricsPeriodicAlertCalculatorTest { @Mock private DataSourceService dataSourceService; @@ -65,7 +66,7 @@ class PeriodicAlertCalculatorTest { private AlarmCacheManager alarmCacheManager; @InjectMocks - private PeriodicAlertCalculator periodicAlertCalculator; + private MetricsPeriodicAlertCalculator periodicAlertCalculator; private AlertDefine rule; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java similarity index 98% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java index 5492fd3d9ad..886951c84cf 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/LogRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java @@ -15,12 +15,14 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorMatchTest.java similarity index 98% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorMatchTest.java index a94b2546b4e..40432a7144d 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorMatchTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorMatchTest.java @@ -15,10 +15,12 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import com.google.common.collect.Lists; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java similarity index 95% rename from hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java rename to hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java index 6d6ca317d3f..ccd91a59e6a 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/MetricsRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java @@ -15,11 +15,13 @@ * limitations under the License. */ -package org.apache.hertzbeat.alert.calculate; +package org.apache.hertzbeat.alert.calculate.realtime; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java index 824c19e9864..2e3df491faa 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineServiceTest.java @@ -18,7 +18,7 @@ package org.apache.hertzbeat.alert.service; import com.google.common.collect.Lists; -import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler; +import org.apache.hertzbeat.alert.calculate.periodic.PeriodicAlertRuleScheduler; import org.apache.hertzbeat.alert.dao.AlertDefineDao; import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java index d4c4b03a249..9c1858fb2cd 100644 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java +++ b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java @@ -23,7 +23,7 @@ import javax.naming.NamingException; import org.apache.hertzbeat.alert.AlerterProperties; import org.apache.hertzbeat.alert.AlerterWorkerPool; -import org.apache.hertzbeat.alert.calculate.MetricsRealTimeAlertCalculator; +import org.apache.hertzbeat.alert.calculate.realtime.MetricsRealTimeAlertCalculator; import org.apache.hertzbeat.alert.controller.AlertDefineController; import org.apache.hertzbeat.alert.controller.AlertDefinesController; import org.apache.hertzbeat.alert.controller.AlertsController; From 7c17778d9def5b54f9ebb17b26263b0da85e94f4 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Tue, 5 Aug 2025 20:03:46 +0800 Subject: [PATCH 21/39] improvament: keep the firing fingerprint but add the log entity to alert labels --- .../AbstractRealTimeAlertCalculator.java | 77 ---------- .../realtime/LogRealTimeAlertCalculator.java | 145 +++++++++++++++++- .../MetricsRealTimeAlertCalculator.java | 81 +++++++++- 3 files changed, 223 insertions(+), 80 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java index 159ad940823..56c9e6f81c3 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java @@ -128,81 +128,4 @@ public void startCalculate() { * @param data The data to calculate alerts for */ protected abstract void calculate(T data); - - /** - * Handle alert after threshold rule match - */ - protected void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, - Map fieldValueMap, AlertDefine define, - Map annotations) { - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); - fieldValueMap.putAll(define.getLabels()); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - Map alertLabels = new HashMap<>(8); - alertLabels.putAll(fingerPrints); - Map alertAnnotations = new HashMap<>(8); - if (annotations != null) { - alertAnnotations.putAll(annotations); - } - if (define.getAnnotations() != null) { - alertAnnotations.putAll(define.getAnnotations()); - } - // render var content in annotations - for (Map.Entry entry : alertAnnotations.entrySet()) { - entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); - } - SingleAlert newAlert = SingleAlert.builder() - .labels(alertLabels) - .annotations(alertAnnotations) - // render var content in content - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, newAlert); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(defineId, fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, existingAlert); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - - /** - * Handle recovered alert - */ - protected void handleRecoveredAlert(Long defineId, Map fingerprints) { - String fingerprint = AlertUtil.calculateFingerprint(fingerprints); - SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); - if (firingAlert != null) { - // todo consider multi times to tig for resolved alert - firingAlert.setTriggerTimes(1); - firingAlert.setEndAt(System.currentTimeMillis()); - firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); - alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); - } - alarmCacheManager.removePending(defineId, fingerprint); - } } \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java index 80be0aa4d72..3ca21ef5541 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java @@ -25,8 +25,11 @@ import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.alert.util.AlertUtil; import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.springframework.beans.factory.annotation.Autowired; @@ -102,17 +105,22 @@ protected void calculate(LogEntry logEntry) { continue; } Map commonFingerPrints = new HashMap<>(8); + Map extendFingerPrints = new HashMap<>(8); + // here use the alert name as finger, not care the alert name may be changed - // todo add more fingerprints commonFingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); commonFingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); commonFingerPrints.putAll(define.getLabels()); + addLogEntryToMap(logEntry, extendFingerPrints); + + Map annotations = new HashMap<>(); + annotations.putAll(define.getAnnotations()); try { boolean match = jexlExprCalculator.execAlertExpression(commonContext, expr, false); try { if (match) { - afterThresholdRuleMatch(define.getId(), currentTimeMilli, commonFingerPrints, commonContext, define, null); + afterThresholdRuleMatch(define.getId(), currentTimeMilli, commonFingerPrints, extendFingerPrints, commonContext, define, annotations); } } catch (Exception e) { log.error(e.getMessage(), e); @@ -120,4 +128,137 @@ protected void calculate(LogEntry logEntry) { } catch (Exception ignored) {} } } + + /** + * Handle alert after threshold rule match + */ + protected void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, + Map extendFingerPrints, + Map fieldValueMap, AlertDefine define, + Map annotations) { + // fingerprint for the padding cache + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); + fieldValueMap.putAll(define.getLabels()); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + Map alertLabels = new HashMap<>(8); + alertLabels.putAll(fingerPrints); + alertLabels.putAll(extendFingerPrints); + Map alertAnnotations = new HashMap<>(8); + if (annotations != null) { + alertAnnotations.putAll(annotations); + } + if (define.getAnnotations() != null) { + alertAnnotations.putAll(define.getAnnotations()); + } + // render var content in annotations + for (Map.Entry entry : alertAnnotations.entrySet()) { + entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + SingleAlert newAlert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + // render var content in content + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(defineId, fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + + + /** + * Add the content from LogEntry object (except timestamp) to commonFingerPrints + * + * @param logEntry log entry object + * @param context context + */ + private void addLogEntryToMap(LogEntry logEntry, Map context) { + // Add basic fields + if (logEntry.getSeverityNumber() != null) { + context.put("severityNumber", String.valueOf(logEntry.getSeverityNumber())); + } + if (logEntry.getSeverityText() != null) { + context.put("severityText", logEntry.getSeverityText()); + } + if (logEntry.getBody() != null) { + context.put("body", String.valueOf(logEntry.getBody())); + } + if (logEntry.getDroppedAttributesCount() != null) { + context.put("droppedAttributesCount", String.valueOf(logEntry.getDroppedAttributesCount())); + } + if (logEntry.getTraceId() != null) { + context.put("traceId", logEntry.getTraceId()); + } + if (logEntry.getSpanId() != null) { + context.put("spanId", logEntry.getSpanId()); + } + if (logEntry.getTraceFlags() != null) { + context.put("traceFlags", String.valueOf(logEntry.getTraceFlags())); + } + + // Add attributes + if (logEntry.getAttributes() != null && !logEntry.getAttributes().isEmpty()) { + for (Map.Entry entry : logEntry.getAttributes().entrySet()) { + if (entry.getValue() != null) { + context.put("attr_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + + // Add resource + if (logEntry.getResource() != null && !logEntry.getResource().isEmpty()) { + for (Map.Entry entry : logEntry.getResource().entrySet()) { + if (entry.getValue() != null) { + context.put("resource_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + + // Add instrumentationScope + if (logEntry.getInstrumentationScope() != null) { + LogEntry.InstrumentationScope scope = logEntry.getInstrumentationScope(); + if (scope.getName() != null) { + context.put("scope_name", scope.getName()); + } + if (scope.getVersion() != null) { + context.put("scope_version", scope.getVersion()); + } + if (scope.getDroppedAttributesCount() != null) { + context.put("scope_droppedAttributesCount", String.valueOf(scope.getDroppedAttributesCount())); + } + if (scope.getAttributes() != null && !scope.getAttributes().isEmpty()) { + for (Map.Entry entry : scope.getAttributes().entrySet()) { + if (entry.getValue() != null) { + context.put("scope_attr_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + } + } } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java index fe21749df46..d70a31cc2d9 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java @@ -34,8 +34,11 @@ import org.apache.hertzbeat.alert.dao.SingleAlertDao; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.alert.util.AlertUtil; import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.apache.hertzbeat.common.entity.message.CollectRep; import org.apache.hertzbeat.common.queue.CommonDataQueue; import org.apache.hertzbeat.common.util.CommonUtil; @@ -93,7 +96,7 @@ public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQu AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator, boolean start) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager,jexlExprCalculator, start); + super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, start); } @Override @@ -243,6 +246,66 @@ protected void calculate(CollectRep.MetricsData metricsData) { } } + /** + * Handle alert after threshold rule match + */ + private void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, + Map fieldValueMap, AlertDefine define, + Map annotations) { + // fingerprint for the padding cache + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); + fieldValueMap.putAll(define.getLabels()); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + Map alertLabels = new HashMap<>(8); + alertLabels.putAll(fingerPrints); + Map alertAnnotations = new HashMap<>(8); + if (annotations != null) { + alertAnnotations.putAll(annotations); + } + if (define.getAnnotations() != null) { + alertAnnotations.putAll(define.getAnnotations()); + } + // render var content in annotations + for (Map.Entry entry : alertAnnotations.entrySet()) { + entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + SingleAlert newAlert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + // render var content in content + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(defineId, fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + /** * Filter alert definitions by app, metrics and instance * @@ -312,6 +375,22 @@ public List filterThresholdsByAppAndMetrics(List thres .collect(Collectors.toList()); } + /** + * Handle recovered alert + */ + private void handleRecoveredAlert(Long defineId, Map fingerprints) { + String fingerprint = AlertUtil.calculateFingerprint(fingerprints); + SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); + if (firingAlert != null) { + // todo consider multi times to tig for resolved alert + firingAlert.setTriggerTimes(1); + firingAlert.setEndAt(System.currentTimeMillis()); + firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); + alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); + } + alarmCacheManager.removePending(defineId, fingerprint); + } + private Set kvLabelsToKvStringSet(Map labels) { if (labels == null || labels.isEmpty()) { return Collections.singleton(""); From 1fa8e0cf9d16da21ec98f45a16c4a1aae99d03e7 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 11 Aug 2025 18:05:57 +0800 Subject: [PATCH 22/39] improvement: refactor alert calculate, add window aggregator to log realtime, add single alert and group alert to log realtime and log periodic --- .../hertzbeat/alert/AlerterWorkerPool.java | 26 ++ .../AbstractPeriodicAlertCalculator.java | 140 -------- .../periodic/LogPeriodicAlertCalculator.java | 208 ++++++++++-- .../MetricsPeriodicAlertCalculator.java | 98 +++++- .../AbstractRealTimeAlertCalculator.java | 131 ------- .../realtime/LogRealTimeAlertCalculator.java | 264 --------------- .../MetricsRealTimeAlertCalculator.java | 180 ++++++---- .../WindowedLogRealTimeAlertCalculator.java | 127 +++++++ .../realtime/window/AlarmEvaluator.java | 320 ++++++++++++++++++ .../calculate/realtime/window/LogWorker.java | 123 +++++++ .../realtime/window/MatchingLogEvent.java | 40 +++ .../realtime/window/TimeService.java | 190 +++++++++++ .../realtime/window/WindowAggregator.java | 252 ++++++++++++++ .../alert/reduce/AlarmCommonReduce.java | 18 + .../alert/reduce/AlarmGroupReduce.java | 15 +- .../LogRealTimeAlertCalculatorTest.java | 303 ----------------- .../common/entity/alerter/AlertDefine.java | 2 +- .../alert-setting.component.html | 40 ++- .../alert-setting/alert-setting.component.ts | 12 + web-app/src/assets/i18n/en-US.json | 5 + web-app/src/assets/i18n/ja-JP.json | 5 + web-app/src/assets/i18n/pt-BR.json | 5 + web-app/src/assets/i18n/zh-CN.json | 8 + web-app/src/assets/i18n/zh-TW.json | 5 + 24 files changed, 1574 insertions(+), 943 deletions(-) delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java delete mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/WindowedLogRealTimeAlertCalculator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/AlarmEvaluator.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/LogWorker.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/MatchingLogEvent.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/TimeService.java create mode 100644 hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java delete mode 100644 hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java index 86b9437c3be..652d4a349d3 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java @@ -35,10 +35,12 @@ public class AlerterWorkerPool { private ThreadPoolExecutor workerExecutor; private ThreadPoolExecutor notifyExecutor; + private ThreadPoolExecutor logWorkerExecutor; public AlerterWorkerPool() { initWorkExecutor(); initNotifyExecutor(); + initLogWorkerExecutor(); } private void initWorkExecutor() { @@ -77,6 +79,21 @@ private void initNotifyExecutor() { new ThreadPoolExecutor.AbortPolicy()); } + private void initLogWorkerExecutor() { + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setUncaughtExceptionHandler((thread, throwable) -> { + log.error("Alerter logWorkerExecutor has uncaughtException."); + log.error(throwable.getMessage(), throwable); + }) + .setDaemon(true) + .setNameFormat("log-worker-%d") + .build(); + logWorkerExecutor = new ThreadPoolExecutor(4, 10, 10, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + threadFactory, + new ThreadPoolExecutor.AbortPolicy()); + } + /** * Run the alerter task * @param runnable task @@ -96,4 +113,13 @@ public void executeNotify(Runnable runnable) throws RejectedExecutionException { notifyExecutor.execute(runnable); } + /** + * Executes the given runnable task using the logWorkerExecutor. + * + * @param runnable the task to be executed + * @throws RejectedExecutionException if the task cannot be accepted for execution + */ + public void executeLogJob(Runnable runnable) throws RejectedExecutionException { + logWorkerExecutor.execute(runnable); + } } diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java deleted file mode 100644 index 364d2ea50a4..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/AbstractPeriodicAlertCalculator.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.hertzbeat.alert.calculate.periodic; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; -import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; -import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; -import org.apache.hertzbeat.alert.util.AlertTemplateUtil; -import org.apache.hertzbeat.alert.util.AlertUtil; -import org.apache.hertzbeat.common.constants.CommonConstants; -import org.apache.hertzbeat.common.entity.alerter.AlertDefine; -import org.apache.hertzbeat.common.entity.alerter.SingleAlert; - -import java.util.HashMap; -import java.util.Map; - -/** - * Abstract base class for periodic alert calculators - */ -@Slf4j -public abstract class AbstractPeriodicAlertCalculator { - - protected final AlarmCommonReduce alarmCommonReduce; - protected final AlarmCacheManager alarmCacheManager; - protected final JexlExprCalculator jexlExprCalculator; - - protected AbstractPeriodicAlertCalculator(AlarmCommonReduce alarmCommonReduce, - AlarmCacheManager alarmCacheManager, - JexlExprCalculator jexlExprCalculator) { - this.alarmCommonReduce = alarmCommonReduce; - this.alarmCacheManager = alarmCacheManager; - this.jexlExprCalculator = jexlExprCalculator; - } - - /** - * Calculate alerts for the given alert definition - * @param define The alert definition to calculate - */ - public void calculate(AlertDefine define) { - if (!define.isEnable() || StringUtils.isEmpty(define.getExpr())) { - log.error("Periodic define {} is disabled or expression is empty", define.getName()); - return; - } - - long currentTimeMilli = System.currentTimeMillis(); - try { - doCalculate(define, currentTimeMilli); - } catch (Exception e) { - log.error("Calculate periodic define {} failed: {}", define.getName(), e.getMessage()); - } - } - - /** - * Perform the actual calculation for the alert definition - * @param define The alert definition - * @param currentTimeMilli Current timestamp - */ - protected abstract void doCalculate(AlertDefine define, long currentTimeMilli); - - /** - * Handle alert after threshold rule match - */ - protected void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, - Map fieldValueMap, AlertDefine define) { - Long defineId = define.getId(); - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); - Map labels = new HashMap<>(8); - fieldValueMap.putAll(define.getLabels()); - labels.putAll(fingerPrints); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - SingleAlert newAlert = SingleAlert.builder() - .labels(labels) - .annotations(define.getAnnotations()) - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, newAlert); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(defineId, fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(defineId, fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCacheManager.putFiring(defineId, fingerprint, existingAlert); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - - /** - * Handle recovered alert - */ - protected void handleRecoveredAlert(Long defineId, Map fingerprints) { - String fingerprint = AlertUtil.calculateFingerprint(fingerprints); - SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); - if (firingAlert != null) { - firingAlert.setTriggerTimes(1); - firingAlert.setEndAt(System.currentTimeMillis()); - firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); - alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); - } - alarmCacheManager.removePending(defineId, fingerprint); - } -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java index e5ef9030425..eee041434d6 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/LogPeriodicAlertCalculator.java @@ -19,36 +19,62 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.DataSourceService; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.springframework.stereotype.Component; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Log Periodic Alert Calculator */ @Slf4j @Component -public class LogPeriodicAlertCalculator extends AbstractPeriodicAlertCalculator { +public class LogPeriodicAlertCalculator { - private static final String TIMESTAMP = "__timestamp__"; private static final String ROWS = "__rows__"; + private static final String ALERT_MODE_LABEL = "alert_mode"; + private static final String ALERT_MODE_GROUP = "group"; + private static final String ALERT_MODE_INDIVIDUAL = "individual"; private final DataSourceService dataSourceService; + private final AlarmCommonReduce alarmCommonReduce; + private final AlarmCacheManager alarmCacheManager; + private final JexlExprCalculator jexlExprCalculator; + public LogPeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { - super(alarmCommonReduce, alarmCacheManager, jexlExprCalculator); + this.alarmCommonReduce = alarmCommonReduce; + this.alarmCacheManager = alarmCacheManager; + this.jexlExprCalculator = jexlExprCalculator; this.dataSourceService = dataSourceService; } - @Override - protected void doCalculate(AlertDefine define, long currentTimeMilli) { + public void calculate(AlertDefine define) { + if (!define.isEnable() || StringUtils.isEmpty(define.getExpr())) { + log.error("Log define {} is disabled or expression is empty", define.getName()); + return; + } + try { + doCalculate(define); + } catch (Exception e) { + log.error("Calculate periodic define {} failed: {}", define.getName(), e.getMessage()); + } + } + + private void doCalculate(AlertDefine define) { try { // Log-based queries are SQL queries with log-specific expressions List> results = dataSourceService.query(define.getDatasource(), define.getQueryExpr()); @@ -59,21 +85,7 @@ protected void doCalculate(AlertDefine define, long currentTimeMilli) { if (CollectionUtils.isEmpty(results)) { return; } - - for (Map result : results) { - Map fingerPrints = new HashMap<>(8); - // Here use the alert name as finger, not care the alert name may be changed - fingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); - fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); - fingerPrints.putAll(define.getLabels()); - for (Map.Entry entry : result.entrySet()) { - if (entry.getValue() != null) { - fingerPrints.put(entry.getKey(), entry.getValue().toString()); - } - } - Map fieldValueMap = new HashMap<>(fingerPrints); - afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, define); - } + afterThresholdRuleMatch(results, define); } catch (Exception ignored) { // Ignore the query exception eg: no result, timeout, etc } @@ -100,4 +112,158 @@ private List> calculateLogThreshold(List } return newResults; } + + + private String getAlertMode(AlertDefine alertDefine) { + String mode = null; + if (alertDefine.getLabels() != null) { + mode = alertDefine.getLabels().get(ALERT_MODE_LABEL); + } + if (mode == null || mode.isEmpty()) { + return ALERT_MODE_GROUP; // Default to group mode if not specified + } else { + return mode; + } + } + + /** + * Handle alert after threshold rule match + */ + private void afterThresholdRuleMatch(List> alertContext, AlertDefine define) { + // Determine alert mode from configuration + String alertMode = getAlertMode(define); + + long currentTime = System.currentTimeMillis(); + + switch (alertMode) { + case ALERT_MODE_INDIVIDUAL: + // Generate individual alerts for each matching log + for (Map context : alertContext) { + generateIndividualAlert(define, context, currentTime); + } + break; + + case ALERT_MODE_GROUP: + // Generate a single alert group for all matching logs + generateGroupAlert(define, alertContext, currentTime); + break; + default: + log.warn("Unknown alert mode for define {}: {}", define.getName(), alertMode); + } + } + + private void generateIndividualAlert(AlertDefine define, Map context, long currentTime) { + + Map alertLabels = new HashMap<>(8); + + Map commonFingerPrints = createCommonFingerprints(define); + alertLabels.putAll(commonFingerPrints); + addContextToMap(context, alertLabels); + + Map fieldValueMap = createFieldValueMap(context, define); + Map alertAnnotations = createAlertAnnotations(define, fieldValueMap); + // Create and send group alert + SingleAlert alert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_FIRING) + .triggerTimes(1) + .startAt(currentTime) + .activeAt(currentTime) + .build(); + + alarmCommonReduce.reduceAndSendAlarm(alert.clone()); + + log.debug("Generated individual alert for define: {}", define.getName()); + } + + private void addContextToMap(Map context, Map alertLabels) { + for (Map.Entry entry : context.entrySet()) { + if (entry.getValue() != null) { + alertLabels.put(entry.getKey(), entry.getValue().toString()); + } + } + } + + private void generateGroupAlert(AlertDefine define, List> alertContext, long currentTime) { + + List alerts = new ArrayList<>(alertContext.size()); + + // Create fingerprints for group alert + Map commonFingerPrints = createCommonFingerprints(define); + + // Add context information to fingerprints + commonFingerPrints.put(ROWS, String.valueOf(alertContext.size())); + commonFingerPrints.put(ALERT_MODE_LABEL, ALERT_MODE_GROUP); + + for (Map context : alertContext) { + + Map alertLabels = new HashMap<>(8); + + alertLabels.putAll(commonFingerPrints); + // add the context to commonFingerPrints + addContextToMap(context, alertLabels); + + Map fieldValueMap = createFieldValueMap(context, define); + Map alertAnnotations = createAlertAnnotations(define, fieldValueMap); + // Create and send group alert + SingleAlert alert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_FIRING) + .triggerTimes(alertContext.size()) + .startAt(currentTime) + .activeAt(currentTime) + .build(); + alerts.add(alert.clone()); + } + alarmCommonReduce.reduceAndSendAlarmGroup(commonFingerPrints, alerts); + + log.debug("Generated group alert for define: {} with {} matching data", + define.getName(), alertContext.size()); + } + + + + private Map createCommonFingerprints(AlertDefine define) { + Map fingerprints = new HashMap<>(8); + fingerprints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + fingerprints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); + + if (define.getLabels() != null) { + fingerprints.putAll(define.getLabels()); + } + + return fingerprints; + } + + private Map createFieldValueMap(Map context, AlertDefine define) { + Map fieldValueMap = new HashMap<>(8); + for (Map.Entry entry : context.entrySet()) { + if (entry.getValue() != null) { + fieldValueMap.put(entry.getKey(), entry.getValue().toString()); + } + } + if (define.getLabels() != null) { + fieldValueMap.putAll(define.getLabels()); + } + + return fieldValueMap; + } + + private Map createAlertAnnotations(AlertDefine define, Map fieldValueMap) { + Map annotations = new HashMap<>(8); + + if (define.getAnnotations() != null) { + for (Map.Entry entry : define.getAnnotations().entrySet()) { + annotations.put(entry.getKey(), + AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + } + + return annotations; + } + } \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java index 1fc61dea4b2..6077305df95 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java @@ -19,12 +19,16 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.DataSourceService; +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.alert.util.AlertUtil; import org.apache.hertzbeat.common.constants.CommonConstants; import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -36,21 +40,41 @@ */ @Slf4j @Component -public class MetricsPeriodicAlertCalculator extends AbstractPeriodicAlertCalculator { +public class MetricsPeriodicAlertCalculator { private static final String VALUE = "__value__"; private static final String TIMESTAMP = "__timestamp__"; private final DataSourceService dataSourceService; + private final AlarmCommonReduce alarmCommonReduce; + private final AlarmCacheManager alarmCacheManager; public MetricsPeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce, - AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { - super(alarmCommonReduce, alarmCacheManager, jexlExprCalculator); + AlarmCacheManager alarmCacheManager) { + this.alarmCommonReduce = alarmCommonReduce; + this.alarmCacheManager = alarmCacheManager; this.dataSourceService = dataSourceService; } - @Override - protected void doCalculate(AlertDefine define, long currentTimeMilli) { + /** + * Calculate alerts for the given alert definition + * @param define The alert definition to calculate + */ + public void calculate(AlertDefine define) { + if (!define.isEnable() || StringUtils.isEmpty(define.getExpr())) { + log.error("Periodic define {} is disabled or expression is empty", define.getName()); + return; + } + + long currentTimeMilli = System.currentTimeMillis(); + try { + doCalculate(define, currentTimeMilli); + } catch (Exception e) { + log.error("Calculate periodic define {} failed: {}", define.getName(), e.getMessage()); + } + } + + private void doCalculate(AlertDefine define, long currentTimeMilli) { try { List> results = dataSourceService.calculate( define.getDatasource(), @@ -96,4 +120,68 @@ protected void doCalculate(AlertDefine define, long currentTimeMilli) { } } + /** + * Handle alert after threshold rule match + */ + private void afterThresholdRuleMatch(long currentTimeMilli, Map fingerPrints, + Map fieldValueMap, AlertDefine define) { + Long defineId = define.getId(); + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); + Map labels = new HashMap<>(8); + fieldValueMap.putAll(define.getLabels()); + labels.putAll(fingerPrints); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + SingleAlert newAlert = SingleAlert.builder() + .labels(labels) + .annotations(define.getAnnotations()) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(defineId, fingerprint, newAlert); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(defineId, fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(defineId, fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCacheManager.putFiring(defineId, fingerprint, existingAlert); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + + /** + * Handle recovered alert + */ + private void handleRecoveredAlert(Long defineId, Map fingerprints) { + String fingerprint = AlertUtil.calculateFingerprint(fingerprints); + SingleAlert firingAlert = alarmCacheManager.removeFiring(defineId, fingerprint); + if (firingAlert != null) { + firingAlert.setTriggerTimes(1); + firingAlert.setEndAt(System.currentTimeMillis()); + firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED); + alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone()); + } + alarmCacheManager.removePending(defineId, fingerprint); + } + } \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java deleted file mode 100644 index 56c9e6f81c3..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/AbstractRealTimeAlertCalculator.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.hertzbeat.alert.calculate.realtime; - -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.alert.AlerterWorkerPool; -import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; -import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; -import org.apache.hertzbeat.alert.dao.SingleAlertDao; -import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; -import org.apache.hertzbeat.alert.service.AlertDefineService; -import org.apache.hertzbeat.alert.util.AlertTemplateUtil; -import org.apache.hertzbeat.alert.util.AlertUtil; -import org.apache.hertzbeat.common.constants.CommonConstants; -import org.apache.hertzbeat.common.entity.alerter.AlertDefine; -import org.apache.hertzbeat.common.entity.alerter.SingleAlert; -import org.apache.hertzbeat.common.queue.CommonDataQueue; -import java.util.HashMap; -import java.util.Map; - -/** - * Abstract base class for real-time alert calculators - * @param The type of data being processed for alerts - */ -@Slf4j -public abstract class AbstractRealTimeAlertCalculator { - - protected static final int CALCULATE_THREADS = 3; - - protected final AlerterWorkerPool workerPool; - protected final CommonDataQueue dataQueue; - protected final AlertDefineService alertDefineService; - protected final AlarmCommonReduce alarmCommonReduce; - protected final AlarmCacheManager alarmCacheManager; - protected final JexlExprCalculator jexlExprCalculator; - - /** - * Constructor with auto-start enabled - */ - protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, - JexlExprCalculator jexlExprCalculator) { - this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, true); - } - - /** - * Constructor with configurable auto-start - * - * @param workerPool The worker pool used for concurrent alert calculation. - * @param dataQueue The queue from which data is pulled and pushed. - * @param alertDefineService The service providing alert definition rules. - * @param singleAlertDao The DAO for fetching persisted alert states from storage. - * @param alarmCommonReduce The component responsible for reducing and sending alerts. - * @param alarmCacheManager The cache manager for managing alert states. - * @param start If true, the alert calculation threads will start automatically; - * set to false to disable thread start (useful for unit testing). - */ - protected AbstractRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, - JexlExprCalculator jexlExprCalculator, boolean start) { - this.workerPool = workerPool; - this.dataQueue = dataQueue; - this.alarmCommonReduce = alarmCommonReduce; - this.alertDefineService = alertDefineService; - this.alarmCacheManager = alarmCacheManager; - this.jexlExprCalculator = jexlExprCalculator; - if (start) { - startCalculate(); - } - } - - /** - * Start the alert calculation threads - */ - public void startCalculate() { - Runnable runnable = () -> { - while (!Thread.currentThread().isInterrupted()) { - try { - T data = pollData(); - if (data != null) { - calculate(data); - processDataAfterCalculation(data); - } - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - log.error("calculate alarm error: {}.", e.getMessage(), e); - } - } - }; - for (int i = 0; i < CALCULATE_THREADS; i++) { - workerPool.executeJob(runnable); - } - } - - /** - * Poll data from the queue - * @return The data to process - * @throws InterruptedException if interrupted while waiting - */ - protected abstract T pollData() throws InterruptedException; - - /** - * Process the data after alert calculation - * @param data The data that was processed - */ - protected abstract void processDataAfterCalculation(T data); - - /** - * Calculate alerts based on the data - * @param data The data to calculate alerts for - */ - protected abstract void calculate(T data); -} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java deleted file mode 100644 index 3ca21ef5541..00000000000 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculator.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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.hertzbeat.alert.calculate.realtime; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.hertzbeat.alert.AlerterWorkerPool; -import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; -import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; -import org.apache.hertzbeat.alert.dao.SingleAlertDao; -import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; -import org.apache.hertzbeat.alert.service.AlertDefineService; -import org.apache.hertzbeat.alert.util.AlertTemplateUtil; -import org.apache.hertzbeat.alert.util.AlertUtil; -import org.apache.hertzbeat.common.constants.CommonConstants; -import org.apache.hertzbeat.common.entity.alerter.AlertDefine; -import org.apache.hertzbeat.common.entity.alerter.SingleAlert; -import org.apache.hertzbeat.common.entity.log.LogEntry; -import org.apache.hertzbeat.common.queue.CommonDataQueue; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Calculate alarms based on the alarm definition rules and log data - */ -@Component -@Slf4j -public class LogRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator { - - public static final String LOG_PREFIX = "log"; - - @Autowired - public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, - JexlExprCalculator jexlExprCalculator) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator); - } - - /** - * Constructor for LogRealTimeAlertCalculator with a toggle to control whether to start alert calculation threads. - * - * @param workerPool The worker pool used for concurrent alert calculation. - * @param dataQueue The queue from which log data is pulled and pushed. - * @param alertDefineService The service providing alert definition rules. - * @param singleAlertDao The DAO for fetching persisted alert states from storage. - * @param alarmCommonReduce The component responsible for reducing and sending alerts. - * @param alarmCacheManager The cache manager for managing alert states. - * @param start If true, the alert calculation threads will start automatically; - * set to false to disable thread start (useful for unit testing). - */ - public LogRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, - AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, - AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, - JexlExprCalculator jexlExprCalculator, boolean start) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, start); - } - - @Override - protected LogEntry pollData() throws InterruptedException { - return dataQueue.pollLogEntry(); - } - - @Override - protected void processDataAfterCalculation(LogEntry logEntry) { - dataQueue.sendLogEntryToStorage(logEntry); - } - - @Override - protected void calculate(LogEntry logEntry) { - long currentTimeMilli = System.currentTimeMillis(); - List thresholds = this.alertDefineService.getLogRealTimeAlertDefines(); - - Map commonContext = new HashMap<>(8); - commonContext.put(LOG_PREFIX, logEntry); - - for (AlertDefine define : thresholds) { - if (define.getLabels() == null) { - define.setLabels(new HashMap<>(8)); - } - if (define.getAnnotations() == null) { - define.setAnnotations(new HashMap<>(8)); - } - final String expr = define.getExpr(); - if (StringUtils.isBlank(expr)) { - continue; - } - Map commonFingerPrints = new HashMap<>(8); - Map extendFingerPrints = new HashMap<>(8); - - // here use the alert name as finger, not care the alert name may be changed - commonFingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); - commonFingerPrints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); - commonFingerPrints.putAll(define.getLabels()); - addLogEntryToMap(logEntry, extendFingerPrints); - - Map annotations = new HashMap<>(); - annotations.putAll(define.getAnnotations()); - - try { - boolean match = jexlExprCalculator.execAlertExpression(commonContext, expr, false); - try { - if (match) { - afterThresholdRuleMatch(define.getId(), currentTimeMilli, commonFingerPrints, extendFingerPrints, commonContext, define, annotations); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } catch (Exception ignored) {} - } - } - - /** - * Handle alert after threshold rule match - */ - protected void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, - Map extendFingerPrints, - Map fieldValueMap, AlertDefine define, - Map annotations) { - // fingerprint for the padding cache - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); - fieldValueMap.putAll(define.getLabels()); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - Map alertLabels = new HashMap<>(8); - alertLabels.putAll(fingerPrints); - alertLabels.putAll(extendFingerPrints); - Map alertAnnotations = new HashMap<>(8); - if (annotations != null) { - alertAnnotations.putAll(annotations); - } - if (define.getAnnotations() != null) { - alertAnnotations.putAll(define.getAnnotations()); - } - // render var content in annotations - for (Map.Entry entry : alertAnnotations.entrySet()) { - entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); - } - SingleAlert newAlert = SingleAlert.builder() - .labels(alertLabels) - .annotations(alertAnnotations) - // render var content in content - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(defineId, fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - - - /** - * Add the content from LogEntry object (except timestamp) to commonFingerPrints - * - * @param logEntry log entry object - * @param context context - */ - private void addLogEntryToMap(LogEntry logEntry, Map context) { - // Add basic fields - if (logEntry.getSeverityNumber() != null) { - context.put("severityNumber", String.valueOf(logEntry.getSeverityNumber())); - } - if (logEntry.getSeverityText() != null) { - context.put("severityText", logEntry.getSeverityText()); - } - if (logEntry.getBody() != null) { - context.put("body", String.valueOf(logEntry.getBody())); - } - if (logEntry.getDroppedAttributesCount() != null) { - context.put("droppedAttributesCount", String.valueOf(logEntry.getDroppedAttributesCount())); - } - if (logEntry.getTraceId() != null) { - context.put("traceId", logEntry.getTraceId()); - } - if (logEntry.getSpanId() != null) { - context.put("spanId", logEntry.getSpanId()); - } - if (logEntry.getTraceFlags() != null) { - context.put("traceFlags", String.valueOf(logEntry.getTraceFlags())); - } - - // Add attributes - if (logEntry.getAttributes() != null && !logEntry.getAttributes().isEmpty()) { - for (Map.Entry entry : logEntry.getAttributes().entrySet()) { - if (entry.getValue() != null) { - context.put("attr_" + entry.getKey(), String.valueOf(entry.getValue())); - } - } - } - - // Add resource - if (logEntry.getResource() != null && !logEntry.getResource().isEmpty()) { - for (Map.Entry entry : logEntry.getResource().entrySet()) { - if (entry.getValue() != null) { - context.put("resource_" + entry.getKey(), String.valueOf(entry.getValue())); - } - } - } - - // Add instrumentationScope - if (logEntry.getInstrumentationScope() != null) { - LogEntry.InstrumentationScope scope = logEntry.getInstrumentationScope(); - if (scope.getName() != null) { - context.put("scope_name", scope.getName()); - } - if (scope.getVersion() != null) { - context.put("scope_version", scope.getVersion()); - } - if (scope.getDroppedAttributesCount() != null) { - context.put("scope_droppedAttributesCount", String.valueOf(scope.getDroppedAttributesCount())); - } - if (scope.getAttributes() != null && !scope.getAttributes().isEmpty()) { - for (Map.Entry entry : scope.getAttributes().entrySet()) { - if (entry.getValue() != null) { - context.put("scope_attr_" + entry.getKey(), String.valueOf(entry.getValue())); - } - } - } - } - } -} diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java index d70a31cc2d9..00f4a577c48 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java @@ -51,8 +51,10 @@ */ @Component @Slf4j -public class MetricsRealTimeAlertCalculator extends AbstractRealTimeAlertCalculator { - +public class MetricsRealTimeAlertCalculator { + + private static final int CALCULATE_THREADS = 3; + private static final String KEY_INSTANCE = "__instance__"; private static final String KEY_INSTANCE_NAME = "__instancename__"; private static final String KEY_INSTANCE_HOST = "__instancehost__"; @@ -72,12 +74,20 @@ public class MetricsRealTimeAlertCalculator extends AbstractRealTimeAlertCalcula private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\\s*\"(\\d+)\"\\)"); private static final Pattern METRICS_PATTERN = Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)"); + private final AlerterWorkerPool workerPool; + private final CommonDataQueue dataQueue; + private final AlertDefineService alertDefineService; + private final AlarmCommonReduce alarmCommonReduce; + private final AlarmCacheManager alarmCacheManager; + private final JexlExprCalculator jexlExprCalculator; + + @Autowired public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue, AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator); + this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, true); } /** @@ -96,20 +106,39 @@ public MetricsRealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQu AlertDefineService alertDefineService, SingleAlertDao singleAlertDao, AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, JexlExprCalculator jexlExprCalculator, boolean start) { - super(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, jexlExprCalculator, start); - } - - @Override - protected CollectRep.MetricsData pollData() throws InterruptedException { - return dataQueue.pollMetricsDataToAlerter(); + this.workerPool = workerPool; + this.dataQueue = dataQueue; + this.alarmCommonReduce = alarmCommonReduce; + this.alertDefineService = alertDefineService; + this.alarmCacheManager = alarmCacheManager; + this.jexlExprCalculator = jexlExprCalculator; + if (start) { + startCalculate(); + } } - @Override - protected void processDataAfterCalculation(CollectRep.MetricsData metricsData) { - dataQueue.sendMetricsDataToStorage(metricsData); + /** + * Start the alert calculation threads + */ + public void startCalculate() { + Runnable runnable = () -> { + while (!Thread.currentThread().isInterrupted()) { + try { + CollectRep.MetricsData metricsData = dataQueue.pollMetricsDataToAlerter(); + calculate(metricsData); + dataQueue.sendMetricsDataToStorage(metricsData); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("calculate alarm error: {}.", e.getMessage(), e); + } + } + }; + for (int i = 0; i < CALCULATE_THREADS; i++) { + workerPool.executeJob(runnable); + } } - @Override protected void calculate(CollectRep.MetricsData metricsData) { long currentTimeMilli = System.currentTimeMillis(); String instance = String.valueOf(metricsData.getId()); @@ -246,66 +275,6 @@ protected void calculate(CollectRep.MetricsData metricsData) { } } - /** - * Handle alert after threshold rule match - */ - private void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, - Map fieldValueMap, AlertDefine define, - Map annotations) { - // fingerprint for the padding cache - String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); - SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); - fieldValueMap.putAll(define.getLabels()); - int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); - if (existingAlert == null) { - // First time triggering alert, create new alert and set to pending status - Map alertLabels = new HashMap<>(8); - alertLabels.putAll(fingerPrints); - Map alertAnnotations = new HashMap<>(8); - if (annotations != null) { - alertAnnotations.putAll(annotations); - } - if (define.getAnnotations() != null) { - alertAnnotations.putAll(define.getAnnotations()); - } - // render var content in annotations - for (Map.Entry entry : alertAnnotations.entrySet()) { - entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); - } - SingleAlert newAlert = SingleAlert.builder() - .labels(alertLabels) - .annotations(alertAnnotations) - // render var content in content - .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) - .status(CommonConstants.ALERT_STATUS_PENDING) - .triggerTimes(1) - .startAt(currentTimeMilli) - .activeAt(currentTimeMilli) - .build(); - - // If required trigger times is 1, set to firing status directly - if (requiredTimes <= 1) { - newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); - } else { - // Otherwise put into pending queue first - alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); - } - } else { - // Update existing alert - existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); - existingAlert.setActiveAt(currentTimeMilli); - - // Check if required trigger times reached - if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { - // Reached trigger times threshold, change to firing status - alarmCacheManager.removePending(defineId, fingerprint); - existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); - alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); - } - } - } - /** * Filter alert definitions by app, metrics and instance * @@ -330,7 +299,7 @@ public List filterThresholdsByAppAndMetrics(List thres if (!appMatcher.find() || !app.equals(appMatcher.group(1))) { return false; } - + // Extract and check available - required if (priority != 0) { Matcher availableMatcher = AVAILABLE_PATTERN.matcher(expr); @@ -352,7 +321,7 @@ public List filterThresholdsByAppAndMetrics(List thres if (!instanceMatcher.find() && !labelMatcher.find()) { return true; } - + // Reset matcher to check all instances instanceMatcher.reset(); labelMatcher.reset(); @@ -391,6 +360,67 @@ private void handleRecoveredAlert(Long defineId, Map fingerprint alarmCacheManager.removePending(defineId, fingerprint); } + + /** + * Handle alert after threshold rule match + */ + private void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map fingerPrints, + Map fieldValueMap, AlertDefine define, + Map annotations) { + // fingerprint for the padding cache + String fingerprint = AlertUtil.calculateFingerprint(fingerPrints); + SingleAlert existingAlert = alarmCacheManager.getPending(defineId, fingerprint); + fieldValueMap.putAll(define.getLabels()); + int requiredTimes = define.getTimes() == null ? 1 : define.getTimes(); + if (existingAlert == null) { + // First time triggering alert, create new alert and set to pending status + Map alertLabels = new HashMap<>(8); + alertLabels.putAll(fingerPrints); + Map alertAnnotations = new HashMap<>(8); + if (annotations != null) { + alertAnnotations.putAll(annotations); + } + if (define.getAnnotations() != null) { + alertAnnotations.putAll(define.getAnnotations()); + } + // render var content in annotations + for (Map.Entry entry : alertAnnotations.entrySet()) { + entry.setValue(AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + SingleAlert newAlert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + // render var content in content + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_PENDING) + .triggerTimes(1) + .startAt(currentTimeMilli) + .activeAt(currentTimeMilli) + .build(); + + // If required trigger times is 1, set to firing status directly + if (requiredTimes <= 1) { + newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(newAlert.clone()); + } else { + // Otherwise put into pending queue first + alarmCacheManager.putPending(define.getId(), fingerprint, newAlert); + } + } else { + // Update existing alert + existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1); + existingAlert.setActiveAt(currentTimeMilli); + + // Check if required trigger times reached + if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) { + // Reached trigger times threshold, change to firing status + alarmCacheManager.removePending(defineId, fingerprint); + existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING); + alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone()); + } + } + } + private Set kvLabelsToKvStringSet(Map labels) { if (labels == null || labels.isEmpty()) { return Collections.singleton(""); diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/WindowedLogRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/WindowedLogRealTimeAlertCalculator.java new file mode 100644 index 00000000000..bceed5e3c0a --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/WindowedLogRealTimeAlertCalculator.java @@ -0,0 +1,127 @@ +/* + * 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.hertzbeat.alert.calculate.realtime; + +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.alert.calculate.realtime.window.LogWorker; +import org.apache.hertzbeat.alert.calculate.realtime.window.TimeService; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.springframework.stereotype.Component; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * WindowedLogRealTimeAlertCalculator - Single entry point for log stream processing + * Responsible for: + * 1. Reading from original log stream + * 2. Extracting event timestamps + * 3. Maintaining maxTimestamp for watermark generation + * 4. Distributing logs to workers + */ +@Component +@Slf4j +public class WindowedLogRealTimeAlertCalculator implements Runnable { + + private static final int CALCULATE_THREADS = 3; + + private final CommonDataQueue dataQueue; + private final TimeService timeService; + private ThreadPoolExecutor dispatcherExecutor; + private final LogWorker logWorker; + + public WindowedLogRealTimeAlertCalculator(CommonDataQueue dataQueue, TimeService timeService, LogWorker logWorker) { + this.dataQueue = dataQueue; + this.timeService = timeService; + this.logWorker = logWorker; + } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + LogEntry logEntry = dataQueue.pollLogEntry(); + if (logEntry != null) { + processLogEntry(logEntry); + dataQueue.sendLogEntryToStorage(logEntry); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + log.error("Error in log dispatch loop: {}", e.getMessage(), e); + } + } + } + + private void processLogEntry(LogEntry logEntry) { + // Extract event timestamp + long eventTimestamp = extractEventTimestamp(logEntry); + + // Update max timestamp + timeService.updateMaxTimestamp(eventTimestamp); + logWorker.reduceAndSendLogTask(logEntry); + } + + private long extractEventTimestamp(LogEntry logEntry) { + if (logEntry.getTimeUnixNano() != null && logEntry.getTimeUnixNano() != 0) { + return logEntry.getTimeUnixNano() / 1_000_000; // Convert to milliseconds + } + if (logEntry.getObservedTimeUnixNano() != null && logEntry.getObservedTimeUnixNano() != 0) { + return logEntry.getObservedTimeUnixNano() / 1_000_000; // Convert to milliseconds + } + return System.currentTimeMillis(); + } + + @PostConstruct + public void start() { + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setUncaughtExceptionHandler((thread, throwable) -> { + log.error("Alerter workerExecutor has uncaughtException."); + log.error(throwable.getMessage(), throwable); + }) + .setDaemon(true) + .setNameFormat("log-dispatcher-%d") + .build(); + // Create dispatcher thread executor + this.dispatcherExecutor = new ThreadPoolExecutor( + CALCULATE_THREADS, + CALCULATE_THREADS, + 10, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + threadFactory, + new ThreadPoolExecutor.AbortPolicy() + ); + for (int i = 0; i < CALCULATE_THREADS; i++) { + dispatcherExecutor.execute(this); + } + } + + @PreDestroy + public void stop() { + if (dispatcherExecutor != null) { + dispatcherExecutor.shutdownNow(); + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/AlarmEvaluator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/AlarmEvaluator.java new file mode 100644 index 00000000000..180855dea75 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/AlarmEvaluator.java @@ -0,0 +1,320 @@ +/* + * 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.hertzbeat.alert.calculate.realtime.window; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; +import org.springframework.stereotype.Component; + +import org.apache.hertzbeat.alert.util.AlertTemplateUtil; +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.alerter.SingleAlert; +import org.apache.hertzbeat.common.entity.log.LogEntry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Alarm Evaluator - Final alarm logic trigger + * Responsible for: + * 1. Receiving closed window data from WindowAggregator + * 2. Applying alert logic (count threshold, alert mode) + * 3. Generating individual alerts or alert groups + * 4. Sending alerts to AlarmCommonReduce + */ +@Component +@Slf4j +public class AlarmEvaluator { + + private static final String ALERT_MODE_LABEL = "alert_mode"; + private static final String WINDOW_START_TIME = "window_start_time"; + private static final String WINDOW_END_TIME = "window_end_time"; + private static final String MATCHING_LOGS_COUNT = "matching_logs_count"; + private static final String ALERT_MODE_GROUP = "group"; + private static final String ALERT_MODE_INDIVIDUAL = "individual"; + + private final AlarmCommonReduce alarmCommonReduce; + private ThreadPoolExecutor workerExecutor; + + public AlarmEvaluator(AlarmCommonReduce alarmCommonReduce) { + this.alarmCommonReduce = alarmCommonReduce; + initAlarmEvaluator(); + } + + public void initAlarmEvaluator() { + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setUncaughtExceptionHandler((thread, throwable) -> { + log.error("alerter-reduce-worker has uncaughtException."); + log.error(throwable.getMessage(), throwable); + }) + .setDaemon(true) + .setNameFormat("alerter-reduce-worker-%d") + .build(); + workerExecutor = new ThreadPoolExecutor(2, + 10, + 10, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + threadFactory, + new ThreadPoolExecutor.AbortPolicy()); + } + + public void sendAndProcessWindowData(WindowAggregator.WindowData windowData) { + workerExecutor.execute(processWindowData(windowData)); + } + + private Runnable processWindowData(WindowAggregator.WindowData windowData) { + return () -> { + AlertDefine alertDefine = windowData.getAlertDefine(); + List matchingLogs = windowData.getMatchingLogs(); + + if (matchingLogs.isEmpty()) { + return; + } + + // Check if count threshold is met + int requiredTimes = alertDefine.getTimes() != null ? alertDefine.getTimes() : 1; + if (matchingLogs.size() < requiredTimes) { + log.debug("Window {} has {} matching logs, but requires {} times", + windowData.getWindowKey(), matchingLogs.size(), requiredTimes); + return; + } + + // Determine alert mode from configuration + String alertMode = getAlertMode(alertDefine); + + long currentTime = System.currentTimeMillis(); + + switch (alertMode) { + case ALERT_MODE_INDIVIDUAL: + // Generate individual alerts for each matching log + for (MatchingLogEvent matchingLog : matchingLogs) { + generateIndividualAlert(matchingLog, currentTime); + } + break; + + case ALERT_MODE_GROUP: + // Generate a single alert group for all matching logs + generateGroupAlert(windowData, matchingLogs, currentTime); + break; + default: + log.warn("Unknown alert mode for define {}: {}", alertDefine.getName(), alertMode); + } + }; + } + + private void generateIndividualAlert(MatchingLogEvent matchingLog, long currentTime) { + AlertDefine define = matchingLog.getAlertDefine(); + LogEntry logEntry = matchingLog.getLogEntry(); + + Map alertLabels = new HashMap<>(8); + + // Create fingerprints for group alert + Map commonFingerPrints = createCommonFingerprints(define); + alertLabels.putAll(commonFingerPrints); + // add the log data to commonFingerPrints + addLogEntryToMap(logEntry, alertLabels); + + Map fieldValueMap = createFieldValueMap(logEntry, define); + Map alertAnnotations = createAlertAnnotations(define, fieldValueMap); + // Create and send group alert + SingleAlert alert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_FIRING) + .triggerTimes(1) + .startAt(currentTime) + .activeAt(currentTime) + .build(); + + alarmCommonReduce.reduceAndSendAlarm(alert.clone()); + + log.debug("Generated individual alert for define: {}", define.getName()); + } + + private void generateGroupAlert(WindowAggregator.WindowData windowData, + List matchingLogs, long currentTime) { + + List alerts = new ArrayList<>(matchingLogs.size()); + AlertDefine define = windowData.getAlertDefine(); + + // Create fingerprints for group alert + Map commonFingerPrints = createCommonFingerprints(define); + + // Add window information to fingerprints + commonFingerPrints.put(WINDOW_START_TIME, String.valueOf(windowData.getStartTime())); + commonFingerPrints.put(WINDOW_END_TIME, String.valueOf(windowData.getEndTime())); + commonFingerPrints.put(ALERT_MODE_LABEL, ALERT_MODE_GROUP); + commonFingerPrints.put(MATCHING_LOGS_COUNT, String.valueOf(matchingLogs.size())); + + for (MatchingLogEvent event: matchingLogs) { + LogEntry logEntry = event.getLogEntry(); + + Map alertLabels = new HashMap<>(8); + + alertLabels.putAll(commonFingerPrints); + // add the log data to commonFingerPrints + addLogEntryToMap(logEntry, alertLabels); + + Map fieldValueMap = createFieldValueMap(logEntry, define); + Map alertAnnotations = createAlertAnnotations(define, fieldValueMap); + // Create and send group alert + SingleAlert alert = SingleAlert.builder() + .labels(alertLabels) + .annotations(alertAnnotations) + .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) + .status(CommonConstants.ALERT_STATUS_FIRING) + .triggerTimes(matchingLogs.size()) + .startAt(currentTime) + .activeAt(currentTime) + .build(); + alerts.add(alert.clone()); + } + alarmCommonReduce.reduceAndSendAlarmGroup(commonFingerPrints, alerts); + + log.debug("Generated group alert for define: {} with {} matching logs", + define.getName(), matchingLogs.size()); + } + + private String getAlertMode(AlertDefine alertDefine) { + String mode = null; + if (alertDefine.getLabels() != null) { + mode = alertDefine.getLabels().get(ALERT_MODE_LABEL); + } + if (mode == null || mode.isEmpty()) { + return ALERT_MODE_GROUP; // Default to group mode if not specified + } else { + return mode; + } + } + + private Map createCommonFingerprints(AlertDefine define) { + Map fingerprints = new HashMap<>(8); + fingerprints.put(CommonConstants.LABEL_ALERT_NAME, define.getName()); + fingerprints.put(CommonConstants.LABEL_DEFINE_ID, String.valueOf(define.getId())); + + if (define.getLabels() != null) { + fingerprints.putAll(define.getLabels()); + } + + return fingerprints; + } + + private Map createFieldValueMap(LogEntry logEntry, AlertDefine define) { + Map fieldValueMap = new HashMap<>(8); + fieldValueMap.put("log", logEntry); + + if (define.getLabels() != null) { + fieldValueMap.putAll(define.getLabels()); + } + + return fieldValueMap; + } + + private Map createAlertAnnotations(AlertDefine define, Map fieldValueMap) { + Map annotations = new HashMap<>(8); + + if (define.getAnnotations() != null) { + for (Map.Entry entry : define.getAnnotations().entrySet()) { + annotations.put(entry.getKey(), + AlertTemplateUtil.render(entry.getValue(), fieldValueMap)); + } + } + + return annotations; + } + + /** + * Add the content from LogEntry object (except timestamp) to commonFingerPrints + * + * @param logEntry log entry object + * @param context context + */ + private void addLogEntryToMap(LogEntry logEntry, Map context) { + // Add basic fields + if (logEntry.getSeverityNumber() != null) { + context.put("severityNumber", String.valueOf(logEntry.getSeverityNumber())); + } + if (logEntry.getSeverityText() != null) { + context.put("severityText", logEntry.getSeverityText()); + } + if (logEntry.getBody() != null) { + context.put("body", String.valueOf(logEntry.getBody())); + } + if (logEntry.getDroppedAttributesCount() != null) { + context.put("droppedAttributesCount", String.valueOf(logEntry.getDroppedAttributesCount())); + } + if (logEntry.getTraceId() != null) { + context.put("traceId", logEntry.getTraceId()); + } + if (logEntry.getSpanId() != null) { + context.put("spanId", logEntry.getSpanId()); + } + if (logEntry.getTraceFlags() != null) { + context.put("traceFlags", String.valueOf(logEntry.getTraceFlags())); + } + + // Add attributes + if (logEntry.getAttributes() != null && !logEntry.getAttributes().isEmpty()) { + for (Map.Entry entry : logEntry.getAttributes().entrySet()) { + if (entry.getValue() != null) { + context.put("attr_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + + // Add resource + if (logEntry.getResource() != null && !logEntry.getResource().isEmpty()) { + for (Map.Entry entry : logEntry.getResource().entrySet()) { + if (entry.getValue() != null) { + context.put("resource_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + + // Add instrumentationScope + if (logEntry.getInstrumentationScope() != null) { + LogEntry.InstrumentationScope scope = logEntry.getInstrumentationScope(); + if (scope.getName() != null) { + context.put("scope_name", scope.getName()); + } + if (scope.getVersion() != null) { + context.put("scope_version", scope.getVersion()); + } + if (scope.getDroppedAttributesCount() != null) { + context.put("scope_droppedAttributesCount", String.valueOf(scope.getDroppedAttributesCount())); + } + if (scope.getAttributes() != null && !scope.getAttributes().isEmpty()) { + for (Map.Entry entry : scope.getAttributes().entrySet()) { + if (entry.getValue() != null) { + context.put("scope_attr_" + entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/LogWorker.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/LogWorker.java new file mode 100644 index 00000000000..16f25c62b41 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/LogWorker.java @@ -0,0 +1,123 @@ +/* + * 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.hertzbeat.alert.calculate.realtime.window; + +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.alert.AlerterWorkerPool; +import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; +import org.apache.hertzbeat.alert.service.AlertDefineService; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * LogWorker Responsible for: + * 1. Receiving logs from WindowedLogRealTimeAlertCalculator + * 2. Evaluating alert expressions against logs + * 3. Sending matching logs to WindowAggregator + */ +@Slf4j +@Component +public class LogWorker { + + private static final String LOG_PREFIX = "log"; + + private final AlertDefineService alertDefineService; + private final JexlExprCalculator jexlExprCalculator; + private final WindowAggregator windowAggregator; + private final AlerterWorkerPool workerPool; + + @Autowired + public LogWorker(AlertDefineService alertDefineService, + JexlExprCalculator jexlExprCalculator, WindowAggregator windowAggregator, + AlerterWorkerPool workerPool) { + this.alertDefineService = alertDefineService; + this.jexlExprCalculator = jexlExprCalculator; + this.windowAggregator = windowAggregator; + this.workerPool = workerPool; + } + + public void reduceAndSendLogTask(LogEntry logEntry) { + workerPool.executeLogJob(reduceLogTask(logEntry)); + } + + public Runnable reduceLogTask(LogEntry logEntry) { + return () -> { + try { + processLogEntry(logEntry); + } catch (Exception e) { + log.error("Error processing log entry in worker: {}", e.getMessage(), e); + } + }; + } + + private void processLogEntry(LogEntry logEntry) { + // Get all log alert definitions + List alertDefines = alertDefineService.getLogRealTimeAlertDefines(); + + // Create context for expression evaluation + Map context = new HashMap<>(8); + context.put(LOG_PREFIX, logEntry); + + // Process each alert definition + for (AlertDefine define : alertDefines) { + if (define.getExpr() == null || define.getExpr().trim().isEmpty()) { + continue; + } + + try { + // Evaluate alert expression + boolean match = jexlExprCalculator.execAlertExpression(context, define.getExpr(), false); + + if (match) { + // Create matching log event and send to WindowAggregator + MatchingLogEvent event = createMatchingLogEvent(logEntry, define); + windowAggregator.addMatchingLog(event); + } + } catch (Exception e) { + log.warn("Error evaluating expression for alert define {}: {}", define.getName(), e.getMessage()); + } + } + } + + private MatchingLogEvent createMatchingLogEvent(LogEntry logEntry, AlertDefine define) { + long eventTimestamp = extractEventTimestamp(logEntry); + + return MatchingLogEvent.builder() + .logEntry(logEntry) + .alertDefine(define) + .eventTimestamp(eventTimestamp) + .workerTimestamp(System.currentTimeMillis()) + .build(); + } + + private long extractEventTimestamp(LogEntry logEntry) { + if (logEntry.getTimeUnixNano() != null && logEntry.getTimeUnixNano() != 0) { + return logEntry.getTimeUnixNano() / 1_000_000; // Convert to milliseconds + } + if (logEntry.getObservedTimeUnixNano() != null && logEntry.getObservedTimeUnixNano() != 0) { + return logEntry.getObservedTimeUnixNano() / 1_000_000; // Convert to milliseconds + } + return System.currentTimeMillis(); + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/MatchingLogEvent.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/MatchingLogEvent.java new file mode 100644 index 00000000000..34f9adda25e --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/MatchingLogEvent.java @@ -0,0 +1,40 @@ +/* + * 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.hertzbeat.alert.calculate.realtime.window; + +import lombok.Builder; +import lombok.Data; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.apache.hertzbeat.common.entity.log.LogEntry; + +/** + * Represents a log entry that matched an alert expression + * Used for communication between LogWorker and WindowAggregator + */ +@Data +@Builder +public class MatchingLogEvent { + + private LogEntry logEntry; + + private AlertDefine alertDefine; + + private long eventTimestamp; + + private long workerTimestamp; +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/TimeService.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/TimeService.java new file mode 100644 index 00000000000..2dbe6998f7d --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/TimeService.java @@ -0,0 +1,190 @@ +/* + * 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.hertzbeat.alert.calculate.realtime.window; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Watermark Generator responsible for: + * 1. Receiving maxTimestamp updates from WindowedLogRealTimeAlertCalculator + * 2. Calculating watermarks based on configurable delay + * 3. Broadcasting watermarks to all subscribers (WindowAggregator) + */ +@Component +@Slf4j +public class TimeService { + + private static final long DEFAULT_WATERMARK_DELAY_MS = 30_000; // 30 seconds + private static final long WATERMARK_BROADCAST_INTERVAL_MS = 5_000; // 5 seconds + + private final AtomicLong maxTimestamp = new AtomicLong(0); + private final AtomicLong currentWatermark = new AtomicLong(0); + private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); + private ScheduledExecutorService scheduler; + + public TimeService(List initialListeners) { + listeners.addAll(initialListeners); + } + + @PostConstruct + public void start() { + // Create internal scheduled executor + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setUncaughtExceptionHandler((thread, throwable) -> { + log.error("TimeService scheduler has uncaughtException."); + log.error(throwable.getMessage(), throwable); + }) + .setDaemon(true) + .setNameFormat("timeservice-scheduler-%d") + .build(); + + this.scheduler = Executors.newSingleThreadScheduledExecutor(threadFactory); + + // Start watermark broadcast scheduler + scheduler.scheduleAtFixedRate( + this::broadcastWatermark, + 0, + WATERMARK_BROADCAST_INTERVAL_MS, + TimeUnit.MILLISECONDS + ); + + log.info("TimeService started with watermark delay: {}ms", DEFAULT_WATERMARK_DELAY_MS); + } + + @PreDestroy + public void stop() { + if (scheduler != null && !scheduler.isShutdown()) { + log.info("Shutting down TimeService scheduler..."); + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) { + log.warn("TimeService scheduler did not terminate within 10 seconds, forcing shutdown"); + scheduler.shutdownNow(); + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + log.error("TimeService scheduler did not terminate"); + } + } + } catch (InterruptedException e) { + log.warn("Interrupted while waiting for TimeService scheduler to terminate"); + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + log.info("TimeService stopped"); + } + + /** + * Update max timestamp from WindowedLogRealTimeAlertCalculator + */ + public void updateMaxTimestamp(long timestamp) { + long currentMax = maxTimestamp.get(); + if (timestamp > currentMax) { + maxTimestamp.compareAndSet(currentMax, timestamp); + } + } + + /** + * Add watermark listener + */ + public void addWatermarkListener(WatermarkListener listener) { + listeners.add(listener); + } + + /** + * Remove watermark listener + */ + public void removeWatermarkListener(WatermarkListener listener) { + listeners.remove(listener); + } + + /** + * Get current watermark + */ + public long getCurrentWatermark() { + return currentWatermark.get(); + } + + /** + * Calculate and broadcast watermark + */ + private void broadcastWatermark() { + + try { + long maxTs = maxTimestamp.get(); + if (maxTs < 0) { + return; + } + // Calculate watermark: maxTimestamp - delay + long newWatermark = maxTs - DEFAULT_WATERMARK_DELAY_MS; + long currentWm = currentWatermark.get(); + + // Only advance watermark (monotonic property) + if (newWatermark <= currentWm) { + return; + } + if (currentWatermark.compareAndSet(currentWm, newWatermark)) { + // Broadcast to all listeners + Watermark watermark = new Watermark(newWatermark); + for (WatermarkListener listener : listeners) { + try { + listener.onWatermark(watermark); + } catch (Exception e) { + log.error("Error notifying watermark listener: {}", e.getMessage(), e); + } + } + log.debug("Broadcast watermark: {} (maxTimestamp: {})", newWatermark, maxTs); + } + } catch (Exception e) { + log.error("Error in watermark broadcast: {}", e.getMessage(), e); + } + } + + /** + * Watermark data class + */ + @Data + @AllArgsConstructor + @Getter + public static class Watermark { + private final long timestamp; + } + + /** + * Interface for watermark listeners + */ + public interface WatermarkListener { + void onWatermark(Watermark watermark); + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java new file mode 100644 index 00000000000..b9b9ede6340 --- /dev/null +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java @@ -0,0 +1,252 @@ +/* + * 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.hertzbeat.alert.calculate.realtime.window; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.alerter.AlertDefine; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + + +/** + * Window Aggregator Responsible for: + * 1. Managing window data structure for all active windows + * 2. Receiving matching logs from Workers + * 3. Receiving watermarks from TimeService + * 4. Sending closed windows to AlarmEvaluator + */ +@Component +@Slf4j +public class WindowAggregator implements TimeService.WatermarkListener, Runnable { + + private static final long DEFAULT_WINDOW_SIZE_MS = 1 * 60 * 1000; // 1 minutes + + private final AlarmEvaluator alarmEvaluator; + private final BlockingQueue eventQueue = new LinkedBlockingQueue<>(); + private final Map activeWindows = new HashMap<>(); + private final Object windowLock = new Object(); + + private ExecutorService aggregatorExecutor; + + public WindowAggregator(AlarmEvaluator alarmEvaluator) { + this.alarmEvaluator = alarmEvaluator; + } + + public void addMatchingLog(MatchingLogEvent event) { + try { + eventQueue.put(event); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("Interrupted while adding matching log to aggregator"); + } + } + + @Override + public void onWatermark(TimeService.Watermark watermark) { + List closedWindows; + + // 缩小锁范围:只保护Map操作 + synchronized (windowLock) { + closedWindows = new ArrayList<>(); + + // Find windows that should be closed based on watermark + Iterator> iterator = activeWindows.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + WindowData windowData = entry.getValue(); + + // Close window if its end time <= watermark timestamp + if (windowData.getEndTime() <= watermark.getTimestamp()) { + closedWindows.add(windowData); + iterator.remove(); + + log.debug("Closing window: {} with {} matching logs", + entry.getKey(), windowData.getMatchingLogs().size()); + } + } + } + + for (WindowData windowData : closedWindows) { + alarmEvaluator.sendAndProcessWindowData(windowData); + } + } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + MatchingLogEvent event = eventQueue.take(); + processMatchingLogEvent(event); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + log.error("Error processing matching log event: {}", e.getMessage(), e); + } + } + } + + private void processMatchingLogEvent(MatchingLogEvent event) { + + // Determine window size from alert define (if specified) or use default + long windowSizeMs = getWindowSize(event.getAlertDefine()); + + // Calculate window boundaries + long eventTime = event.getEventTimestamp(); + long windowStart = (eventTime / windowSizeMs) * windowSizeMs; + long windowEnd = windowStart + windowSizeMs; + + // Create window key + WindowKey windowKey = new WindowKey( + event.getAlertDefine().getId(), + windowStart, + windowEnd + ); + synchronized (windowLock) { + // Get or create window data + WindowData windowData = activeWindows.computeIfAbsent(windowKey, + key -> new WindowData(key, event.getAlertDefine())); + // Add matching log to window + windowData.addMatchingLog(event); + log.debug("Added matching log to window: {} (total logs: {})", + windowKey, windowData.getMatchingLogs().size()); + } + } + + private long getWindowSize(AlertDefine alertDefine) { + // Check if alert define has custom window size configuration + if (alertDefine.getPeriod() != null) { + return alertDefine.getPeriod() * 1000; // Convert seconds to milliseconds + } + log.info("Using default window size of {} ms for alert define: {}", + DEFAULT_WINDOW_SIZE_MS, alertDefine.getName()); + return DEFAULT_WINDOW_SIZE_MS; + } + + @PostConstruct + public void start() { + // Create internal executor + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setUncaughtExceptionHandler((thread, throwable) -> { + log.error("WindowAggregator executor has uncaughtException."); + log.error(throwable.getMessage(), throwable); + }) + .setDaemon(true) + .setNameFormat("window-aggregator-%d") + .build(); + + aggregatorExecutor = Executors.newSingleThreadExecutor(threadFactory); + + // Submit aggregation task + aggregatorExecutor.submit(this); + + log.info("WindowAggregator started"); + } + + @PreDestroy + public void stop() { + if (aggregatorExecutor != null && !aggregatorExecutor.isShutdown()) { + log.info("Shutting down WindowAggregator executor..."); + aggregatorExecutor.shutdown(); + try { + if (!aggregatorExecutor.awaitTermination(10, TimeUnit.SECONDS)) { + log.warn("WindowAggregator executor did not terminate within 10 seconds, forcing shutdown"); + aggregatorExecutor.shutdownNow(); + if (!aggregatorExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + log.error("WindowAggregator executor did not terminate"); + } + } + } catch (InterruptedException e) { + log.warn("Interrupted while waiting for WindowAggregator executor to terminate"); + aggregatorExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + log.info("WindowAggregator stopped"); + } + + /** + * Window key for identifying unique windows + */ + @Data + @Getter + @AllArgsConstructor + public static class WindowKey { + private final long alertDefineId; + private final long startTime; + private final long endTime; + } + + /** + * Window data container + */ + public static class WindowData { + @Getter + private final WindowKey windowKey; + @Getter + private final AlertDefine alertDefine; + private final List matchingLogs = new ArrayList<>(); + @Getter + private final long createdTime; + + public WindowData(WindowKey windowKey, AlertDefine alertDefine) { + this.windowKey = windowKey; + this.alertDefine = alertDefine; + this.createdTime = System.currentTimeMillis(); + } + + public void addMatchingLog(MatchingLogEvent event) { + matchingLogs.add(event); + } + + public List getMatchingLogs() { + return new ArrayList<>(matchingLogs); + } + + /** + * Get start time of the window + * @return start time + */ + public long getStartTime() { return windowKey.getStartTime(); } + + /** + * Get end time of the window + * @return end time + */ + public long getEndTime() { return windowKey.getEndTime(); } + } +} \ No newline at end of file diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmCommonReduce.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmCommonReduce.java index bbaf7b12984..0ef336921ac 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmCommonReduce.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmCommonReduce.java @@ -18,6 +18,8 @@ package org.apache.hertzbeat.alert.reduce; import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; @@ -66,6 +68,22 @@ private void initWorkExecutor() { public void reduceAndSendAlarm(SingleAlert alert) { workerExecutor.execute(reduceAlarmTask(alert)); } + + public void reduceAndSendAlarmGroup(Map groupLabels, List alerts) { + workerExecutor.execute(() -> { + try { + // Generate alert fingerprint + for (SingleAlert alert : alerts) { + String fingerprint = generateAlertFingerprint(alert.getLabels()); + alert.setFingerprint(fingerprint); + } + // Process the group alert + alarmGroupReduce.processGroupAlert(groupLabels, alerts); + } catch (Exception e) { + log.error("Reduce alarm group failed: {}", e.getMessage()); + } + }); + } Runnable reduceAlarmTask(SingleAlert alert) { return () -> { diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduce.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduce.java index 112c83291a1..94c6cb873e2 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduce.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduce.java @@ -159,7 +159,20 @@ public void processGroupAlert(SingleAlert alert) { sendSingleAlert(alert); } } - + + public void processGroupAlert(Map groupLabels, List alertList) { + GroupAlert groupAlert = GroupAlert.builder() + .groupKey(generateGroupKey(groupLabels)) + .groupLabels(groupLabels) + .commonLabels(extractCommonLabels(alertList)) + .commonAnnotations(extractCommonAnnotations(alertList)) + .alerts(alertList) + .status(CommonConstants.ALERT_STATUS_FIRING) + .build(); + + alarmInhibitReduce.inhibitAlarm(groupAlert); + } + private boolean hasRequiredLabels(Map labels, List requiredLabels) { return requiredLabels.stream().allMatch(labels::containsKey); } diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java deleted file mode 100644 index 886951c84cf..00000000000 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/LogRealTimeAlertCalculatorTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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.hertzbeat.alert.calculate.realtime; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.apache.hertzbeat.alert.AlerterWorkerPool; -import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; -import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; -import org.apache.hertzbeat.alert.dao.SingleAlertDao; -import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; -import org.apache.hertzbeat.alert.service.AlertDefineService; -import org.apache.hertzbeat.common.entity.log.LogEntry; -import org.apache.hertzbeat.common.queue.CommonDataQueue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.util.HashMap; -import java.util.Map; - -class LogRealTimeAlertCalculatorTest { - - private LogRealTimeAlertCalculator calculator; - - @BeforeEach - void setUp() { - AlerterWorkerPool mockPool = Mockito.mock(AlerterWorkerPool.class); - CommonDataQueue mockQueue = Mockito.mock(CommonDataQueue.class); - AlertDefineService mockAlertDefineService = Mockito.mock(AlertDefineService.class); - SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class); - AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class); - AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class); - JexlExprCalculator mockExprCalculator = Mockito.mock(JexlExprCalculator.class); - - calculator = new LogRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, - mockDao, mockReduce, alarmCacheManager, mockExprCalculator,false); - } - - @Test - void testExecAlertExpression_SeverityNumberAndSeverityText_ShouldMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "ERROR", "System error occurred", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match when severityNumber is 2 and severityText contains 'ERROR'"); - } - - @Test - void testExecAlertExpression_SeverityNumberMatches_SeverityTextNotMatch_ShouldNotMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "INFO", "Information message", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertFalse(result, "Expression should not match when severityText doesn't contain 'ERROR'"); - } - - @Test - void testExecAlertExpression_SeverityNumberNotMatch_SeverityTextMatches_ShouldNotMatch() { - // Arrange - LogEntry logEntry = createLogEntry(1, "ERROR", "System error occurred", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 2) && (contains(log.severityText, \"ERROR\"))"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertFalse(result, "Expression should not match when severityNumber is not 2"); - } - - @Test - void testExecAlertExpression_CaseInsensitiveContains_ShouldMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "error", "System error occurred", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 2) && (contains(log.severityText.toLowerCase(), \"error\"))"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match with case-insensitive comparison"); - } - - @Test - void testExecAlertExpression_SeverityGreaterThan_ShouldMatch() { - // Arrange - LogEntry logEntry = createLogEntry(5, "CRITICAL", "Critical error", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "log.severityNumber > 2"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match when severityNumber is greater than 2"); - } - - @Test - void testExecAlertExpression_BodyContains_ShouldMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "ERROR", "Database connection failed", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "contains(log.body, \"Database\")"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match when body contains 'Database'"); - } - - @Test - void testExecAlertExpression_AttributesCheck_ShouldMatch() { - // Arrange - Map attributes = new HashMap<>(); - attributes.put("service", "user-service"); - attributes.put("environment", "production"); - - LogEntry logEntry = createLogEntry(3, "WARN", "Service warning", attributes); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "log.attributes.service == \"user-service\""; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match when attributes contain expected service"); - } - - @Test - void testExecAlertExpression_ComplexExpression_ShouldMatch() { - // Arrange - Map attributes = new HashMap<>(); - attributes.put("service", "user-service"); - attributes.put("environment", "production"); - - LogEntry logEntry = createLogEntry(4, "ERROR", "Authentication failed", attributes); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber >= 3) && (contains(log.severityText, \"ERROR\")) && (log.attributes.environment == \"production\")"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Complex expression should match all conditions"); - } - - @Test - void testExecAlertExpression_OrExpression_ShouldMatch() { - // Arrange - LogEntry logEntry = createLogEntry(1, "DEBUG", "Debug information", null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 5) || (contains(log.body, \"Debug\"))"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "OR expression should match when at least one condition is true"); - } - - @Test - void testExecAlertExpression_TimestampCheck_ShouldMatch() { - // Arrange - long currentTime = System.currentTimeMillis(); - long nanoTime = currentTime * 1_000_000; // Convert to nanoseconds - - LogEntry logEntry = LogEntry.builder() - .timeUnixNano(nanoTime) - .severityNumber(2) - .severityText("ERROR") - .body("Timestamp test") - .build(); - - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "log.timeUnixNano > 0"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Expression should match when timestamp is greater than 0"); - } - - @Test - void testExecAlertExpression_NullBody_ShouldNotMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "ERROR", null, null); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "contains(log.body, \"error\")"; - - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - // Act & Assert - assertFalse(result, "Expression should not match when body is null"); - } - - @Test - void testExecAlertExpression_EmptyAttributes_ShouldNotMatch() { - // Arrange - LogEntry logEntry = createLogEntry(2, "ERROR", "Test message", new HashMap<>()); - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "log.attributes.service == \"user-service\""; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertFalse(result, "Expression should not match when attributes is empty"); - } - - @Test - void testExecAlertExpression_MultipleLogFields_ShouldMatch() { - // Arrange - Map attributes = new HashMap<>(); - attributes.put("level", "error"); - attributes.put("component", "authentication"); - - LogEntry logEntry = LogEntry.builder() - .severityNumber(4) - .severityText("ERROR") - .body("Login failed for user admin") - .attributes(attributes) - .traceId("abc123") - .spanId("def456") - .build(); - - Map fieldValueMap = new HashMap<>(); - fieldValueMap.put("log", logEntry); - - String expr = "(log.severityNumber == 4) && " - + "(contains(log.body, \"Login failed\")) && " - + "(log.attributes.component == \"authentication\") && " - + "(log.traceId == \"abc123\")"; - - // Act - boolean result = calculator.jexlExprCalculator.execAlertExpression(fieldValueMap, expr, false); - - // Assert - assertTrue(result, "Complex expression with multiple log fields should match"); - } - - private LogEntry createLogEntry(Integer severityNumber, String severityText, Object body, Map attributes) { - return LogEntry.builder() - .severityNumber(severityNumber) - .severityText(severityText) - .body(body) - .attributes(attributes) - .timeUnixNano(System.currentTimeMillis() * 1_000_000L) // Current time in nanoseconds - .build(); - } -} diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java index bc121349bad..6e087db6e27 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/alerter/AlertDefine.java @@ -73,7 +73,7 @@ public class AlertDefine { @Column(length = 2048) private String expr; - @Schema(title = "Execution Period (seconds) - For periodic rules", example = "300") + @Schema(title = "Execution Period/ Window Size (seconds) - For periodic rules/ For log realtime", example = "300") private Integer period; @Schema(title = "Alarm Trigger Times.The alarm is triggered only after the required number of times is reached", diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 9cddd4aadaf..31e9d0ebed4 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -820,6 +820,24 @@ {{ 'common.time.unit.second' | i18n }} + + + {{ 'alert.setting.window' | i18n }} + + + + + {{ 'common.time.unit.second' | i18n }} + + {{ 'alert.severity' | i18n }} @@ -839,7 +857,25 @@ - + + + {{ 'alert.mode' | i18n }} + + + + + + + + + {{ 'alert.setting.times' | i18n }} @@ -847,7 +883,7 @@ - + {{ 'alert.setting.template' | i18n }} diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index 142a2bc0874..df655ac83d9 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -326,6 +326,7 @@ export class AlertSettingComponent implements OnInit { this.alertType = type; this.define = new AlertDefine(); this.severity = ''; + this.alertMode = ''; this.userExpr = ''; this.selectedMonitorIds = new Set(); this.selectedLabels = new Set(); @@ -571,6 +572,7 @@ export class AlertSettingComponent implements OnInit { isExpr = false; userExpr!: string; severity!: string; + alertMode!: string; logFields: any[] = []; editAlertDefine(alertDefineId: number) { @@ -597,6 +599,9 @@ export class AlertSettingComponent implements OnInit { if (this.define.labels && this.define.labels['severity']) { this.severity = this.define.labels['severity']; } + if (this.define.labels && this.define.labels['alert_mode']) { + this.alertMode = this.define.labels['alert_mode']; + } // Set default period for periodic_metric alert if not set if (this.define.type === 'periodic_metric' && !this.define.period) { this.define.period = 300; @@ -1010,6 +1015,13 @@ export class AlertSettingComponent implements OnInit { this.define.labels = { ...this.define.labels, severity: this.severity }; } + onAlertModeChange() { + if (!this.define.labels) { + this.define.labels = {}; + } + this.define.labels = { ...this.define.labels, alert_mode: this.alertMode }; + } + onManageModalCancel() { this.cascadeValues = []; this.isExpr = false; diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 28e3d4e5771..9769a2d0fb4 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -398,6 +398,11 @@ "alert.severity.1": "Critical", "alert.severity.2": "Warning", "alert.severity.all": "All Severity", + "alert.mode": "Alert Mode", + "alert.mode.group": "Alert Group", + "alert.mode.individual": "Individual Alert", + "alert.setting.mode.tip": "Select alert mode, alert group mode will merge alerts with the same conditions, individual alert mode will send each alert separately", + "alert.notice.rule.mode.placeholder": "Please select alert mode", "alert.silence.delete": "Delete Silence Strategy", "alert.silence.edit": "Edit Silence Strategy", "alert.silence.labels": "Label Match", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index 23481443c74..0342d2bc012 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -343,6 +343,11 @@ "alert.severity.1": "クリティカル", "alert.severity.2": "警告", "alert.severity.all": "すべての重大度", + "alert.mode": "アラートモード", + "alert.mode.group": "アラートグループ", + "alert.mode.individual": "個別アラート", + "alert.setting.mode.tip": "アラートモードを選択してください。アラートグループモードは同じ条件のアラートを統合し、個別アラートモードは各アラートを個別に送信します", + "alert.notice.rule.mode.placeholder": "アラートモードを選択してください", "alert.silence.delete": "サイレンス戦略を削除", "alert.silence.edit": "サイレンス戦略を編集", "alert.silence.labels": "タグ一致", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index 566a4de907a..8a934c70339 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -569,6 +569,11 @@ "alert.severity.1": "Alarme sério", "alert.severity.2": "Alerta de aviso", "alert.severity.all": "Todos", + "alert.mode": "Modo de Alerta", + "alert.mode.group": "Grupo de Alerta", + "alert.mode.individual": "Alerta Individual", + "alert.setting.mode.tip": "Selecione o modo de alerta, o modo de grupo de alerta irá mesclar alertas com as mesmas condições, o modo de alerta individual irá enviar cada alerta separadamente", + "alert.notice.rule.mode.placeholder": "Por favor selecione o modo de alerta", "alert.status": "Estado do alarme", "alert.status.all": "Todos os status", "alert.status.firing": "Alarmante", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 633e5e0217a..42ce10a5391 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -395,11 +395,19 @@ "alert.setting.type.realtime.desc": "实时计算数据,触发阈值时立即告警", "alert.setting.type.realtime.log": "日志实时", "alert.setting.type.realtime.metric": "指标实时", + "alert.setting.window": "计算窗口", + "alert.setting.window.tip": "实时阈值计算窗口时间,单位秒", + "alert.setting.window.placeholder": "请输入计算窗口时间", "alert.severity": "告警级别", "alert.severity.0": "紧急告警", "alert.severity.1": "严重告警", "alert.severity.2": "警告告警", "alert.severity.all": "全部级别", + "alert.mode": "告警模式", + "alert.mode.group": "告警组", + "alert.mode.individual": "单条告警", + "alert.setting.mode.tip": "选择告警模式,告警组模式会将相同条件的告警合并,单条告警模式会分别发送每条告警", + "alert.notice.rule.mode.placeholder": "请选择告警模式", "alert.silence.delete": "删除静默策略", "alert.silence.edit": "编辑静默策略", "alert.silence.labels": "匹配标签", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 1eb107251e0..5810bdda610 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -347,6 +347,11 @@ "alert.severity.1": "嚴重告警", "alert.severity.2": "警告告警", "alert.severity.all": "全部級別", + "alert.mode": "告警模式", + "alert.mode.group": "告警組", + "alert.mode.individual": "單條告警", + "alert.setting.mode.tip": "選擇告警模式,告警組模式會將相同條件的告警合併,單條告警模式會分別發送每條告警", + "alert.notice.rule.mode.placeholder": "請選擇告警模式", "alert.silence.delete": "刪除靜默策略", "alert.silence.edit": "編輯靜默策略", "alert.silence.labels": "匹配標籤", From 5c0b77c6def3ea24dad62c660d2caa2da6a96b34 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 11 Aug 2025 18:21:06 +0800 Subject: [PATCH 23/39] improvement: remove chinese comments --- .../alert/calculate/realtime/window/WindowAggregator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java index b9b9ede6340..935461e50b1 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/window/WindowAggregator.java @@ -78,7 +78,6 @@ public void addMatchingLog(MatchingLogEvent event) { public void onWatermark(TimeService.Watermark watermark) { List closedWindows; - // 缩小锁范围:只保护Map操作 synchronized (windowLock) { closedWindows = new ArrayList<>(); From e00a10985854dc53bb7ecff2abf8bb5de5d9f697 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 11 Aug 2025 18:34:35 +0800 Subject: [PATCH 24/39] improvement: fix code style --- .../apache/hertzbeat/alert/calculate/JexlExprCalculator.java | 3 +++ .../calculate/periodic/MetricsPeriodicAlertCalculator.java | 1 - .../calculate/realtime/MetricsRealTimeAlertCalculatorTest.java | 3 ++- .../store/history/tsdb/greptime/GreptimeSqlQueryContent.java | 1 - .../routes/alert/alert-setting/alert-setting.component.html | 2 +- .../app/routes/alert/alert-setting/alert-setting.component.ts | 2 +- web-app/src/assets/app-data.json | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java index 56d2b14ad43..f61f29736f4 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java @@ -8,6 +8,9 @@ import java.util.Map; +/** + * JexlExprCalculator is a utility class for evaluating JEXL expressions + */ @Slf4j @Component public class JexlExprCalculator { diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java index 6077305df95..124c2d59aae 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/periodic/MetricsPeriodicAlertCalculator.java @@ -21,7 +21,6 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hertzbeat.alert.calculate.AlarmCacheManager; -import org.apache.hertzbeat.alert.calculate.JexlExprCalculator; import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce; import org.apache.hertzbeat.alert.service.DataSourceService; import org.apache.hertzbeat.alert.util.AlertTemplateUtil; diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java index ccd91a59e6a..ec483424143 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java @@ -54,7 +54,8 @@ void setUp() { Mockito.when(mockDao.querySingleAlertsByStatus(Mockito.anyString())) .thenReturn(Collections.emptyList()); - calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, mockExprCalculator,false); + calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService + , mockDao, mockReduce, alarmCacheManager, mockExprCalculator, false); } @Test diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java index 9419bab80e6..7efa79fcc44 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeSqlQueryContent.java @@ -25,7 +25,6 @@ import lombok.NoArgsConstructor; import java.util.List; -import java.util.Map; /** * GreptimeDB SQL query content diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html index 31e9d0ebed4..7a3259e3db0 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html @@ -883,7 +883,7 @@ - +
{{ 'alert.setting.template' | i18n }} diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index df655ac83d9..71547547c6c 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -1681,7 +1681,7 @@ export class AlertSettingComponent implements OnInit { return; } this.previewTableLoading = true; - + this.alertDefineSvc.getMonitorsDefinePreview(this.define.datasource, this.define.type, this.define.queryExpr).subscribe({ next: res => { if (res.code === 15 || res.code === 1 || res.code === 4) { diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index 4ebd13b2d3e..d438b9c6a52 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -151,7 +151,7 @@ { "text": "Stream", "i18n": "menu.log.stream", - "icon": "anticon-api", + "icon": "anticon-file-text", "link": "/log/stream" } ] From 888d4ddd78330e0209b69eb2b5c4a5a36c1c85e8 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 11 Aug 2025 19:40:23 +0800 Subject: [PATCH 25/39] bugfix: fix common data queue test and kafka data queue topic --- .../apache/hertzbeat/common/config/CommonProperties.java | 4 ++++ .../hertzbeat/common/queue/impl/KafkaCommonDataQueue.java | 2 +- .../common/queue/impl/KafkaCommonDataQueueTest.java | 2 ++ .../common/queue/impl/RedisCommonDataQueueTest.java | 7 +++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java index f69d4721f9c..0c7186fc305 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/config/CommonProperties.java @@ -149,5 +149,9 @@ public static class KafkaProperties extends BaseKafkaProperties { * log entry data topic */ private String logEntryDataTopic; + /** + * log entry data to storage topic + */ + private String logEntryDataToStorageTopic; } } diff --git a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java index 614fc17c828..b29b8dbbac3 100644 --- a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java +++ b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueue.java @@ -235,7 +235,7 @@ public LogEntry pollLogEntry() throws InterruptedException { public void sendLogEntryToStorage(LogEntry logEntry) { if (logEntryProducer != null) { try { - ProducerRecord record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry); + ProducerRecord record = new ProducerRecord<>(kafka.getLogEntryDataToStorageTopic(), logEntry); logEntryProducer.send(record); } catch (Exception e) { log.error("Failed to send LogEntry to storage via Kafka: {}", e.getMessage()); diff --git a/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueueTest.java b/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueueTest.java index 206a63dfc36..f756122dcab 100644 --- a/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueueTest.java +++ b/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/KafkaCommonDataQueueTest.java @@ -70,6 +70,8 @@ void setUp() throws Exception { // Set all required topics when(kafkaProperties.getMetricsDataTopic()).thenReturn("metricsDataTopic"); + when(kafkaProperties.getLogEntryDataTopic()).thenReturn("logEntryDataTopic"); + when(kafkaProperties.getLogEntryDataToStorageTopic()).thenReturn("logEntryDataToStorageTopic"); when(kafkaProperties.getAlertsDataTopic()).thenReturn("alertsDataTopic"); when(kafkaProperties.getMetricsDataToStorageTopic()).thenReturn("metricsDataToStorageTopic"); when(kafkaProperties.getServiceDiscoveryDataTopic()).thenReturn("serviceDiscoveryDataTopic"); diff --git a/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueueTest.java b/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueueTest.java index 846b399515b..2f9b1499b61 100644 --- a/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueueTest.java +++ b/hertzbeat-common/src/test/java/org/apache/hertzbeat/common/queue/impl/RedisCommonDataQueueTest.java @@ -28,7 +28,9 @@ import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.sync.RedisCommands; import org.apache.hertzbeat.common.config.CommonProperties; +import org.apache.hertzbeat.common.entity.log.LogEntry; import org.apache.hertzbeat.common.entity.message.CollectRep; +import org.apache.hertzbeat.common.serialize.RedisLogEntryCodec; import org.apache.hertzbeat.common.serialize.RedisMetricsDataCodec; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,6 +48,9 @@ class RedisCommonDataQueueTest { @Mock private StatefulRedisConnection connection; + @Mock + private StatefulRedisConnection logEntryConnection; + @Mock private RedisCommands syncCommands; @@ -71,6 +76,8 @@ public void setUp() { try (MockedStatic mockedRedisClient = mockStatic(RedisClient.class)) { mockedRedisClient.when(() -> RedisClient.create(any(RedisURI.class))).thenReturn(redisClient); when(redisClient.connect(any(RedisMetricsDataCodec.class))).thenReturn(connection); + when(redisClient.connect(any(RedisLogEntryCodec.class))).thenReturn(logEntryConnection); + when(logEntryConnection.sync()).thenReturn(mock(RedisCommands.class)); when(connection.sync()).thenReturn(syncCommands); redisCommonDataQueue = new RedisCommonDataQueue(commonProperties); From 9fb1731d719dafe3564b1020459805bdcd521321 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 11 Aug 2025 20:39:13 +0800 Subject: [PATCH 26/39] bugfix: fix metrics real time alert not add firing status to cache --- .../calculate/realtime/MetricsRealTimeAlertCalculator.java | 2 ++ .../realtime/MetricsRealTimeAlertCalculatorMatchTest.java | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java index 00f4a577c48..8dd44014187 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculator.java @@ -401,6 +401,7 @@ private void afterThresholdRuleMatch(long defineId, long currentTimeMilli, Map Date: Mon, 11 Aug 2025 20:44:42 +0800 Subject: [PATCH 27/39] bugfix: fix mock jexl expr calculator default return false --- .../calculate/realtime/MetricsRealTimeAlertCalculatorTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java index ec483424143..3d4b41164df 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/realtime/MetricsRealTimeAlertCalculatorTest.java @@ -49,13 +49,12 @@ void setUp() { SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class); AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class); AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class); - JexlExprCalculator mockExprCalculator = Mockito.mock(JexlExprCalculator.class); Mockito.when(mockDao.querySingleAlertsByStatus(Mockito.anyString())) .thenReturn(Collections.emptyList()); calculator = new MetricsRealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService - , mockDao, mockReduce, alarmCacheManager, mockExprCalculator, false); + , mockDao, mockReduce, alarmCacheManager, new JexlExprCalculator(), false); } @Test From 5ca536efcf157621280d01d4e957254900431398 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 17 Aug 2025 00:07:55 +0800 Subject: [PATCH 28/39] feat: Add log manage module --- .../log/controller/LogManagerController.java | 64 ++ .../log/controller/LogQueryController.java | 226 +++++++ .../store/history/tsdb/HistoryDataWriter.java | 65 +- .../tsdb/greptime/GreptimeDbDataStorage.java | 244 +++++++- .../log/log-manage/log-manage.component.html | 358 +++++++++++ .../log/log-manage/log-manage.component.less | 82 +++ .../log-manage/log-manage.component.spec.ts | 43 ++ .../log/log-manage/log-manage.component.ts | 566 ++++++++++++++++++ .../src/app/routes/log/log-routing.module.ts | 4 +- web-app/src/app/routes/log/log.module.ts | 4 +- web-app/src/app/service/log.service.ts | 123 ++++ web-app/src/assets/app-data.json | 6 + web-app/src/assets/i18n/en-US.json | 142 +++-- web-app/src/assets/i18n/ja-JP.json | 122 ++-- web-app/src/assets/i18n/pt-BR.json | 120 ++-- web-app/src/assets/i18n/zh-CN.json | 141 +++-- web-app/src/assets/i18n/zh-TW.json | 112 +++- 17 files changed, 2209 insertions(+), 213 deletions(-) create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogManagerController.java create mode 100644 hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogQueryController.java create mode 100644 web-app/src/app/routes/log/log-manage/log-manage.component.html create mode 100644 web-app/src/app/routes/log/log-manage/log-manage.component.less create mode 100644 web-app/src/app/routes/log/log-manage/log-manage.component.spec.ts create mode 100644 web-app/src/app/routes/log/log-manage/log-manage.component.ts create mode 100644 web-app/src/app/service/log.service.ts diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogManagerController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogManagerController.java new file mode 100644 index 00000000000..1b93dd51e27 --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogManagerController.java @@ -0,0 +1,64 @@ +/* + * 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.hertzbeat.log.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.dto.Message; +import org.apache.hertzbeat.warehouse.store.history.tsdb.HistoryDataWriter; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static org.apache.hertzbeat.common.constants.CommonConstants.FAIL_CODE; + +/** + * Controller for managing log entries in HertzBeat. + */ +@RestController +@RequestMapping("/api/logs") +@Tag(name = "Log Management Controller") +@Slf4j +public class LogManagerController { + + private final HistoryDataWriter historyDataWriter; + + public LogManagerController(HistoryDataWriter historyDataWriter) { + this.historyDataWriter = historyDataWriter; + } + + @DeleteMapping + @Operation(summary = "Batch delete logs", + description = "Batch delete logs by time timestamps. Deletes multiple log entries based on their Unix nanosecond timestamps.") + public ResponseEntity> batchDelete( + @Parameter(description = "List of Unix nanosecond timestamps for logs to delete", example = "1640995200000000000") + @RequestParam(required = false) List timeUnixNanos) { + boolean result = historyDataWriter.batchDeleteLogs(timeUnixNanos); + if (result) { + return ResponseEntity.ok(Message.success("Logs deleted successfully")); + } else { + return ResponseEntity.ok(Message.fail(FAIL_CODE, "Failed to delete logs")); + } + } +} diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogQueryController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogQueryController.java new file mode 100644 index 00000000000..d8932ffde38 --- /dev/null +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogQueryController.java @@ -0,0 +1,226 @@ +/* + * 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.hertzbeat.log.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; + +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.entity.dto.Message; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.warehouse.store.history.tsdb.HistoryDataWriter; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Log query and statistics APIs for UI consumption + */ +@RestController +@RequestMapping("/api/logs") +@Tag(name = "Log Query Controller") +@Slf4j +public class LogQueryController { + + private final HistoryDataWriter historyDataWriter; + + public LogQueryController(HistoryDataWriter historyDataWriter) { + this.historyDataWriter = historyDataWriter; + } + + @GetMapping("/list") + @Operation(summary = "Query logs by time range with optional filters", + description = "Query logs by [start,end] in ms and optional filters with pagination. Returns paginated log entries sorted by timestamp in descending order.") + public ResponseEntity>> list( + @Parameter(description = "Start timestamp in milliseconds (Unix timestamp)", example = "1640995200000") + @RequestParam(value = "start", required = false) Long start, + @Parameter(description = "End timestamp in milliseconds (Unix timestamp)", example = "1641081600000") + @RequestParam(value = "end", required = false) Long end, + @Parameter(description = "Trace ID for distributed tracing", example = "1234567890abcdef") + @RequestParam(value = "traceId", required = false) String traceId, + @Parameter(description = "Span ID for distributed tracing", example = "abcdef1234567890") + @RequestParam(value = "spanId", required = false) String spanId, + @Parameter(description = "Log severity number (1-24 according to OpenTelemetry standard)", example = "9") + @RequestParam(value = "severityNumber", required = false) Integer severityNumber, + @Parameter(description = "Log severity text (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)", example = "INFO") + @RequestParam(value = "severityText", required = false) String severityText, + @Parameter(description = "Page index starting from 0", example = "0") + @RequestParam(value = "pageIndex", required = false, defaultValue = "0") Integer pageIndex, + @Parameter(description = "Number of items per page", example = "20") + @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize) { + Page result = getPagedLogs(start, end, traceId, spanId, severityNumber, severityText, pageIndex, pageSize); + return ResponseEntity.ok(Message.success(result)); + } + + @GetMapping("/stats/overview") + @Operation(summary = "Log overview statistics", + description = "Overall counts and basic statistics with filters. Provides counts by severity levels according to OpenTelemetry standard.") + public ResponseEntity>> overviewStats( + @Parameter(description = "Start timestamp in milliseconds (Unix timestamp)", example = "1640995200000") + @RequestParam(value = "start", required = false) Long start, + @Parameter(description = "End timestamp in milliseconds (Unix timestamp)", example = "1641081600000") + @RequestParam(value = "end", required = false) Long end, + @Parameter(description = "Trace ID for distributed tracing", example = "1234567890abcdef") + @RequestParam(value = "traceId", required = false) String traceId, + @Parameter(description = "Span ID for distributed tracing", example = "abcdef1234567890") + @RequestParam(value = "spanId", required = false) String spanId, + @Parameter(description = "Log severity number (1-24 according to OpenTelemetry standard)", example = "9") + @RequestParam(value = "severityNumber", required = false) Integer severityNumber, + @Parameter(description = "Log severity text (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)", example = "INFO") + @RequestParam(value = "severityText", required = false) String severityText) { + List logs = getFilteredLogs(start, end, traceId, spanId, severityNumber, severityText); + + Map overview = new HashMap<>(); + overview.put("totalCount", logs.size()); + + // Count by severity levels according to OpenTelemetry standard + // TRACE: 1-4, DEBUG: 5-8, INFO: 9-12, WARN: 13-16, ERROR: 17-20, FATAL: 21-24 + long fatalCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 21 && log.getSeverityNumber() <= 24).count(); + long errorCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 17 && log.getSeverityNumber() <= 20).count(); + long warnCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 13 && log.getSeverityNumber() <= 16).count(); + long infoCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 9 && log.getSeverityNumber() <= 12).count(); + long debugCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 5 && log.getSeverityNumber() <= 8).count(); + long traceCount = logs.stream().filter(log -> log.getSeverityNumber() != null && log.getSeverityNumber() >= 1 && log.getSeverityNumber() <= 4).count(); + + overview.put("fatalCount", fatalCount); + overview.put("errorCount", errorCount); + overview.put("warnCount", warnCount); + overview.put("infoCount", infoCount); + overview.put("debugCount", debugCount); + overview.put("traceCount", traceCount); + + return ResponseEntity.ok(Message.success(overview)); + } + + @GetMapping("/stats/trace-coverage") + @Operation(summary = "Trace coverage statistics", + description = "Statistics about trace information availability. Shows how many logs have trace IDs, span IDs, or both for distributed tracing analysis.") + public ResponseEntity>> traceCoverageStats( + @Parameter(description = "Start timestamp in milliseconds (Unix timestamp)", example = "1640995200000") + @RequestParam(value = "start", required = false) Long start, + @Parameter(description = "End timestamp in milliseconds (Unix timestamp)", example = "1641081600000") + @RequestParam(value = "end", required = false) Long end, + @Parameter(description = "Trace ID for distributed tracing", example = "1234567890abcdef") + @RequestParam(value = "traceId", required = false) String traceId, + @Parameter(description = "Span ID for distributed tracing", example = "abcdef1234567890") + @RequestParam(value = "spanId", required = false) String spanId, + @Parameter(description = "Log severity number (1-24 according to OpenTelemetry standard)", example = "9") + @RequestParam(value = "severityNumber", required = false) Integer severityNumber, + @Parameter(description = "Log severity text (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)", example = "INFO") + @RequestParam(value = "severityText", required = false) String severityText) { + List logs = getFilteredLogs(start, end, traceId, spanId, severityNumber, severityText); + + Map result = new HashMap<>(); + + // Trace coverage statistics + long withTraceId = logs.stream().filter(log -> log.getTraceId() != null && !log.getTraceId().isEmpty()).count(); + long withSpanId = logs.stream().filter(log -> log.getSpanId() != null && !log.getSpanId().isEmpty()).count(); + long withBothTraceAndSpan = logs.stream().filter(log -> + log.getTraceId() != null && !log.getTraceId().isEmpty() + && log.getSpanId() != null && !log.getSpanId().isEmpty()).count(); + long withoutTrace = logs.size() - withTraceId; + + Map traceCoverage = new HashMap<>(); + traceCoverage.put("withTrace", withTraceId); + traceCoverage.put("withoutTrace", withoutTrace); + traceCoverage.put("withSpan", withSpanId); + traceCoverage.put("withBothTraceAndSpan", withBothTraceAndSpan); + + result.put("traceCoverage", traceCoverage); + return ResponseEntity.ok(Message.success(result)); + } + + @GetMapping("/stats/trend") + @Operation(summary = "Log trend over time", + description = "Count logs by hour intervals with filters. Groups logs by hour and provides time-series data for trend analysis.") + public ResponseEntity>> trendStats( + @Parameter(description = "Start timestamp in milliseconds (Unix timestamp)", example = "1640995200000") + @RequestParam(value = "start", required = false) Long start, + @Parameter(description = "End timestamp in milliseconds (Unix timestamp)", example = "1641081600000") + @RequestParam(value = "end", required = false) Long end, + @Parameter(description = "Trace ID for distributed tracing", example = "1234567890abcdef") + @RequestParam(value = "traceId", required = false) String traceId, + @Parameter(description = "Span ID for distributed tracing", example = "abcdef1234567890") + @RequestParam(value = "spanId", required = false) String spanId, + @Parameter(description = "Log severity number (1-24 according to OpenTelemetry standard)", example = "9") + @RequestParam(value = "severityNumber", required = false) Integer severityNumber, + @Parameter(description = "Log severity text (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)", example = "INFO") + @RequestParam(value = "severityText", required = false) String severityText) { + List logs = getFilteredLogs(start, end, traceId, spanId, severityNumber, severityText); + + // Group by hour + Map hourlyStats = logs.stream() + .filter(log -> log.getTimeUnixNano() != null) + .collect(Collectors.groupingBy( + log -> { + long timestampMs = log.getTimeUnixNano() / 1_000_000L; + LocalDateTime dateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(timestampMs), + ZoneId.systemDefault()); + return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00")); + }, + Collectors.counting())); + + Map result = new HashMap<>(); + result.put("hourlyStats", hourlyStats); + return ResponseEntity.ok(Message.success(result)); + } + + private List getFilteredLogs(Long start, Long end, String traceId, String spanId, + Integer severityNumber, String severityText) { + // Use the new multi-condition query method + return historyDataWriter.queryLogsByMultipleConditions(start, end, traceId, spanId, severityNumber, severityText); + } + + private Page getPagedLogs(Long start, Long end, String traceId, String spanId, + Integer severityNumber, String severityText, Integer pageIndex, Integer pageSize) { + // Calculate pagination parameters + int offset = pageIndex * pageSize; + + // Get total count and paginated data + long totalElements = historyDataWriter.countLogsByMultipleConditions(start, end, traceId, spanId, severityNumber, severityText); + List pagedLogs = historyDataWriter.queryLogsByMultipleConditionsWithPagination( + start, end, traceId, spanId, severityNumber, severityText, offset, pageSize); + + // Create PageRequest (sorted by timestamp descending) + Sort sort = Sort.by(Sort.Direction.DESC, "timeUnixNano"); + PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sort); + + // Return Spring Data Page object + return new PageImpl<>(pagedLogs, pageRequest, totalElements); + } +} + + diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java index a2845575e3b..f3308e7f9f7 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/HistoryDataWriter.java @@ -21,7 +21,6 @@ import org.apache.hertzbeat.common.entity.message.CollectRep; import java.util.List; -import java.util.Map; /** * history data writer @@ -47,24 +46,62 @@ default void saveLogData(LogEntry logEntry) { throw new UnsupportedOperationException("save log data is not supported"); } - - default List queryLogs(Long startTime, Long endTime, Integer limit){ - return null; - } - - default List queryLogsByResource(Map resource, Long startTime, Long endTime, Integer limit){ - return null; + /** + * Query logs with multiple filter conditions + * @param startTime start time in milliseconds + * @param endTime end time in milliseconds + * @param traceId trace ID filter + * @param spanId span ID filter + * @param severityNumber severity number filter + * @param severityText severity text filter + * @return filtered log entries + */ + default List queryLogsByMultipleConditions(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText) { + throw new UnsupportedOperationException("query logs by multiple conditions is not supported"); } - default List queryLogsBySpanId(String spanId, Integer limit){ - return null; + /** + * Query logs with multiple filter conditions and pagination + * @param startTime start time in milliseconds + * @param endTime end time in milliseconds + * @param traceId trace ID filter + * @param spanId span ID filter + * @param severityNumber severity number filter + * @param severityText severity text filter + * @param offset pagination offset + * @param limit pagination limit + * @return filtered log entries with pagination + */ + default List queryLogsByMultipleConditionsWithPagination(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText, Integer offset, Integer limit) { + throw new UnsupportedOperationException("query logs by multiple conditions with pagination is not supported"); } - default List queryLogsBySeverity(Integer severityNumber, Long startTime, Long endTime, Integer limit){ - return null; + /** + * Count logs with multiple filter conditions + * @param startTime start time in milliseconds + * @param endTime end time in milliseconds + * @param traceId trace ID filter + * @param spanId span ID filter + * @param severityNumber severity number filter + * @param severityText severity text filter + * @return count of matching log entries + */ + default long countLogsByMultipleConditions(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText) { + throw new UnsupportedOperationException("count logs by multiple conditions is not supported"); } - default List queryLogsByTraceId(String traceId, Integer limit){ - return null; + /** + * Batch delete logs by time timestamps + * @param timeUnixNanos list of time timestamps to delete + * @return true if deletion is successful, false otherwise + */ + default boolean batchDeleteLogs(List timeUnixNanos) { + throw new UnsupportedOperationException("batch delete logs is not supported"); } } diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java index 343a3286257..a8241117862 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/greptime/GreptimeDbDataStorage.java @@ -37,6 +37,7 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAmount; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -44,6 +45,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.math.NumberUtils; @@ -58,6 +60,7 @@ import org.apache.hertzbeat.common.util.TimePeriodUtil; import org.apache.hertzbeat.warehouse.store.history.tsdb.AbstractHistoryDataStorage; import org.apache.hertzbeat.warehouse.store.history.tsdb.vm.PromQlQueryContent; +import org.apache.hertzbeat.warehouse.db.GreptimeSqlQueryExecutor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -90,13 +93,17 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage { private final RestTemplate restTemplate; - public GreptimeDbDataStorage(GreptimeProperties greptimeProperties, RestTemplate restTemplate) { + private final GreptimeSqlQueryExecutor greptimeSqlQueryExecutor; + + public GreptimeDbDataStorage(GreptimeProperties greptimeProperties, RestTemplate restTemplate, + GreptimeSqlQueryExecutor greptimeSqlQueryExecutor) { if (greptimeProperties == null) { log.error("init error, please config Warehouse GreptimeDB props in application.yml"); throw new IllegalArgumentException("please config Warehouse GreptimeDB props"); } this.restTemplate = restTemplate; this.greptimeProperties = greptimeProperties; + this.greptimeSqlQueryExecutor = greptimeSqlQueryExecutor; serverAvailable = initGreptimeDbClient(greptimeProperties); } @@ -351,4 +358,239 @@ public void saveLogData(LogEntry logEntry) { } } + @Override + public List queryLogsByMultipleConditions(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText) { + try { + StringBuilder sql = new StringBuilder("SELECT * FROM ").append(LOG_TABLE_NAME); + buildWhereConditions(sql, startTime, endTime, traceId, spanId, severityNumber, severityText); + sql.append(" ORDER BY time_unix_nano DESC"); + + List> rows = greptimeSqlQueryExecutor.execute(sql.toString()); + return mapRowsToLogEntries(rows); + } catch (Exception e) { + log.error("[warehouse greptime-log] queryLogsByMultipleConditions error: {}", e.getMessage(), e); + return List.of(); + } + } + + @Override + public List queryLogsByMultipleConditionsWithPagination(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText, Integer offset, Integer limit) { + try { + StringBuilder sql = new StringBuilder("SELECT * FROM ").append(LOG_TABLE_NAME); + buildWhereConditions(sql, startTime, endTime, traceId, spanId, severityNumber, severityText); + sql.append(" ORDER BY time_unix_nano DESC"); + + // Add pagination + if (limit != null && limit > 0) { + sql.append(" LIMIT ").append(limit); + if (offset != null && offset > 0) { + sql.append(" OFFSET ").append(offset); + } + } + + List> rows = greptimeSqlQueryExecutor.execute(sql.toString()); + return mapRowsToLogEntries(rows); + } catch (Exception e) { + log.error("[warehouse greptime-log] queryLogsByMultipleConditionsWithPagination error: {}", e.getMessage(), e); + return List.of(); + } + } + + @Override + public long countLogsByMultipleConditions(Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, + String severityText) { + try { + StringBuilder sql = new StringBuilder("SELECT COUNT(*) as count FROM ").append(LOG_TABLE_NAME); + buildWhereConditions(sql, startTime, endTime, traceId, spanId, severityNumber, severityText); + + List> rows = greptimeSqlQueryExecutor.execute(sql.toString()); + if (rows != null && !rows.isEmpty()) { + Object countObj = rows.get(0).get("count"); + if (countObj instanceof Number) { + return ((Number) countObj).longValue(); + } + } + return 0; + } catch (Exception e) { + log.error("[warehouse greptime-log] countLogsByMultipleConditions error: {}", e.getMessage(), e); + return 0; + } + } + + private static long msToNs(Long ms) { + return ms * 1_000_000L; + } + + private static String safeString(String input) { + if (input == null) { + return ""; + } + return input.replace("'", "''"); + } + + /** + * build WHERE conditions + * @param sql SQL builder + * @param startTime start time + * @param endTime end time + * @param traceId trace id + * @param spanId span id + * @param severityNumber severity number + */ + private void buildWhereConditions(StringBuilder sql, Long startTime, Long endTime, String traceId, + String spanId, Integer severityNumber, String severityText) { + List conditions = new ArrayList<>(); + + // Time range condition + if (startTime != null && endTime != null) { + conditions.add("time_unix_nano >= " + msToNs(startTime) + " AND time_unix_nano <= " + msToNs(endTime)); + } + + // TraceId condition + if (StringUtils.hasText(traceId)) { + conditions.add("trace_id = '" + safeString(traceId) + "'"); + } + + // SpanId condition + if (StringUtils.hasText(spanId)) { + conditions.add("span_id = '" + safeString(spanId) + "'"); + } + + // Severity condition + if (severityNumber != null) { + conditions.add("severity_number = " + severityNumber); + } + + // SeverityText condition + if (StringUtils.hasText(severityText)) { + conditions.add("severity_text = '" + safeString(severityText) + "'"); + } + + // Add WHERE clause if there are conditions + if (!conditions.isEmpty()) { + sql.append(" WHERE ").append(String.join(" AND ", conditions)); + } + } + + private List mapRowsToLogEntries(List> rows) { + List list = new LinkedList<>(); + if (rows == null || rows.isEmpty()) { + return list; + } + for (Map row : rows) { + try { + LogEntry.InstrumentationScope scope = null; + Object scopeObj = row.get("instrumentation_scope"); + if (scopeObj instanceof String scopeStr && StringUtils.hasText(scopeStr)) { + try { + scope = JsonUtil.fromJson(scopeStr, LogEntry.InstrumentationScope.class); + } catch (Exception ignore) { + scope = null; + } + } + + Object bodyObj = parseJsonMaybe(row.get("body")); + Map attributes = castToMap(parseJsonMaybe(row.get("attributes"))); + Map resource = castToMap(parseJsonMaybe(row.get("resource"))); + + LogEntry entry = LogEntry.builder() + .timeUnixNano(castToLong(row.get("time_unix_nano"))) + .observedTimeUnixNano(castToLong(row.get("observed_time_unix_nano"))) + .severityNumber(castToInteger(row.get("severity_number"))) + .severityText(castToString(row.get("severity_text"))) + .body(bodyObj) + .traceId(castToString(row.get("trace_id"))) + .spanId(castToString(row.get("span_id"))) + .traceFlags(castToInteger(row.get("trace_flags"))) + .attributes(attributes) + .resource(resource) + .instrumentationScope(scope) + .droppedAttributesCount(castToInteger(row.get("dropped_attributes_count"))) + .build(); + list.add(entry); + } catch (Exception e) { + log.warn("[warehouse greptime-log] map row to LogEntry error: {}", e.getMessage()); + } + } + return list; + } + + private static Object parseJsonMaybe(Object value) { + if (value == null) return null; + if (value instanceof Map) return value; + if (value instanceof String str) { + String s = str.trim(); + if ((s.startsWith("{") && s.endsWith("}")) || (s.startsWith("[") && s.endsWith("]"))) { + try { + return JsonUtil.fromJson(s, Object.class); + } catch (Exception e) { + return s; + } + } + return s; + } + return value; + } + + @SuppressWarnings("unchecked") + private static Map castToMap(Object obj) { + if (obj instanceof Map) { + return (Map) obj; + } + return null; + } + + private static Long castToLong(Object obj) { + if (obj == null) return null; + if (obj instanceof Number n) return n.longValue(); + try { + return Long.parseLong(String.valueOf(obj)); + } catch (Exception e) { + return null; + } + } + + private static Integer castToInteger(Object obj) { + if (obj == null) return null; + if (obj instanceof Number n) return n.intValue(); + try { + return Integer.parseInt(String.valueOf(obj)); + } catch (Exception e) { + return null; + } + } + + private static String castToString(Object obj) { + return obj == null ? null : String.valueOf(obj); + } + + @Override + public boolean batchDeleteLogs(List timeUnixNanos) { + if (!isServerAvailable() || timeUnixNanos == null || timeUnixNanos.isEmpty()) { + return false; + } + + try { + StringBuilder sql = new StringBuilder("DELETE FROM ").append(LOG_TABLE_NAME).append(" WHERE time_unix_nano IN ("); + sql.append(timeUnixNanos.stream() + .filter(time -> time != null) + .map(String::valueOf) + .collect(Collectors.joining(", "))); + sql.append(")"); + + greptimeSqlQueryExecutor.execute(sql.toString()); + log.info("[warehouse greptime-log] Batch delete executed successfully for {} logs", timeUnixNanos.size()); + return true; + + } catch (Exception e) { + log.error("[warehouse greptime-log] batchDeleteLogs error: {}", e.getMessage(), e); + return false; + } + } + } diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.html b/web-app/src/app/routes/log/log-manage/log-manage.component.html new file mode 100644 index 00000000000..5394fd781d0 --- /dev/null +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.html @@ -0,0 +1,358 @@ + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + +
+ +
+
+
+
+ + +
+ +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + {{ 'log.manage.table.column.time' | i18n }} + {{ + 'log.manage.table.column.observed-time' | i18n + }} + {{ 'log.manage.table.column.severity' | i18n }} + {{ 'log.manage.table.column.body' | i18n }} + {{ + 'log.manage.table.column.attributes' | i18n + }} + {{ 'log.manage.table.column.resource' | i18n }} + {{ 'log.manage.table.column.trace-id' | i18n }} + {{ 'log.manage.table.column.span-id' | i18n }} + {{ + 'log.manage.table.column.trace-flags' | i18n + }} + {{ + 'log.manage.table.column.instrumentation' | i18n + }} + {{ + 'log.manage.table.column.dropped-count' | i18n + }} + + + + + + + + + + {{ (i.timeUnixNano || 0) / 1000000 | date : 'yyyy-MM-dd HH:mm:ss' }} + + + + + {{ (i.observedTimeUnixNano || 0) / 1000000 | date : 'yyyy-MM-dd HH:mm:ss' }} + + - + + + + {{ i.severityText || i.severityNumber || '-' }} + + + +
+ {{ getBodyText(i.body) }} +
+ + +
+ {{ getObjectText(i.attributes) }} +
+ - + + +
+ {{ getObjectText(i.resource) }} +
+ - + + + {{ i.traceId | slice : 0 : 8 }}... + - + + + {{ i.spanId | slice : 0 : 8 }}... + - + + + {{ i.traceFlags }} + - + + +
+ {{ i.instrumentationScope.name }} + ({{ i.instrumentationScope.version }}) +
+ - + + + {{ i.droppedAttributesCount }} + - + + + +
+ + + + +
+ + +
+
+ {{ 'log.manage.severity-text' | i18n }}: + + {{ selectedLogEntry.severityText || selectedLogEntry.severityNumber || '未知' }} + +
+
+ {{ 'log.manage.timestamp' | i18n }}: + {{ formatTimestamp(selectedLogEntry.timeUnixNano) }} +
+
+ {{ 'log.manage.trace-id' | i18n }}: + {{ selectedLogEntry.traceId }} +
+
+ {{ 'log.manage.span-id' | i18n }}: + {{ selectedLogEntry.spanId }} +
+
+
+ + + +
+ +
{{ getLogEntryJson(selectedLogEntry) }}
+
+
+
+
+
diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.less b/web-app/src/app/routes/log/log-manage/log-manage.component.less new file mode 100644 index 00000000000..71361bddfdc --- /dev/null +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.less @@ -0,0 +1,82 @@ +.manager-card { + .filters-container { + padding: 8px 0; + margin-bottom: 24px; + } +} + +.log-body { + max-width: 400px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; +} + +.trace-id, .span-id { + font-family: 'Courier New', monospace; + font-size: 12px; + color: #666; +} + +.attributes-text, .resource-text { + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + cursor: pointer; +} + +.text-muted { + color: #999; + font-style: italic; +} + +nz-statistic { + text-align: center; +} + +.column-control-container { + width: 280px; + max-height: 400px; + overflow-y: auto; + .column-control-reset-button { + text-align: center; + } +} + +.log-details-modal { + .basic-info-card { + margin-bottom: 16px; + .info-row { + margin-bottom: 8px; + } + .info-label { + display: inline-block; + width: 100px; + font-weight: bold; + } + .info-value { + font-family: monospace; + word-break: break-all; + } + } + .json-content { + position: relative; + .copy-button { + position: absolute; + top: 8px; + right: 8px; + z-index: 1; + } + .pre { + background: #f5f5f5; + padding: 16px; + border-radius: 4px; + overflow-x: auto; + margin: 0; + padding-top: 40px; + } + } +} diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.spec.ts b/web-app/src/app/routes/log/log-manage/log-manage.component.spec.ts new file mode 100644 index 00000000000..e7e11e6ca21 --- /dev/null +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.spec.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LogManageComponent } from './log-manage.component'; + +describe('LogManageComponent', () => { + let component: LogManageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [LogManageComponent] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LogManageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.ts b/web-app/src/app/routes/log/log-manage/log-manage.component.ts new file mode 100644 index 00000000000..4ae72e8b0e8 --- /dev/null +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.ts @@ -0,0 +1,566 @@ +/* + * 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. + */ + +import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { I18NService } from '@core'; +import { ALAIN_I18N_TOKEN } from '@delon/theme'; +import { SharedModule } from '@shared'; +import { EChartsOption } from 'echarts'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzCardModule } from 'ng-zorro-antd/card'; +import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; +import { NzCollapseModule } from 'ng-zorro-antd/collapse'; +import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; +import { NzDividerModule } from 'ng-zorro-antd/divider'; +import { NzEmptyModule } from 'ng-zorro-antd/empty'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzListModule } from 'ng-zorro-antd/list'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzModalService, NzModalModule } from 'ng-zorro-antd/modal'; +import { NzPopoverModule } from 'ng-zorro-antd/popover'; +import { NzSpaceModule } from 'ng-zorro-antd/space'; +import { NzStatisticModule } from 'ng-zorro-antd/statistic'; +import { NzTableModule } from 'ng-zorro-antd/table'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { NgxEchartsModule } from 'ngx-echarts'; + +import { LogEntry } from '../../../pojo/LogEntry'; +import { LogService } from '../../../service/log.service'; + +@Component({ + selector: 'app-log-manage', + standalone: true, + imports: [ + CommonModule, + FormsModule, + SharedModule, + NzCardModule, + NzTableModule, + NzDatePickerModule, + NzInputModule, + NzButtonModule, + NzTagModule, + NzToolTipModule, + NzEmptyModule, + NgxEchartsModule, + NzStatisticModule, + NzSpaceModule, + NzIconModule, + NzDividerModule, + NzCollapseModule, + NzModalModule, + NzCheckboxModule, + NzPopoverModule, + NzListModule + ], + templateUrl: './log-manage.component.html', + styleUrl: './log-manage.component.less' +}) +export class LogManageComponent implements OnInit { + constructor( + private logSvc: LogService, + private msg: NzMessageService, + private modal: NzModalService, + @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService + ) {} + + // filters + timeRange: Date[] = []; + severityNumber?: number; + severityText?: string; + traceId: string = ''; + spanId: string = ''; + + // table with pagination + loading = false; + data: LogEntry[] = []; + pageIndex = 1; + pageSize = 20; + totalElements = 0; + totalPages = 0; + + // charts + severityOption!: EChartsOption; + trendOption!: EChartsOption; + traceCoverageOption!: EChartsOption; + severityInstance: any; + trendInstance: any; + traceCoverageInstance: any; + + // Modal state + isModalVisible: boolean = false; + selectedLogEntry: LogEntry | null = null; + + // Statistics visibility control + showStatistics: boolean = false; + + // Batch selection for table + checked = false; + indeterminate = false; + setOfCheckedId = new Set(); + + // overview stats + overviewStats: any = { + totalCount: 0, + fatalCount: 0, + errorCount: 0, + warnCount: 0, + infoCount: 0, + debugCount: 0, + traceCount: 0 + }; + + // column visibility + columnVisibility = { + time: { visible: true, label: this.i18n.fanyi('log.manage.table.column.time') }, + observedTime: { visible: true, label: this.i18n.fanyi('log.manage.table.column.observed-time') }, + severity: { visible: true, label: this.i18n.fanyi('log.manage.table.column.severity') }, + body: { visible: true, label: this.i18n.fanyi('log.manage.table.column.body') }, + attributes: { visible: true, label: this.i18n.fanyi('log.manage.table.column.attributes') }, + resource: { visible: true, label: this.i18n.fanyi('log.manage.table.column.resource') }, + traceId: { visible: true, label: this.i18n.fanyi('log.manage.table.column.trace-id') }, + spanId: { visible: true, label: this.i18n.fanyi('log.manage.table.column.span-id') }, + traceFlags: { visible: true, label: this.i18n.fanyi('log.manage.table.column.trace-flags') }, + instrumentation: { visible: true, label: this.i18n.fanyi('log.manage.table.column.instrumentation') }, + droppedCount: { visible: true, label: this.i18n.fanyi('log.manage.table.column.dropped-count') } + }; + + // column control visible + columnControlVisible = false; + + ngOnInit(): void { + this.initChartThemes(); + this.query(); + } + + initChartThemes() { + this.severityOption = { + tooltip: { trigger: 'axis' }, + xAxis: { type: 'category', data: ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'] }, + yAxis: { type: 'value' }, + series: [ + { + type: 'bar', + data: [0, 0, 0, 0, 0, 0], + itemStyle: { + color: function (params: any) { + const colors = ['#d9d9d9', '#52c41a', '#1890ff', '#faad14', '#ff4d4f', '#722ed1']; + return colors[params.dataIndex]; + } + } + } + ] + }; + + this.trendOption = { + tooltip: { trigger: 'axis' }, + xAxis: { type: 'category', data: [] }, + yAxis: { type: 'value' }, + series: [ + { + type: 'line', + data: [], + smooth: true, + areaStyle: { opacity: 0.3 } + } + ] + }; + + this.traceCoverageOption = { + tooltip: { trigger: 'item', formatter: '{b}: {c}' }, + series: [ + { + type: 'pie', + radius: ['40%', '70%'], + data: [ + { name: this.i18n.fanyi('log.manage.chart.trace-coverage.with-trace'), value: 0, itemStyle: { color: '#52c41a' } }, + { name: this.i18n.fanyi('log.manage.chart.trace-coverage.without-trace'), value: 0, itemStyle: { color: '#ff4d4f' } }, + { name: this.i18n.fanyi('log.manage.chart.trace-coverage.with-span'), value: 0, itemStyle: { color: '#1890ff' } }, + { name: this.i18n.fanyi('log.manage.chart.trace-coverage.complete-trace-info'), value: 0, itemStyle: { color: '#722ed1' } } + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + }; + } + + onSeverityChartInit(ec: any) { + this.severityInstance = ec; + } + onTrendChartInit(ec: any) { + this.trendInstance = ec; + } + onTraceCoverageChartInit(ec: any) { + this.traceCoverageInstance = ec; + } + + refreshSeverityChartFromOverview(overviewStats: any) { + const traceCount = overviewStats.traceCount || 0; + const debugCount = overviewStats.debugCount || 0; + const infoCount = overviewStats.infoCount || 0; + const warnCount = overviewStats.warnCount || 0; + const errorCount = overviewStats.errorCount || 0; + const fatalCount = overviewStats.fatalCount || 0; + + const data = [traceCount, debugCount, infoCount, warnCount, errorCount, fatalCount]; + const option: EChartsOption = { + ...this.severityOption, + series: [ + { + ...(this.severityOption.series as any)?.[0], + data + } + ] + }; + this.severityOption = option; + if (this.severityInstance) this.severityInstance.setOption(option); + } + + refreshTrendChart(hourlyStats: Record) { + const sortedHours = Object.keys(hourlyStats).sort(); + const data = sortedHours.map(hour => hourlyStats[hour]); + const option: EChartsOption = { + ...this.trendOption, + xAxis: { + type: 'category', + data: sortedHours + }, + series: [{ ...(this.trendOption.series as any)?.[0], data }] + }; + this.trendOption = option; + if (this.trendInstance) this.trendInstance.setOption(option); + } + + refreshTraceCoverageChart(traceCoverageData: any) { + const coverage = traceCoverageData.traceCoverage || {}; + const data = [ + { + name: this.i18n.fanyi('log.manage.chart.trace-coverage.with-trace'), + value: coverage.withTrace || 0, + itemStyle: { color: '#52c41a' } + }, + { + name: this.i18n.fanyi('log.manage.chart.trace-coverage.without-trace'), + value: coverage.withoutTrace || 0, + itemStyle: { color: '#ff4d4f' } + }, + { + name: this.i18n.fanyi('log.manage.chart.trace-coverage.with-span'), + value: coverage.withSpan || 0, + itemStyle: { color: '#1890ff' } + }, + { + name: this.i18n.fanyi('log.manage.chart.trace-coverage.complete-trace-info'), + value: coverage.withBothTraceAndSpan || 0, + itemStyle: { color: '#722ed1' } + } + ]; + const option: EChartsOption = { + ...this.traceCoverageOption, + series: [{ ...(this.traceCoverageOption.series as any)?.[0], data }] + }; + this.traceCoverageOption = option; + if (this.traceCoverageInstance) this.traceCoverageInstance.setOption(option); + } + + query() { + this.loading = true; + const start = this.timeRange?.[0]?.getTime(); + const end = this.timeRange?.[1]?.getTime(); + + const obs = this.logSvc.list( + start, + end, + this.traceId, + this.spanId, + this.severityNumber, + this.severityText, + this.pageIndex - 1, + this.pageSize + ); + + obs.subscribe({ + next: message => { + if (message.code === 0) { + const pageData = message.data; + this.data = pageData.content; + this.totalElements = pageData.totalElements; + this.totalPages = pageData.totalPages; + this.pageIndex = pageData.number + 1; + + // Clear selection when data changes + this.setOfCheckedId.clear(); + this.refreshCheckedStatus(); + + this.loadStatsWithFilters(); + } else { + this.msg.warning(message.msg || this.i18n.fanyi('common.notify.query-fail')); + } + this.loading = false; + }, + error: () => { + this.loading = false; + this.msg.error(this.i18n.fanyi('common.notify.query-fail')); + } + }); + } + + loadStatsWithFilters() { + const start = this.timeRange?.[0]?.getTime(); + const end = this.timeRange?.[1]?.getTime(); + const traceId = this.traceId || undefined; + const spanId = this.spanId || undefined; + const severity = this.severityNumber || undefined; + const severityText = this.severityText || undefined; + + this.logSvc.overviewStats(start, end, traceId, spanId, severity, severityText).subscribe({ + next: message => { + if (message.code === 0) { + this.overviewStats = message.data || {}; + this.refreshSeverityChartFromOverview(this.overviewStats); + } + } + }); + + this.logSvc.traceCoverageStats(start, end, traceId, spanId, severity, severityText).subscribe({ + next: message => { + if (message.code === 0) { + this.refreshTraceCoverageChart(message.data || {}); + } + } + }); + + this.logSvc.trendStats(start, end, traceId, spanId, severity, severityText).subscribe({ + next: message => { + if (message.code === 0) { + this.refreshTrendChart(message.data?.hourlyStats || {}); + } + } + }); + } + + clearFilters() { + this.timeRange = []; + this.severityNumber = undefined; + this.traceId = ''; + this.spanId = ''; + this.severityText = ''; + this.pageIndex = 1; + this.query(); + } + + toggleStatistics() { + this.showStatistics = !this.showStatistics; + } + + // Batch selection methods + updateCheckedSet(id: string, checked: boolean): void { + if (checked) { + this.setOfCheckedId.add(id); + } else { + this.setOfCheckedId.delete(id); + } + } + + onItemChecked(id: string, checked: boolean): void { + this.updateCheckedSet(id, checked); + this.refreshCheckedStatus(); + } + + onAllChecked(checked: boolean): void { + this.data.forEach(item => this.updateCheckedSet(this.getLogId(item), checked)); + this.refreshCheckedStatus(); + } + + refreshCheckedStatus(): void { + this.checked = this.data.every(item => this.setOfCheckedId.has(this.getLogId(item))); + this.indeterminate = this.data.some(item => this.setOfCheckedId.has(this.getLogId(item))) && !this.checked; + } + + getLogId(item: LogEntry): string { + return `${item.timeUnixNano}_${item.traceId || 'no-trace'}`; + } + + batchDelete(): void { + if (this.setOfCheckedId.size === 0) { + this.msg.warning(this.i18n.fanyi('common.notify.no-select-delete')); + return; + } + + const selectedItems = this.data.filter(item => this.setOfCheckedId.has(this.getLogId(item))); + + this.modal.confirm({ + nzTitle: this.i18n.fanyi('common.confirm.delete-batch'), + nzOkText: this.i18n.fanyi('common.button.delete'), + nzOkDanger: true, + nzCancelText: this.i18n.fanyi('common.button.cancel'), + nzOnOk: () => { + this.performBatchDelete(selectedItems); + } + }); + } + + performBatchDelete(selectedItems: LogEntry[]): void { + const timeUnixNanos = selectedItems.filter(item => item.timeUnixNano != null).map(item => item.timeUnixNano!); + + if (timeUnixNanos.length === 0) { + this.msg.warning(this.i18n.fanyi('common.notify.no-select-delete')); + return; + } + + this.logSvc.batchDelete(timeUnixNanos).subscribe({ + next: message => { + if (message.code === 0) { + this.msg.success(this.i18n.fanyi('common.notify.delete-success')); + this.setOfCheckedId.clear(); + this.refreshCheckedStatus(); + this.query(); + } else { + this.msg.error(message.msg || this.i18n.fanyi('common.notify.delete-fail')); + } + }, + error: () => { + this.msg.error(this.i18n.fanyi('common.notify.delete-fail')); + } + }); + } + + onTablePageChange(params: { pageIndex: number; pageSize: number; sort: any; filter: any }) { + this.pageIndex = params.pageIndex; + this.pageSize = params.pageSize; + this.query(); + } + + getSeverityColor(severityNumber?: number): string { + if (!severityNumber) return 'default'; + if (severityNumber >= 21 && severityNumber <= 24) return 'purple'; // FATAL + if (severityNumber >= 17 && severityNumber <= 20) return 'red'; // ERROR + if (severityNumber >= 13 && severityNumber <= 16) return 'orange'; // WARN + if (severityNumber >= 9 && severityNumber <= 12) return 'blue'; // INFO + if (severityNumber >= 5 && severityNumber <= 8) return 'green'; // DEBUG + if (severityNumber >= 1 && severityNumber <= 4) return 'default'; // TRACE + return 'default'; + } + + getBodyText(body: any): string { + if (!body) return ''; + if (typeof body === 'string') return body.length > 100 ? `${body.substr(0, 100)}...` : body; + if (typeof body === 'object') { + const str = JSON.stringify(body); + return str.length > 100 ? `${str.substr(0, 100)}...` : str; + } + return String(body); + } + + getObjectText(obj: any): string { + if (!obj) return ''; + if (typeof obj === 'object') { + const keys = Object.keys(obj); + if (keys.length === 0) return ''; + if (keys.length === 1) { + return `${keys[0]}: ${obj[keys[0]]}`; + } + return `${keys[0]}: ${obj[keys[0]]} (+${keys.length - 1} more)`; + } + return String(obj); + } + + getInstrumentationText(scope: any): string { + if (!scope) return ''; + const parts = []; + if (scope.name) parts.push(`Name: ${scope.name}`); + if (scope.version) parts.push(`Version: ${scope.version}`); + if (scope.attributes && Object.keys(scope.attributes).length > 0) { + parts.push(`Attributes: ${Object.keys(scope.attributes).length} items`); + } + return parts.join('\n'); + } + + // Modal methods + showLogDetails(logEntry: LogEntry): void { + this.selectedLogEntry = logEntry; + this.isModalVisible = true; + } + + handleModalCancel(): void { + this.isModalVisible = false; + this.selectedLogEntry = null; + } + + getLogEntryJson(logEntry: LogEntry): string { + return JSON.stringify(logEntry, null, 2); + } + + formatTimestamp(timeUnixNano: number | undefined): string { + if (!timeUnixNano) return ''; + return new Date(timeUnixNano / 1000000).toLocaleString(); + } + + copyToClipboard(text: string): void { + navigator.clipboard + .writeText(text) + .then(() => { + this.msg.success(this.i18n.fanyi('common.notify.copy-success')); + }) + .catch(err => { + console.error('Failed to copy: ', err); + this.msg.error(this.i18n.fanyi('common.notify.copy-fail')); + }); + } + + toggleColumnVisibility(column: string): void { + if (this.columnVisibility[column as keyof typeof this.columnVisibility]) { + this.columnVisibility[column as keyof typeof this.columnVisibility].visible = + !this.columnVisibility[column as keyof typeof this.columnVisibility].visible; + } + } + + getVisibleColumnsCount(): number { + return Object.values(this.columnVisibility).filter(col => col.visible).length; + } + + getColumnKeys(): string[] { + return Object.keys(this.columnVisibility); + } + + getColumnVisibility(): any { + return this.columnVisibility; + } + + resetColumns(): void { + Object.keys(this.columnVisibility).forEach(key => { + this.columnVisibility[key as keyof typeof this.columnVisibility].visible = true; + }); + this.msg.success(this.i18n.fanyi('common.notify.operate-success')); + } + + toggleColumnControl(): void { + this.columnControlVisible = !this.columnControlVisible; + } +} diff --git a/web-app/src/app/routes/log/log-routing.module.ts b/web-app/src/app/routes/log/log-routing.module.ts index 04dd31568f6..7ba2eb7916e 100644 --- a/web-app/src/app/routes/log/log-routing.module.ts +++ b/web-app/src/app/routes/log/log-routing.module.ts @@ -21,12 +21,14 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { LogIntegrationComponent } from './log-integration/log-integration.component'; +import { LogManageComponent } from './log-manage/log-manage.component'; import { LogStreamComponent } from './log-stream/log-stream.component'; const routes: Routes = [ { path: '', component: LogIntegrationComponent }, { path: 'integration/:source', component: LogIntegrationComponent }, - { path: 'stream', component: LogStreamComponent } + { path: 'stream', component: LogStreamComponent }, + { path: 'manage', component: LogManageComponent } ]; @NgModule({ diff --git a/web-app/src/app/routes/log/log.module.ts b/web-app/src/app/routes/log/log.module.ts index eca9852b9bf..1f117409421 100644 --- a/web-app/src/app/routes/log/log.module.ts +++ b/web-app/src/app/routes/log/log.module.ts @@ -27,6 +27,7 @@ import { NzTableModule } from 'ng-zorro-antd/table'; import { NzTagModule } from 'ng-zorro-antd/tag'; import { LogIntegrationComponent } from './log-integration/log-integration.component'; +import { LogManageComponent } from './log-manage/log-manage.component'; import { LogRoutingModule } from './log-routing.module'; import { LogStreamComponent } from './log-stream/log-stream.component'; @@ -43,7 +44,8 @@ const COMPONENTS: Array> = []; NzEmptyModule, NzBreadCrumbModule, LogIntegrationComponent, - LogStreamComponent + LogStreamComponent, + LogManageComponent ], declarations: COMPONENTS }) diff --git a/web-app/src/app/service/log.service.ts b/web-app/src/app/service/log.service.ts new file mode 100644 index 00000000000..2a4a0a19aae --- /dev/null +++ b/web-app/src/app/service/log.service.ts @@ -0,0 +1,123 @@ +/* + * 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. + */ + +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { LogEntry } from '../pojo/LogEntry'; +import { Message } from '../pojo/Message'; +import { Page } from '../pojo/Page'; + +// endpoints +const logs_list_uri = '/logs/list'; +const logs_stats_overview_uri = '/logs/stats/overview'; +const logs_stats_trend_uri = '/logs/stats/trend'; +const logs_stats_trace_coverage_uri = '/logs/stats/trace-coverage'; +const logs_batch_delete_uri = '/logs'; + +@Injectable({ providedIn: 'root' }) +export class LogService { + constructor(private http: HttpClient) {} + + public list( + start?: number, + end?: number, + traceId?: string, + spanId?: string, + severityNumber?: number, + severityText?: string, + pageIndex: number = 0, + pageSize: number = 20 + ): Observable>> { + let params = new HttpParams(); + if (start != null) params = params.set('start', start); + if (end != null) params = params.set('end', end); + if (traceId) params = params.set('traceId', traceId); + if (spanId) params = params.set('spanId', spanId); + if (severityNumber != null) params = params.set('severityNumber', severityNumber); + if (severityText) params = params.set('severityText', severityText); + params = params.set('pageIndex', pageIndex); + params = params.set('pageSize', pageSize); + return this.http.get>(logs_list_uri, { params }); + } + + public overviewStats( + start?: number, + end?: number, + traceId?: string, + spanId?: string, + severityNumber?: number, + severityText?: string + ): Observable> { + let params = new HttpParams(); + if (start != null) params = params.set('start', start); + if (end != null) params = params.set('end', end); + if (traceId) params = params.set('traceId', traceId); + if (spanId) params = params.set('spanId', spanId); + if (severityNumber != null) params = params.set('severityNumber', severityNumber); + if (severityText) params = params.set('severityText', severityText); + return this.http.get>(logs_stats_overview_uri, { params }); + } + + public trendStats( + start?: number, + end?: number, + traceId?: string, + spanId?: string, + severityNumber?: number, + severityText?: string + ): Observable> { + let params = new HttpParams(); + if (start != null) params = params.set('start', start); + if (end != null) params = params.set('end', end); + if (traceId) params = params.set('traceId', traceId); + if (spanId) params = params.set('spanId', spanId); + if (severityNumber != null) params = params.set('severityNumber', severityNumber); + if (severityText) params = params.set('severityText', severityText); + return this.http.get>(logs_stats_trend_uri, { params }); + } + + public traceCoverageStats( + start?: number, + end?: number, + traceId?: string, + spanId?: string, + severityNumber?: number, + severityText?: string + ): Observable> { + let params = new HttpParams(); + if (start != null) params = params.set('start', start); + if (end != null) params = params.set('end', end); + if (traceId) params = params.set('traceId', traceId); + if (spanId) params = params.set('spanId', spanId); + if (severityNumber != null) params = params.set('severityNumber', severityNumber); + if (severityText) params = params.set('severityText', severityText); + return this.http.get>(logs_stats_trace_coverage_uri, { params }); + } + + public batchDelete(timeUnixNanos: number[]): Observable> { + let httpParams = new HttpParams(); + timeUnixNanos.forEach(timeUnixNano => { + httpParams = httpParams.append('timeUnixNanos', timeUnixNano); + }); + const options = { params: httpParams }; + return this.http.delete>(logs_batch_delete_uri, options); + } +} diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index d438b9c6a52..bc7102bf880 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -153,6 +153,12 @@ "i18n": "menu.log.stream", "icon": "anticon-file-text", "link": "/log/stream" + }, + { + "text": "Manage", + "i18n": "menu.log.manage", + "icon": "anticon-database", + "link": "/log/manage" } ] }, diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 9769a2d0fb4..6c151acd6bf 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -107,52 +107,6 @@ "alert.integration.token.new": "Click to Generate Token", "alert.integration.token.notice": "Token only be displayed once. Please keep your token secure. Do not share it with others.", "alert.integration.token.title": "Access Token", - "log.help.integration": "Unified management of logs, integrating and receiving log messages from external log sources, and performing actions such as collection, storage, alerting, and analysis.", - "log.help.integration.link": "https://hertzbeat.apache.org", - "log.help.stream": "Real-time log streaming and monitoring interface for viewing live log entries with filtering and search capabilities.", - "log.help.stream.link": "https://hertzbeat.apache.org", - "log.integration.source": "Integration Source", - "log.integration.source.otlp": "OTLP Protocol", - "log.integration.token.desc": "Token you generated that can be used to access the HertzBeat API.", - "log.integration.token.new": "Click to Generate Token", - "log.stream.title": "Log Stream", - "log.stream.live-logs": "Live Logs", - "log.stream.connected": "Connected", - "log.stream.connecting": "Connecting...", - "log.stream.disconnected": "Disconnected", - "log.stream.logs": "logs", - "log.stream.toggle-filters": "Toggle Filters", - "log.stream.hide-filters": "Hide Filters", - "log.stream.show-filters": "Show Filters", - "log.stream.resume": "Resume", - "log.stream.pause": "Pause", - "log.stream.clear": "Clear", - "log.stream.scroll-to-top": "Scroll to Top", - "log.stream.severity-number": "Severity Number:", - "log.stream.severity-number-placeholder": "Enter severity number", - "log.stream.severity-text": "Severity:", - "log.stream.severity-text-placeholder": "Enter severity text", - "log.stream.trace-id": "Trace ID:", - "log.stream.trace-id-placeholder": "Enter trace ID", - "log.stream.span-id": "Span ID:", - "log.stream.span-id-placeholder": "Enter span ID", - "log.stream.clear-filters": "Clear Filters", - "log.stream.clear-filters-tooltip": "Clear all filters", - "log.stream.no-logs": "No logs available", - "log.stream.unknown": "UNKNOWN", - "log.stream.trace": "Trace:", - "log.stream.span": "Span:", - "log.stream.copy-message": "Copy message", - "log.stream.attributes": "attributes", - "log.stream.resource": "resource", - "log.stream.log-entry-details": "Log Entry Details", - "log.stream.basic-information": "Basic Information", - "log.stream.severity": "Severity:", - "log.stream.timestamp": "Timestamp:", - "log.stream.trace-id-full": "Trace ID:", - "log.stream.span-id-full": "Span ID:", - "log.stream.message": "Message", - "log.stream.complete-json-data": "Complete JSON Data", "alert.notice.receiver": "Notice Receiver", "alert.notice.receiver.delete": "Delete Receiver", "alert.notice.receiver.edit": "Edit Receiver", @@ -630,6 +584,98 @@ "label.value": "Label Value", "labels.help": "Labels are everywhere. We can apply labels in resource grouping, tag matching under rules and others. [Label Manage] is used for unified management of labels, including adding, deleting, editing, etc.
You can use labels to classify and manage monitoring resources, such as binding labels for production and testing environments separately.", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", + "log.help.integration": "Unified log management, integrating and accessing log messages from external log sources for collection, storage, alerting, analysis, and more.", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.help.manage": "Log management and statistics, supporting operations such as log query, statistics, deletion, etc.", + "log.help.manage.link": "https://hertzbeat.apache.org", + "log.help.stream": "Real-time log stream monitoring interface for viewing real-time log entries, with filtering and search functions.", + "log.help.stream.link": "https://hertzbeat.apache.org", + "log.integration.source": "Integrated Log Source", + "log.integration.source.otlp": "OTLP Protocol", + "log.integration.token.desc": "The generated Token can be used to access the HertzBeat log access API", + "log.integration.token.new": "Click to generate Token", + "log.integration.token.notice": "This content will only be displayed once. Please keep your Token safe and do not disclose it to others.", + "log.integration.token.title": "Access Authentication Token", + "log.manage.batch-delete": "Batch Delete", + "log.manage.basic-information": "Basic Information", + "log.manage.clear": "Clear", + "log.manage.chart.log-trend": "Log Trend", + "log.manage.chart.severity-distribution": "Severity Distribution", + "log.manage.chart.trace-coverage": "Trace Coverage", + "log.manage.chart.trace-coverage.complete-trace-info": "Complete Trace Info", + "log.manage.chart.trace-coverage.with-span": "With Span ID", + "log.manage.chart.trace-coverage.with-trace": "With Trace ID", + "log.manage.chart.trace-coverage.without-trace": "Without Trace ID", + "log.manage.column-control": "Column Display Control", + "log.manage.column-setting": "Column Setting", + "log.manage.complete-json-data": "Complete JSON Data", + "log.manage.hide-statistics": "Hide Statistics", + "log.manage.log-entry-details": "Log Entry Details", + "log.manage.overview.debug-logs": "Debug Logs", + "log.manage.overview.error-logs": "Error Logs", + "log.manage.overview.fatal-logs": "Fatal Logs", + "log.manage.overview.info-logs": "Info Logs", + "log.manage.overview.total-logs": "Total Logs", + "log.manage.overview.warning-logs": "Warning Logs", + "log.manage.reset-all-columns": "Reset All Columns", + "log.manage.search": "Search", + "log.manage.severity-number": "Severity Number", + "log.manage.severity-text": "Severity", + "log.manage.show-statistics": "Show Statistics", + "log.manage.span-id": "Span ID", + "log.manage.table.column.attributes": "Attributes", + "log.manage.table.column.body": "Log Content", + "log.manage.table.column.dropped-count": "Dropped Count", + "log.manage.table.column.instrumentation": "Instrumentation", + "log.manage.table.column.observed-time": "Observed Time", + "log.manage.table.column.resource": "Resource", + "log.manage.table.column.severity": "Severity", + "log.manage.table.column.span-id": "Span ID", + "log.manage.table.column.time": "Time", + "log.manage.table.column.trace-flags": "Trace Flags", + "log.manage.table.column.trace-id": "Trace ID", + "log.manage.table.title": "Log List", + "log.manage.timestamp": "Timestamp", + "log.manage.title": "Log Management & Statistics", + "log.manage.trace-id": "Trace ID", + "log.stream.attributes": "Attributes", + "log.stream.basic-information": "Basic Information", + "log.stream.clear": "Clear", + "log.stream.clear-filters": "Clear Filters", + "log.stream.clear-filters-tooltip": "Clear All Filters", + "log.stream.complete-json-data": "Complete JSON Data", + "log.stream.connected": "Connected", + "log.stream.connecting": "Connecting...", + "log.stream.copy-message": "Copy Message", + "log.stream.disconnected": "Disconnected", + "log.stream.hide-filters": "Hide Filters", + "log.stream.live-logs": "Live Logs", + "log.stream.log-entry-details": "Log Entry Details", + "log.stream.logs": "Logs", + "log.stream.message": "Message", + "log.stream.no-logs": "No Available Logs", + "log.stream.pause": "Pause", + "log.stream.resource": "Resources", + "log.stream.resume": "Resume", + "log.stream.scroll-to-top": "Scroll to Top", + "log.stream.severity": "Severity:", + "log.stream.severity-number": "Severity Number:", + "log.stream.severity-number-placeholder": "Enter Severity Number", + "log.stream.severity-text": "Severity:", + "log.stream.severity-text-placeholder": "Enter Severity", + "log.stream.show-filters": "Show Filters", + "log.stream.span": "Span:", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.span-id-placeholder": "Enter Span ID", + "log.stream.title": "Log Stream", + "log.stream.timestamp": "Timestamp:", + "log.stream.toggle-filters": "Toggle Filters", + "log.stream.trace": "Trace:", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.trace-id-placeholder": "Enter Trace ID", + "log.stream.unknown": "Unknown", "menu.account": "Personal", "menu.account.binding": "Account Binding", "menu.account.center": "Personal Center", @@ -664,6 +710,10 @@ "menu.lang": "Language", "menu.link.guild": "User Guide", "menu.link.question": "FAQ", + "menu.log": "Log", + "menu.log.integration": "Integration", + "menu.log.manage": "Log Manage", + "menu.log.stream": "Log Stream", "menu.main": "Main", "menu.monitor": "Monitoring", "menu.monitor.bigdata": "Bigdata Monitor", diff --git a/web-app/src/assets/i18n/ja-JP.json b/web-app/src/assets/i18n/ja-JP.json index 0342d2bc012..35f1e4ddf7b 100644 --- a/web-app/src/assets/i18n/ja-JP.json +++ b/web-app/src/assets/i18n/ja-JP.json @@ -575,52 +575,98 @@ "label.value": "ラベル値", "labels.help": "ラベルは至る所にあります。リソースグループ化、ルール下のタグマッチングなどにラベルを適用できます。[ラベル管理]は、ラベルの統一管理に使用され、新規、削除、編集などが可能です。
ラベルを使用して監視リソースを分類および管理できます。例えば、本番環境とテスト環境にラベルをバインドすることができます。", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", - "log.help.integration": "ログの統一管理、外部ログソースからのログメッセージの統合および受信、収集、保存、アラート、分析などのアクションの実行。", + "log.help.integration": "ログを統合的に管理し、外部ログソースからのログメッセージを収集、保存、アラート、分析などを行います。", "log.help.integration.link": "https://hertzbeat.apache.org", - "log.integration.source": "統合ソース", - "log.integration.source.otlp": "OTLPプロトコル", - "log.integration.token.desc": "HertzBeat APIにアクセスするために生成したトークン。", - "log.integration.token.new": "トークンを生成するにはクリック", - "log.help.stream": "フィルタリングと検索機能を備えたライブログエントリを表示するためのリアルタイムログストリーミングおよび監視インターフェース。", + "log.help.manage": "ログ管理と統計、ログの検索、統計、削除などの操作をサポートします。", + "log.help.manage.link": "https://hertzbeat.apache.org", + "log.help.stream": "リアルタイムログストリーム監視画面で、リアルタイムログエントリの閲覧、フィルタや検索機能を備えています。", "log.help.stream.link": "https://hertzbeat.apache.org", - "log.stream.title": "ログストリーム", - "log.stream.live-logs": "ライブログ", + "log.integration.source": "統合ログソース", + "log.integration.source.otlp": "OTLPプロトコル", + "log.integration.token.desc": "生成されたトークンはHertzBeatログAPIへのアクセスに使用できます", + "log.integration.token.new": "クリックしてトークンを生成", + "log.integration.token.notice": "この内容は一度しか表示されません。トークンを大切に保管し、他人に漏らさないでください。", + "log.integration.token.title": "アクセス認証トークン", + "log.manage.batch-delete": "一括削除", + "log.manage.basic-information": "基本情報", + "log.manage.clear": "クリア", + "log.manage.chart.log-trend": "ログトレンド", + "log.manage.chart.severity-distribution": "重大度分布", + "log.manage.chart.trace-coverage": "トレースカバレッジ", + "log.manage.chart.trace-coverage.complete-trace-info": "完全なTrace情報", + "log.manage.chart.trace-coverage.with-span": "Span IDあり", + "log.manage.chart.trace-coverage.with-trace": "Trace IDあり", + "log.manage.chart.trace-coverage.without-trace": "Trace IDなし", + "log.manage.column-control": "列表示コントロール", + "log.manage.column-setting": "列設定", + "log.manage.complete-json-data": "完全なJSONデータ", + "log.manage.hide-statistics": "統計を非表示", + "log.manage.log-entry-details": "ログエントリ詳細", + "log.manage.overview.debug-logs": "デバッグログ", + "log.manage.overview.error-logs": "エラーログ", + "log.manage.overview.fatal-logs": "重大なログ", + "log.manage.overview.info-logs": "インフォログ", + "log.manage.overview.total-logs": "総ログ数", + "log.manage.overview.warning-logs": "警告ログ", + "log.manage.reset-all-columns": "すべての列をリセット", + "log.manage.search": "検索", + "log.manage.severity-number": "重大度番号", + "log.manage.severity-text": "重大度", + "log.manage.show-statistics": "統計を表示", + "log.manage.span-id": "Span ID", + "log.manage.table.column.attributes": "属性", + "log.manage.table.column.body": "ログ内容", + "log.manage.table.column.dropped-count": "ドロップ数", + "log.manage.table.column.instrumentation": "収集ツール", + "log.manage.table.column.observed-time": "観測時間", + "log.manage.table.column.resource": "リソース", + "log.manage.table.column.severity": "重大度", + "log.manage.table.column.span-id": "Span ID", + "log.manage.table.column.time": "時間", + "log.manage.table.column.trace-flags": "Traceフラグ", + "log.manage.table.column.trace-id": "Trace ID", + "log.manage.table.title": "ログリスト", + "log.manage.timestamp": "タイムスタンプ", + "log.manage.title": "ログ管理と統計", + "log.manage.trace-id": "Trace ID", + "log.stream.attributes": "属性", + "log.stream.basic-information": "基本情報", + "log.stream.clear": "クリア", + "log.stream.clear-filters": "フィルターをクリア", + "log.stream.clear-filters-tooltip": "すべてのフィルターをクリア", + "log.stream.complete-json-data": "完全なJSONデータ", "log.stream.connected": "接続済み", "log.stream.connecting": "接続中...", - "log.stream.disconnected": "切断", - "log.stream.logs": "ログ", - "log.stream.toggle-filters": "フィルターを切り替え", + "log.stream.copy-message": "メッセージをコピー", + "log.stream.disconnected": "切断されました", "log.stream.hide-filters": "フィルターを非表示", - "log.stream.show-filters": "フィルターを表示", - "log.stream.resume": "再開", + "log.stream.live-logs": "リアルタイムログ", + "log.stream.log-entry-details": "ログエントリ詳細", + "log.stream.logs": "件のログ", + "log.stream.message": "メッセージ", + "log.stream.no-logs": "利用可能なログはありません", "log.stream.pause": "一時停止", - "log.stream.clear": "クリア", - "log.stream.scroll-to-top": "トップにスクロール", + "log.stream.resource": "件のリソース", + "log.stream.resume": "再開", + "log.stream.scroll-to-top": "トップへスクロール", + "log.stream.severity": "重大度:", "log.stream.severity-number": "重大度番号:", - "log.stream.severity-number-placeholder": "重大度番号を入力してください", + "log.stream.severity-number-placeholder": "重大度番号を入力", "log.stream.severity-text": "重大度:", - "log.stream.severity-text-placeholder": "重大度テキストを入力してください", - "log.stream.trace-id": "トレースID:", - "log.stream.trace-id-placeholder": "トレースIDを入力してください", - "log.stream.span-id": "スパンID:", - "log.stream.span-id-placeholder": "スパンIDを入力してください", - "log.stream.clear-filters": "フィルターをクリア", - "log.stream.clear-filters-tooltip": "すべてのフィルターをクリア", - "log.stream.no-logs": "利用可能なログがありません", - "log.stream.unknown": "不明", - "log.stream.trace": "トレース:", + "log.stream.severity-text-placeholder": "重大度を入力", + "log.stream.show-filters": "フィルターを表示", "log.stream.span": "スパン:", - "log.stream.copy-message": "メッセージをコピー", - "log.stream.attributes": "属性", - "log.stream.resource": "リソース", - "log.stream.log-entry-details": "ログエントリの詳細", - "log.stream.basic-information": "基本情報", - "log.stream.severity": "重大度:", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.span-id-placeholder": "Span IDを入力", + "log.stream.title": "ログストリーム", "log.stream.timestamp": "タイムスタンプ:", - "log.stream.trace-id-full": "トレースID:", - "log.stream.span-id-full": "スパンID:", - "log.stream.message": "メッセージ", - "log.stream.complete-json-data": "完全なJSONデータ", + "log.stream.toggle-filters": "フィルターを切り替え", + "log.stream.trace": "トレース:", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.trace-id-placeholder": "Trace IDを入力", + "log.stream.unknown": "不明", "menu.account": "個人", "menu.account.binding": "アカウントバインディング", "menu.account.center": "個人センター", @@ -655,6 +701,10 @@ "menu.lang": "言語", "menu.link.guild": "ユーザーガイド", "menu.link.question": "FAQ", + "menu.log": "ログ", + "menu.log.integration": "統合", + "menu.log.manage": "ログ管理", + "menu.log.stream": "ログストリーム", "menu.main": "メイン", "menu.monitor": "監視", "menu.monitor.bigdata": "ビッグデータ監視", diff --git a/web-app/src/assets/i18n/pt-BR.json b/web-app/src/assets/i18n/pt-BR.json index 8a934c70339..3e03bd8016c 100644 --- a/web-app/src/assets/i18n/pt-BR.json +++ b/web-app/src/assets/i18n/pt-BR.json @@ -627,54 +627,104 @@ "dashboard.monitors.sub-title": "A Distribuição dos Monitores", "dashboard.monitors.formatter": " Monitores ", "dashboard.monitors.distribute": "Distribuição do Monitor", - "log.help.integration": "Gerenciamento unificado de logs, integrando e recebendo mensagens de log de fontes de log externas, e executando ações como coleta, armazenamento, alertas e análise.", + "log.help.integration": "Gerencie logs de forma unificada, integre e colete mensagens de logs de fontes externas, realize coleta, armazenamento, alertas e análise.", "log.help.integration.link": "https://hertzbeat.apache.org", - "log.integration.source": "Fonte de Integração", - "log.integration.source.otlp": "Protocolo OTLP", - "log.integration.token.desc": "Token que você gerou que pode ser usado para acessar a API do HertzBeat.", - "log.integration.token.new": "Clique para Gerar Token", - "log.help.stream": "Interface de streaming e monitoramento de logs em tempo real para visualizar entradas de log ao vivo com capacidades de filtragem e pesquisa.", + "log.help.manage": "Gerenciamento e estatísticas de logs, suporta consulta, estatísticas e exclusão de logs.", + "log.help.manage.link": "https://hertzbeat.apache.org", + "log.help.stream": "Interface de monitoramento de fluxo de logs em tempo real, usada para visualizar entradas de logs em tempo real, com funções de filtragem e pesquisa.", "log.help.stream.link": "https://hertzbeat.apache.org", - "log.stream.title": "Fluxo de Log", - "log.stream.live-logs": "Logs ao Vivo", + "log.integration.source": "Fonte de log integrada", + "log.integration.source.otlp": "Protocolo OTLP", + "log.integration.token.desc": "O Token gerado pode ser usado para acessar a API de integração de logs do HertzBeat", + "log.integration.token.new": "Clique para gerar Token", + "log.integration.token.notice": "Este conteúdo será exibido apenas uma vez, por favor, guarde seu Token com segurança e não o compartilhe com outros", + "log.integration.token.title": "Token de autenticação de acesso", + "log.manage.batch-delete": "Excluir em lote", + "log.manage.basic-information": "Informações básicas", + "log.manage.clear": "Limpar", + "log.manage.chart.log-trend": "Tendência de logs", + "log.manage.chart.severity-distribution": "Distribuição de severidade", + "log.manage.chart.trace-coverage": "Cobertura de rastreamento", + "log.manage.chart.trace-coverage.complete-trace-info": "Informações completas de Trace", + "log.manage.chart.trace-coverage.with-span": "Com Span ID", + "log.manage.chart.trace-coverage.with-trace": "Com Trace ID", + "log.manage.chart.trace-coverage.without-trace": "Sem Trace ID", + "log.manage.column-control": "Controle de exibição de colunas", + "log.manage.column-setting": "Configuração de colunas", + "log.manage.complete-json-data": "Dados JSON completos", + "log.manage.hide-statistics": "Ocultar estatísticas", + "log.manage.log-entry-details": "Detalhes da entrada de log", + "log.manage.overview.debug-logs": "Logs de depuração", + "log.manage.overview.error-logs": "Logs de erro", + "log.manage.overview.fatal-logs": "Logs críticos", + "log.manage.overview.info-logs": "Logs informativos", + "log.manage.overview.total-logs": "Total de logs", + "log.manage.overview.warning-logs": "Logs de aviso", + "log.manage.reset-all-columns": "Redefinir todas as colunas", + "log.manage.search": "Pesquisar", + "log.manage.severity-number": "Número de severidade", + "log.manage.severity-text": "Severidade", + "log.manage.show-statistics": "Mostrar estatísticas", + "log.manage.span-id": "Span ID", + "log.manage.table.column.attributes": "Atributos", + "log.manage.table.column.body": "Conteúdo do log", + "log.manage.table.column.dropped-count": "Contagem descartada", + "log.manage.table.column.instrumentation": "Ferramenta de coleta", + "log.manage.table.column.observed-time": "Hora de observação", + "log.manage.table.column.resource": "Fonte", + "log.manage.table.column.severity": "Severidade", + "log.manage.table.column.span-id": "Span ID", + "log.manage.table.column.time": "Hora", + "log.manage.table.column.trace-flags": "Marca de Trace", + "log.manage.table.column.trace-id": "Trace ID", + "log.manage.table.title": "Lista de logs", + "log.manage.timestamp": "Timestamp", + "log.manage.title": "Gerenciamento e estatísticas de logs", + "log.manage.trace-id": "Trace ID", + "log.stream.attributes": "atributos", + "log.stream.basic-information": "Informações básicas", + "log.stream.clear": "Limpar", + "log.stream.clear-filters": "Limpar filtros", + "log.stream.clear-filters-tooltip": "Limpar todos os filtros", + "log.stream.complete-json-data": "Dados JSON completos", "log.stream.connected": "Conectado", "log.stream.connecting": "Conectando...", + "log.stream.copy-message": "Copiar mensagem", "log.stream.disconnected": "Desconectado", + "log.stream.hide-filters": "Ocultar filtros", + "log.stream.live-logs": "Logs em tempo real", + "log.stream.log-entry-details": "Detalhes da entrada de log", "log.stream.logs": "logs", - "log.stream.toggle-filters": "Alternar Filtros", - "log.stream.hide-filters": "Ocultar Filtros", - "log.stream.show-filters": "Mostrar Filtros", - "log.stream.resume": "Retomar", + "log.stream.message": "Mensagem", + "log.stream.no-logs": "Nenhum log disponível", "log.stream.pause": "Pausar", - "log.stream.clear": "Limpar", - "log.stream.scroll-to-top": "Rolar para o Topo", - "log.stream.severity-number": "Número de Severidade:", + "log.stream.resource": "fontes", + "log.stream.resume": "Retomar", + "log.stream.scroll-to-top": "Rolar para o topo", + "log.stream.severity": "Severidade:", + "log.stream.severity-number": "Número de severidade:", "log.stream.severity-number-placeholder": "Digite o número de severidade", "log.stream.severity-text": "Severidade:", - "log.stream.severity-text-placeholder": "Digite o texto de severidade", - "log.stream.trace-id": "ID do Rastreamento:", - "log.stream.trace-id-placeholder": "Digite o ID do rastreamento", - "log.stream.span-id": "ID do Span:", - "log.stream.span-id-placeholder": "Digite o ID do span", - "log.stream.clear-filters": "Limpar Filtros", - "log.stream.clear-filters-tooltip": "Limpar todos os filtros", - "log.stream.no-logs": "Nenhum log disponível", - "log.stream.unknown": "DESCONHECIDO", - "log.stream.trace": "Rastreamento:", + "log.stream.severity-text-placeholder": "Digite a severidade", + "log.stream.show-filters": "Mostrar filtros", "log.stream.span": "Span:", - "log.stream.copy-message": "Copiar mensagem", - "log.stream.attributes": "atributos", - "log.stream.resource": "recurso", - "log.stream.log-entry-details": "Detalhes da Entrada de Log", - "log.stream.basic-information": "Informações Básicas", - "log.stream.severity": "Severidade:", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.span-id-placeholder": "Digite o Span ID", + "log.stream.title": "Fluxo de log", "log.stream.timestamp": "Timestamp:", - "log.stream.trace-id-full": "ID do Rastreamento:", - "log.stream.span-id-full": "ID do Span:", - "log.stream.message": "Mensagem", - "log.stream.complete-json-data": "Dados JSON Completos", + "log.stream.toggle-filters": "Alternar filtros", + "log.stream.trace": "Trace:", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.trace-id-placeholder": "Digite o Trace ID", + "log.stream.unknown": "Desconhecido", "menu.link.question": "FAQ", "menu.link.guild": "Guia do Usuário", + "menu.log": "Log", + "menu.log.integration": "Integração", + "menu.log.manage": "Gerenciamento de Log", + "menu.log.stream": "Fluxo de Log", "menu.account": "Página pessoal", "menu.account.binding": "Vinculação de conta", "menu.account.center": "Centro pessoal", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 42ce10a5391..40faa0d3f3c 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -107,54 +107,6 @@ "alert.integration.token.new": "点击生成 Token", "alert.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", "alert.integration.token.title": "访问认证 Token", - "log.help.integration": "统一管理日志,集成接入外部日志源的日志消息,对其进行收集,存储,告警和分析等。", - "log.help.integration.link": "https://hertzbeat.apache.org", - "log.help.stream": "实时日志流监控界面,用于查看实时日志条目,具备过滤和搜索功能。", - "log.help.stream.link": "https://hertzbeat.apache.org", - "log.integration.source": "集成日志源", - "log.integration.source.otlp": "OTLP 协议", - "log.integration.token.desc": "生成的 Token 可用于访问 HertzBeat 日志接入API", - "log.integration.token.new": "点击生成 Token", - "log.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", - "log.integration.token.title": "访问认证 Token", - "log.stream.title": "日志流", - "log.stream.live-logs": "实时日志", - "log.stream.connected": "已连接", - "log.stream.connecting": "连接中...", - "log.stream.disconnected": "已断开", - "log.stream.logs": "条日志", - "log.stream.toggle-filters": "切换过滤器", - "log.stream.hide-filters": "隐藏过滤器", - "log.stream.show-filters": "显示过滤器", - "log.stream.resume": "恢复", - "log.stream.pause": "暂停", - "log.stream.clear": "清除", - "log.stream.scroll-to-top": "滚动到顶部", - "log.stream.severity-number": "严重程度编号:", - "log.stream.severity-number-placeholder": "输入严重程度编号", - "log.stream.severity-text": "严重程度:", - "log.stream.severity-text-placeholder": "输入严重程度", - "log.stream.trace-id": "Trace ID:", - "log.stream.trace-id-placeholder": "输入Trace ID", - "log.stream.span-id": "Span ID:", - "log.stream.span-id-placeholder": "输入Span ID", - "log.stream.clear-filters": "清除过滤器", - "log.stream.clear-filters-tooltip": "清除所有过滤器", - "log.stream.no-logs": "暂无可用日志", - "log.stream.unknown": "未知", - "log.stream.trace": "跟踪:", - "log.stream.span": "跨度:", - "log.stream.copy-message": "复制消息", - "log.stream.attributes": "个属性", - "log.stream.resource": "个源", - "log.stream.log-entry-details": "日志条目详情", - "log.stream.basic-information": "基本信息", - "log.stream.severity": "严重程度:", - "log.stream.timestamp": "时间戳:", - "log.stream.trace-id-full": "Trace ID:", - "log.stream.span-id-full": "Span ID:", - "log.stream.message": "消息", - "log.stream.complete-json-data": "完整JSON数据", "alert.notice.receiver": "通知媒介", "alert.notice.receiver.delete": "删除接收对象", "alert.notice.receiver.edit": "编辑接收对象", @@ -635,6 +587,98 @@ "label.value": "标签值", "labels.help": "标签无处不在,我们可以应用标签在资源分组,规则下的标签匹配等场景。标签管理用于对标签的统一管理维护,包含新增,删除,编辑等操作。
例如:您可以使用标签对监控资源进行分类管理,给资源分别绑定生产环境、测试环境的标签,在告警通知时通过标签匹配不同的通知人。", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", + "log.help.integration": "统一管理日志,集成接入外部日志源的日志消息,对其进行收集,存储,告警和分析等。", + "log.help.integration.link": "https://hertzbeat.apache.org", + "log.help.manage": "日志管理与统计,支持对日志的查询,统计,删除等操作。", + "log.help.manage.link": "https://hertzbeat.apache.org", + "log.help.stream": "实时日志流监控界面,用于查看实时日志条目,具备过滤和搜索功能。", + "log.help.stream.link": "https://hertzbeat.apache.org", + "log.integration.source": "集成日志源", + "log.integration.source.otlp": "OTLP 协议", + "log.integration.token.desc": "生成的 Token 可用于访问 HertzBeat 日志接入API", + "log.integration.token.new": "点击生成 Token", + "log.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人", + "log.integration.token.title": "访问认证 Token", + "log.manage.batch-delete": "批量删除", + "log.manage.basic-information": "基本信息", + "log.manage.clear": "清空", + "log.manage.chart.log-trend": "日志趋势", + "log.manage.chart.severity-distribution": "严重程度分布", + "log.manage.chart.trace-coverage": "跟踪覆盖率", + "log.manage.chart.trace-coverage.complete-trace-info": "完整 Trace 信息", + "log.manage.chart.trace-coverage.with-span": "有 Span ID", + "log.manage.chart.trace-coverage.with-trace": "有 Trace ID", + "log.manage.chart.trace-coverage.without-trace": "无 Trace ID", + "log.manage.column-control": "列显示控制", + "log.manage.column-setting": "列设置", + "log.manage.complete-json-data": "完整JSON数据", + "log.manage.hide-statistics": "隐藏统计", + "log.manage.log-entry-details": "日志条目详情", + "log.manage.overview.debug-logs": "调试日志", + "log.manage.overview.error-logs": "错误日志", + "log.manage.overview.fatal-logs": "严重日志", + "log.manage.overview.info-logs": "普通日志", + "log.manage.overview.total-logs": "总日志", + "log.manage.overview.warning-logs": "警告日志", + "log.manage.reset-all-columns": "重置所有列", + "log.manage.search": "搜索", + "log.manage.severity-number": "严重程度编号", + "log.manage.severity-text": "严重程度", + "log.manage.show-statistics": "显示统计", + "log.manage.span-id": "Span ID", + "log.manage.table.column.attributes": "属性", + "log.manage.table.column.body": "日志内容", + "log.manage.table.column.dropped-count": "丢弃计数", + "log.manage.table.column.instrumentation": "采集工具", + "log.manage.table.column.observed-time": "观察时间", + "log.manage.table.column.resource": "来源", + "log.manage.table.column.severity": "严重程度", + "log.manage.table.column.span-id": "Span ID", + "log.manage.table.column.time": "时间", + "log.manage.table.column.trace-flags": "Trace 标记", + "log.manage.table.column.trace-id": "Trace ID", + "log.manage.table.title": "日志列表", + "log.manage.timestamp": "时间戳", + "log.manage.title": "日志管理与统计", + "log.manage.trace-id": "Trace ID", + "log.stream.attributes": "个属性", + "log.stream.basic-information": "基本信息", + "log.stream.clear": "清除", + "log.stream.clear-filters": "清除过滤器", + "log.stream.clear-filters-tooltip": "清除所有过滤器", + "log.stream.complete-json-data": "完整JSON数据", + "log.stream.connected": "已连接", + "log.stream.connecting": "连接中...", + "log.stream.copy-message": "复制消息", + "log.stream.disconnected": "已断开", + "log.stream.hide-filters": "隐藏过滤器", + "log.stream.live-logs": "实时日志", + "log.stream.log-entry-details": "日志条目详情", + "log.stream.logs": "条日志", + "log.stream.message": "消息", + "log.stream.no-logs": "暂无可用日志", + "log.stream.pause": "暂停", + "log.stream.resource": "个源", + "log.stream.resume": "恢复", + "log.stream.scroll-to-top": "滚动到顶部", + "log.stream.severity": "严重程度:", + "log.stream.severity-number": "严重程度编号:", + "log.stream.severity-number-placeholder": "输入严重程度编号", + "log.stream.severity-text": "严重程度:", + "log.stream.severity-text-placeholder": "输入严重程度", + "log.stream.show-filters": "显示过滤器", + "log.stream.span": "跨度:", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.span-id-placeholder": "输入Span ID", + "log.stream.title": "日志流", + "log.stream.timestamp": "时间戳:", + "log.stream.toggle-filters": "切换过滤器", + "log.stream.trace": "跟踪:", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.trace-id-placeholder": "输入Trace ID", + "log.stream.unknown": "未知", "menu.account": "个人页", "menu.account.binding": "账号绑定", "menu.account.center": "个人中心", @@ -659,6 +703,7 @@ "menu.alert.silence": "告警静默", "menu.log": "日志", "menu.log.integration": "集成接入", + "menu.log.manage": "日志管理", "menu.log.stream": "日志流", "menu.clear.local.storage": "清理本地缓存", "menu.dashboard": "仪表盘", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 5810bdda610..25998b18e8f 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -579,52 +579,98 @@ "label.value": "標簽值", "labels.help": "標簽無處不在,我們可以應用標簽在資源分組,規則下的標簽匹配等場景。標簽管理用于對標簽的統壹管理維護,包含新增,刪除,編輯等操作。
例如:您可以使用標簽對監控資源進行分類管理,給資源分別綁定生産環境、測試環境的標簽,在告警通知時通過標簽匹配不同的通知人。", "labels.help.link": "https://hertzbeat.apache.org/zh-cn/docs/", - "log.help.integration": "統一管理日誌,整合接入外部日誌源的日誌訊息,對其進行收集,儲存,告警和分析等。", + "log.help.integration": "統一管理日誌,整合接入外部日誌源的日誌訊息,對其進行收集、儲存、告警和分析等。", "log.help.integration.link": "https://hertzbeat.apache.org", + "log.help.manage": "日誌管理與統計,支援對日誌的查詢、統計、刪除等操作。", + "log.help.manage.link": "https://hertzbeat.apache.org", + "log.help.stream": "即時日誌流監控介面,用於查看即時日誌條目,具備過濾和搜尋功能。", + "log.help.stream.link": "https://hertzbeat.apache.org", "log.integration.source": "整合日誌源", "log.integration.source.otlp": "OTLP 協議", - "log.integration.token.desc": "生成的 Token 可用於存取 HertzBeat 日誌接入API", + "log.integration.token.desc": "生成的 Token 可用於存取 HertzBeat 日誌接入 API", "log.integration.token.new": "點擊生成 Token", - "log.help.stream": "即時日誌流監控介面,用於檢視即時日誌條目,具備過濾和搜尋功能。", - "log.help.stream.link": "https://hertzbeat.apache.org", - "log.stream.title": "日誌流", - "log.stream.live-logs": "即時日誌", + "log.integration.token.notice": "此內容只會顯示一次,請妥善保管您的 Token,不要洩漏給他人", + "log.integration.token.title": "存取認證 Token", + "log.manage.batch-delete": "批次刪除", + "log.manage.basic-information": "基本資訊", + "log.manage.clear": "清空", + "log.manage.chart.log-trend": "日誌趨勢", + "log.manage.chart.severity-distribution": "嚴重程度分布", + "log.manage.chart.trace-coverage": "追蹤覆蓋率", + "log.manage.chart.trace-coverage.complete-trace-info": "完整 Trace 資訊", + "log.manage.chart.trace-coverage.with-span": "有 Span ID", + "log.manage.chart.trace-coverage.with-trace": "有 Trace ID", + "log.manage.chart.trace-coverage.without-trace": "無 Trace ID", + "log.manage.column-control": "欄位顯示控制", + "log.manage.column-setting": "欄位設定", + "log.manage.complete-json-data": "完整JSON資料", + "log.manage.hide-statistics": "隱藏統計", + "log.manage.log-entry-details": "日誌條目詳情", + "log.manage.overview.debug-logs": "除錯日誌", + "log.manage.overview.error-logs": "錯誤日誌", + "log.manage.overview.fatal-logs": "嚴重日誌", + "log.manage.overview.info-logs": "一般日誌", + "log.manage.overview.total-logs": "總日誌", + "log.manage.overview.warning-logs": "警告日誌", + "log.manage.reset-all-columns": "重設所有欄位", + "log.manage.search": "搜尋", + "log.manage.severity-number": "嚴重程度編號", + "log.manage.severity-text": "嚴重程度", + "log.manage.show-statistics": "顯示統計", + "log.manage.span-id": "Span ID", + "log.manage.table.column.attributes": "屬性", + "log.manage.table.column.body": "日誌內容", + "log.manage.table.column.dropped-count": "丟棄計數", + "log.manage.table.column.instrumentation": "採集工具", + "log.manage.table.column.observed-time": "觀察時間", + "log.manage.table.column.resource": "來源", + "log.manage.table.column.severity": "嚴重程度", + "log.manage.table.column.span-id": "Span ID", + "log.manage.table.column.time": "時間", + "log.manage.table.column.trace-flags": "Trace 標記", + "log.manage.table.column.trace-id": "Trace ID", + "log.manage.table.title": "日誌列表", + "log.manage.timestamp": "時間戳", + "log.manage.title": "日誌管理與統計", + "log.manage.trace-id": "Trace ID", + "log.stream.attributes": "個屬性", + "log.stream.basic-information": "基本資訊", + "log.stream.clear": "清除", + "log.stream.clear-filters": "清除過濾器", + "log.stream.clear-filters-tooltip": "清除所有過濾器", + "log.stream.complete-json-data": "完整JSON資料", "log.stream.connected": "已連線", "log.stream.connecting": "連線中...", + "log.stream.copy-message": "複製訊息", "log.stream.disconnected": "已斷線", - "log.stream.logs": "條日誌", - "log.stream.toggle-filters": "切換過濾器", "log.stream.hide-filters": "隱藏過濾器", - "log.stream.show-filters": "顯示過濾器", - "log.stream.resume": "恢復", + "log.stream.live-logs": "即時日誌", + "log.stream.log-entry-details": "日誌條目詳情", + "log.stream.logs": "條日誌", + "log.stream.message": "訊息", + "log.stream.no-logs": "暫無可用日誌", "log.stream.pause": "暫停", - "log.stream.clear": "清除", + "log.stream.resource": "個來源", + "log.stream.resume": "恢復", "log.stream.scroll-to-top": "捲動到頂部", + "log.stream.severity": "嚴重程度:", "log.stream.severity-number": "嚴重程度編號:", - "log.stream.severity-number-placeholder": "輸入嚴重程度编号", + "log.stream.severity-number-placeholder": "輸入嚴重程度編號", "log.stream.severity-text": "嚴重程度:", "log.stream.severity-text-placeholder": "輸入嚴重程度", - "log.stream.trace-id": "追蹤ID:", - "log.stream.trace-id-placeholder": "輸入追蹤ID", - "log.stream.span-id": "跨度ID:", - "log.stream.span-id-placeholder": "輸入跨度ID", - "log.stream.clear-filters": "清除過濾器", - "log.stream.clear-filters-tooltip": "清除所有過濾器", - "log.stream.no-logs": "暫無可用日誌", - "log.stream.unknown": "未知", - "log.stream.trace": "追蹤:", + "log.stream.show-filters": "顯示過濾器", "log.stream.span": "跨度:", - "log.stream.copy-message": "複製訊息", - "log.stream.attributes": "個屬性", - "log.stream.resource": "個資源", - "log.stream.log-entry-details": "日誌條目詳情", - "log.stream.basic-information": "基本資訊", - "log.stream.severity": "嚴重程度:", + "log.stream.span-id": "Span ID:", + "log.stream.span-id-full": "Span ID:", + "log.stream.span-id-placeholder": "輸入Span ID", + "log.stream.title": "日誌流", "log.stream.timestamp": "時間戳:", - "log.stream.trace-id-full": "追蹤ID:", - "log.stream.span-id-full": "跨度ID:", - "log.stream.message": "訊息", - "log.stream.complete-json-data": "完整JSON資料", + "log.stream.toggle-filters": "切換過濾器", + "log.stream.trace": "追蹤:", + "log.stream.trace-id": "Trace ID:", + "log.stream.trace-id-full": "Trace ID:", + "log.stream.trace-id-placeholder": "輸入Trace ID", + "log.stream.unknown": "未知", "menu.account": "個人頁", "menu.account.binding": "賬號綁定", "menu.account.center": "個人中心", @@ -659,6 +705,10 @@ "menu.lang": "語言", "menu.link.guild": "使用指南", "menu.link.question": "常見問題", + "menu.log": "日誌", + "menu.log.integration": "整合", + "menu.log.manage": "日誌管理", + "menu.log.stream": "日誌流", "menu.main": "主導航", "menu.monitor": "監控", "menu.monitor.bigdata": "大數據監控", From 54aa761265c0520485a72ec057c2f4ce4072cc32 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 17 Aug 2025 15:08:23 +0800 Subject: [PATCH 29/39] bugfix: fix flyway mysql sql script error --- .../migration/mysql/V173__update_column.sql | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql index 3ee63e0dcc0..4769f9ee57f 100644 --- a/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql +++ b/hertzbeat-manager/src/main/resources/db/migration/mysql/V173__update_column.sql @@ -22,36 +22,31 @@ DELIMITER // CREATE PROCEDURE UpdateAlertDefineColumns() BEGIN - DECLARE table_exists INT; + DECLARE table_exists INT; DECLARE column_exists INT; - -- Check if the table exists SELECT COUNT(*) INTO table_exists FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE'; + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hzb_alert_define'; IF table_exists = 1 THEN - -- Update type from 'realtime' to 'realtime_metric' - UPDATE HZB_ALERT_DEFINE + UPDATE hzb_alert_define SET type = 'realtime_metric' WHERE type = 'realtime'; - -- Update type from 'periodic' to 'periodic_metric' - UPDATE HZB_ALERT_DEFINE + UPDATE hzb_alert_define SET type = 'periodic_metric' WHERE type = 'periodic'; - -- Modify annotations column length from 4096 to 2048 - ALTER TABLE HZB_ALERT_DEFINE + ALTER TABLE hzb_alert_define MODIFY COLUMN annotations VARCHAR(2048); - -- Add query_expr column if not exists SELECT COUNT(*) INTO column_exists FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'HZB_ALERT_DEFINE' AND COLUMN_NAME = 'query_expr'; + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hzb_alert_define' AND COLUMN_NAME = 'query_expr'; IF column_exists = 0 THEN - ALTER TABLE HZB_ALERT_DEFINE + ALTER TABLE hzb_alert_define ADD COLUMN query_expr VARCHAR(2048); END IF; END IF; From 50344ff1a81cee5099bbe5323dbac9477e4ab71b Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Sun, 17 Aug 2025 15:37:02 +0800 Subject: [PATCH 30/39] bugfix: add license header --- .../alert/calculate/JexlExprCalculator.java | 19 ++++++++++++++++++ .../log/notice/LogSseFilterCriteria.java | 19 ++++++++++++++++++ .../hertzbeat/log/notice/LogSseManager.java | 19 ++++++++++++++++++ .../db/GreptimeSqlQueryExecutor.java | 19 ++++++++++++++++++ web-app/src/app/pojo/LogEntry.ts | 20 ++++++++++++++++++- .../log-integration.component.spec.ts | 19 ++++++++++++++++++ .../log-stream/log-stream.component.spec.ts | 19 ++++++++++++++++++ 7 files changed, 133 insertions(+), 1 deletion(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java index f61f29736f4..5e8c9f89e6b 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/JexlExprCalculator.java @@ -1,3 +1,22 @@ +/* + * 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.hertzbeat.alert.calculate; import lombok.extern.slf4j.Slf4j; diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java index 84d63770fb3..6a7453a0e80 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseFilterCriteria.java @@ -1,3 +1,22 @@ +/* + * 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.hertzbeat.log.notice; import lombok.Data; diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java index 127cabc6b21..6f497bdccbe 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/notice/LogSseManager.java @@ -1,3 +1,22 @@ +/* + * 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.hertzbeat.log.notice; import lombok.AllArgsConstructor; diff --git a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java index 43ac30efc63..7e4c64341bd 100644 --- a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java +++ b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/db/GreptimeSqlQueryExecutor.java @@ -1,3 +1,22 @@ +/* + * 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.hertzbeat.warehouse.db; import lombok.extern.slf4j.Slf4j; diff --git a/web-app/src/app/pojo/LogEntry.ts b/web-app/src/app/pojo/LogEntry.ts index 0faa22a8ac7..be866605ceb 100644 --- a/web-app/src/app/pojo/LogEntry.ts +++ b/web-app/src/app/pojo/LogEntry.ts @@ -1,4 +1,22 @@ -// Define LogEntry interface based on backend structure +/* + * 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. + */ + export class LogEntry { timeUnixNano?: number; observedTimeUnixNano?: number; diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts index 92e8b52524c..be8ff0b2398 100644 --- a/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.spec.ts @@ -1,3 +1,22 @@ +/* + * 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. + */ + import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LogIntegrationComponent } from './log-integration.component'; diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts index f169eeee65f..393c1c9310d 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.spec.ts @@ -1,3 +1,22 @@ +/* + * 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. + */ + import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LogStreamComponent } from './log-stream.component'; From 2607f83df15500c53f1777e7225ba99427495e05 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:26:29 +0800 Subject: [PATCH 31/39] improvement: Correct thread pool scaling in AlerterWorkerPool --- .../java/org/apache/hertzbeat/alert/AlerterWorkerPool.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java index 652d4a349d3..23426ddcf06 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterWorkerPool.java @@ -88,8 +88,8 @@ private void initLogWorkerExecutor() { .setDaemon(true) .setNameFormat("log-worker-%d") .build(); - logWorkerExecutor = new ThreadPoolExecutor(4, 10, 10, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), + logWorkerExecutor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(1000), threadFactory, new ThreadPoolExecutor.AbortPolicy()); } From 5d738a41a25bbfbf5d638f803018733ef1dff78f Mon Sep 17 00:00:00 2001 From: Yang Chen <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:31:11 +0800 Subject: [PATCH 32/39] Update web-app/src/assets/i18n/zh-TW.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Yang Chen <1597081640@qq.com> --- web-app/src/assets/i18n/zh-TW.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 25998b18e8f..b905b2a8718 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -245,7 +245,7 @@ "alert.setting.default.tip": "此告警阈值配置是否應用于全局所有此類型監控", "alert.setting.delete": "刪除阈值規則", "alert.setting.edit": "編輯阈值規則", - "alert.setting.edit.periodic": "編輯周期阈值", + "alert.setting.edit.periodic": "編輯周期閾值", "alert.setting.edit.realtime": "編輯實時阈值", "alert.setting.enable": "啓用閾值", "alert.setting.enable.tip": "此告警阈值配置開啓生效或關閉", From 72d266f5a89edb831306bdcbb3873a8d13f59835 Mon Sep 17 00:00:00 2001 From: Yang Chen <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:36:12 +0800 Subject: [PATCH 33/39] improvement: Remove console.log Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Yang Chen <1597081640@qq.com> --- web-app/src/app/routes/log/log-stream/log-stream.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web-app/src/app/routes/log/log-stream/log-stream.component.ts b/web-app/src/app/routes/log/log-stream/log-stream.component.ts index 7ad189cfc7b..c5861a386ff 100644 --- a/web-app/src/app/routes/log/log-stream/log-stream.component.ts +++ b/web-app/src/app/routes/log/log-stream/log-stream.component.ts @@ -337,7 +337,6 @@ export class LogStreamComponent implements OnInit, OnDestroy, AfterViewInit { // Utility methods getSeverityColor(severityNumber: number | undefined): string { - console.log('severityNumber', severityNumber); if (!severityNumber) { return 'default'; } From 649319c72b9f928ef518770cd76c1841ad5727a7 Mon Sep 17 00:00:00 2001 From: Yang Chen <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:38:03 +0800 Subject: [PATCH 34/39] improvement: Use substring() instead of deprecated substr() method Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Yang Chen <1597081640@qq.com> --- .../src/app/routes/log/log-manage/log-manage.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.ts b/web-app/src/app/routes/log/log-manage/log-manage.component.ts index 4ae72e8b0e8..38642ff1ca9 100644 --- a/web-app/src/app/routes/log/log-manage/log-manage.component.ts +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.ts @@ -473,7 +473,10 @@ export class LogManageComponent implements OnInit { if (typeof body === 'string') return body.length > 100 ? `${body.substr(0, 100)}...` : body; if (typeof body === 'object') { const str = JSON.stringify(body); - return str.length > 100 ? `${str.substr(0, 100)}...` : str; + if (typeof body === 'string') return body.length > 100 ? `${body.substring(0, 100)}...` : body; + if (typeof body === 'object') { + const str = JSON.stringify(body); + return str.length > 100 ? `${str.substring(0, 100)}...` : str; } return String(body); } From 821fa9f40de5945a43a3db548ab4e870e9bb218c Mon Sep 17 00:00:00 2001 From: Yang Chen <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:39:21 +0800 Subject: [PATCH 35/39] improvement: Replace deprecated document.execCommand('copy') Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Yang Chen <1597081640@qq.com> --- .../log-integration.component.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.ts index 8ca1b9de05c..d0e33c5cd9c 100644 --- a/web-app/src/app/routes/log/log-integration/log-integration.component.ts +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.ts @@ -147,6 +147,25 @@ export class LogIntegrationComponent implements OnInit { el.select(); document.execCommand('copy'); document.body.removeChild(el); - this.notifySvc.success(this.i18nSvc.fanyi('common.notify.copy-success'), this.i18nSvc.fanyi('log.integration.token.notice')); + if (navigator && navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(this.token) + .then(() => { + this.notifySvc.success( + this.i18nSvc.fanyi('common.notify.copy-success'), + this.i18nSvc.fanyi('log.integration.token.notice') + ); + }) + .catch(() => { + this.notifySvc.error( + this.i18nSvc.fanyi('common.notify.copy-fail'), + this.i18nSvc.fanyi('log.integration.token.notice') + ); + }); + } else { + this.notifySvc.error( + this.i18nSvc.fanyi('common.notify.copy-fail'), + this.i18nSvc.fanyi('log.integration.token.notice') + ); + } } } From dbda9fa5fecae400fb40d9104886c4e2057679f1 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Thu, 21 Aug 2025 14:46:14 +0800 Subject: [PATCH 36/39] improvement: Use substring() instead of deprecated substr() method --- .../log-integration.component.ts | 18 +++++------------- .../log/log-manage/log-manage.component.ts | 3 --- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/web-app/src/app/routes/log/log-integration/log-integration.component.ts b/web-app/src/app/routes/log/log-integration/log-integration.component.ts index d0e33c5cd9c..f057925b587 100644 --- a/web-app/src/app/routes/log/log-integration/log-integration.component.ts +++ b/web-app/src/app/routes/log/log-integration/log-integration.component.ts @@ -148,24 +148,16 @@ export class LogIntegrationComponent implements OnInit { document.execCommand('copy'); document.body.removeChild(el); if (navigator && navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(this.token) + navigator.clipboard + .writeText(this.token) .then(() => { - this.notifySvc.success( - this.i18nSvc.fanyi('common.notify.copy-success'), - this.i18nSvc.fanyi('log.integration.token.notice') - ); + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.copy-success'), this.i18nSvc.fanyi('log.integration.token.notice')); }) .catch(() => { - this.notifySvc.error( - this.i18nSvc.fanyi('common.notify.copy-fail'), - this.i18nSvc.fanyi('log.integration.token.notice') - ); + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.copy-fail'), this.i18nSvc.fanyi('log.integration.token.notice')); }); } else { - this.notifySvc.error( - this.i18nSvc.fanyi('common.notify.copy-fail'), - this.i18nSvc.fanyi('log.integration.token.notice') - ); + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.copy-fail'), this.i18nSvc.fanyi('log.integration.token.notice')); } } } diff --git a/web-app/src/app/routes/log/log-manage/log-manage.component.ts b/web-app/src/app/routes/log/log-manage/log-manage.component.ts index 38642ff1ca9..d468e03f793 100644 --- a/web-app/src/app/routes/log/log-manage/log-manage.component.ts +++ b/web-app/src/app/routes/log/log-manage/log-manage.component.ts @@ -470,9 +470,6 @@ export class LogManageComponent implements OnInit { getBodyText(body: any): string { if (!body) return ''; - if (typeof body === 'string') return body.length > 100 ? `${body.substr(0, 100)}...` : body; - if (typeof body === 'object') { - const str = JSON.stringify(body); if (typeof body === 'string') return body.length > 100 ? `${body.substring(0, 100)}...` : body; if (typeof body === 'object') { const str = JSON.stringify(body); From 3c203d0d25a99930ffb5f92daa80a5a09834b84c Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Fri, 22 Aug 2025 11:00:43 +0800 Subject: [PATCH 37/39] test: add unit test for log ingestion controller and add json produces --- .../controller/LogIngestionController.java | 6 +- .../LogIngestionControllerTest.java | 147 ++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 hertzbeat-log/src/test/java/org/apache/hertzbeat/log/controller/LogIngestionControllerTest.java diff --git a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java index faeaf4de1ef..125b10904da 100644 --- a/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java +++ b/hertzbeat-log/src/main/java/org/apache/hertzbeat/log/controller/LogIngestionController.java @@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** @@ -37,6 +38,7 @@ */ @Tag(name = "Log Ingestion Controller") @RestController +@RequestMapping(path = "/api/logs", produces = "application/json") @Slf4j public class LogIngestionController { @@ -55,7 +57,7 @@ public LogIngestionController(List protocolAdapters) { * @param protocol log protocol identifier * @param content raw request body */ - @PostMapping("/api/logs/ingest/{protocol}") + @PostMapping("/ingest/{protocol}") public ResponseEntity> ingestExternLog(@PathVariable("protocol") String protocol, @RequestBody String content) { log.info("Receive extern log from protocol: {}, content length: {}", protocol, content == null ? 0 : content.length()); @@ -83,7 +85,7 @@ public ResponseEntity> ingestExternLog(@PathVariable("protocol") S * Receive default log payload (when protocol is not specified). * It will look for a service whose supportProtocol() returns "otlp". */ - @PostMapping("/api/logs/ingest") + @PostMapping("/ingest") public ResponseEntity> ingestDefaultExternLog(@RequestBody String content) { log.info("Receive default extern log content, length: {}", content == null ? 0 : content.length()); LogProtocolAdapter adapter = protocolAdapters.stream() diff --git a/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/controller/LogIngestionControllerTest.java b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/controller/LogIngestionControllerTest.java new file mode 100644 index 00000000000..1e41c23b792 --- /dev/null +++ b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/controller/LogIngestionControllerTest.java @@ -0,0 +1,147 @@ +/* + * 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.hertzbeat.log.controller; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.apache.hertzbeat.common.constants.CommonConstants; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.util.JsonUtil; +import org.apache.hertzbeat.log.service.LogProtocolAdapter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +/** + * Unit test for {@link LogIngestionController} + */ +@ExtendWith(MockitoExtension.class) +class LogIngestionControllerTest { + + private MockMvc mockMvc; + + @Mock + private LogProtocolAdapter otlpAdapter; + + private LogIngestionController logIngestionController; + + @BeforeEach + void setUp() { + List adapters = Arrays.asList(otlpAdapter); + this.logIngestionController = new LogIngestionController(adapters); + this.mockMvc = MockMvcBuilders.standaloneSetup(logIngestionController).build(); + } + + @Test + void testIngestExternLogWithOtlpProtocol() throws Exception { + LogEntry logEntry = LogEntry.builder() + .timeUnixNano(1734005477630L) + .severityNumber(1) + .severityText("INFO") + .body("Test log message") + .attributes(new HashMap<>()) + .build(); + + when(otlpAdapter.supportProtocol()).thenReturn("otlp"); + + mockMvc.perform( + MockMvcRequestBuilders + .post("/api/logs/ingest/otlp") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(logEntry)) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE)) + .andExpect(jsonPath("$.msg").value("Add extern log success")) + .andReturn(); + } + + @Test + void testIngestExternLogWithUnsupportedProtocol() throws Exception { + String unsupportedLogContent = "{\"message\":\"Unsupported protocol log\"}"; + + when(otlpAdapter.supportProtocol()).thenReturn("otlp"); + + mockMvc.perform( + MockMvcRequestBuilders + .post("/api/logs/ingest/unsupported") + .contentType(MediaType.APPLICATION_JSON) + .content(unsupportedLogContent) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value((int) CommonConstants.FAIL_CODE)) + .andExpect(jsonPath("$.msg").value("Not support the unsupported protocol log")); + } + + @Test + void testIngestDefaultExternLog() throws Exception { + LogEntry logEntry = LogEntry.builder() + .timeUnixNano(1734005477630L) + .severityNumber(2) + .severityText("WARN") + .body("Default protocol log message") + .attributes(new HashMap<>()) + .build(); + + when(otlpAdapter.supportProtocol()).thenReturn("otlp"); + + mockMvc.perform( + MockMvcRequestBuilders + .post("/api/logs/ingest") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(logEntry)) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE)) + .andExpect(jsonPath("$.msg").value("Add extern log success")); + } + + @Test + void testIngestDefaultExternLogWithAdapterException() throws Exception { + String logContent = "{\"message\":\"Default log message that will cause exception\"}"; + + when(otlpAdapter.supportProtocol()).thenReturn("otlp"); + Mockito.doThrow(new IllegalArgumentException("Invalid log format")).when(otlpAdapter).ingest(anyString()); + + mockMvc.perform( + MockMvcRequestBuilders + .post("/api/logs/ingest") + .contentType(MediaType.APPLICATION_JSON) + .content(logContent) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value((int) CommonConstants.FAIL_CODE)) + .andExpect(jsonPath("$.msg").value("Add extern log failed: Invalid log format")); + } +} From 9c4886816315ea2953c0136ea7e2bbddb4a47a36 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 25 Aug 2025 13:48:55 +0800 Subject: [PATCH 38/39] feat: add otlp adapter unit test --- .../impl/OtlpLogProtocolAdapterTest.java | 292 ++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java diff --git a/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java new file mode 100644 index 00000000000..30aa8f0f239 --- /dev/null +++ b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java @@ -0,0 +1,292 @@ +/* + * 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.hertzbeat.log.service.impl; + +import com.google.protobuf.ByteString; +import com.google.protobuf.util.JsonFormat; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.proto.common.v1.AnyValue; +import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.logs.v1.LogRecord; +import io.opentelemetry.proto.logs.v1.ResourceLogs; +import io.opentelemetry.proto.logs.v1.ScopeLogs; +import io.opentelemetry.proto.resource.v1.Resource; +import io.opentelemetry.proto.common.v1.InstrumentationScope; +import org.apache.hertzbeat.common.entity.log.LogEntry; +import org.apache.hertzbeat.common.queue.CommonDataQueue; +import org.apache.hertzbeat.log.notice.LogSseManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * Unit tests for OtlpLogProtocolAdapter. + */ +@ExtendWith(MockitoExtension.class) +class OtlpLogProtocolAdapterTest { + + @Mock + private CommonDataQueue commonDataQueue; + + @Mock + private LogSseManager logSseManager; + + private OtlpLogProtocolAdapter adapter; + + @BeforeEach + void setUp() { + adapter = new OtlpLogProtocolAdapter(commonDataQueue, logSseManager); + } + + @Test + void testIngestWithNullContent() { + adapter.ingest(null); + verifyNoInteractions(commonDataQueue, logSseManager); + } + + @Test + void testIngestWithEmptyContent() { + adapter.ingest(""); + verifyNoInteractions(commonDataQueue, logSseManager); + } + + @Test + void testIngestWithValidOtlpLogData() throws Exception { + String otlpPayload = createValidOtlpLogPayload(); + + adapter.ingest(otlpPayload); + + ArgumentCaptor logEntryCaptor = ArgumentCaptor.forClass(LogEntry.class); + verify(commonDataQueue, times(1)).sendLogEntry(logEntryCaptor.capture()); + verify(logSseManager, times(1)).broadcast(logEntryCaptor.capture()); + + LogEntry capturedEntry = logEntryCaptor.getValue(); + assertNotNull(capturedEntry); + assertEquals("test-service", capturedEntry.getResource().get("service_name")); + assertEquals("test-version", capturedEntry.getResource().get("service_version")); + assertEquals("test-scope", capturedEntry.getInstrumentationScope().getName()); + assertEquals("1.0.0", capturedEntry.getInstrumentationScope().getVersion()); + assertEquals("test log message", capturedEntry.getBody()); + assertEquals("INFO", capturedEntry.getSeverityText()); + assertEquals(9, capturedEntry.getSeverityNumber()); + } + + @Test + void testIngestWithMultipleLogRecords() throws Exception { + String otlpPayload = createOtlpPayloadWithMultipleLogs(); + + adapter.ingest(otlpPayload); + + verify(commonDataQueue, times(2)).sendLogEntry(any(LogEntry.class)); + verify(logSseManager, times(2)).broadcast(any(LogEntry.class)); + } + + @Test + void testIngestWithComplexAttributes() throws Exception { + String otlpPayload = createOtlpPayloadWithComplexAttributes(); + + adapter.ingest(otlpPayload); + + verify(commonDataQueue, times(1)).sendLogEntry(any(LogEntry.class)); + verify(logSseManager, times(1)).broadcast(any(LogEntry.class)); + + ArgumentCaptor logEntryCaptor = ArgumentCaptor.forClass(LogEntry.class); + verify(commonDataQueue).sendLogEntry(logEntryCaptor.capture()); + + LogEntry capturedEntry = logEntryCaptor.getValue(); + assertNotNull(capturedEntry); + + Map attributes = capturedEntry.getAttributes(); + assertEquals("string_value", attributes.get("string_attr")); + assertEquals(true, attributes.get("bool_attr")); + assertEquals(42L, attributes.get("int_attr")); + assertEquals(3.14, attributes.get("double_attr")); + + List arrayAttr = (List) attributes.get("array_attr"); + assertNotNull(arrayAttr); + assertEquals(3, arrayAttr.size()); + assertEquals("item1", arrayAttr.get(0)); + assertEquals("item2", arrayAttr.get(1)); + assertEquals("item3", arrayAttr.get(2)); + } + + @Test + void testIngestWithTraceAndSpanIds() throws Exception { + String otlpPayload = createOtlpPayloadWithTraceSpanIds(); + + adapter.ingest(otlpPayload); + + verify(commonDataQueue, times(1)).sendLogEntry(any(LogEntry.class)); + + ArgumentCaptor logEntryCaptor = ArgumentCaptor.forClass(LogEntry.class); + verify(commonDataQueue).sendLogEntry(logEntryCaptor.capture()); + + LogEntry capturedEntry = logEntryCaptor.getValue(); + assertEquals("1234567890abcdef1234567890abcdef", capturedEntry.getTraceId()); + assertEquals("1234567890abcdef", capturedEntry.getSpanId()); + assertEquals(1, capturedEntry.getTraceFlags()); + } + + @Test + void testIngestWithInvalidJsonContent() { + String invalidJson = "{ invalid json content }"; + + assertThrows(IllegalArgumentException.class, () -> adapter.ingest(invalidJson)); + verifyNoInteractions(commonDataQueue, logSseManager); + } + + @Test + void testIngestWithEmptyResourceLogs() throws Exception { + String otlpPayload = createEmptyResourceLogsPayload(); + + adapter.ingest(otlpPayload); + + verifyNoInteractions(commonDataQueue, logSseManager); + } + + private String createValidOtlpLogPayload() throws Exception { + ExportLogsServiceRequest request = ExportLogsServiceRequest.newBuilder() + .addResourceLogs(ResourceLogs.newBuilder() + .setResource(Resource.newBuilder() + .addAttributes(KeyValue.newBuilder() + .setKey("service.name") + .setValue(AnyValue.newBuilder().setStringValue("test-service").build()) + .build()) + .addAttributes(KeyValue.newBuilder() + .setKey("service.version") + .setValue(AnyValue.newBuilder().setStringValue("test-version").build()) + .build()) + .build()) + .addScopeLogs(ScopeLogs.newBuilder() + .setScope(InstrumentationScope.newBuilder() + .setName("test-scope") + .setVersion("1.0.0") + .build()) + .addLogRecords(LogRecord.newBuilder() + .setTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setObservedTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setSeverityNumberValue(9) + .setSeverityText("INFO") + .setBody(AnyValue.newBuilder().setStringValue("test log message").build()) + .build()) + .build()) + .build()) + .build(); + + return JsonFormat.printer().print(request); + } + + private String createOtlpPayloadWithMultipleLogs() throws Exception { + ExportLogsServiceRequest request = ExportLogsServiceRequest.newBuilder() + .addResourceLogs(ResourceLogs.newBuilder() + .setResource(Resource.newBuilder().build()) + .addScopeLogs(ScopeLogs.newBuilder() + .setScope(InstrumentationScope.newBuilder().build()) + .addLogRecords(LogRecord.newBuilder() + .setTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setBody(AnyValue.newBuilder().setStringValue("first log").build()) + .build()) + .addLogRecords(LogRecord.newBuilder() + .setTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setBody(AnyValue.newBuilder().setStringValue("second log").build()) + .build()) + .build()) + .build()) + .build(); + + return JsonFormat.printer().print(request); + } + + private String createOtlpPayloadWithComplexAttributes() throws Exception { + ExportLogsServiceRequest request = ExportLogsServiceRequest.newBuilder() + .addResourceLogs(ResourceLogs.newBuilder() + .setResource(Resource.newBuilder().build()) + .addScopeLogs(ScopeLogs.newBuilder() + .setScope(InstrumentationScope.newBuilder().build()) + .addLogRecords(LogRecord.newBuilder() + .setTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setBody(AnyValue.newBuilder().setStringValue("complex attributes test").build()) + .addAttributes(KeyValue.newBuilder() + .setKey("string.attr") + .setValue(AnyValue.newBuilder().setStringValue("string_value").build()) + .build()) + .addAttributes(KeyValue.newBuilder() + .setKey("bool.attr") + .setValue(AnyValue.newBuilder().setBoolValue(true).build()) + .build()) + .addAttributes(KeyValue.newBuilder() + .setKey("int.attr") + .setValue(AnyValue.newBuilder().setIntValue(42).build()) + .build()) + .addAttributes(KeyValue.newBuilder() + .setKey("double.attr") + .setValue(AnyValue.newBuilder().setDoubleValue(3.14).build()) + .build()) + .addAttributes(KeyValue.newBuilder() + .setKey("array.attr") + .setValue(AnyValue.newBuilder() + .setArrayValue(io.opentelemetry.proto.common.v1.ArrayValue.newBuilder() + .addValues(AnyValue.newBuilder().setStringValue("item1").build()) + .addValues(AnyValue.newBuilder().setStringValue("item2").build()) + .addValues(AnyValue.newBuilder().setStringValue("item3").build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .build(); + + return JsonFormat.printer().print(request); + } + + private String createOtlpPayloadWithTraceSpanIds() throws Exception { + ExportLogsServiceRequest request = ExportLogsServiceRequest.newBuilder() + .addResourceLogs(ResourceLogs.newBuilder() + .setResource(Resource.newBuilder().build()) + .addScopeLogs(ScopeLogs.newBuilder() + .setScope(InstrumentationScope.newBuilder().build()) + .addLogRecords(LogRecord.newBuilder() + .setTimeUnixNano(System.currentTimeMillis() * 1_000_000) + .setBody(AnyValue.newBuilder().setStringValue("trace test").build()) + .setTraceId(ByteString.fromHex("1234567890abcdef1234567890abcdef")) + .setSpanId(ByteString.fromHex("1234567890abcdef")) + .setFlags(1) + .build()) + .build()) + .build()) + .build(); + + return JsonFormat.printer().print(request); + } + + private String createEmptyResourceLogsPayload() throws Exception { + ExportLogsServiceRequest request = ExportLogsServiceRequest.newBuilder().build(); + return JsonFormat.printer().print(request); + } +} From 8eb114916d441cde875a473a808606d8f2f54378 Mon Sep 17 00:00:00 2001 From: Chen Yang <1597081640@qq.com> Date: Mon, 25 Aug 2025 13:58:48 +0800 Subject: [PATCH 39/39] improvement: replace import * --- .../log/service/impl/OtlpLogProtocolAdapterTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java index 30aa8f0f239..4f72671e8e4 100644 --- a/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java +++ b/hertzbeat-log/src/test/java/org/apache/hertzbeat/log/service/impl/OtlpLogProtocolAdapterTest.java @@ -40,9 +40,13 @@ import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; /** * Unit tests for OtlpLogProtocolAdapter.