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:40 UTC

[skywalking-cli] branch command/browser created (now 47ff4ca)

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

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


      at 47ff4ca  Add some commands for the browser

This branch includes the following new commits:

     new 47ff4ca  Add some commands for the browser

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.


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

Posted by zh...@apache.org.
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