Support Metrics Query Expression(MQE) and allows users to do simple query-stage calculation through the expression. (#10881)
This commit is contained in:
parent
ecda943fba
commit
0f847438c7
|
|
@ -0,0 +1,124 @@
|
|||
# Metrics Query Expression(MQE) Syntax
|
||||
MQE is a string that consists of one or more expressions. Each expression could be a combination of one or more operations.
|
||||
The expression allows users to do simple query-stage calculation through [MQE APIs](./query-protocol.md#mqe-apis).
|
||||
|
||||
```text
|
||||
Expression = <Operation> Expression1 <Operation> Expression2 <Operation> Expression3 ...
|
||||
```
|
||||
|
||||
The following document lists the operations supported by MQE.
|
||||
|
||||
## Metrics Expression
|
||||
### Common Value Metrics
|
||||
Expression:
|
||||
```text
|
||||
<metric_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
If we want to query the `service_sla` metric, we can use the following expression:
|
||||
```text
|
||||
service_sla
|
||||
```
|
||||
|
||||
### Labeled Value Metrics
|
||||
Expression:
|
||||
```text
|
||||
<metric_name>{labels='<label_name1>,...', relabels='<new_label_name1>,...'}
|
||||
```
|
||||
`labels` is the selected labels name of the metric, and `relabels` is the labels name of the metric after the label is renamed.
|
||||
|
||||
If `labels` is not specified, all labels of the metric will be selected. And if `relabels` is not specified, the label name will not be changed.
|
||||
|
||||
For example:
|
||||
If we want to query the `service_percentile` metric with the labels `0,1,2,3,4`, and rename the labels to `P50,P75,P90,P95,P99`, we can use the following expression:
|
||||
```text
|
||||
service_percentile{labels='0,1,2,3,4', relabels='P50,P75,P90,P95,P99'}
|
||||
```
|
||||
|
||||
|
||||
## Binary Operation
|
||||
Binary Operation is an operation that takes two expressions and performs a calculation on their results.
|
||||
The following table lists the binary operations supported by MQE.
|
||||
|
||||
Expression:
|
||||
```text
|
||||
Expression1 <Binary-Operator> Expression2
|
||||
```
|
||||
|
||||
| Operator | Definition |
|
||||
|----------|----------------------|
|
||||
| + | addition |
|
||||
| - | subtraction |
|
||||
| * | multiplication |
|
||||
| / | division |
|
||||
| % | modulo |
|
||||
|
||||
For example:
|
||||
If we want to transform the service_sla metric value to percent, we can use the following expression:
|
||||
```text
|
||||
service_sla / 100
|
||||
```
|
||||
|
||||
## Aggregation Operation
|
||||
Aggregation Operation takes an expression and performs aggregate calculation on its results.
|
||||
|
||||
|
||||
Expression:
|
||||
```text
|
||||
<Aggregation-Operator>(Expression)
|
||||
```
|
||||
|
||||
|
||||
| Operator | Definition |
|
||||
|----------|--------------------------------|
|
||||
| avg | average the result |
|
||||
| count | count number of the result |
|
||||
| sum | sum the result |
|
||||
| max | select maximum from the result |
|
||||
| min | select minimum from the result |
|
||||
|
||||
For example:
|
||||
If we want to query the average value of the `service_cpm` metric, we can use the following expression:
|
||||
|
||||
```text
|
||||
avg(service_cpm)
|
||||
```
|
||||
|
||||
|
||||
## Function Operation
|
||||
Function Operation takes an expression and performs function calculation on its results.
|
||||
|
||||
<Function-Operator>(Expression, parameters)
|
||||
|
||||
| Operator | Definition | parameters |
|
||||
|----------|---------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||
| abs | returns the absolute value of the result | |
|
||||
| ceil | returns the smallest integer value that is greater or equal to the result | |
|
||||
| floor | returns the largest integer value that is greater or equal to the result | |
|
||||
| round | returns result round to specific decimal places | `places`: a positive integer specific decimal places of the result |
|
||||
|
||||
For example:
|
||||
If we want to query the average value of the `service_cpm` metric in seconds,
|
||||
and round the result to 2 decimal places, we can use the following expression:
|
||||
|
||||
```text
|
||||
round(service_cpm / 60 , 2)
|
||||
```
|
||||
|
||||
## TopN Operation
|
||||
TopN Operation takes an expression and performs TopN calculation on its results.
|
||||
```text
|
||||
top_n(<metric_name>, <top_number>, <order>)
|
||||
```
|
||||
|
||||
`top_number` is the number of the top results, should be a positive integer.
|
||||
|
||||
`order` is the order of the top results. The value of `order` can be `asc` or `des`.
|
||||
|
||||
For example:
|
||||
If we want to query the top 10 services with the highest `service_cpm` metric value, we can use the following expression:
|
||||
|
||||
```text
|
||||
top_n(service_instance_cpm, 10, des)
|
||||
```
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
# Deprecated Query Protocol
|
||||
The following query services are deprecated since 9.5.0. All these queries are still available for the short term to keep compatibility.
|
||||
|
||||
Query protocol official repository, https://github.com/apache/skywalking-query-protocol.
|
||||
|
||||
### Metrics
|
||||
Metrics query targets all objects defined in [OAL script](../concepts-and-designs/oal.md) and [MAL](../concepts-and-designs/mal.md).
|
||||
You may obtain the metrics data in linear or thermodynamic matrix formats based on the aggregation functions in script.
|
||||
|
||||
#### V2 APIs
|
||||
Provide Metrics V2 query APIs since 8.0.0, including metadata, single/multiple values, heatmap, and sampled records metrics.
|
||||
```graphql
|
||||
extend type Query {
|
||||
# Read metrics single value in the duration of required metrics
|
||||
readMetricsValue(condition: MetricsCondition!, duration: Duration!): Long!
|
||||
# Read metrics single value in the duration of required metrics
|
||||
# NullableValue#isEmptyValue == true indicates no telemetry data rather than aggregated value is actually zero.
|
||||
readNullableMetricsValue(condition: MetricsCondition!, duration: Duration!): NullableValue!
|
||||
# Read time-series values in the duration of required metrics
|
||||
readMetricsValues(condition: MetricsCondition!, duration: Duration!): MetricsValues!
|
||||
# Read entity list of required metrics and parent entity type.
|
||||
sortMetrics(condition: TopNCondition!, duration: Duration!): [SelectedRecord!]!
|
||||
# Read value in the given time duration, usually as a linear.
|
||||
# labels: the labels you need to query.
|
||||
readLabeledMetricsValues(condition: MetricsCondition!, labels: [String!]!, duration: Duration!): [MetricsValues!]!
|
||||
# Heatmap is bucket based value statistic result.
|
||||
readHeatMap(condition: MetricsCondition!, duration: Duration!): HeatMap
|
||||
# Deprecated since 9.3.0, replaced by readRecords defined in record.graphqls
|
||||
# Read the sampled records
|
||||
# TopNCondition#scope is not required.
|
||||
readSampledRecords(condition: TopNCondition!, duration: Duration!): [SelectedRecord!]!
|
||||
}
|
||||
```
|
||||
|
||||
#### V1 APIs
|
||||
3 types of metrics can be queried. V1 APIs were introduced since 6.x. Now they are a shell to V2 APIs.
|
||||
1. Single value. Most default metrics are in single value. `getValues` and `getLinearIntValues` are suitable for this purpose.
|
||||
2. Multiple value. A metric defined in OAL includes multiple value calculations. Use `getMultipleLinearIntValues` to obtain all values. `percentile` is a typical multiple value function in OAL.
|
||||
3. Heatmap value. Read [Heatmap in WIKI](https://en.wikipedia.org/wiki/Heat_map) for details. `thermodynamic` is the only OAL function. Use `getThermodynamic` to get the values.
|
||||
```graphql
|
||||
extend type Query {
|
||||
getValues(metric: BatchMetricConditions!, duration: Duration!): IntValues
|
||||
getLinearIntValues(metric: MetricCondition!, duration: Duration!): IntValues
|
||||
# Query the type of metrics including multiple values, and format them as multiple lines.
|
||||
# The seq of these multiple lines base on the calculation func in OAL
|
||||
# Such as, should us this to query the result of func percentile(50,75,90,95,99) in OAL,
|
||||
# then five lines will be responded, p50 is the first element of return value.
|
||||
getMultipleLinearIntValues(metric: MetricCondition!, numOfLinear: Int!, duration: Duration!): [IntValues!]!
|
||||
getThermodynamic(metric: MetricCondition!, duration: Duration!): Thermodynamic
|
||||
}
|
||||
```
|
||||
|
||||
### Aggregation
|
||||
Aggregation query means that the metrics data need a secondary aggregation at query stage, which causes the query
|
||||
interfaces to have some different arguments. A typical example of aggregation query is the `TopN` list of services.
|
||||
Metrics stream aggregation simply calculates the metrics values of each service, but the expected list requires ordering metrics data
|
||||
by their values.
|
||||
|
||||
Aggregation query is for single value metrics only.
|
||||
|
||||
```graphql
|
||||
# The aggregation query is different with the metric query.
|
||||
# All aggregation queries require backend or/and storage do aggregation in query time.
|
||||
extend type Query {
|
||||
# TopN is an aggregation query.
|
||||
getServiceTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getAllServiceInstanceTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getServiceInstanceTopN(serviceId: ID!, name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getAllEndpointTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getEndpointTopN(serviceId: ID!, name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
}
|
||||
```
|
||||
|
||||
### Record
|
||||
Record is a general and abstract type for collected raw data.
|
||||
In the observability, traces and logs have specific and well-defined meanings, meanwhile, the general records represent other
|
||||
collected records. Such as sampled slow SQL statement, HTTP request raw data(request/response header/body)
|
||||
|
||||
```graphql
|
||||
extend type Query {
|
||||
# Query collected records with given metric name and parent entity conditions, and return in the requested order.
|
||||
readRecords(condition: RecordCondition!, duration: Duration!): [Record!]!
|
||||
}
|
||||
```
|
||||
|
|
@ -4,6 +4,8 @@ native visualization tool or 3rd party system, including Web UI, CLI or private
|
|||
|
||||
Query protocol official repository, https://github.com/apache/skywalking-query-protocol.
|
||||
|
||||
All deprecated APIs are moved [here](./query-protocol-deprecated.md).
|
||||
|
||||
### Metadata
|
||||
Metadata contains concise information on all services and their instances, endpoints, etc. under monitoring.
|
||||
You may query the metadata in different ways.
|
||||
|
|
@ -68,11 +70,13 @@ extend type Query {
|
|||
```
|
||||
|
||||
### Metrics
|
||||
Metrics query targets all objects defined in [OAL script](../concepts-and-designs/oal.md) and [MAL](../concepts-and-designs/mal.md).
|
||||
You may obtain the metrics data in linear or thermodynamic matrix formats based on the aggregation functions in script.
|
||||
Metrics query targets all objects defined in [OAL script](../concepts-and-designs/oal.md) and [MAL](../concepts-and-designs/mal.md).
|
||||
|
||||
#### V3 APIs
|
||||
Provide Metrics V3 query APIs since 9.5.0, including metadata and MQE.
|
||||
SkyWalking Metrics Query Expression(MQE) is an extension query mechanism. MQE allows users to do simple query-stage calculation like well known PromQL
|
||||
through GraphQL. The expression's syntax can refer to [here](./metrics-query-expression.md).
|
||||
|
||||
#### V2 APIs
|
||||
Provide Metrics V2 query APIs since 8.0.0, including metadata, single/multiple values, heatmap, and sampled records metrics.
|
||||
```graphql
|
||||
extend type Query {
|
||||
# Metrics definition metadata query. Response the metrics type which determines the suitable query methods.
|
||||
|
|
@ -80,75 +84,38 @@ extend type Query {
|
|||
# Get the list of all available metrics in the current OAP server.
|
||||
# Param, regex, could be used to filter the metrics by name.
|
||||
listMetrics(regex: String): [MetricDefinition!]!
|
||||
|
||||
# Read metrics single value in the duration of required metrics
|
||||
readMetricsValue(condition: MetricsCondition!, duration: Duration!): Long!
|
||||
# Read time-series values in the duration of required metrics
|
||||
readMetricsValues(condition: MetricsCondition!, duration: Duration!): MetricsValues!
|
||||
# Read entity list of required metrics and parent entity type.
|
||||
sortMetrics(condition: TopNCondition!, duration: Duration!): [SelectedRecord!]!
|
||||
# Read value in the given time duration, usually as a linear.
|
||||
# labels: the labels you need to query.
|
||||
readLabeledMetricsValues(condition: MetricsCondition!, labels: [String!]!, duration: Duration!): [MetricsValues!]!
|
||||
# Heatmap is bucket based value statistic result.
|
||||
readHeatMap(condition: MetricsCondition!, duration: Duration!): HeatMap
|
||||
# Deprecated since 9.3.0, replaced by readRecords defined in record.graphqls
|
||||
# Read the sampled records
|
||||
# TopNCondition#scope is not required.
|
||||
readSampledRecords(condition: TopNCondition!, duration: Duration!): [SelectedRecord!]!
|
||||
# The return type of the given expression, the MQEValues will be empty.
|
||||
returnTypeOfMQE(expression: String!): ExpressionResult!
|
||||
execExpression(expression: String!, entity: Entity!, duration: Duration!): ExpressionResult!
|
||||
}
|
||||
```
|
||||
|
||||
#### V1 APIs
|
||||
3 types of metrics can be queried. V1 APIs were introduced since 6.x. Now they are a shell to V2 APIs.
|
||||
1. Single value. Most default metrics are in single value. `getValues` and `getLinearIntValues` are suitable for this purpose.
|
||||
1. Multiple value. A metric defined in OAL includes multiple value calculations. Use `getMultipleLinearIntValues` to obtain all values. `percentile` is a typical multiple value function in OAL.
|
||||
1. Heatmap value. Read [Heatmap in WIKI](https://en.wikipedia.org/wiki/Heat_map) for details. `thermodynamic` is the only OAL function. Use `getThermodynamic` to get the values.
|
||||
```graphql
|
||||
extend type Query {
|
||||
getValues(metric: BatchMetricConditions!, duration: Duration!): IntValues
|
||||
getLinearIntValues(metric: MetricCondition!, duration: Duration!): IntValues
|
||||
# Query the type of metrics including multiple values, and format them as multiple lines.
|
||||
# The seq of these multiple lines base on the calculation func in OAL
|
||||
# Such as, should us this to query the result of func percentile(50,75,90,95,99) in OAL,
|
||||
# then five lines will be responded, p50 is the first element of return value.
|
||||
getMultipleLinearIntValues(metric: MetricCondition!, numOfLinear: Int!, duration: Duration!): [IntValues!]!
|
||||
getThermodynamic(metric: MetricCondition!, duration: Duration!): Thermodynamic
|
||||
type ExpressionResult {
|
||||
type: ExpressionResultType!
|
||||
# When the type == TIME_SERIES_VALUES, the results would be a collection of MQEValues.
|
||||
# In other legal type cases, only one MQEValues is expected in the array.
|
||||
results: [MQEValues!]!
|
||||
# When type == ExpressionResultType.UNKNOWN,
|
||||
# the error message includes the expression resolving errors.
|
||||
error: String
|
||||
}
|
||||
```
|
||||
|
||||
Metrics are defined in the `config/oal/*.oal` files.
|
||||
|
||||
### Aggregation
|
||||
Aggregation query means that the metrics data need a secondary aggregation at query stage, which causes the query
|
||||
interfaces to have some different arguments. A typical example of aggregation query is the `TopN` list of services.
|
||||
Metrics stream aggregation simply calculates the metrics values of each service, but the expected list requires ordering metrics data
|
||||
by their values.
|
||||
|
||||
Aggregation query is for single value metrics only.
|
||||
|
||||
```graphql
|
||||
# The aggregation query is different with the metric query.
|
||||
# All aggregation queries require backend or/and storage do aggregation in query time.
|
||||
extend type Query {
|
||||
# TopN is an aggregation query.
|
||||
getServiceTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getAllServiceInstanceTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getServiceInstanceTopN(serviceId: ID!, name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getAllEndpointTopN(name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
getEndpointTopN(serviceId: ID!, name: String!, topN: Int!, duration: Duration!, order: Order!): [TopNEntity!]!
|
||||
}
|
||||
```
|
||||
|
||||
### Record
|
||||
Record is a general and abstract type for collected raw data.
|
||||
In the observability, traces and logs have specific and well-defined meanings, meanwhile, the general records represent other
|
||||
collected records. Such as sampled slow SQL statement, HTTP request raw data(request/response header/body)
|
||||
|
||||
```graphql
|
||||
extend type Query {
|
||||
# Query collected records with given metric name and parent entity conditions, and return in the requested order.
|
||||
readRecords(condition: RecordCondition!, duration: Duration!): [Record!]!
|
||||
enum ExpressionResultType {
|
||||
# Can't resolve the type of the given expression.
|
||||
UNKNOWN
|
||||
# A single value
|
||||
SINGLE_VALUE
|
||||
# A collection of time-series values.
|
||||
# The value could have labels or not.
|
||||
TIME_SERIES_VALUES
|
||||
# A collection of aggregated values through metric sort function
|
||||
SORTED_LIST
|
||||
# A collection of sampled records.
|
||||
# When the original metric type is sampled records
|
||||
RECORD_LIST
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@
|
|||
* Support Redis slow logs collection.
|
||||
* Fix data loss when query continuous profiling task record.
|
||||
* Adapt the continuous profiling task query GraphQL.
|
||||
* Support Metrics Query Expression(MQE) and allows users to do simple query-stage calculation through the expression.
|
||||
* Deprecated metrics query v2 protocol.
|
||||
* Deprecated record query protocol.
|
||||
|
||||
#### UI
|
||||
* Revert: cpm5d function. This feature is cancelled from backend.
|
||||
|
|
@ -77,6 +80,7 @@
|
|||
* Add grizzly icon.
|
||||
* Fix: the Instance List data display error.
|
||||
* Fix: set topN type to Number.
|
||||
* Support Metrics Query Expression(MQE) and allows users to do simple query-stage calculation through the expression.
|
||||
|
||||
#### Documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -292,7 +292,13 @@ catalog:
|
|||
- name: "Query APIs"
|
||||
catalog:
|
||||
- name: "GraphQL APIs"
|
||||
path: "/en/api/query-protocol"
|
||||
catalog:
|
||||
- name: "GraphQL APIs"
|
||||
path: "/en/api/query-protocol"
|
||||
- name: "MQE Syntax"
|
||||
path: "/en/api/metrics-query-expression"
|
||||
- name: "Deprecated APIs"
|
||||
path: "/en/api/query-protocol-deprecated"
|
||||
- name: "PromQL APIs"
|
||||
path: "/en/api/promql-service"
|
||||
- name: "Security Notice"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* SelectedRecord is an abtract data element, including id, name, value and a reference id.
|
||||
* SelectedRecord is an abstract data element, including id, name, value and a reference id.
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>server-query-plugin</artifactId>
|
||||
<groupId>org.apache.skywalking</groupId>
|
||||
<version>9.5.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>mqe-grammar</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>antlr</id>
|
||||
<goals>
|
||||
<goal>antlr4</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<visitor>true</visitor>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
lexer grammar MQELexer;
|
||||
|
||||
// Keywords
|
||||
BOOL options { caseInsensitive=true; }: 'bool';
|
||||
LABLES: 'labels';
|
||||
RELABELS: 'relabels';
|
||||
|
||||
|
||||
// Constructors symbols
|
||||
DOT: '.';
|
||||
COMMA: ',';
|
||||
L_PAREN: '(';
|
||||
R_PAREN: ')';
|
||||
L_BRACKET: '[';
|
||||
R_BRACKET: ']';
|
||||
L_BRACE: '{';
|
||||
R_BRACE: '}';
|
||||
EQ: '=';
|
||||
|
||||
// Scalar Binary operators
|
||||
SUB: '-';
|
||||
ADD: '+';
|
||||
MUL: '*';
|
||||
DIV: '/';
|
||||
MOD: '%';
|
||||
DEQ: '==';
|
||||
NEQ: '!=';
|
||||
LTE: '<=';
|
||||
LT: '<';
|
||||
GTE: '>=';
|
||||
GT: '>';
|
||||
|
||||
// Aggregation operators
|
||||
AVG: 'avg';
|
||||
COUNT: 'count';
|
||||
MAX: 'max';
|
||||
MIN: 'min';
|
||||
SUM: 'sum';
|
||||
|
||||
// Functions
|
||||
ABS: 'abs';
|
||||
CEIL: 'ceil';
|
||||
FLOOR: 'floor';
|
||||
ROUND: 'round';
|
||||
|
||||
// TopN
|
||||
TOP_N: 'top_n';
|
||||
|
||||
// Order
|
||||
ASC options { caseInsensitive=true; }: 'asc';
|
||||
DES options { caseInsensitive=true; }: 'des';
|
||||
|
||||
// Literals
|
||||
INTEGER: Digit+;
|
||||
DECIMAL: Digit+ DOT Digit+;
|
||||
NAME_STRING: NameLetter+;
|
||||
VALUE_STRING: '\'' .*? '\'' | '"' .*? '"';
|
||||
|
||||
|
||||
// Fragments
|
||||
fragment Digit: [0-9];
|
||||
fragment NameLetter: [a-zA-Z0-9_];
|
||||
|
||||
WS : [ \t\r\n]+ -> skip;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
parser grammar MQEParser;
|
||||
|
||||
options { tokenVocab = MQELexer; }
|
||||
|
||||
root: expression;
|
||||
|
||||
expression
|
||||
: expressionNode # exprNode
|
||||
| expression mulDivMod expression # mulDivModOp
|
||||
| expression addSub expression # addSubOp
|
||||
| expression compare expression # compareOp
|
||||
| aggregation L_PAREN expression R_PAREN # aggregationOp
|
||||
| function0 L_PAREN expression R_PAREN #function0OP
|
||||
| function1 L_PAREN expression COMMA parameter R_PAREN #function1OP
|
||||
| topN L_PAREN metric COMMA parameter COMMA order R_PAREN #topNOP
|
||||
;
|
||||
|
||||
expressionNode: metric| scalar;
|
||||
|
||||
addSub: ADD | SUB ;
|
||||
mulDivMod: MUL | DIV | MOD;
|
||||
compare: (DEQ | NEQ | LTE | LT | GTE | GT) BOOL?;
|
||||
|
||||
metricName: NAME_STRING;
|
||||
metric: metricName | metricName L_BRACE labelList? R_BRACE;
|
||||
|
||||
labelName: LABLES | RELABELS;
|
||||
labelValue: VALUE_STRING;
|
||||
label: labelName EQ labelValue;
|
||||
labelList: label (COMMA label)*;
|
||||
|
||||
|
||||
scalar: INTEGER | DECIMAL;
|
||||
|
||||
aggregation:
|
||||
AVG | COUNT | SUM | MAX | MIN ;
|
||||
|
||||
// 0 parameter function
|
||||
function0:
|
||||
ABS | CEIL | FLOOR;
|
||||
// 1 parameter function
|
||||
function1:
|
||||
ROUND;
|
||||
|
||||
topN: TOP_N;
|
||||
|
||||
parameter: INTEGER;
|
||||
|
||||
order: ASC | DES;
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
<modules>
|
||||
<module>query-graphql-plugin</module>
|
||||
<module>zipkin-query-plugin</module>
|
||||
<module>mqe-grammar</module>
|
||||
<module>promql-plugin</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ public class PromQLApiHandler {
|
|||
if (match.isPresent()) {
|
||||
PromQLLexer lexer = new PromQLLexer(
|
||||
CharStreams.fromString(match.get()));
|
||||
lexer.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
PromQLParser parser = new PromQLParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
|
|
@ -217,9 +217,9 @@ public class PromQLApiHandler {
|
|||
SeriesQueryRsp response = new SeriesQueryRsp();
|
||||
PromQLLexer lexer = new PromQLLexer(
|
||||
CharStreams.fromString(match));
|
||||
lexer.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
PromQLParser parser = new PromQLParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
|
|
@ -295,9 +295,9 @@ public class PromQLApiHandler {
|
|||
|
||||
PromQLLexer lexer = new PromQLLexer(
|
||||
CharStreams.fromString(query));
|
||||
lexer.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
PromQLParser parser = new PromQLParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
|
|
@ -356,9 +356,9 @@ public class PromQLApiHandler {
|
|||
ExprQueryRsp response = new ExprQueryRsp();
|
||||
PromQLLexer lexer = new PromQLLexer(
|
||||
CharStreams.fromString(query));
|
||||
lexer.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
PromQLParser parser = new PromQLParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(ParseErrorListener.INSTANCE);
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ import org.antlr.v4.runtime.RecognitionException;
|
|||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
public class ParseErrorListener extends BaseErrorListener {
|
||||
public static final ParseErrorListener INSTANCE = new ParseErrorListener();
|
||||
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@
|
|||
<artifactId>log-analyzer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.skywalking</groupId>
|
||||
<artifactId>mqe-grammar</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java</artifactId>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.apache.skywalking.oap.query.graphql.resolver.LogTestQuery;
|
|||
import org.apache.skywalking.oap.query.graphql.resolver.MetadataQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.MetadataQueryV2;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.MetricQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.MetricsExpressionQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.MetricsQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.Mutation;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.OndemandLogQuery;
|
||||
|
|
@ -98,11 +99,13 @@ public class GraphQLQueryProvider extends ModuleProvider {
|
|||
.file("query-protocol/topology.graphqls")
|
||||
.resolvers(new TopologyQuery(getManager()))
|
||||
/*
|
||||
* Metrics v2 query protocol is an alternative metrics query(s) of original v1,
|
||||
* defined in the metric.graphql, top-n-records.graphqls, and aggregation.graphqls.
|
||||
* Since 9.5.0.
|
||||
* Metrics v3 query protocol is an enhanced metrics query(s) from original v1 and v2
|
||||
* powered by newly added Metrics Query Expression Language to fetch and
|
||||
* manipulate metrics data in the query stage.
|
||||
*/
|
||||
.file("query-protocol/metrics-v2.graphqls")
|
||||
.resolvers(new MetricsQuery(getManager()))
|
||||
.file("query-protocol/metrics-v3.graphqls")
|
||||
.resolvers(new MetricsExpressionQuery(getManager()))
|
||||
////////
|
||||
//Deprecated Queries
|
||||
////////
|
||||
|
|
@ -112,6 +115,9 @@ public class GraphQLQueryProvider extends ModuleProvider {
|
|||
.resolvers(new AggregationQuery(getManager()))
|
||||
.file("query-protocol/top-n-records.graphqls")
|
||||
.resolvers(new TopNRecordsQuery(getManager()))
|
||||
//Deprecated since 9.5.0
|
||||
.file("query-protocol/metrics-v2.graphqls")
|
||||
.resolvers(new MetricsQuery(getManager()))
|
||||
////////
|
||||
.file("query-protocol/trace.graphqls")
|
||||
.resolvers(new TraceQuery(getManager()))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.mqe.rt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.operation.AggregationOp;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.MetricsQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.resolver.RecordsQuery;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResult;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResultType;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValue;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValues;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.Metadata;
|
||||
import org.apache.skywalking.oap.server.core.Const;
|
||||
import org.apache.skywalking.oap.server.core.query.DurationUtils;
|
||||
import org.apache.skywalking.oap.server.core.query.PointOfTime;
|
||||
import org.apache.skywalking.oap.server.core.query.enumeration.Order;
|
||||
import org.apache.skywalking.oap.server.core.query.input.Duration;
|
||||
import org.apache.skywalking.oap.server.core.query.input.Entity;
|
||||
import org.apache.skywalking.oap.server.core.query.input.MetricsCondition;
|
||||
import org.apache.skywalking.oap.server.core.query.input.RecordCondition;
|
||||
import org.apache.skywalking.oap.server.core.query.input.TopNCondition;
|
||||
import org.apache.skywalking.oap.server.core.query.type.KVInt;
|
||||
import org.apache.skywalking.oap.server.core.query.type.KeyValue;
|
||||
import org.apache.skywalking.oap.server.core.query.type.MetricsValues;
|
||||
import org.apache.skywalking.oap.server.core.query.type.Record;
|
||||
import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
|
||||
import org.apache.skywalking.oap.server.core.storage.annotation.Column;
|
||||
import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
|
||||
import org.apache.skywalking.oap.server.library.util.StringUtil;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParserBaseVisitor;
|
||||
|
||||
import static org.apache.skywalking.oap.query.graphql.mqe.rt.operation.BinaryOp.doBinaryOp;
|
||||
import static org.apache.skywalking.oap.query.graphql.mqe.rt.operation.FunctionOp.doFunction0Op;
|
||||
import static org.apache.skywalking.oap.query.graphql.mqe.rt.operation.FunctionOp.doFunction1Op;
|
||||
|
||||
@Slf4j
|
||||
public class MQEVisitor extends MQEParserBaseVisitor<ExpressionResult> {
|
||||
private final MetricsQuery metricsQuery;
|
||||
private final RecordsQuery recordsQuery;
|
||||
private final Entity entity;
|
||||
private final Duration duration;
|
||||
|
||||
public MQEVisitor(final MetricsQuery metricsQuery,
|
||||
final RecordsQuery recordsQuery,
|
||||
final Entity entity,
|
||||
final Duration duration) {
|
||||
this.metricsQuery = metricsQuery;
|
||||
this.recordsQuery = recordsQuery;
|
||||
this.entity = entity;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is only used for parsing expresstions, won't query any metrics from backend.
|
||||
*/
|
||||
public MQEVisitor() {
|
||||
this.metricsQuery = null;
|
||||
this.recordsQuery = null;
|
||||
this.entity = null;
|
||||
this.duration = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitAddSubOp(MQEParser.AddSubOpContext ctx) {
|
||||
ExpressionResult left = visit(ctx.expression(0));
|
||||
if (StringUtil.isNotBlank(left.getError())) {
|
||||
return left;
|
||||
}
|
||||
ExpressionResult right = visit(ctx.expression(1));
|
||||
if (StringUtil.isNotBlank(right.getError())) {
|
||||
return right;
|
||||
}
|
||||
int opType = ctx.addSub().getStart().getType();
|
||||
try {
|
||||
return doBinaryOp(left, right, opType);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError(e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitMulDivModOp(MQEParser.MulDivModOpContext ctx) {
|
||||
ExpressionResult left = visit(ctx.expression(0));
|
||||
if (StringUtil.isNotBlank(left.getError())) {
|
||||
return left;
|
||||
}
|
||||
ExpressionResult right = visit(ctx.expression(1));
|
||||
if (StringUtil.isNotBlank(right.getError())) {
|
||||
return right;
|
||||
}
|
||||
int opType = ctx.mulDivMod().getStart().getType();
|
||||
try {
|
||||
return doBinaryOp(left, right, opType);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError(e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitScalar(MQEParser.ScalarContext ctx) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
double value = Double.parseDouble(ctx.getText());
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setDoubleValue(value);
|
||||
mqeValue.setEmptyValue(false);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.getValues().add(mqeValue);
|
||||
result.getResults().add(mqeValues);
|
||||
result.setType(ExpressionResultType.SINGLE_VALUE);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitAggregationOp(MQEParser.AggregationOpContext ctx) {
|
||||
int opType = ctx.aggregation().getStart().getType();
|
||||
ExpressionResult expResult = visit(ctx.expression());
|
||||
if (StringUtil.isNotEmpty(expResult.getError())) {
|
||||
return expResult;
|
||||
}
|
||||
try {
|
||||
return AggregationOp.doAggregationOp(expResult, opType);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError(e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitFunction0OP(MQEParser.Function0OPContext ctx) {
|
||||
int opType = ctx.function0().getStart().getType();
|
||||
ExpressionResult expResult = visit(ctx.expression());
|
||||
if (StringUtil.isNotEmpty(expResult.getError())) {
|
||||
return expResult;
|
||||
}
|
||||
try {
|
||||
return doFunction0Op(expResult, opType);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError(e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitFunction1OP(MQEParser.Function1OPContext ctx) {
|
||||
int opType = ctx.function1().getStart().getType();
|
||||
ExpressionResult expResult = visit(ctx.expression());
|
||||
if (StringUtil.isNotEmpty(expResult.getError())) {
|
||||
return expResult;
|
||||
}
|
||||
try {
|
||||
return doFunction1Op(expResult, opType, ctx.parameter());
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError(e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitTopNOP(MQEParser.TopNOPContext ctx) {
|
||||
return visit(ctx.metric());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitMetric(MQEParser.MetricContext ctx) {
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
String metricName = ctx.metricName().getText();
|
||||
Optional<ValueColumnMetadata.ValueColumn> valueColumn = ValueColumnMetadata.INSTANCE.readValueColumnDefinition(
|
||||
metricName);
|
||||
if (valueColumn.isEmpty()) {
|
||||
result.setType(ExpressionResultType.UNKNOWN);
|
||||
result.setError("Metric: [" + metricName + "] dose not exist.");
|
||||
return result;
|
||||
}
|
||||
|
||||
Column.ValueDataType dataType = valueColumn.get().getDataType();
|
||||
try {
|
||||
if (Column.ValueDataType.COMMON_VALUE == dataType) {
|
||||
if (ctx.parent instanceof MQEParser.TopNOPContext) {
|
||||
MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
|
||||
querySortMetrics(metricName, Integer.parseInt(parent.parameter().getText()),
|
||||
Order.valueOf(parent.order().getText().toUpperCase()), result
|
||||
);
|
||||
} else {
|
||||
queryMetrics(metricName, result);
|
||||
}
|
||||
} else if (Column.ValueDataType.LABELED_VALUE == dataType) {
|
||||
List<String> queryLabelList = Collections.emptyList();
|
||||
List<String> relabelList = Collections.emptyList();
|
||||
if (ctx.labelList() != null) {
|
||||
for (MQEParser.LabelContext labelCtx : ctx.labelList().label()) {
|
||||
int labelName = labelCtx.labelName().getStart().getType();
|
||||
String labelValue = labelCtx.labelValue().getText();
|
||||
String labelValueTrim = labelValue.substring(1, labelValue.length() - 1);
|
||||
switch (labelName) {
|
||||
case MQEParser.LABLES:
|
||||
if (StringUtil.isNotBlank(labelValueTrim)) {
|
||||
queryLabelList = Arrays.asList(labelValueTrim.split(Const.COMMA));
|
||||
}
|
||||
break;
|
||||
case MQEParser.RELABELS:
|
||||
if (StringUtil.isNotBlank(labelValueTrim)) {
|
||||
relabelList = Arrays.asList(labelValueTrim.split(Const.COMMA));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
queryLabeledMetrics(metricName, queryLabelList, relabelList, result);
|
||||
} else if (Column.ValueDataType.SAMPLED_RECORD == dataType) {
|
||||
if (ctx.parent instanceof MQEParser.TopNOPContext) {
|
||||
MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
|
||||
queryRecords(metricName, Integer.parseInt(parent.parameter().getText()),
|
||||
Order.valueOf(parent.order().getText()), result
|
||||
);
|
||||
} else {
|
||||
throw new IllegalExpressionException(
|
||||
"Metric: [" + metricName + "] is topN record, need top_n function for query.");
|
||||
}
|
||||
}
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError(e.getMessage());
|
||||
return errorResult;
|
||||
} catch (IOException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError("Internal IO exception, query metrics error.");
|
||||
log.error("Query metrics from backend error.", e);
|
||||
return errorResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void querySortMetrics(String metricName,
|
||||
int topN,
|
||||
Order order,
|
||||
ExpressionResult result) throws IOException {
|
||||
List<SelectedRecord> selectedRecords;
|
||||
// API `returnTypeOfMQE` not require entity and duration.
|
||||
if (entity != null && duration != null && metricsQuery != null) {
|
||||
TopNCondition topNCondition = new TopNCondition();
|
||||
topNCondition.setName(metricName);
|
||||
topNCondition.setTopN(topN);
|
||||
topNCondition.setParentService(entity.getServiceName());
|
||||
topNCondition.setOrder(order);
|
||||
topNCondition.setNormal(entity.getNormal());
|
||||
selectedRecords = metricsQuery.sortMetrics(topNCondition, duration);
|
||||
} else {
|
||||
selectedRecords = Collections.emptyList();
|
||||
}
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(selectedRecords.size());
|
||||
selectedRecords.forEach(selectedRecord -> {
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setId(selectedRecord.getName());
|
||||
mqeValue.setEmptyValue(false);
|
||||
mqeValue.setDoubleValue(Double.parseDouble(selectedRecord.getValue()));
|
||||
mqeValueList.add(mqeValue);
|
||||
});
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName(metricName);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.setValues(mqeValueList);
|
||||
mqeValues.setMetric(metadata);
|
||||
result.getResults().add(mqeValues);
|
||||
result.setType(ExpressionResultType.SORTED_LIST);
|
||||
}
|
||||
|
||||
private void queryRecords(String metricName, int topN, Order order, ExpressionResult result) throws IOException {
|
||||
List<Record> records;
|
||||
// API `returnTypeOfMQE` not require entity and duration.
|
||||
if (entity != null && duration != null && recordsQuery != null) {
|
||||
RecordCondition recordCondition = new RecordCondition();
|
||||
recordCondition.setName(metricName);
|
||||
recordCondition.setTopN(topN);
|
||||
recordCondition.setParentEntity(entity);
|
||||
recordCondition.setOrder(order);
|
||||
records = recordsQuery.readRecords(recordCondition, duration);
|
||||
} else {
|
||||
records = Collections.emptyList();
|
||||
}
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(records.size());
|
||||
records.forEach(record -> {
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setId(record.getName());
|
||||
mqeValue.setEmptyValue(false);
|
||||
mqeValue.setDoubleValue(Double.parseDouble(record.getValue()));
|
||||
mqeValue.setTraceID(record.getRefId());
|
||||
mqeValueList.add(mqeValue);
|
||||
});
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName(metricName);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.setValues(mqeValueList);
|
||||
mqeValues.setMetric(metadata);
|
||||
result.getResults().add(mqeValues);
|
||||
result.setType(ExpressionResultType.RECORD_LIST);
|
||||
}
|
||||
|
||||
private void queryMetrics(String metricName, ExpressionResult result) throws IOException {
|
||||
MetricsValues metricsValues;
|
||||
List<PointOfTime> times;
|
||||
if (entity != null && duration != null && metricsQuery != null) {
|
||||
MetricsCondition metricsCondition = new MetricsCondition();
|
||||
metricsCondition.setName(metricName);
|
||||
metricsCondition.setEntity(entity);
|
||||
metricsValues = metricsQuery.readMetricsValues(metricsCondition, duration);
|
||||
times = duration.assembleDurationPoints();
|
||||
} else {
|
||||
metricsValues = new MetricsValues();
|
||||
times = Collections.emptyList();
|
||||
}
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(times.size());
|
||||
for (int i = 0; i < times.size(); i++) {
|
||||
long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(duration.getStep(), times.get(i).getPoint())
|
||||
.getMillis();
|
||||
KVInt kvInt = metricsValues.getValues().getValues().get(i);
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setId(Long.toString(retTimestamp));
|
||||
mqeValue.setEmptyValue(kvInt.isEmptyValue());
|
||||
mqeValue.setDoubleValue(kvInt.getValue());
|
||||
mqeValueList.add(mqeValue);
|
||||
}
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName(metricName);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.setValues(mqeValueList);
|
||||
mqeValues.setMetric(metadata);
|
||||
result.getResults().add(mqeValues);
|
||||
result.setType(ExpressionResultType.TIME_SERIES_VALUES);
|
||||
}
|
||||
|
||||
private void queryLabeledMetrics(String metricName,
|
||||
List<String> queryLabelList,
|
||||
List<String> relabelList,
|
||||
ExpressionResult result) throws IOException {
|
||||
List<MetricsValues> metricsValuesList;
|
||||
List<PointOfTime> times;
|
||||
if (entity != null && duration != null && metricsQuery != null) {
|
||||
MetricsCondition metricsCondition = new MetricsCondition();
|
||||
metricsCondition.setName(metricName);
|
||||
metricsCondition.setEntity(entity);
|
||||
metricsValuesList = metricsQuery.readLabeledMetricsValues(
|
||||
metricsCondition, queryLabelList, duration);
|
||||
times = duration.assembleDurationPoints();
|
||||
} else {
|
||||
metricsValuesList = Collections.emptyList();
|
||||
times = Collections.emptyList();
|
||||
}
|
||||
Map<String, String> relabelMap = new HashMap<>();
|
||||
for (int i = 0; i < queryLabelList.size(); i++) {
|
||||
if (relabelList.size() > i) {
|
||||
relabelMap.put(queryLabelList.get(i), relabelList.get(i));
|
||||
continue;
|
||||
}
|
||||
relabelMap.put(queryLabelList.get(i), queryLabelList.get(i));
|
||||
}
|
||||
metricsValuesList.forEach(metricsValues -> {
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(times.size());
|
||||
for (int i = 0; i < times.size(); i++) {
|
||||
long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(duration.getStep(), times.get(i).getPoint())
|
||||
.getMillis();
|
||||
KVInt kvInt = metricsValues.getValues().getValues().get(i);
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setEmptyValue(kvInt.isEmptyValue());
|
||||
mqeValue.setId(Long.toString(retTimestamp));
|
||||
mqeValueList.add(mqeValue);
|
||||
if (!kvInt.isEmptyValue()) {
|
||||
mqeValue.setDoubleValue(kvInt.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName(metricName);
|
||||
KeyValue labelValue = new KeyValue("label", relabelMap.getOrDefault(
|
||||
metricsValues.getLabel(),
|
||||
metricsValues.getLabel()
|
||||
));
|
||||
metadata.getLabels().add(labelValue);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.setValues(mqeValueList);
|
||||
mqeValues.setMetric(metadata);
|
||||
result.getResults().add(mqeValues);
|
||||
});
|
||||
result.setType(ExpressionResultType.TIME_SERIES_VALUES);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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.skywalking.oap.query.graphql.mqe.rt.exception;
|
||||
|
||||
public class IllegalExpressionException extends Exception {
|
||||
public IllegalExpressionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.mqe.rt.exception;
|
||||
|
||||
import graphql.org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
public class ParseErrorListener extends BaseErrorListener {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
int line,
|
||||
int charPositionInLine,
|
||||
String msg,
|
||||
RecognitionException e)
|
||||
throws ParseCancellationException {
|
||||
|
||||
throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.mqe.rt.operation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.DoubleStream;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResult;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResultType;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValue;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValues;
|
||||
|
||||
public class AggregationOp {
|
||||
public static ExpressionResult doAggregationOp(ExpressionResult result,
|
||||
int opType) throws IllegalExpressionException {
|
||||
switch (opType) {
|
||||
case MQEParser.AVG:
|
||||
return aggregateResult(result, mqeValues -> mqeValues.getValues()
|
||||
.stream()
|
||||
.filter(mqeValue -> !mqeValue.isEmptyValue())
|
||||
.flatMapToDouble(mqeValue -> DoubleStream.of(
|
||||
mqeValue.getDoubleValue()))
|
||||
.average());
|
||||
case MQEParser.COUNT:
|
||||
return aggregateResult(result, mqeValues -> OptionalDouble.of(
|
||||
mqeValues.getValues().stream().filter(mqeValue -> !mqeValue.isEmptyValue()).count()));
|
||||
case MQEParser.MAX:
|
||||
return selectResult(result, mqeValues -> mqeValues.getValues()
|
||||
.stream()
|
||||
.filter(mqeValue -> !mqeValue.isEmptyValue())
|
||||
.max(Comparator.comparingDouble(
|
||||
MQEValue::getDoubleValue)));
|
||||
case MQEParser.MIN:
|
||||
return selectResult(result, mqeValues -> mqeValues.getValues()
|
||||
.stream()
|
||||
.filter(mqeValue -> !mqeValue.isEmptyValue())
|
||||
.min(Comparator.comparingDouble(
|
||||
MQEValue::getDoubleValue)));
|
||||
case MQEParser.SUM:
|
||||
return aggregateResult(result, mqeValues -> OptionalDouble.of(mqeValues.getValues()
|
||||
.stream()
|
||||
.filter(
|
||||
mqeValue -> !mqeValue.isEmptyValue())
|
||||
.flatMapToDouble(
|
||||
mqeValue -> DoubleStream.of(
|
||||
mqeValue.getDoubleValue()))
|
||||
.sum()));
|
||||
default:
|
||||
throw new IllegalExpressionException("Unsupported aggregation operation.");
|
||||
}
|
||||
}
|
||||
|
||||
private static ExpressionResult aggregateResult(ExpressionResult result,
|
||||
Function<MQEValues, OptionalDouble> aggregator) {
|
||||
for (MQEValues resultValues : result.getResults()) {
|
||||
OptionalDouble resultValue = aggregator.apply(resultValues);
|
||||
if (resultValue.isPresent()) {
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(1);
|
||||
//no id
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setEmptyValue(false);
|
||||
mqeValue.setDoubleValue(resultValue.getAsDouble());
|
||||
mqeValueList.add(mqeValue);
|
||||
resultValues.setValues(mqeValueList);
|
||||
} else {
|
||||
resultValues.setValues(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
result.setType(ExpressionResultType.SINGLE_VALUE);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ExpressionResult selectResult(ExpressionResult result,
|
||||
Function<MQEValues, Optional<MQEValue>> aggregator) {
|
||||
for (MQEValues resultValues : result.getResults()) {
|
||||
Optional<MQEValue> resultValue = aggregator.apply(resultValues);
|
||||
if (resultValue.isPresent()) {
|
||||
List<MQEValue> mqeValueList = new ArrayList<>(1);
|
||||
mqeValueList.add(resultValue.get());
|
||||
resultValues.setValues(mqeValueList);
|
||||
} else {
|
||||
resultValues.setValues(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
result.setType(ExpressionResultType.SINGLE_VALUE);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.mqe.rt.operation;
|
||||
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResult;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResultType;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValue;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.MQEValues;
|
||||
|
||||
public class BinaryOp {
|
||||
public static ExpressionResult doBinaryOp(ExpressionResult left,
|
||||
ExpressionResult right,
|
||||
int opType) throws IllegalExpressionException {
|
||||
if (left.getType() == ExpressionResultType.SINGLE_VALUE && right.getType() == ExpressionResultType.SINGLE_VALUE) {
|
||||
double scalarLeft = left.getResults().get(0).getValues().get(0).getDoubleValue();
|
||||
double scalarRight = right.getResults().get(0).getValues().get(0).getDoubleValue();
|
||||
double value = scalarBinaryOp(scalarLeft, scalarRight, opType);
|
||||
ExpressionResult result = new ExpressionResult();
|
||||
MQEValue mqeValue = new MQEValue();
|
||||
mqeValue.setDoubleValue(value);
|
||||
mqeValue.setEmptyValue(false);
|
||||
MQEValues mqeValues = new MQEValues();
|
||||
mqeValues.getValues().add(mqeValue);
|
||||
result.getResults().add(mqeValues);
|
||||
result.setType(ExpressionResultType.SINGLE_VALUE);
|
||||
return result;
|
||||
} else if ((left.getType() == ExpressionResultType.TIME_SERIES_VALUES ||
|
||||
left.getType() == ExpressionResultType.SORTED_LIST ||
|
||||
left.getType() == ExpressionResultType.RECORD_LIST)
|
||||
&& right.getType() == ExpressionResultType.SINGLE_VALUE) {
|
||||
return many2OneBinaryOp(left, right, opType);
|
||||
} else if (left.getType() == ExpressionResultType.SINGLE_VALUE &&
|
||||
(right.getType() == ExpressionResultType.TIME_SERIES_VALUES ||
|
||||
right.getType() == ExpressionResultType.SORTED_LIST ||
|
||||
right.getType() == ExpressionResultType.RECORD_LIST)) {
|
||||
return many2OneBinaryOp(right, left, opType);
|
||||
} else if (left.getType() == ExpressionResultType.TIME_SERIES_VALUES && right.getType() == ExpressionResultType.TIME_SERIES_VALUES) {
|
||||
return seriesBinaryOp(right, left, opType);
|
||||
}
|
||||
|
||||
throw new IllegalExpressionException("Unsupported binary operation.");
|
||||
}
|
||||
|
||||
//scalar with scalar
|
||||
private static double scalarBinaryOp(double leftValue, double rightValue, int opType) {
|
||||
double calculatedResult = 0;
|
||||
switch (opType) {
|
||||
case MQEParser.ADD:
|
||||
calculatedResult = leftValue + rightValue;
|
||||
break;
|
||||
case MQEParser.SUB:
|
||||
calculatedResult = leftValue - rightValue;
|
||||
break;
|
||||
case MQEParser.MUL:
|
||||
calculatedResult = leftValue * rightValue;
|
||||
break;
|
||||
case MQEParser.DIV:
|
||||
calculatedResult = leftValue / rightValue;
|
||||
break;
|
||||
case MQEParser.MOD:
|
||||
calculatedResult = leftValue % rightValue;
|
||||
break;
|
||||
}
|
||||
return calculatedResult;
|
||||
}
|
||||
|
||||
//series with series
|
||||
private static ExpressionResult seriesBinaryOp(ExpressionResult seriesLeft,
|
||||
ExpressionResult seriesRight,
|
||||
int opType) throws IllegalExpressionException {
|
||||
if (seriesLeft.getResults().size() == 1 && seriesRight.getResults().size() == 1) {
|
||||
MQEValues mqeValuesL = seriesLeft.getResults().get(0);
|
||||
MQEValues mqeValuesR = seriesRight.getResults().get(0);
|
||||
for (int i = 0; i < mqeValuesL.getValues().size(); i++) {
|
||||
//clean metric info
|
||||
mqeValuesL.setMetric(null);
|
||||
MQEValue valueL = mqeValuesL.getValues().get(i);
|
||||
MQEValue valueR = mqeValuesR.getValues().get(i);
|
||||
if (valueL.isEmptyValue() || valueR.isEmptyValue()) {
|
||||
valueL.setEmptyValue(true);
|
||||
valueL.setDoubleValue(0);
|
||||
continue;
|
||||
}
|
||||
//time should be mapped
|
||||
double newValue = scalarBinaryOp(valueL.getDoubleValue(), valueR.getDoubleValue(), opType);
|
||||
mqeValuesL.getValues().get(i).setDoubleValue(newValue);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalExpressionException("Binary operation don't support labeled metrics.");
|
||||
}
|
||||
|
||||
return seriesLeft;
|
||||
}
|
||||
|
||||
//series or list with scalar
|
||||
private static ExpressionResult many2OneBinaryOp(ExpressionResult manyResult, ExpressionResult singleResult, int opType) {
|
||||
manyResult.getResults().forEach(mqeValues -> {
|
||||
mqeValues.getValues().forEach(mqeValue -> {
|
||||
if (!mqeValue.isEmptyValue()) {
|
||||
double newValue = scalarBinaryOp(
|
||||
mqeValue.getDoubleValue(), singleResult.getResults()
|
||||
.get(0)
|
||||
.getValues()
|
||||
.get(0)
|
||||
.getDoubleValue(), opType);
|
||||
mqeValue.setDoubleValue(newValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
return manyResult;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.mqe.rt.operation;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.function.Function;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResult;
|
||||
|
||||
public class FunctionOp {
|
||||
public static ExpressionResult doFunction0Op(ExpressionResult expResult,
|
||||
int opType) throws IllegalExpressionException {
|
||||
switch (opType) {
|
||||
case MQEParser.ABS:
|
||||
return FunctionOp.transResult(expResult, Math::abs);
|
||||
case MQEParser.CEIL:
|
||||
return FunctionOp.transResult(expResult, Math::ceil);
|
||||
case MQEParser.FLOOR:
|
||||
return FunctionOp.transResult(expResult, Math::floor);
|
||||
}
|
||||
|
||||
throw new IllegalExpressionException("Unsupported function.");
|
||||
}
|
||||
|
||||
public static ExpressionResult doFunction1Op(ExpressionResult expResult,
|
||||
int opType,
|
||||
MQEParser.ParameterContext params) throws IllegalExpressionException {
|
||||
switch (opType) {
|
||||
case MQEParser.ROUND:
|
||||
return FunctionOp.transResult(expResult, aDouble -> {
|
||||
BigDecimal bd = BigDecimal.valueOf(aDouble);
|
||||
return bd.setScale(Integer.parseInt(params.INTEGER().getText()), RoundingMode.HALF_UP).doubleValue();
|
||||
});
|
||||
}
|
||||
|
||||
throw new IllegalExpressionException("Unsupported function.");
|
||||
}
|
||||
|
||||
private static ExpressionResult transResult(ExpressionResult expResult, Function<Double, Double> calculator) {
|
||||
expResult.getResults().forEach(resultValues -> {
|
||||
resultValues.getValues().forEach(mqeValue -> {
|
||||
if (!mqeValue.isEmptyValue()) {
|
||||
double newValue = calculator.apply(mqeValue.getDoubleValue());
|
||||
mqeValue.setDoubleValue(newValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
return expResult;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.printf(String.valueOf(Math.floor(1.1112)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.resolver;
|
||||
|
||||
import graphql.kickstart.tools.GraphQLQueryResolver;
|
||||
import graphql.org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Collections;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.MQEVisitor;
|
||||
import org.apache.skywalking.oap.query.graphql.mqe.rt.exception.ParseErrorListener;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResult;
|
||||
import org.apache.skywalking.oap.query.graphql.type.mql.ExpressionResultType;
|
||||
import org.apache.skywalking.oap.server.core.query.input.Entity;
|
||||
import org.apache.skywalking.oap.server.core.query.input.Duration;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleManager;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQELexer;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
|
||||
public class MetricsExpressionQuery implements GraphQLQueryResolver {
|
||||
private final MetricsQuery metricsQuery;
|
||||
private final RecordsQuery recordsQuery;
|
||||
private final DecimalFormat valueFormat = new DecimalFormat();
|
||||
|
||||
public MetricsExpressionQuery(ModuleManager moduleManager) {
|
||||
this.metricsQuery = new MetricsQuery(moduleManager);
|
||||
this.recordsQuery = new RecordsQuery(moduleManager);
|
||||
this.valueFormat.setGroupingUsed(false);
|
||||
}
|
||||
|
||||
public ExpressionResult returnTypeOfMQE(String expression) {
|
||||
MQELexer lexer = new MQELexer(
|
||||
CharStreams.fromString(expression));
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
} catch (ParseCancellationException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError(e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
MQEVisitor visitor = new MQEVisitor();
|
||||
ExpressionResult parseResult = visitor.visit(tree);
|
||||
parseResult.setResults(Collections.emptyList());
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
public ExpressionResult execExpression(String expression, Entity entity, Duration duration) {
|
||||
MQELexer lexer = new MQELexer(
|
||||
CharStreams.fromString(expression));
|
||||
lexer.addErrorListener(new ParseErrorListener());
|
||||
MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
|
||||
parser.addErrorListener(new ParseErrorListener());
|
||||
ParseTree tree;
|
||||
try {
|
||||
tree = parser.expression();
|
||||
} catch (ParseCancellationException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError(e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
MQEVisitor visitor = new MQEVisitor(metricsQuery, recordsQuery, entity, duration);
|
||||
ExpressionResult parseResult = visitor.visit(tree);
|
||||
|
||||
parseResult.getResults().forEach(mqeValues -> {
|
||||
mqeValues.getValues().forEach(mqeValue -> {
|
||||
if (!mqeValue.isEmptyValue()) {
|
||||
mqeValue.setValue(valueFormat.format(mqeValue.getDoubleValue()));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return parseResult;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.type.mql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ExpressionResult {
|
||||
private ExpressionResultType type = ExpressionResultType.UNKNOWN;
|
||||
private List<MQEValues> results = new ArrayList<>();
|
||||
private String error;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.type.mql;
|
||||
|
||||
public enum ExpressionResultType {
|
||||
|
||||
/**
|
||||
* Can't resolve the type of the given expression.
|
||||
*/
|
||||
UNKNOWN,
|
||||
/**
|
||||
* A single value
|
||||
*/
|
||||
SINGLE_VALUE,
|
||||
/**
|
||||
* A collection of time-series values.
|
||||
* The value could have labels or not.
|
||||
*/
|
||||
TIME_SERIES_VALUES,
|
||||
/**
|
||||
* A collection of aggregated values through metric sort function.
|
||||
*/
|
||||
SORTED_LIST,
|
||||
/**
|
||||
* A collection of sampled records.
|
||||
* When the original metric type is sampled records.
|
||||
*/
|
||||
RECORD_LIST
|
||||
}
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.type.mql;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MQEValue {
|
||||
private String id;
|
||||
private String value;
|
||||
private String traceID;
|
||||
|
||||
private double doubleValue;
|
||||
private boolean isEmptyValue;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.type.mql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class MQEValues {
|
||||
@Getter
|
||||
@Setter
|
||||
private Metadata metric;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<MQEValue> values = new ArrayList<>();
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.query.graphql.type.mql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.skywalking.oap.server.core.query.type.KeyValue;
|
||||
|
||||
public class Metadata {
|
||||
@Getter
|
||||
@Setter
|
||||
private String name;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<KeyValue> labels = new ArrayList<>();
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit a711fd9d0f94a67a933eacf30a12976c6219c416
|
||||
Subproject commit 957b0120af74dde1b2fca2449914739bd97c435f
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 986fe8b90e238e889ab93ae92dc5b7482bf122f5
|
||||
Subproject commit dc22f8da6e5fe60fa0c4e90dc4e3f37361384b03
|
||||
Loading…
Reference in New Issue