You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by zh...@apache.org on 2021/10/22 06:19:41 UTC

[skywalking-cli] 01/01: Add some commands for the browser

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

zhangwei24 pushed a commit to branch command/browser
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git

commit 47ff4ca6d58265c70e3cadff8db243279946c2ad
Author: zhangwei <zh...@apache.org>
AuthorDate: Fri Oct 22 14:19:30 2021 +0800

    Add some commands for the browser
---
 assets/graphqls/logs/BrowserLogs.graphql           | 36 +++++++++
 .../graphqls/metadata/AllBrowserServices.graphql   | 22 ++++++
 .../graphqls/metadata/SearchBrowserService.graphql | 22 ++++++
 cmd/swctl/main.go                                  |  2 +
 .../log.go => internal/commands/browser/browser.go | 31 ++++----
 internal/commands/browser/logs/list.go             | 91 ++++++++++++++++++++++
 .../log => internal/commands/browser/logs}/log.go  | 29 +++----
 internal/commands/browser/page/list.go             | 68 ++++++++++++++++
 .../commands/browser/page/page.go                  | 28 ++-----
 internal/commands/browser/service/list.go          | 76 ++++++++++++++++++
 .../commands/browser/service/service.go            | 25 ++----
 internal/commands/browser/version/list.go          | 70 +++++++++++++++++
 .../commands/browser/version/version.go            | 25 ++----
 .../commands/interceptor/page.go                   | 33 ++++----
 internal/commands/interceptor/service.go           | 31 ++++++--
 .../commands/interceptor/version.go                | 33 ++++----
 pkg/graphql/log/log.go => internal/flags/page.go   | 40 +++++-----
 .../log/log.go => internal/flags/version.go        | 39 ++++------
 pkg/graphql/log/log.go                             | 11 +++
 pkg/graphql/metadata/metadata.go                   | 28 +++++++
 20 files changed, 563 insertions(+), 177 deletions(-)

diff --git a/assets/graphqls/logs/BrowserLogs.graphql b/assets/graphqls/logs/BrowserLogs.graphql
new file mode 100644
index 0000000..a484839
--- /dev/null
+++ b/assets/graphqls/logs/BrowserLogs.graphql
@@ -0,0 +1,36 @@
+# 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 ($condition: BrowserErrorLogQueryCondition!) {
+    result: queryBrowserErrorLogs(condition: $condition) {
+        total
+        logs {
+            service
+            serviceVersion
+            time
+            pagePath
+            category
+            grade
+            message
+            line
+            col
+            stack
+            errorUrl
+            firstReportedError
+        }
+    }
+}
diff --git a/assets/graphqls/metadata/AllBrowserServices.graphql b/assets/graphqls/metadata/AllBrowserServices.graphql
new file mode 100644
index 0000000..08c0c19
--- /dev/null
+++ b/assets/graphqls/metadata/AllBrowserServices.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: getAllBrowserServices(duration: $duration) {
+        id name
+    }
+}
diff --git a/assets/graphqls/metadata/SearchBrowserService.graphql b/assets/graphqls/metadata/SearchBrowserService.graphql
new file mode 100644
index 0000000..b7cbeb5
--- /dev/null
+++ b/assets/graphqls/metadata/SearchBrowserService.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 ($serviceCode: String!) {
+    result: searchBrowserService(serviceCode: $serviceCode) {
+        id name
+    }
+}
diff --git a/cmd/swctl/main.go b/cmd/swctl/main.go
index 141a78e..2f54415 100644
--- a/cmd/swctl/main.go
+++ b/cmd/swctl/main.go
@@ -22,6 +22,7 @@ import (
 	"os"
 	"runtime"
 
+	"github.com/apache/skywalking-cli/internal/commands/browser"
 	"github.com/apache/skywalking-cli/internal/commands/completion"
 	"github.com/apache/skywalking-cli/internal/commands/dashboard"
 	"github.com/apache/skywalking-cli/internal/commands/dependency"
@@ -82,6 +83,7 @@ services, service instances, etc.`
 	flags := flags()
 
 	app.Commands = []*cli.Command{
+		browser.Command,
 		endpoint.Command,
 		instance.Command,
 		service.Command,
diff --git a/pkg/graphql/log/log.go b/internal/commands/browser/browser.go
similarity index 63%
copy from pkg/graphql/log/log.go
copy to internal/commands/browser/browser.go
index 153bafe..6a77ad5 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/browser/browser.go
@@ -15,26 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package browser
 
 import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
-
-	"github.com/machinebox/graphql"
-
+	"github.com/apache/skywalking-cli/internal/commands/browser/logs"
+	"github.com/apache/skywalking-cli/internal/commands/browser/page"
+	"github.com/apache/skywalking-cli/internal/commands/browser/service"
+	"github.com/apache/skywalking-cli/internal/commands/browser/version"
 	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
 )
 
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+var Command = &cli.Command{
+	Name:  "browser",
+	Usage: "Browser related sub-command",
+	Subcommands: cli.Commands{
+		service.Command,
+		version.Command,
+		page.Command,
+		logs.Command,
+	},
 }
diff --git a/internal/commands/browser/logs/list.go b/internal/commands/browser/logs/list.go
new file mode 100644
index 0000000..84a878b
--- /dev/null
+++ b/internal/commands/browser/logs/list.go
@@ -0,0 +1,91 @@
+// 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 logs
+
+import (
+	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/flags"
+	"github.com/apache/skywalking-cli/internal/model"
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/log"
+	"github.com/urfave/cli/v2"
+
+	api "skywalking.apache.org/repo/goapi/query"
+)
+
+const DefaultPageSize = 15
+
+var listCommand = &cli.Command{
+	Name:    "list",
+	Aliases: []string{"ls"},
+	Usage:   "List browser error logs according to the specified options",
+	UsageText: `List browser error logs according to the specified options.
+
+Examples:
+1. Query all logs:
+$ swctl browser logs list`,
+	Flags: flags.Flags(
+		flags.DurationFlags,
+		flags.VersionFlags,
+		flags.PageFlags,
+	),
+	Before: interceptor.BeforeChain(
+		interceptor.DurationInterceptor,
+		interceptor.ParseVersion(false),
+		interceptor.ParsePage(false),
+	),
+	Action: func(ctx *cli.Context) error {
+		start := ctx.String("start")
+		end := ctx.String("end")
+		step := ctx.Generic("step")
+
+		duration := api.Duration{
+			Start: start,
+			End:   end,
+			Step:  step.(*model.StepEnumValue).Selected,
+		}
+
+		serviceID := ctx.String("service-id")
+		serviceVersionID := ctx.String("version-id")
+		pageID := ctx.String("page-id")
+
+		pageNum := 1
+		needTotal := true
+
+		paging := api.Pagination{
+			PageNum:   &pageNum,
+			PageSize:  DefaultPageSize,
+			NeedTotal: &needTotal,
+		}
+		condition := &api.BrowserErrorLogQueryCondition{
+			ServiceID:        &serviceID,
+			ServiceVersionID: &serviceVersionID,
+			PagePathID:       &pageID,
+			QueryDuration:    &duration,
+			Paging:           &paging,
+		}
+
+		logs, err := log.BrowserLogs(ctx, condition)
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: logs, Condition: condition})
+	},
+}
diff --git a/pkg/graphql/log/log.go b/internal/commands/browser/logs/log.go
similarity index 60%
copy from pkg/graphql/log/log.go
copy to internal/commands/browser/logs/log.go
index 153bafe..6686cfe 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/browser/logs/log.go
@@ -15,26 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logs
 
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
+import "github.com/urfave/cli/v2"
 
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
-)
-
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+var Command = &cli.Command{
+	Name:    "logs",
+	Aliases: []string{"l"},
+	Usage:   "Browser error log related sub-command",
+	Subcommands: cli.Commands{
+		listCommand,
+	},
 }
diff --git a/internal/commands/browser/page/list.go b/internal/commands/browser/page/list.go
new file mode 100644
index 0000000..a03965b
--- /dev/null
+++ b/internal/commands/browser/page/list.go
@@ -0,0 +1,68 @@
+// 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 page
+
+import (
+	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/flags"
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
+	"github.com/urfave/cli/v2"
+)
+
+var listCommand = &cli.Command{
+	Name:    "list",
+	Aliases: []string{"ls"},
+	Usage:   `list all monitored browser page of the give "--service-id" or "--service-name"`,
+	UsageText: `This command lists all page of the browser service, via service id or service name.
+
+Examples:
+1. List all page of the browser service by service name "test-ui":
+$ swctl browser page ls --service-name test-ui
+
+2. List all page of the browser service by service id "dGVzdC11aQ==.1":
+$ swctl browser page ls --service-id dGVzdC11aQ==.1`,
+	Flags: flags.Flags(
+		flags.ServiceFlags,
+
+		[]cli.Flag{
+			&cli.IntFlag{
+				Name:     "limit",
+				Usage:    "returns at most `<limit>` endpoints",
+				Required: false,
+				Value:    100,
+			},
+		},
+	),
+	Before: interceptor.BeforeChain(
+		interceptor.ParseBrowserService(true),
+	),
+	Action: func(ctx *cli.Context) error {
+		serviceID := ctx.String("service-id")
+		limit := ctx.Int("limit")
+
+		endpoints, err := metadata.SearchEndpoints(ctx, serviceID, "", limit)
+
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: endpoints})
+	},
+}
diff --git a/pkg/graphql/log/log.go b/internal/commands/browser/page/page.go
similarity index 60%
copy from pkg/graphql/log/log.go
copy to internal/commands/browser/page/page.go
index 153bafe..a3868a8 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/browser/page/page.go
@@ -15,26 +15,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package page
 
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
+import "github.com/urfave/cli/v2"
 
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
-)
-
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+var Command = &cli.Command{
+	Name:  "page",
+	Usage: "Browser page related sub-command",
+	Subcommands: cli.Commands{
+		listCommand,
+	},
 }
diff --git a/internal/commands/browser/service/list.go b/internal/commands/browser/service/list.go
new file mode 100644
index 0000000..d0f8541
--- /dev/null
+++ b/internal/commands/browser/service/list.go
@@ -0,0 +1,76 @@
+// 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 service
+
+import (
+	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/flags"
+	"github.com/apache/skywalking-cli/internal/model"
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
+	"github.com/urfave/cli/v2"
+
+	api "skywalking.apache.org/repo/goapi/query"
+)
+
+var listCommand = &cli.Command{
+	Name:    "list",
+	Aliases: []string{"ls"},
+	Usage:   "list the monitored browser services",
+	UsageText: `This command lists all browser services if not "<service name>" is given,
+otherwise, it only lists the browser services matching the given "<service name>".
+
+Examples:
+1. List all the browser services:
+$ swctl browser svc ls
+
+2. List a specific browser service named "test-ui":
+$ swctl browser svc ls test-ui`,
+	Flags: flags.DurationFlags,
+	Before: interceptor.BeforeChain(
+		interceptor.DurationInterceptor,
+	),
+	Action: func(ctx *cli.Context) error {
+		end := ctx.String("end")
+		start := ctx.String("start")
+		step := ctx.Generic("step")
+
+		var services []api.Service
+		var err error
+
+		if args := ctx.Args(); args.Len() == 0 {
+			services, err = metadata.AllBrowserServices(ctx, api.Duration{
+				Start: start,
+				End:   end,
+				Step:  step.(*model.StepEnumValue).Selected,
+			})
+			if err != nil {
+				return err
+			}
+		} else {
+			service, err := metadata.SearchBrowserService(ctx, args.First())
+			if err != nil {
+				return err
+			}
+			services = []api.Service{service}
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: services})
+	},
+}
diff --git a/pkg/graphql/log/log.go b/internal/commands/browser/service/service.go
similarity index 63%
copy from pkg/graphql/log/log.go
copy to internal/commands/browser/service/service.go
index 153bafe..b16a185 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/browser/service/service.go
@@ -15,26 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package service
 
 import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
-
-	"github.com/machinebox/graphql"
-
 	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
 )
 
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+var Command = &cli.Command{
+	Name:    "service",
+	Aliases: []string{"s", "svc"},
+	Usage:   "Browser service related sub-command",
+	Subcommands: cli.Commands{
+		listCommand,
+	},
 }
diff --git a/internal/commands/browser/version/list.go b/internal/commands/browser/version/list.go
new file mode 100644
index 0000000..30c23f9
--- /dev/null
+++ b/internal/commands/browser/version/list.go
@@ -0,0 +1,70 @@
+// 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 version
+
+import (
+	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/flags"
+	"github.com/apache/skywalking-cli/internal/model"
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
+	"github.com/urfave/cli/v2"
+
+	api "skywalking.apache.org/repo/goapi/query"
+)
+
+var listCommand = &cli.Command{
+	Name:    "list",
+	Aliases: []string{"ls"},
+	Usage:   `list all monitored browser version of the given "--service-id" or "--service-name"`,
+	UsageText: `This command lists all version of the browser service, via service id or service name.
+
+Examples:
+1. List all version of the browser service by service name "provider":
+$ swctl browser version ls --service-name test-ui
+
+2. List all version of the browser service by service id "dGVzdC11aQ==.1"":
+$ swctl browser version ls --service-id dGVzdC11aQ==.1`,
+	Flags: flags.Flags(
+		flags.DurationFlags,
+		flags.ServiceFlags,
+	),
+	Before: interceptor.BeforeChain(
+		interceptor.DurationInterceptor,
+		interceptor.ParseBrowserService(true),
+	),
+	Action: func(ctx *cli.Context) error {
+		end := ctx.String("end")
+		start := ctx.String("start")
+		step := ctx.Generic("step")
+		serviceID := ctx.String("service-id")
+
+		instances, err := metadata.Instances(ctx, serviceID, api.Duration{
+			Start: start,
+			End:   end,
+			Step:  step.(*model.StepEnumValue).Selected,
+		})
+
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: instances})
+	},
+}
diff --git a/pkg/graphql/log/log.go b/internal/commands/browser/version/version.go
similarity index 63%
copy from pkg/graphql/log/log.go
copy to internal/commands/browser/version/version.go
index 153bafe..15863d9 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/browser/version/version.go
@@ -15,26 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package version
 
 import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
-
-	"github.com/machinebox/graphql"
-
 	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
 )
 
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+var Command = &cli.Command{
+	Name:    "version",
+	Aliases: []string{"v"},
+	Usage:   `Browser service version related sub-command`,
+	Subcommands: cli.Commands{
+		listCommand,
+	},
 }
diff --git a/pkg/graphql/log/log.go b/internal/commands/interceptor/page.go
similarity index 60%
copy from pkg/graphql/log/log.go
copy to internal/commands/interceptor/page.go
index 153bafe..06f6eba 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/interceptor/page.go
@@ -15,26 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package interceptor
 
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
+import "github.com/urfave/cli/v2"
 
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
+const (
+	pageIDFlagName   = "page-id"
+	pageNameFlagName = "page-name"
 )
 
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+// ParsePage parses the endpoint id or endpoint name,
+// and converts the present one to the missing one.
+// See flags.PageFlags.
+func ParsePage(required bool) func(*cli.Context) error {
+	return func(ctx *cli.Context) error {
+		if err := ParseBrowserService(required)(ctx); err != nil {
+			return err
+		}
+		return parseEndpoint(required, pageIDFlagName, pageNameFlagName, serviceIDFlagName)(ctx)
+	}
 }
diff --git a/internal/commands/interceptor/service.go b/internal/commands/interceptor/service.go
index f87fd88..0589b48 100644
--- a/internal/commands/interceptor/service.go
+++ b/internal/commands/interceptor/service.go
@@ -22,24 +22,36 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/urfave/cli/v2"
-
 	"github.com/apache/skywalking-cli/internal/logger"
 	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
+	"github.com/urfave/cli/v2"
+	api "skywalking.apache.org/repo/goapi/query"
 )
 
+type nodeType int
+
 const (
 	serviceIDFlagName       = "service-id"
 	serviceNameFlagName     = "service-name"
 	destServiceIDFlagName   = "dest-service-id"
 	destServiceNameFlagName = "dest-service-name"
+
+	normal  nodeType = iota
+	browser nodeType = iota
 )
 
 // ParseService parses the service id or service name,
 // and converts the present one to the missing one.
 // See flags.ServiceFlags.
 func ParseService(required bool) func(*cli.Context) error {
-	return parseService(required, serviceIDFlagName, serviceNameFlagName)
+	return parseService(required, serviceIDFlagName, serviceNameFlagName, normal)
+}
+
+// ParseBrowserService parses the service id or service name,
+// and converts the present one to the missing one.
+// See flags.ServiceFlags.
+func ParseBrowserService(required bool) func(*cli.Context) error {
+	return parseService(required, serviceIDFlagName, serviceNameFlagName, browser)
 }
 
 // ParseServiceRelation parses the source and destination service id or service name,
@@ -50,11 +62,11 @@ func ParseServiceRelation(required bool) func(*cli.Context) error {
 		if err := ParseService(required)(ctx); err != nil {
 			return err
 		}
-		return parseService(required, destServiceIDFlagName, destServiceNameFlagName)(ctx)
+		return parseService(required, destServiceIDFlagName, destServiceNameFlagName, normal)(ctx)
 	}
 }
 
-func parseService(required bool, idFlagName, nameFlagName string) func(*cli.Context) error {
+func parseService(required bool, idFlagName, nameFlagName string, nodeType nodeType) func(*cli.Context) error {
 	return func(ctx *cli.Context) error {
 		id := ctx.String(idFlagName)
 		name := ctx.String(nameFlagName)
@@ -77,7 +89,14 @@ func parseService(required bool, idFlagName, nameFlagName string) func(*cli.Cont
 			}
 			name = string(s)
 		} else if name != "" {
-			service, err := metadata.SearchService(ctx, name)
+			var service api.Service
+			var err error
+			switch nodeType {
+			case normal:
+				service, err = metadata.SearchService(ctx, name)
+			case browser:
+				service, err = metadata.SearchBrowserService(ctx, name)
+			}
 			if err != nil {
 				return err
 			}
diff --git a/pkg/graphql/log/log.go b/internal/commands/interceptor/version.go
similarity index 59%
copy from pkg/graphql/log/log.go
copy to internal/commands/interceptor/version.go
index 153bafe..73be4b0 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/commands/interceptor/version.go
@@ -15,26 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package interceptor
 
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
+import "github.com/urfave/cli/v2"
 
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
+const (
+	versionIDFlagName   = "version-id"
+	versionNameFlagName = "version-name"
 )
 
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+// ParseVersion parses the service instance id or service instance name,
+// and converts the present one to the missing one.
+// See flags.VersionFlags.
+func ParseVersion(required bool) func(*cli.Context) error {
+	return func(ctx *cli.Context) error {
+		if err := ParseBrowserService(required)(ctx); err != nil {
+			return err
+		}
+		return parseInstance(required, versionIDFlagName, versionNameFlagName, serviceIDFlagName)(ctx)
+	}
 }
diff --git a/pkg/graphql/log/log.go b/internal/flags/page.go
similarity index 60%
copy from pkg/graphql/log/log.go
copy to internal/flags/page.go
index 153bafe..c5ba1fe 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/flags/page.go
@@ -15,26 +15,22 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
-
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
-
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
+package flags
+
+import "github.com/urfave/cli/v2"
+
+// PageFlags take either page id or page name as input,
+// and transform to the other one.
+var PageFlags = append(
+	ServiceFlags, // page level requires service level by default
+	&cli.StringFlag{
+		Name:     "page-id",
+		Usage:    "`page id`, if you don't have page id, use `--page-name` instead",
+		Required: false,
+	},
+	&cli.StringFlag{
+		Name:     "page-name",
+		Usage:    "`page name`, if you already have page id, prefer to use `--page-id`",
+		Required: false,
+	},
 )
-
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
-}
diff --git a/pkg/graphql/log/log.go b/internal/flags/version.go
similarity index 60%
copy from pkg/graphql/log/log.go
copy to internal/flags/version.go
index 153bafe..b66d083 100644
--- a/pkg/graphql/log/log.go
+++ b/internal/flags/version.go
@@ -15,26 +15,21 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
-
-import (
-	"github.com/apache/skywalking-cli/assets"
-	"github.com/apache/skywalking-cli/pkg/graphql/client"
-
-	"github.com/machinebox/graphql"
-
-	"github.com/urfave/cli/v2"
-
-	api "skywalking.apache.org/repo/goapi/query"
-)
-
-func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error) {
-	var response map[string]api.Logs
-
-	request := graphql.NewRequest(assets.Read("graphqls/logs/Logs.graphql"))
-	request.Var("condition", condition)
-
-	err := client.ExecuteQuery(ctx, request, &response)
-
-	return response["result"], err
+package flags
+
+import "github.com/urfave/cli/v2"
+
+// VersionFlags take either browser service version id or browser service version name as input,
+// and transform to the other one.
+var VersionFlags = []cli.Flag{
+	&cli.StringFlag{
+		Name:     "version-id",
+		Usage:    "`version id`, if you don't have version id, use `--version-name` instead",
+		Required: false,
+	},
+	&cli.StringFlag{
+		Name:     "version-name",
+		Usage:    "`version-name`, if you already have version id, prefer to use `--version-id`",
+		Required: false,
+	},
 }
diff --git a/pkg/graphql/log/log.go b/pkg/graphql/log/log.go
index 153bafe..76613d5 100644
--- a/pkg/graphql/log/log.go
+++ b/pkg/graphql/log/log.go
@@ -38,3 +38,14 @@ func Logs(ctx *cli.Context, condition *api.LogQueryCondition) (api.Logs, error)
 
 	return response["result"], err
 }
+
+func BrowserLogs(ctx *cli.Context, condition *api.BrowserErrorLogQueryCondition) (api.BrowserErrorLogs, error) {
+	var response map[string]api.BrowserErrorLogs
+
+	request := graphql.NewRequest(assets.Read("graphqls/logs/BrowserLogs.graphql"))
+	request.Var("condition", condition)
+
+	err := client.ExecuteQuery(ctx, request, &response)
+
+	return response["result"], err
+}
diff --git a/pkg/graphql/metadata/metadata.go b/pkg/graphql/metadata/metadata.go
index e1eced6..8b7e60a 100644
--- a/pkg/graphql/metadata/metadata.go
+++ b/pkg/graphql/metadata/metadata.go
@@ -58,6 +58,34 @@ func SearchService(cliCtx *cli.Context, serviceCode string) (service api.Service
 	return service, err
 }
 
+func AllBrowserServices(cliCtx *cli.Context, duration api.Duration) ([]api.Service, error) {
+	var response map[string][]api.Service
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/AllBrowserServices.graphql"))
+	request.Var("duration", duration)
+
+	err := client.ExecuteQuery(cliCtx, request, &response)
+
+	return response["result"], err
+}
+
+func SearchBrowserService(cliCtx *cli.Context, serviceCode string) (service api.Service, err error) {
+	var response map[string]api.Service
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchBrowserService.graphql"))
+	request.Var("serviceCode", serviceCode)
+
+	err = client.ExecuteQuery(cliCtx, request, &response)
+
+	service = response["result"]
+
+	if service.ID == "" {
+		return service, fmt.Errorf("no such service [%s]", serviceCode)
+	}
+
+	return service, err
+}
+
 func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int) ([]api.Endpoint, error) {
 	var response map[string][]api.Endpoint