diff --git a/apm-protocol/apm-ui-query/pom.xml b/apm-protocol/apm-ui-protocol/pom.xml similarity index 82% rename from apm-protocol/apm-ui-query/pom.xml rename to apm-protocol/apm-ui-protocol/pom.xml index f67c5c86e..0e7e75784 100644 --- a/apm-protocol/apm-ui-query/pom.xml +++ b/apm-protocol/apm-ui-protocol/pom.xml @@ -27,7 +27,13 @@ 4.0.0 - apm-ui-query + apm-ui-protocol - - \ No newline at end of file + + + com.graphql-java + graphql-java + 6.0 + + + diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/README.md b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/README.md new file mode 100644 index 000000000..60ddb04aa --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/README.md @@ -0,0 +1,41 @@ +# Abstract +**apm-ui-protocol** declares all services, using GraphQL API style, which provide by Collector UI module. + +## Services +### [Common](common.graphqls) + +Include common objects, which used in global + +### [Overview Layer Service](overview-layer.graphqls) + +Query data without specific application, server or service. It includes info for overview the whole cluster. + +### [Application Layer Service](application-layer.graphqls) + +Query application related data with specific application code. + +### [Server Layer Service](server-layer.graphqls) + +Query server related data with specific server id. + +### [Service Layer Service](service-layer.graphqls) + +Query service related data with specific service id + +### [Trace Service](trace.graphqls) + +Query trace by some conditions. + +### [Alarm Service](alarm.graphqls) + +Query alarm info. + +## Version +v1alpha1 + +### Versioning +Use URI Versioning, to follow the most straightforward approach, +though it does violate the principle that a URI should refer to a unique resource. + +e.g. +http://collector.host/graphql/v1alpha1 diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/alarm.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/alarm.graphqls new file mode 100644 index 000000000..b63946d8b --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/alarm.graphqls @@ -0,0 +1,15 @@ +type AlarmItem { + content: String! + startTime: String! + alertType: AlarmType! +} + +enum AlarmType { + APPLICATION, + SERVER, + SERVICE +} + +extend type Query { + loadAlertList(keyword: String, alertType: AlarmType, duration:Duration!):[AlarmItem] +} \ No newline at end of file diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/application-layer.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/application-layer.graphqls new file mode 100644 index 000000000..ef9fc7007 --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/application-layer.graphqls @@ -0,0 +1,37 @@ +# ApplicationNode represents this node is under monitoring by agent. +type ApplicationNode implements Node { + id: ID! + name: String! + type: String + # Success rate of all incoming requests. + # Max value is 100. + # 2 Digits after floating point. + sla: Float! + # The number of incoming calls + calls: Long! + # ref: http://www.apdex.org/ + # Max value is 1 + # 2 Digits after floating point. + apdex: Float! + # The number of servers in the application code + numOfServer: Int! + # The number of servers alerting + numOfServerAlarm: Int! + # The number of services alerting + numOfServiceAlarm: Int! +} + +# The conjectural node generated by exit span +type ConjecturalNode implements Node { + id: ID! + name: String! + type: String +} + + +extend type Query { + getAllApplication(duration: Duration!): [ApplicationNode] + getApplicationTopology(applicationId: ID!, duration: Duration!): Topology + getSlowService(applicationId: ID!, duration: Duration!): [ServiceInfo!] + getServerThroughput(applicationId: ID!, duration: Duration!): [AppServerInfo!] +} diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/common.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/common.graphqls new file mode 100644 index 000000000..853f36258 --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/common.graphqls @@ -0,0 +1,88 @@ +schema { + query: Query +} + +#Root node +type Query { + version: String +} + +# The Duration defines the start and end time for each query operation. +# Fields: `start` and `end` +# represents the time span. And each of them matches the step. +# ref https://www.ietf.org/rfc/rfc3339.txt +# The time formats are +# `SECOND` step: yyyy-MM-dd HHmmss +# `MINUTE` step: yyyy-MM-dd HHmm +# `HOUR` step: yyyy-MM-dd HH +# `DAY` step: yyyy-MM-dd +# `MONTH` step: yyyy-MM +# Field: `step` +# represents the accurate time point. +# e.g. +# if step==HOUR , start=2017-11-08 09, end=2017-11-08 19 +# then +# metrics from the following time points expected +# 2017-11-08 9:00 -> 2017-11-08 19:00 +# there are 11 time points (hours) in the time span. +input Duration { + start: String! + end: String! + step: Step! +} + +enum Step { + MONTH + DAY + HOUR + MINUTE + SECOND +} + +###################################### +# Common Metrics and Trends +###################################### +type ResponseTimeTrend { + trendList: [Int!] +} + +type ThroughputTrend { + trendList: [Int!] +} + +type SLATrend { + trendList: [Int!] +} + +# The overview topology of the whole application cluster or services, +type Topology { + nodes: [Node]! + calls: [Call] +} + +# The base Node of all node types in topology +interface Node { + # The global id of each node, + # 1. `Application ID` represents application under monitoring + # 2. `Peer ID` string represents the conjectural dependency. + id: ID! + # Application Code or literal Peer + name: String! + # The type name + # 1. The most important component in the application, from service provider perspective. + # 2. Conjectural dependent component, e.g. MySQL, Redis, Kafka + type: String +} + +# The Call represents a directed distributed call, +# from the `source` to the `target`. +type Call { + source: ID! + target: ID! + isAlert: Boolean + # The protocol and tech stack used in this distributed call + callType: String! + callsPerSec: Int! + # Unit: millisecond + responseTimePerSec: Int! +} diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/overview-layer.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/overview-layer.graphqls new file mode 100644 index 000000000..e7b0bc2d6 --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/overview-layer.graphqls @@ -0,0 +1,37 @@ +# Query the cluster brief based on the given duration +type ClusterBrief { + numOfApplication: Int + numOfService: Int + numOfDatabase: Int + numOfCache: Int + numOfMQ: Int +} + +# Query the trend of alarm rate based on the given duration +type AlarmTrend { + numOfAlarmRate: [Int]! +} + +# Query all conjectural applications based on the given duration +# All applications here are not installed agent. +type ConjecturalAppBrief { + apps: [ConjecturalApp!] +} + +# The basic info of the conjectural application, +# includes the type and num of same type application +type ConjecturalApp { + # The display name of the application + # e.g. MySQL, RocketMQ, Kafka, Nginx + name: String! + num: Int! +} + +extend type Query { + getClusterTopology(duration: Duration!): Topology + getClusterBrief(duration: Duration!): ClusterBrief + getAlarmTrend(duration: Duration!): AlarmTrend + getConjecturalApps(duration: Duration!): ConjecturalAppBrief + getTopNSlowService(duration: Duration!, topN: Int!): [ServiceInfo!] + getTopNServerThroughput(duration: Duration!, topN: Int!): [AppServerInfo!] +} diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/server-layer.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/server-layer.graphqls new file mode 100644 index 000000000..eac33a7dc --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/server-layer.graphqls @@ -0,0 +1,42 @@ +# The server info. +# At here, `Server` represents a process in OS, +# e.g. +# 1. Spring boot application +# 2. A Tomcat server instance +type AppServerInfo { + id: ID! + name: String! + tps: Int! + os: String + host: String + pid: Int + IPv4: String + IPv6: String +} + +type CPUTrend { + cost: [Int!] +} + +# The gc trend represents the numbers of Garbage Collector execution +type GCTrend { + youngGC: [Int!] + oldGC: [Int!] +} + +# The memory used and max limit in heap and noheap space. +type MemoryTrend { + heap: [Int!] + maxHeap: [Int!] + noheap: [Int!] + maxNoheap: [Int!] +} + +extend type Query { + searchServer(keyword: String!, duration: Duration!): [AppServerInfo] + getServerResponseTimeTrend(serverId: ID!, duration: Duration!): ResponseTimeTrend + getServerTPSTrend(serverId: ID!, duration: Duration!): ThroughputTrend + getCPUTrend(serverId: ID!, duration: Duration!): CPUTrend + getGCTrend(serverId: ID!, duration: Duration!): GCTrend + getMemoryTrend(serverId: ID!, duration: Duration!): MemoryTrend +} \ No newline at end of file diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/service-layer.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/service-layer.graphqls new file mode 100644 index 000000000..3e33c8f9e --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/service-layer.graphqls @@ -0,0 +1,35 @@ +type ServiceNode implements Node { + id: ID! + name: String! + type: String + # Success rate of all incoming requests. + # Max value is 100. + # 2 Digits after floating point. + sla: Float! + # The number of incoming calls + calls: Long! + # The number of services alerting + numOfServiceAlarm: Int! +} + +type ServiceInfo { + id: ID! + name: String + # The unit is millisecond. + avgResponseTime: Int! + tps: Int! +} + +type TraceItem { + time: String! + entry: String! + duration: Int! +} + +extend type Query { + searchService(keyword: String!, duration: Duration!): [ServiceNode] + getServiceResponseTimeTrend(serviceId: ID!, duration: Duration!): ResponseTimeTrend + getServiceTPSTrend(serviceId: ID!, duration: Duration!): ThroughputTrend + getServiceSLATrend(serviceId: ID!, duration: Duration!): SLATrend + getServiceTopology(serviceId: ID!, duration: Duration!): Topology +} diff --git a/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/trace.graphqls b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/trace.graphqls new file mode 100644 index 000000000..4e2e1a0ec --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/main/resources/ui-graphql/trace.graphqls @@ -0,0 +1,89 @@ +# The list of traces +type TraceBrief { + traces: [BasicTrace!] +} + +# Trace basic info +type BasicTrace { + operationName: String! + duration: Int! + start: String! + isError: Boolean + traceId: String +} + +# Represent the conditions used for query TraceBrief +input TraceQueryCondition { + applicationCodes: [String!] + traceId: String + operationName: String + # The time range of traces started + queryDuration: Duration + # The mix time of trace + minTraceDuration: Int + # The max time of trace + maxTraceDuration: Int + topN: Boolean + needTotal: Int +} + +enum QueryOrder { + BY_START_TIME + BY_DURATION +} + +# The trace represents a distributed trace, includes all segments and spans. +type Trace { + traceId: ID! + segments: [Segment!] +} + +type Segment { + segmentId: ID! + appName: String! + isSizeLimited: Boolean! + spans: [Span!]! +} + +type Span { + refs: [Ref!] + spanId: Int! + parentSpanId: Int! + startTime: Long! + endTime: Long! + operationName: String + # There are three span types: Local, Entry and Exit + type: String! + # Peer network id, e.g. host+port, ip+port + peer: String + component: String + isError: Boolean + # There are 5 layers: Unknown, Database, RPCFramework, Http, MQ and Cache + layer: String + tags: [KeyValue!] + logs: [LogEntity!] +} + +# Ref represents the link between the segment and its parents. +type Ref { + parentSegmentId: ID! + parentSpanId: Int! + # Ref type represents why did the ref happen. + # Include: 1) CrossProcess 2) CrossThread + type: String! +} + +type KeyValue { + key: String! + value: String +} + +type LogEntity { + time: String + data: [KeyValue!] +} + +extend type Query { + queryBasicTraces(condition: TraceQueryCondition): TraceBrief + queryTrace(traceId: ID!): Trace +} \ No newline at end of file diff --git a/apm-protocol/apm-ui-protocol/src/test/java/org/apache/skywalking/apm/ui/protocol/GraphQLScriptTest.java b/apm-protocol/apm-ui-protocol/src/test/java/org/apache/skywalking/apm/ui/protocol/GraphQLScriptTest.java new file mode 100644 index 000000000..f017d6844 --- /dev/null +++ b/apm-protocol/apm-ui-protocol/src/test/java/org/apache/skywalking/apm/ui/protocol/GraphQLScriptTest.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.skywalking.apm.ui.protocol; + +import graphql.schema.idl.EchoingWiringFactory; +import graphql.schema.idl.RuntimeWiring; +import graphql.schema.idl.SchemaGenerator; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.TypeDefinitionRegistry; +import java.io.File; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class GraphQLScriptTest { + + @Test + public void assertScriptFormat() { + SchemaParser schemaParser = new SchemaParser(); + SchemaGenerator schemaGenerator = new SchemaGenerator(); + + TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry(); + typeRegistry.merge(schemaParser.parse(loadSchema("common.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("trace.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("overview-layer.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("application-layer.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("server-layer.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("service-layer.graphqls"))); + typeRegistry.merge(schemaParser.parse(loadSchema("alarm.graphqls"))); + RuntimeWiring wiring = buildRuntimeWiring(); + assertTrue(schemaGenerator.makeExecutableSchema(typeRegistry, wiring).getAllTypesAsList().size() > 0); + } + + private File loadSchema(final String s) { + return new File(GraphQLScriptTest.class.getClassLoader().getResource("ui-graphql/" + s).getFile()); + } + + private RuntimeWiring buildRuntimeWiring() { + return RuntimeWiring.newRuntimeWiring().wiringFactory(new EchoingWiringFactory()).build(); + } +} diff --git a/apm-protocol/pom.xml b/apm-protocol/pom.xml index cc8684970..fa5708532 100644 --- a/apm-protocol/pom.xml +++ b/apm-protocol/pom.xml @@ -32,6 +32,6 @@ apm-network - apm-ui-query + apm-ui-protocol \ No newline at end of file