You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/03/29 03:15:32 UTC

[skywalking-cli] branch feature/trace updated (dfa7538 -> 7b7c73c)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a change to branch feature/trace
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git.


 discard dfa7538  Add command `trace`
     new 7b7c73c  Add command `trace`

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (dfa7538)
            \
             N -- N -- N   refs/heads/feature/trace (7b7c73c)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:


[skywalking-cli] 01/01: Add command `trace`

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch feature/trace
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git

commit 7b7c73cdc6a78f17d493c5990ab70e22820e1833
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Sun Mar 29 11:15:16 2020 +0800

    Add command `trace`
    
    ### Motivation
    
    Add command `trace` to query trace by trace ID and visualize the traces
    
    ### Modification
    
    - Add command `trace` to query and visualize traces.
    
    - Move `main.go` to `cmd`, as per the recommendation of GO CLI package structure.
    
    - Move GraphQL query to individual `*.graphql` file, for more friendly development, with IDE plugin.
    
    -
---
 .gitignore                                         |   3 +
 .graphqlconfig                                     |  11 +
 Makefile                                           |  21 +-
 README.md                                          |  40 ++++
 assets/assets.go                                   |  33 +++
 .../graphqls/aggregation/AllEndpointTopN.graphql   |  27 +++
 .../aggregation/AllServiceInstanceTopN.graphql     |  27 +++
 assets/graphqls/aggregation/EndpointTopN.graphql   |  28 +++
 .../aggregation/ServiceInstanceTopN.graphql        |  28 +++
 assets/graphqls/aggregation/ServiceTopN.graphql    |  27 +++
 assets/graphqls/metadata/AllServices.graphql       |  22 ++
 assets/graphqls/metadata/Instances.graphql         |  30 +++
 assets/graphqls/metadata/SearchEndpoints.graphql   |  22 ++
 assets/graphqls/metadata/SearchService.graphql     |  22 ++
 assets/graphqls/metadata/ServerTimeInfo.graphql    |  22 ++
 assets/graphqls/metrics/IntValues.graphql          |  22 ++
 assets/graphqls/metrics/LinearIntValues.graphql    |  22 ++
 .../metrics/MultipleLinearIntValues.graphql        |  22 ++
 assets/graphqls/metrics/Thermodynamic.graphql      |  22 ++
 assets/graphqls/trace/Trace.graphql                |  50 +++++
 {swctl => cmd}/main.go                             |   3 +
 commands/trace/list.go                             |  29 +++
 display/graph/graph.go => commands/trace/trace.go  |  48 ++---
 display/graph/graph.go                             |  37 +++-
 display/graph/tree/adapter.go                      | 128 ++++++++++++
 display/graph/tree/tree.go                         | 224 +++++++++++++++++++++
 dist/LICENSE                                       |   1 +
 go.mod                                             |   1 +
 go.sum                                             | 142 +++++++++++++
 graphql/aggregation/aggregation.go                 |  69 +------
 graphql/metadata/metadata.go                       |  83 ++------
 graphql/metrics/metrics.go                         |  54 ++---
 display/graph/graph.go => graphql/trace/trace.go   |  35 +---
 util/lang.go                                       |  26 +++
 34 files changed, 1153 insertions(+), 228 deletions(-)

diff --git a/.gitignore b/.gitignore
index e6e7bf9..0e27e55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@
 bin
 coverage.txt
 graphql/schema/schema.go
+.DS_Store
+*-packr.go
+packrd
diff --git a/.graphqlconfig b/.graphqlconfig
new file mode 100644
index 0000000..022db9c
--- /dev/null
+++ b/.graphqlconfig
@@ -0,0 +1,11 @@
+{
+  "name": "SkyWalking GraphQL Schema",
+  "extensions": {
+    "endpoints": {
+      "SkyWalking GraphQL Endpoint": {
+        "url": "http://122.112.182.72:8080/graphql",
+        "introspect": false
+      }
+    }
+  }
+}
diff --git a/Makefile b/Makefile
index 885df84..a1d8e19 100644
--- a/Makefile
+++ b/Makefile
@@ -28,10 +28,10 @@ GO = go
 GO_PATH = $$($(GO) env GOPATH)
 GO_BUILD = $(GO) build
 GO_GET = $(GO) get
-GO_CLEAN = $(GO) clean
 GO_TEST = $(GO) test
 GO_LINT = $(GO_PATH)/bin/golangci-lint
 GO_LICENSER = $(GO_PATH)/bin/go-licenser
+GO_PACKR = $(GO_PATH)/bin/packr2
 GO_BUILD_FLAGS = -v
 GO_BUILD_LDFLAGS = -X main.version=$(VERSION)
 
@@ -41,20 +41,21 @@ ARCH = amd64
 
 SHELL = /bin/bash
 
-all: clean deps codegen lint test license build
+all: clean license deps codegen lint test build
 
 deps:
 	$(GO_GET) -v -t -d ./...
 
-codegen:
+codegen: clean
 	echo 'scalar Long' > query-protocol/schema.graphqls
 	$(GO) run github.com/99designs/gqlgen generate
 	-rm -rf generated.go
+	cd assets && GO111MODULE=on $(GO_PACKR) -v && cd ..
 
 .PHONY: $(PLATFORMS)
 $(PLATFORMS):
 	mkdir -p $(OUT_DIR)
-	GOOS=$(os) GOARCH=$(ARCH) $(GO_BUILD) $(GO_BUILD_FLAGS) -ldflags "$(GO_BUILD_LDFLAGS)" -o $(OUT_DIR)/$(BINARY)-$(VERSION)-$(os)-$(ARCH) swctl/main.go
+	GOOS=$(os) GOARCH=$(ARCH) $(GO_BUILD) $(GO_BUILD_FLAGS) -ldflags "$(GO_BUILD_LDFLAGS)" -o $(OUT_DIR)/$(BINARY)-$(VERSION)-$(os)-$(ARCH) cmd/main.go
 
 .PHONY: lint
 lint: codegen
@@ -71,7 +72,7 @@ build: deps windows linux darwin
 .PHONY: license
 license: clean
 	$(GO_LICENSER) -version || GO111MODULE=off $(GO_GET) -u github.com/elastic/go-licenser
-	$(GO_LICENSER) -exclude graphql/schema/ -d -licensor='Apache Software Foundation (ASF)' .
+	$(GO_LICENSER) -d -licensor='Apache Software Foundation (ASF)' .
 
 .PHONY: verify
 verify: clean lint test license
@@ -86,15 +87,16 @@ coverage: test
 	bash <(curl -s https://codecov.io/bash) -t a5af28a3-92a2-4b35-9a77-54ad99b1ae00
 
 .PHONY: clean
-clean: codegen
-	$(GO_CLEAN) ./...
+clean:
 	-rm -rf bin
 	-rm -rf coverage.txt
+	-rm -rf query-protocol/schema.graphqls
+	-rm -rf graphql/schema/schema.go
 	-rm -rf *.tgz
 	-rm -rf *.tgz
 	-rm -rf *.asc
 	-rm -rf *.sha512
-	-rm -rf query-protocol/schema.graphqls
+	cd assets && $(GO_PACKR) clean
 
 release-src: clean
 	-tar -zcvf $(RELEASE_SRC).tgz \
@@ -105,6 +107,9 @@ release-src: clean
 	--exclude .github \
 	--exclude $(RELEASE_SRC).tgz \
 	--exclude graphql/schema/schema.go \
+	--exclude query-protocol/schema.graphqls \
+	--exclude assets/packrd \
+	--exclude assets/*-packr.go \
 	.
 
 release-bin: build
diff --git a/README.md b/README.md
index f5e4268..52951a9 100644
--- a/README.md
+++ b/README.md
@@ -231,6 +231,36 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 </details>
 
+<details>
+
+<summary>instance search [--start=start-time] [--end=end-time] [--regex=instance-name-regex] [--service-id=service-id] [--service-name=service-name]</summary>
+
+`instance search` filter the instance in the time range of `[start, end]` and given --regex --service-id or --service-name.
+
+| option | description | default |
+| :--- | :--- | :--- |
+| `--regex` | Query regex of instance name|  |
+| `--service-id` | Query by service id (priority over `--service-name`)|  |
+| `--service-name` | Query by service name if `service-id` is absent |  |
+| `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
+| `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+
+</details>
+
+### `trace`
+
+<details>
+
+<summary>trace [trace id]</summary>
+
+`trace` displays the spans of a given trace.
+
+| argument | description | default |
+| :--- | :--- | :--- |
+| `trace id` | the trace id whose spans are to displayed |  |
+
+</details>
+
 # Use Cases
 
 <details>
@@ -477,6 +507,16 @@ $ ./bin/swctl --display=graph metrics thermodynamic --name all_heatmap
 
 <details>
 
+<summary>Display the spans of a trace</summary>
+
+```shell
+$ ./bin/swctl --display graph trace 1585375544413.464998031.46647
+```
+
+</details>
+
+<details>
+
 <summary>Automatically convert to server side timezone</summary>
 
 if your backend nodes are deployed in docker and the timezone is UTC, you may not want to convert your timezone to UTC every time you type a command, `--timezone` comes to your rescue.
diff --git a/assets/assets.go b/assets/assets.go
new file mode 100644
index 0000000..d5964f9
--- /dev/null
+++ b/assets/assets.go
@@ -0,0 +1,33 @@
+// Licensed to 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. Apache Software Foundation (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 assets
+
+import (
+	"github.com/gobuffalo/packr/v2"
+
+	"github.com/apache/skywalking-cli/logger"
+)
+
+// Read reads all content from a file under assets, which is packed in to the binary
+func Read(filename string) string {
+	content, err := packr.New("assets", ".").FindString(filename)
+	if err != nil {
+		logger.Log.Fatalln("failed to read asset: ", filename, err)
+	}
+	return content
+}
diff --git a/assets/graphqls/aggregation/AllEndpointTopN.graphql b/assets/graphqls/aggregation/AllEndpointTopN.graphql
new file mode 100644
index 0000000..a054b10
--- /dev/null
+++ b/assets/graphqls/aggregation/AllEndpointTopN.graphql
@@ -0,0 +1,27 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+    result: getAllEndpointTopN(
+        duration: $duration,
+        name: $name,
+        topN: $topN,
+        order: $order
+    ) {
+        id name value
+    }
+}
diff --git a/assets/graphqls/aggregation/AllServiceInstanceTopN.graphql b/assets/graphqls/aggregation/AllServiceInstanceTopN.graphql
new file mode 100644
index 0000000..0268c60
--- /dev/null
+++ b/assets/graphqls/aggregation/AllServiceInstanceTopN.graphql
@@ -0,0 +1,27 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+    result: getAllServiceInstanceTopN(
+        duration: $duration,
+        name: $name,
+        topN: $topN,
+        order: $order
+    ) {
+        id name value
+    }
+}
diff --git a/assets/graphqls/aggregation/EndpointTopN.graphql b/assets/graphqls/aggregation/EndpointTopN.graphql
new file mode 100644
index 0000000..b6e05f6
--- /dev/null
+++ b/assets/graphqls/aggregation/EndpointTopN.graphql
@@ -0,0 +1,28 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+    result: getEndpointTopN(
+        serviceId: $serviceId,
+        duration: $duration,
+        name: $name,
+        topN: $topN,
+        order: $order
+    ) {
+        id name value
+    }
+}
diff --git a/assets/graphqls/aggregation/ServiceInstanceTopN.graphql b/assets/graphqls/aggregation/ServiceInstanceTopN.graphql
new file mode 100644
index 0000000..f643170
--- /dev/null
+++ b/assets/graphqls/aggregation/ServiceInstanceTopN.graphql
@@ -0,0 +1,28 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+    result: getServiceInstanceTopN(
+        serviceId: $serviceId,
+        duration: $duration,
+        name: $name,
+        topN: $topN,
+        order: $order
+    ) {
+        id name value
+    }
+}
diff --git a/assets/graphqls/aggregation/ServiceTopN.graphql b/assets/graphqls/aggregation/ServiceTopN.graphql
new file mode 100644
index 0000000..754e827
--- /dev/null
+++ b/assets/graphqls/aggregation/ServiceTopN.graphql
@@ -0,0 +1,27 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+    result: getServiceTopN(
+        duration: $duration,
+        name: $name,
+        topN: $topN,
+        order: $order
+    ) {
+        id name value
+    }
+}
diff --git a/assets/graphqls/metadata/AllServices.graphql b/assets/graphqls/metadata/AllServices.graphql
new file mode 100644
index 0000000..d3a3f86
--- /dev/null
+++ b/assets/graphqls/metadata/AllServices.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($duration: Duration!) {
+    result: getAllServices(duration: $duration) {
+        id name
+    }
+}
diff --git a/assets/graphqls/metadata/Instances.graphql b/assets/graphqls/metadata/Instances.graphql
new file mode 100644
index 0000000..4bb4cbc
--- /dev/null
+++ b/assets/graphqls/metadata/Instances.graphql
@@ -0,0 +1,30 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+
+query ($serviceId: ID!, $duration: Duration!) {
+    result: getServiceInstances(duration: $duration, serviceId: $serviceId) {
+        id
+        name
+        language
+        instanceUUID
+        attributes {
+            name
+            value
+        }
+    }
+}
diff --git a/assets/graphqls/metadata/SearchEndpoints.graphql b/assets/graphqls/metadata/SearchEndpoints.graphql
new file mode 100644
index 0000000..2c21f47
--- /dev/null
+++ b/assets/graphqls/metadata/SearchEndpoints.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
+    result: searchEndpoint(keyword: $keyword, serviceId: $serviceId, limit: $limit) {
+        id name
+    }
+}
diff --git a/assets/graphqls/metadata/SearchService.graphql b/assets/graphqls/metadata/SearchService.graphql
new file mode 100644
index 0000000..525446e
--- /dev/null
+++ b/assets/graphqls/metadata/SearchService.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query searchService($serviceCode: String!) {
+    result: searchService(serviceCode: $serviceCode) {
+        id name
+    }
+}
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/metadata/ServerTimeInfo.graphql
new file mode 100644
index 0000000..d00d8a9
--- /dev/null
+++ b/assets/graphqls/metadata/ServerTimeInfo.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query {
+    result: getTimeInfo {
+        timezone, currentTimestamp
+    }
+}
diff --git a/assets/graphqls/metrics/IntValues.graphql b/assets/graphqls/metrics/IntValues.graphql
new file mode 100644
index 0000000..1138172
--- /dev/null
+++ b/assets/graphqls/metrics/IntValues.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($metric: BatchMetricConditions!, $duration: Duration!) {
+    result: getValues(metric: $metric, duration: $duration) {
+        values { id value }
+    }
+}
diff --git a/assets/graphqls/metrics/LinearIntValues.graphql b/assets/graphqls/metrics/LinearIntValues.graphql
new file mode 100644
index 0000000..6249db2
--- /dev/null
+++ b/assets/graphqls/metrics/LinearIntValues.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($metric: MetricCondition!, $duration: Duration!) {
+    result: getLinearIntValues(metric: $metric, duration: $duration) {
+        values { value }
+    }
+}
diff --git a/assets/graphqls/metrics/MultipleLinearIntValues.graphql b/assets/graphqls/metrics/MultipleLinearIntValues.graphql
new file mode 100644
index 0000000..2450068
--- /dev/null
+++ b/assets/graphqls/metrics/MultipleLinearIntValues.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($metric: MetricCondition!, $numOfLinear: Int!, $duration: Duration!) {
+    result: getMultipleLinearIntValues(metric: $metric, numOfLinear: $numOfLinear, duration: $duration) {
+        values { value }
+    }
+}
diff --git a/assets/graphqls/metrics/Thermodynamic.graphql b/assets/graphqls/metrics/Thermodynamic.graphql
new file mode 100644
index 0000000..9ff50bd
--- /dev/null
+++ b/assets/graphqls/metrics/Thermodynamic.graphql
@@ -0,0 +1,22 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($metric: MetricCondition!, $duration: Duration!) {
+    result: getThermodynamic(metric: $metric, duration: $duration) {
+        nodes axisYStep
+    }
+}
diff --git a/assets/graphqls/trace/Trace.graphql b/assets/graphqls/trace/Trace.graphql
new file mode 100644
index 0000000..233fc9f
--- /dev/null
+++ b/assets/graphqls/trace/Trace.graphql
@@ -0,0 +1,50 @@
+# Licensed to 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. Apache Software Foundation (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.
+
+query ($traceId: ID!) {
+    result: queryTrace(traceId: $traceId) {
+        spans {
+            traceId
+            segmentId
+            spanId
+            parentSpanId
+            refs {
+                traceId
+                parentSegmentId
+                parentSpanId
+                type
+            }
+            serviceCode
+            startTime
+            endTime
+            endpointName
+            type
+            peer
+            component
+            isError
+            layer
+            tags {
+                key value
+            }
+            logs {
+                time data {
+                    key value
+                }
+            }
+        }
+    }
+}
diff --git a/swctl/main.go b/cmd/main.go
similarity index 98%
rename from swctl/main.go
rename to cmd/main.go
index 6c39a23..6577b67 100644
--- a/swctl/main.go
+++ b/cmd/main.go
@@ -21,6 +21,8 @@ import (
 	"io/ioutil"
 	"os"
 
+	"github.com/apache/skywalking-cli/commands/trace"
+
 	"github.com/apache/skywalking-cli/commands/metrics"
 
 	"github.com/apache/skywalking-cli/commands/endpoint"
@@ -84,6 +86,7 @@ func main() {
 		instance.Command,
 		service.Command,
 		metrics.Command,
+		trace.Command,
 	}
 
 	app.Before = interceptor.BeforeChain([]cli.BeforeFunc{
diff --git a/commands/trace/list.go b/commands/trace/list.go
new file mode 100644
index 0000000..f23845e
--- /dev/null
+++ b/commands/trace/list.go
@@ -0,0 +1,29 @@
+// Licensed to 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. Apache Software Foundation (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 trace
+
+import "github.com/urfave/cli"
+
+var ListCommand = cli.Command{
+	Name:      "list",
+	ShortName: "ls",
+	Usage:     "query traces",
+	Action: func(ctx *cli.Context) error {
+		return nil
+	},
+}
diff --git a/display/graph/graph.go b/commands/trace/trace.go
similarity index 50%
copy from display/graph/graph.go
copy to commands/trace/trace.go
index e8fc072..a6dd408 100644
--- a/display/graph/graph.go
+++ b/commands/trace/trace.go
@@ -15,37 +15,33 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package graph
+package trace
 
 import (
 	"fmt"
-	"reflect"
 
-	"github.com/apache/skywalking-cli/display/graph/heatmap"
-	"github.com/apache/skywalking-cli/graphql/schema"
+	"github.com/urfave/cli"
 
-	d "github.com/apache/skywalking-cli/display/displayable"
-	"github.com/apache/skywalking-cli/display/graph/linear"
+	"github.com/apache/skywalking-cli/display"
+	"github.com/apache/skywalking-cli/display/displayable"
+	"github.com/apache/skywalking-cli/graphql/trace"
 )
 
-func Display(displayable *d.Displayable) error {
-	data := displayable.Data
-
-	if reflect.TypeOf(data) == reflect.TypeOf(schema.Thermodynamic{}) {
-		return heatmap.Display(displayable)
-	}
-
-	if reflect.TypeOf(data) == reflect.TypeOf(map[string]float64{}) {
-		kvs := []map[string]float64{data.(map[string]float64)}
-
-		return linear.Display(kvs)
-	}
-
-	if reflect.TypeOf(data) == reflect.TypeOf([]map[string]float64{}) {
-		kvs := data.([]map[string]float64)
-
-		return linear.Display(kvs)
-	}
-
-	return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(data))
+var Command = cli.Command{
+	Name:      "trace",
+	ShortName: "t",
+	Usage:     "trace related sub-command",
+	ArgsUsage: "trace id",
+	Action: func(ctx *cli.Context) error {
+		if ctx.NArg() == 0 {
+			return fmt.Errorf("command trace without sub command requires 1 trace id as argument")
+		}
+
+		trace := trace.Trace(ctx, ctx.Args().First())
+
+		return display.Display(ctx, &displayable.Displayable{Data: trace})
+	},
+	Subcommands: cli.Commands{
+		ListCommand,
+	},
 }
diff --git a/display/graph/graph.go b/display/graph/graph.go
index e8fc072..f951b51 100644
--- a/display/graph/graph.go
+++ b/display/graph/graph.go
@@ -21,6 +21,8 @@ import (
 	"fmt"
 	"reflect"
 
+	"github.com/apache/skywalking-cli/display/graph/tree"
+
 	"github.com/apache/skywalking-cli/display/graph/heatmap"
 	"github.com/apache/skywalking-cli/graphql/schema"
 
@@ -28,24 +30,37 @@ import (
 	"github.com/apache/skywalking-cli/display/graph/linear"
 )
 
+type (
+	Thermodynamic      = schema.Thermodynamic
+	LinearMetrics      = map[string]float64
+	MultiLinearMetrics = []LinearMetrics
+	Trace              = schema.Trace
+)
+
+var (
+	ThermodynamicType      = reflect.TypeOf(Thermodynamic{})
+	LinearMetricsType      = reflect.TypeOf(LinearMetrics{})
+	MultiLinearMetricsType = reflect.TypeOf(MultiLinearMetrics{})
+	TraceType              = reflect.TypeOf(Trace{})
+)
+
 func Display(displayable *d.Displayable) error {
 	data := displayable.Data
 
-	if reflect.TypeOf(data) == reflect.TypeOf(schema.Thermodynamic{}) {
+	switch reflect.TypeOf(data) {
+	case ThermodynamicType:
 		return heatmap.Display(displayable)
-	}
 
-	if reflect.TypeOf(data) == reflect.TypeOf(map[string]float64{}) {
-		kvs := []map[string]float64{data.(map[string]float64)}
+	case LinearMetricsType:
+		return linear.Display([]LinearMetrics{data.(LinearMetrics)})
 
-		return linear.Display(kvs)
-	}
+	case MultiLinearMetricsType:
+		return linear.Display(data.(MultiLinearMetrics))
 
-	if reflect.TypeOf(data) == reflect.TypeOf([]map[string]float64{}) {
-		kvs := data.([]map[string]float64)
+	case TraceType:
+		return tree.Display(tree.Adapt(data.(Trace)))
 
-		return linear.Display(kvs)
+	default:
+		return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(data))
 	}
-
-	return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(data))
 }
diff --git a/display/graph/tree/adapter.go b/display/graph/tree/adapter.go
new file mode 100644
index 0000000..9c33c32
--- /dev/null
+++ b/display/graph/tree/adapter.go
@@ -0,0 +1,128 @@
+// Licensed to 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. Apache Software Foundation (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 tree
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/apache/skywalking-cli/graphql/schema"
+	"github.com/apache/skywalking-cli/util"
+)
+
+func Adapt(trace schema.Trace) []*Node {
+	all := make(map[string]*Node)
+
+	for _, span := range trace.Spans {
+		all[id(span)] = node(span)
+	}
+
+	seen := make(map[string]bool)
+
+	var roots []*Node
+
+	for _, span := range trace.Spans {
+		if isRoot(span) {
+			roots = append(roots, all[id(span)])
+			seen[id(span)] = true
+		}
+	}
+
+	for len(seen) < len(trace.Spans) {
+		for _, span := range trace.Spans {
+			if seen[id(span)] {
+				continue
+			}
+
+			if all[pid(span)] != nil {
+				all[pid(span)].Children = append(all[pid(span)].Children, all[id(span)])
+				seen[id(span)] = true
+			}
+
+			for _, ref := range span.Refs {
+				if all[id0(ref)] != nil {
+					all[id0(ref)].Children = append(all[id0(ref)].Children, all[id(span)])
+					seen[id(span)] = true
+				}
+			}
+		}
+	}
+
+	return roots
+}
+
+func isRoot(span *schema.Span) bool {
+	return span.SpanID == 0 && span.ParentSpanID == -1 && len(span.Refs) == 0
+}
+
+func id(span *schema.Span) string {
+	return fmt.Sprintf("%s:%s:%d", span.TraceID, span.SegmentID, span.SpanID)
+}
+
+func pid(span *schema.Span) string {
+	return fmt.Sprintf("%s:%s:%d", span.TraceID, span.SegmentID, span.ParentSpanID)
+}
+
+func id0(ref *schema.Ref) string {
+	return fmt.Sprintf("%s:%s:%d", ref.TraceID, ref.ParentSegmentID, ref.ParentSpanID)
+}
+
+func node(span *schema.Span) *Node {
+	return &Node{
+		Children: []*Node{},
+		Value:    util.Stringify{Str: value(span)},
+		Detail:   detail(span),
+	}
+}
+
+func value(span *schema.Span) string {
+	if *span.IsError {
+		return fmt.Sprintf(
+			"[|%s| %s [%s/%s]](mod:bold,fg:white,bg:red)",
+			span.Type, *span.EndpointName, *span.Component, *span.Layer,
+		)
+	}
+
+	return fmt.Sprintf("[|%s|](fg:bold,fg:green) %s [[%s/%s]](mod:bold,fg:green)",
+		span.Type, *span.EndpointName, *span.Component, *span.Layer,
+	)
+}
+
+func detail(span *schema.Span) string {
+	var lines []string
+
+	lines = append(lines,
+		fmt.Sprintf("[Endpoint    :](mod:bold,fg:red) %s", *span.EndpointName),
+		fmt.Sprintf("[Span Type   :](mod:bold,fg:red) %s", span.Type),
+		fmt.Sprintf("[Component   :](mod:bold,fg:red) %s", *span.Component),
+		fmt.Sprintf("[Peer        :](mod:bold,fg:red) %s", *span.Peer),
+		fmt.Sprintf("[Error       :](mod:bold,fg:red) %t", *span.IsError),
+	)
+
+	for _, tag := range span.Tags {
+		lines = append(lines, fmt.Sprintf("[%-12s:](mod:bold,fg:red) %s", tag.Key, *tag.Value))
+	}
+
+	for _, log := range span.Logs {
+		for _, datum := range log.Data {
+			lines = append(lines, fmt.Sprintf("%-12s: %s", datum.Key, *datum.Value))
+		}
+	}
+
+	return strings.Join(lines, "\n")
+}
diff --git a/display/graph/tree/tree.go b/display/graph/tree/tree.go
new file mode 100644
index 0000000..5df89ef
--- /dev/null
+++ b/display/graph/tree/tree.go
@@ -0,0 +1,224 @@
+// Licensed to 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. Apache Software Foundation (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 tree
+
+import (
+	"fmt"
+
+	ui "github.com/gizak/termui/v3"
+	"github.com/gizak/termui/v3/widgets"
+
+	"github.com/apache/skywalking-cli/logger"
+)
+
+type Node struct {
+	Children []*Node
+	Detail   string
+	Value    fmt.Stringer
+}
+
+var extra = make(map[*widgets.TreeNode]*Node)
+
+func Display(roots []*Node) error {
+	if err := ui.Init(); err != nil {
+		logger.Log.Fatalf("failed to initialize termui: %v", err)
+	}
+	defer ui.Close()
+
+	nodes := make([]*widgets.TreeNode, len(roots))
+	for i := range nodes {
+		nodes[i] = &widgets.TreeNode{}
+	}
+
+	for i, root := range roots {
+		adapt(root, nodes[i])
+	}
+
+	tree := widgets.NewTree()
+	tree.TextStyle = ui.Style{
+		Fg:       ui.ColorWhite,
+		Bg:       ui.ColorClear,
+		Modifier: 0,
+	}
+	tree.SelectedRowStyle = ui.Style{
+		Fg:       ui.ColorBlack,
+		Bg:       ui.ColorWhite,
+		Modifier: ui.ModifierBold,
+	}
+	tree.WrapText = false
+	tree.SetNodes(nodes)
+	tree.Title = " Press ? to show help "
+	tree.TitleStyle.Modifier = ui.ModifierBold
+	tree.TitleStyle.Fg = ui.ColorRed
+
+	x, y := ui.TerminalDimensions()
+
+	tree.SetRect(0, 0, x, y)
+
+	detail := widgets.NewParagraph()
+	detail.Title = " Detail "
+	detail.WrapText = false
+	detail.SetRect(x, 0, x, y)
+
+	help := widgets.NewParagraph()
+	help.WrapText = false
+	help.SetRect(x, 0, x, y)
+	help.Title = " Keymap "
+	help.Text = `
+		[?          ](fg:red,mod:bold) Toggle this help
+		[k          ](fg:red,mod:bold) Scroll Up
+		[<Up>       ](fg:red,mod:bold) Scroll Up
+		[j          ](fg:red,mod:bold) Scroll Down
+		[<Down>     ](fg:red,mod:bold) Scroll Down
+		[<Ctr-b>    ](fg:red,mod:bold) Scroll Page Up
+		[<Ctr-u>    ](fg:red,mod:bold) Scroll Half Page Up
+		[<Ctr-f>    ](fg:red,mod:bold) Scroll Page Down
+		[<Ctr-d>    ](fg:red,mod:bold) Scroll Half Page Down
+		[<Home>     ](fg:red,mod:bold) Scroll to Top
+		[gg         ](fg:red,mod:bold) Scroll to Top
+		[<Enter>    ](fg:red,mod:bold) Show Trace Detail
+		[<Space>    ](fg:red,mod:bold) Show Trace Detail
+		[o          ](fg:red,mod:bold) Toggle Expand
+		[G          ](fg:red,mod:bold) Scroll to Bottom
+		[<End>      ](fg:red,mod:bold) Scroll to Bottom
+		[E          ](fg:red,mod:bold) Expand All
+		[C          ](fg:red,mod:bold) Collapse All
+		[q          ](fg:red,mod:bold) Quit
+		[<Ctr-c>    ](fg:red,mod:bold) Quit
+	`
+
+	ui.Render(tree, detail, help)
+
+	listenKeyboard(tree, detail, help)
+
+	return nil
+}
+
+func adapt(n1 *Node, n2 *widgets.TreeNode) {
+	if n1 == nil || n2 == nil {
+		return
+	}
+
+	n2.Expanded = true
+	n2.Value = n1.Value
+	n2.Nodes = []*widgets.TreeNode{}
+	extra[n2] = n1
+
+	for _, child := range n1.Children {
+		node := &widgets.TreeNode{}
+		n2.Nodes = append(n2.Nodes, node)
+		adapt(child, node)
+	}
+}
+
+func actions(key string, tree *widgets.Tree) func() {
+	// mostly vim style
+	actions := map[string]func(){
+		"k":      tree.ScrollUp,
+		"<Up>":   tree.ScrollUp,
+		"j":      tree.ScrollDown,
+		"<Down>": tree.ScrollDown,
+		"<C-b>":  tree.ScrollPageUp,
+		"<C-u>":  tree.ScrollHalfPageUp,
+		"<C-f>":  tree.ScrollPageDown,
+		"<C-d>":  tree.ScrollHalfPageDown,
+		"<Home>": tree.ScrollTop,
+		"o":      tree.ToggleExpand,
+		"G":      tree.ScrollBottom,
+		"<End>":  tree.ScrollBottom,
+		"E":      tree.ExpandAll,
+		"C":      tree.CollapseAll,
+		"<Resize>": func() {
+			x, y := ui.TerminalDimensions()
+			tree.SetRect(0, 0, x, y)
+		},
+	}
+
+	return actions[key]
+}
+
+func listenKeyboard(tree *widgets.Tree, detail, help *widgets.Paragraph) {
+	var previousKey string
+	var previousSelected *Node
+
+	visibilities := make(map[interface{}]bool)
+
+	uiEvents := ui.PollEvents()
+
+	for {
+		e := <-uiEvents
+
+		switch e.ID {
+		case "q", "<C-c>":
+			return
+		case "g":
+			if previousKey == "g" {
+				tree.ScrollTop()
+			}
+		case "<Enter>", "<Space>":
+			selected := extra[tree.SelectedNode()]
+			detail.Text = selected.Detail
+
+			selectionChanged := previousSelected != selected
+			visibilities[detail] = selectionChanged || !visibilities[detail]
+
+			previousSelected = selected
+		case "?":
+			visibilities[help] = !visibilities[help]
+		default:
+			if action := actions(e.ID, tree); action != nil {
+				action()
+			}
+		}
+
+		if previousKey == "g" {
+			previousKey = ""
+		} else {
+			previousKey = e.ID
+		}
+
+		redraw(visibilities, tree, detail, help)
+	}
+}
+
+func redraw(shouldShow map[interface{}]bool, tree *widgets.Tree, detail, help *widgets.Paragraph) {
+	x, y := ui.TerminalDimensions()
+
+	shouldDisplaySideBar := shouldShow[detail] || shouldShow[help]
+	if shouldDisplaySideBar {
+		tree.SetRect(0, 0, x*2/3, y)
+	} else {
+		tree.SetRect(0, 0, x, y)
+	}
+
+	if shouldShow[detail] && shouldShow[help] {
+		detail.SetRect(x*2/3, 0, x, y/2)
+		help.SetRect(x*2/3, y/2+1, x, y)
+	} else if shouldShow[help] {
+		detail.SetRect(0, 0, 0, 0)
+		help.SetRect(x*2/3, 0, x, y)
+	} else if shouldShow[detail] {
+		detail.SetRect(x*2/3, 0, x, y)
+		help.SetRect(0, 0, 0, 0)
+	} else {
+		help.SetRect(0, 0, 0, 0)
+		detail.SetRect(0, 0, 0, 0)
+	}
+
+	ui.Render(tree, detail, help)
+}
diff --git a/dist/LICENSE b/dist/LICENSE
index 4dfc4c4..2cbc4ea 100644
--- a/dist/LICENSE
+++ b/dist/LICENSE
@@ -224,6 +224,7 @@ The text of each license is also included at licenses/LICENSE-[project].txt.
 	nsf (termbox-go) 0.0.0-20190817171036-93860e161317: https://github.com/nsf/termbox-go MIT
 	gizak (termui) v3: https://github.com/gizak/termui MIT
 	mattn (go-runewidth) v3: https://github.com/mattn/go-runewidth MIT
+	gobuffalo (packr) v2: https://github.com/gobuffalo/packr MIT
 
 ========================================================================
 BSD licenses
diff --git a/go.mod b/go.mod
index 7586d33..b979715 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.13
 require (
 	github.com/99designs/gqlgen v0.11.1 // indirect
 	github.com/gizak/termui/v3 v3.1.0
+	github.com/gobuffalo/packr/v2 v2.8.0
 	github.com/machinebox/graphql v0.2.2
 	github.com/mattn/go-runewidth v0.0.4
 	github.com/mum4k/termdash v0.10.0
diff --git a/go.sum b/go.sum
index 932479d..019c53d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,30 +1,85 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 github.com/99designs/gqlgen v0.11.1 h1:QoSL8/AAJ2T3UOeQbdnBR32JcG4pO08+P/g5jdbFkUg=
 github.com/99designs/gqlgen v0.11.1/go.mod h1:vjFOyBZ7NwDl+GdSD4PFn7BQn5Fy7ohJwXn7Vk8zz+c=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
 github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gizak/termui v3.1.0+incompatible h1:N3CFm+j087lanTxPpHOmQs0uS3s5I9TxoAFy6DqPqv8=
 github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
 github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
 github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc=
+github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=
+github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
+github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
+github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY=
+github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g=
 github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
 github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew=
+github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -33,6 +88,13 @@ github.com/lytics/logrus v0.0.0-20170528191427-4389a17ed024 h1:QaKVrqyQRNPbdBNCp
 github.com/lytics/logrus v0.0.0-20170528191427-4389a17ed024/go.mod h1:SkQviJ2s7rFyzyuxdVp6osZceHOabU91ZhKsEXF0RWg=
 github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
 github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
+github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
+github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
+github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
+github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -40,23 +102,42 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
 github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
 github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mum4k/termdash v0.10.0 h1:uqM6ePiMf+smecb1tJJeON36o1hREeCfOmLFG0iz4a0=
 github.com/mum4k/termdash v0.10.0/go.mod h1:l3tO+lJi9LZqXRq7cu7h5/8rDIK3AzelSuq2v/KncxI=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
 github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 h1:hhGN4SFXgXo61Q4Sjj/X9sBjyeSa2kdpaOzCO+8EVQw=
 github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/olekukonko/tablewriter v0.0.2 h1:sq53g+DWf0J6/ceFUHpQ0nAEb6WgM++fq16MZ91cS6o=
 github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
 github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
 github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -65,26 +146,66 @@ github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJ
 github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
 github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
 github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
+golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -93,16 +214,37 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
 golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE=
+golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
diff --git a/graphql/aggregation/aggregation.go b/graphql/aggregation/aggregation.go
index fc11392..6effe5e 100644
--- a/graphql/aggregation/aggregation.go
+++ b/graphql/aggregation/aggregation.go
@@ -21,6 +21,8 @@ import (
 	"github.com/machinebox/graphql"
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/assets"
+
 	"github.com/apache/skywalking-cli/graphql/client"
 	"github.com/apache/skywalking-cli/graphql/schema"
 )
@@ -28,18 +30,7 @@ import (
 func ServiceTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
 	var response map[string][]schema.TopNEntity
 
-	request := graphql.NewRequest(`
-		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
-			result: getServiceTopN(
-				duration: $duration,
-				name: $name,
-				topN: $topN,
-				order: $order
-			) {
-				id name value
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/aggregation/ServiceTopN.graphql"))
 	request.Var("name", name)
 	request.Var("topN", topN)
 	request.Var("duration", duration)
@@ -53,18 +44,7 @@ func ServiceTopN(ctx *cli.Context, name string, topN int, duration schema.Durati
 func AllServiceInstanceTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
 	var response map[string][]schema.TopNEntity
 
-	request := graphql.NewRequest(`
-		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
-			result: getAllServiceInstanceTopN(
-				duration: $duration,
-				name: $name,
-				topN: $topN,
-				order: $order
-			) {
-				id name value
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/aggregation/AllServiceInstanceTopN.graphql"))
 	request.Var("name", name)
 	request.Var("topN", topN)
 	request.Var("duration", duration)
@@ -78,19 +58,7 @@ func AllServiceInstanceTopN(ctx *cli.Context, name string, topN int, duration sc
 func ServiceInstanceTopN(ctx *cli.Context, serviceID, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
 	var response map[string][]schema.TopNEntity
 
-	request := graphql.NewRequest(`
-		query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
-			result: getServiceInstanceTopN(
-				serviceId: $serviceId,
-				duration: $duration,
-				name: $name,
-				topN: $topN,
-				order: $order
-			) {
-				id name value
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/aggregation/ServiceInstanceTopN.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("name", name)
 	request.Var("topN", topN)
@@ -105,18 +73,7 @@ func ServiceInstanceTopN(ctx *cli.Context, serviceID, name string, topN int, dur
 func AllEndpointTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
 	var response map[string][]schema.TopNEntity
 
-	request := graphql.NewRequest(`
-		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
-			result: getAllEndpointTopN(
-				duration: $duration,
-				name: $name,
-				topN: $topN,
-				order: $order
-			) {
-				id name value
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/aggregation/AllEndpointTopN.graphql"))
 	request.Var("name", name)
 	request.Var("topN", topN)
 	request.Var("duration", duration)
@@ -130,19 +87,7 @@ func AllEndpointTopN(ctx *cli.Context, name string, topN int, duration schema.Du
 func EndpointTopN(ctx *cli.Context, serviceID, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
 	var response map[string][]schema.TopNEntity
 
-	request := graphql.NewRequest(`
-		query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
-			result: getEndpointTopN(
-				serviceId: $serviceId,
-				duration: $duration,
-				name: $name,
-				topN: $topN,
-				order: $order
-			) {
-				id name value
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/aggregation/EndpointTopN.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("name", name)
 	request.Var("topN", topN)
diff --git a/graphql/metadata/metadata.go b/graphql/metadata/metadata.go
index 93b6875..49e88be 100644
--- a/graphql/metadata/metadata.go
+++ b/graphql/metadata/metadata.go
@@ -20,6 +20,8 @@ package metadata
 import (
 	"fmt"
 
+	"github.com/apache/skywalking-cli/assets"
+
 	"github.com/machinebox/graphql"
 	"github.com/urfave/cli"
 
@@ -29,105 +31,64 @@ import (
 
 func AllServices(cliCtx *cli.Context, duration schema.Duration) []schema.Service {
 	var response map[string][]schema.Service
-	request := graphql.NewRequest(`
-		query ($duration: Duration!) {
-			services: getAllServices(duration: $duration) {
-				id name
-			}
-		}
-	`)
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/AllServices.graphql"))
 	request.Var("duration", duration)
 
 	client.ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["services"]
+	return response["result"]
 }
 
 func SearchService(cliCtx *cli.Context, serviceCode string) (service schema.Service, err error) {
 	var response map[string]schema.Service
-	request := graphql.NewRequest(`
-		query searchService($serviceCode: String!) {
-			service: searchService(serviceCode: $serviceCode) {
-				id name
-			}
-		}
-	`)
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchService.graphql"))
 	request.Var("serviceCode", serviceCode)
 
 	client.ExecuteQueryOrFail(cliCtx, request, &response)
-	service = response["service"]
+
+	service = response["result"]
+
 	if service.ID == "" {
 		return service, fmt.Errorf("no such service [%s]", serviceCode)
 	}
+
 	return service, nil
 }
 
 func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int) []schema.Endpoint {
 	var response map[string][]schema.Endpoint
-	request := graphql.NewRequest(`
-		query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
-			endpoints: searchEndpoint(keyword: $keyword, serviceId: $serviceId, limit: $limit) {
-				id name
-			}
-		}
-	`)
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchEndpoints.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("keyword", keyword)
 	request.Var("limit", limit)
 
 	client.ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["endpoints"]
-}
-
-func EndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
-	var response map[string]schema.Endpoint
-	request := graphql.NewRequest(`
-		query ($endpointId: ID!) {
-			endpoint: getEndpointInfo(endpointId: $endpointId) {
-				id name
-			}
-		}
-	`)
-	request.Var("endpointId", endpointID)
 
-	client.ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["endpoint"]
+	return response["result"]
 }
 
 func Instances(cliCtx *cli.Context, serviceID string, duration schema.Duration) []schema.ServiceInstance {
 	var response map[string][]schema.ServiceInstance
-	request := graphql.NewRequest(`
-		query ($serviceId: ID!, $duration: Duration!) {
-			instances: getServiceInstances(duration: $duration, serviceId: $serviceId) {
-				id
-				name
-				language
-				instanceUUID
-				attributes {
-					name
-					value
-				}
-			}
-		}
-	`)
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/Instances.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("duration", duration)
 
 	client.ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["instances"]
+
+	return response["result"]
 }
 
 func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
 	var response map[string]schema.TimeInfo
-	request := graphql.NewRequest(`
-		query {
-			timeInfo: getTimeInfo {
-				timezone, currentTimestamp
-			}
-		}
-	`)
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/ServerTimeInfo.graphql"))
 
 	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
 		return schema.TimeInfo{}, err
 	}
-	return response["timeInfo"], nil
+
+	return response["result"], nil
 }
diff --git a/graphql/metrics/metrics.go b/graphql/metrics/metrics.go
index ef71e32..6b12837 100644
--- a/graphql/metrics/metrics.go
+++ b/graphql/metrics/metrics.go
@@ -21,6 +21,8 @@ import (
 	"github.com/machinebox/graphql"
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/assets"
+
 	"github.com/apache/skywalking-cli/graphql/client"
 
 	"github.com/apache/skywalking-cli/graphql/schema"
@@ -29,72 +31,52 @@ import (
 func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, duration schema.Duration) schema.IntValues {
 	var response map[string]schema.IntValues
 
-	request := graphql.NewRequest(`
-		query ($metric: BatchMetricConditions!, $duration: Duration!) {
-			metrics: getValues(metric: $metric, duration: $duration) {
-				values { id value }
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/IntValues.graphql"))
+
 	request.Var("metric", condition)
 	request.Var("duration", duration)
 
 	client.ExecuteQueryOrFail(ctx, request, &response)
 
-	return response["metrics"]
+	return response["result"]
 }
 
 func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, duration schema.Duration) schema.IntValues {
 	var response map[string]schema.IntValues
 
-	request := graphql.NewRequest(`
-		query ($metric: MetricCondition!, $duration: Duration!) {
-			metrics: getLinearIntValues(metric: $metric, duration: $duration) {
-				values { value }
-			}
-		}
-	`)
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/LinearIntValues.graphql"))
+
 	request.Var("metric", condition)
 	request.Var("duration", duration)
 
 	client.ExecuteQueryOrFail(ctx, request, &response)
 
-	return response["metrics"]
+	return response["result"]
 }
 
 func MultipleLinearIntValues(ctx *cli.Context, condition schema.MetricCondition, numOfLinear int, duration schema.Duration) []schema.IntValues {
-	request := graphql.NewRequest(`
-		query ($metric: MetricCondition!, $numOfLinear: Int!, $duration: Duration!) {
-			metrics: getMultipleLinearIntValues(metric: $metric, numOfLinear: $numOfLinear, duration: $duration) {
-				values { value }
-			}
-		}
-	`)
+	var response map[string][]schema.IntValues
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/MultipleLinearIntValues.graphql"))
+
 	request.Var("metric", condition)
 	request.Var("numOfLinear", numOfLinear)
 	request.Var("duration", duration)
 
-	var response map[string][]schema.IntValues
-
 	client.ExecuteQueryOrFail(ctx, request, &response)
 
-	return response["metrics"]
+	return response["result"]
 }
 
 func Thermodynamic(ctx *cli.Context, condition schema.MetricCondition, duration schema.Duration) schema.Thermodynamic {
-	request := graphql.NewRequest(`
-		query ($metric: MetricCondition!, $duration: Duration!) {
-			metrics: getThermodynamic(metric: $metric, duration: $duration) {
-				nodes axisYStep
-			}
-		}
-	`)
+	var response map[string]schema.Thermodynamic
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/Thermodynamic.graphql"))
+
 	request.Var("metric", condition)
 	request.Var("duration", duration)
 
-	var response map[string]schema.Thermodynamic
-
 	client.ExecuteQueryOrFail(ctx, request, &response)
 
-	return response["metrics"]
+	return response["result"]
 }
diff --git a/display/graph/graph.go b/graphql/trace/trace.go
similarity index 53%
copy from display/graph/graph.go
copy to graphql/trace/trace.go
index e8fc072..b09a56f 100644
--- a/display/graph/graph.go
+++ b/graphql/trace/trace.go
@@ -15,37 +15,24 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package graph
+package trace
 
 import (
-	"fmt"
-	"reflect"
+	"github.com/machinebox/graphql"
+	"github.com/urfave/cli"
 
-	"github.com/apache/skywalking-cli/display/graph/heatmap"
+	"github.com/apache/skywalking-cli/assets"
+	"github.com/apache/skywalking-cli/graphql/client"
 	"github.com/apache/skywalking-cli/graphql/schema"
-
-	d "github.com/apache/skywalking-cli/display/displayable"
-	"github.com/apache/skywalking-cli/display/graph/linear"
 )
 
-func Display(displayable *d.Displayable) error {
-	data := displayable.Data
-
-	if reflect.TypeOf(data) == reflect.TypeOf(schema.Thermodynamic{}) {
-		return heatmap.Display(displayable)
-	}
-
-	if reflect.TypeOf(data) == reflect.TypeOf(map[string]float64{}) {
-		kvs := []map[string]float64{data.(map[string]float64)}
-
-		return linear.Display(kvs)
-	}
+func Trace(ctx *cli.Context, traceID string) schema.Trace {
+	var response map[string]schema.Trace
 
-	if reflect.TypeOf(data) == reflect.TypeOf([]map[string]float64{}) {
-		kvs := data.([]map[string]float64)
+	request := graphql.NewRequest(assets.Read("graphqls/trace/Trace.graphql"))
+	request.Var("traceId", traceID)
 
-		return linear.Display(kvs)
-	}
+	client.ExecuteQueryOrFail(ctx, request, &response)
 
-	return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(data))
+	return response["result"]
 }
diff --git a/util/lang.go b/util/lang.go
new file mode 100644
index 0000000..4ba5fec
--- /dev/null
+++ b/util/lang.go
@@ -0,0 +1,26 @@
+// Licensed to 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. Apache Software Foundation (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 util
+
+type Stringify struct {
+	Str string
+}
+
+func (s Stringify) String() string {
+	return s.Str
+}