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

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


The following commit(s) were added to refs/heads/master by this push:
     new 45a5f98  Allow setting `start` `end` with relative time (#128)
45a5f98 is described below

commit 45a5f9807126dc6ea9fe06e17e7aafbe212436d2
Author: kezhenxu94 <[email protected]>
AuthorDate: Tue Oct 26 19:42:30 2021 +0800

    Allow setting `start` `end` with relative time (#128)
---
 internal/commands/interceptor/duration.go | 48 +++++++++++++++++--------------
 internal/flags/duration.go                | 20 +++++++++----
 pkg/display/graph/dashboard/global.go     | 14 ++++++---
 3 files changed, 52 insertions(+), 30 deletions(-)

diff --git a/internal/commands/interceptor/duration.go 
b/internal/commands/interceptor/duration.go
index 9f8e627..ea9f291 100644
--- a/internal/commands/interceptor/duration.go
+++ b/internal/commands/interceptor/duration.go
@@ -18,6 +18,7 @@
 package interceptor
 
 import (
+       "fmt"
        "strconv"
        "time"
 
@@ -28,9 +29,10 @@ import (
        "github.com/urfave/cli/v2"
 
        "github.com/apache/skywalking-cli/internal/logger"
+       "github.com/apache/skywalking-cli/internal/model"
 )
 
-func TryParseTime(unparsed string) (api.Step, time.Time, error) {
+func TryParseTime(unparsed string, userStep api.Step) (api.Step, time.Time, 
error) {
        var possibleError error
        for step, layout := range utils.StepFormats {
                t, err := time.Parse(layout, unparsed)
@@ -39,7 +41,11 @@ func TryParseTime(unparsed string) (api.Step, time.Time, 
error) {
                }
                possibleError = err
        }
-       return api.StepSecond, time.Time{}, possibleError
+       duration, err := time.ParseDuration(unparsed)
+       if err == nil {
+               return userStep, time.Now().Add(duration), nil
+       }
+       return userStep, time.Time{}, fmt.Errorf("the given time %v is neither 
absolute time nor relative time: %+v %+v", unparsed, possibleError, err)
 }
 
 // DurationInterceptor sets the duration if absent, and formats it accordingly,
@@ -47,9 +53,20 @@ func TryParseTime(unparsed string) (api.Step, time.Time, 
error) {
 func DurationInterceptor(ctx *cli.Context) error {
        start := ctx.String("start")
        end := ctx.String("end")
-       timezone := ctx.String("timezone")
+       userStep := ctx.Generic("step")
+       if timezone := ctx.String("timezone"); timezone != "" {
+               if offset, err := strconv.Atoi(timezone); err == nil {
+                       // `offset` is in form of "+1300", while 
`time.FixedZone` takes offset in seconds
+                       time.Local = time.FixedZone("", offset/100*60*60)
+               }
+       }
+
+       var s api.Step
+       if userStep != nil {
+               s = userStep.(*model.StepEnumValue).Selected
+       }
 
-       startTime, endTime, step, dt := ParseDuration(start, end, timezone)
+       startTime, endTime, step, dt := ParseDuration(start, end, s)
 
        if err := ctx.Set("start", startTime.Format(utils.StepFormats[step])); 
err != nil {
                return err
@@ -71,20 +88,11 @@ func DurationInterceptor(ctx *cli.Context) error {
 //   then: end := now + 30 units, where unit is the precision of `start`, 
(hours, minutes, etc.)
 // if --start is absent, --end is given,
 //   then: start := end - 30 units, where unit is the precision of `end`, 
(hours, minutes, etc.)
-func ParseDuration(start, end, timezone string) (startTime, endTime time.Time, 
step api.Step, dt utils.DurationType) {
-       logger.Log.Debugln("Start time:", start, "end time:", end, "timezone:", 
timezone)
+func ParseDuration(start, end string, userStep api.Step) (startTime, endTime 
time.Time, step api.Step, dt utils.DurationType) {
+       logger.Log.Debugln("Start time:", start, "end time:", end, "timezone:", 
time.Local)
 
        now := time.Now()
 
-       if timezone != "" {
-               if offset, err := strconv.Atoi(timezone); err == nil {
-                       // `offset` is in form of "+1300", while 
`time.FixedZone` takes offset in seconds
-                       now = now.In(time.FixedZone("", offset/100*60*60))
-
-                       logger.Log.Debugln("Now:", now, "with server 
timezone:", timezone)
-               }
-       }
-
        // both are absent
        if start == "" && end == "" {
                return now.Add(-30 * time.Minute), now, api.StepMinute, 
utils.BothAbsent
@@ -94,23 +102,21 @@ func ParseDuration(start, end, timezone string) 
(startTime, endTime time.Time, s
 
        // both are present
        if len(start) > 0 && len(end) > 0 {
-               start, end = AlignPrecision(start, end)
-
-               if _, startTime, err = TryParseTime(start); err != nil {
+               if userStep, startTime, err = TryParseTime(start, userStep); 
err != nil {
                        logger.Log.Fatalln("Unsupported time format:", start, 
err)
                }
-               if step, endTime, err = TryParseTime(end); err != nil {
+               if step, endTime, err = TryParseTime(end, userStep); err != nil 
{
                        logger.Log.Fatalln("Unsupported time format:", end, err)
                }
 
                return startTime, endTime, step, utils.BothPresent
        } else if end == "" { // end is absent
-               if step, startTime, err = TryParseTime(start); err != nil {
+               if step, startTime, err = TryParseTime(start, userStep); err != 
nil {
                        logger.Log.Fatalln("Unsupported time format:", start, 
err)
                }
                return startTime, startTime.Add(30 * utils.StepDuration[step]), 
step, utils.EndAbsent
        } else { // start is absent
-               if step, endTime, err = TryParseTime(end); err != nil {
+               if step, endTime, err = TryParseTime(end, userStep); err != nil 
{
                        logger.Log.Fatalln("Unsupported time format:", end, err)
                }
                return endTime.Add(-30 * utils.StepDuration[step]), endTime, 
step, utils.StartAbsent
diff --git a/internal/flags/duration.go b/internal/flags/duration.go
index f4a3f67..1e3793a 100644
--- a/internal/flags/duration.go
+++ b/internal/flags/duration.go
@@ -25,8 +25,9 @@ import (
        "github.com/apache/skywalking-cli/internal/model"
 )
 
-var startEndUsage = `"start" and "end" specify a time range during which the 
query is preformed, 
-               they are both optional and their default values follow the 
rules below: 
+var startEndUsage = `"start" and "end" specify a time range during which the 
query is preformed,
+               they can be absolute time like "2019-01-01 12", "2019-01-01 
1213", or relative time (to the
+               current time) like "-30m", "30m". They are both optional and 
their default values follow the rules below: 
                1. when "start" and "end" are both absent, "start = now - 30 
minutes" and "end = now", 
                namely past 30 minutes; 
                2. when "start" and "end" are both present, they are aligned to 
the same precision by 
@@ -39,7 +40,16 @@ var startEndUsage = `"start" and "end" specify a time range 
during which the que
                4. when "start" is present and "end" is absent, will determine 
the precision of "start" 
                and then use the precision to calculate "end" (plus 30 units), 
e.g. "start = 2019-11-09 1204", 
                the precision is "MINUTE", so "end = start + 30 minutes = 
2019-11-09 1234", 
-               and if "start = 2019-11-08 06", the precision is "HOUR", so 
"end = start + 30HOUR = 2019-11-09 12".`
+               and if "start = 2019-11-08 06", the precision is "HOUR", so 
"end = start + 30HOUR = 2019-11-09 12".
+               Examples:
+               1. Query the metrics from 20 minutes ago to 10 minutes ago
+               $ swctl metrics linear --name=service_resp_time --service-name 
business-zone::projectB --start "-20m" --end "-10m"
+               2. Query the metrics from 1 hour ago to 10 minutes ago
+               $ swctl metrics linear --name=service_resp_time --service-name 
business-zone::projectB --start "-1h" --end "-10m"
+               3. Query the metrics from 1 hour ago to now
+               $ swctl metrics linear --name=service_resp_time --service-name 
business-zone::projectB --start "-1h" --end "0m"
+               4. Query the metrics from "2021-10-26 1047" to "2021-10-26 1127"
+               $ swctl metrics linear --name=service_resp_time --service-name 
business-zone::projectB --start "2021-10-26 1047" --end "2021-10-26 1127"`
 
 // DurationFlags are healthcheck flags that involves a duration, composed
 // by a start time, an end time, and a step, which is commonly used
@@ -54,8 +64,8 @@ var DurationFlags = []cli.Flag{
                Usage: `end time of the query duration. Check the usage of 
"start"`,
        },
        &cli.GenericFlag{
-               Name:   "step",
-               Hidden: true,
+               Name:  "step",
+               Usage: `time step between start time and end time, should be 
one of SECOND, MINUTE, HOUR, DAY`,
                Value: &model.StepEnumValue{
                        Enum:     api.AllStep,
                        Default:  api.StepMinute,
diff --git a/pkg/display/graph/dashboard/global.go 
b/pkg/display/graph/dashboard/global.go
index 52a01c3..b7f8682 100644
--- a/pkg/display/graph/dashboard/global.go
+++ b/pkg/display/graph/dashboard/global.go
@@ -38,6 +38,7 @@ import (
        "github.com/mum4k/termdash/terminal/terminalapi"
        "github.com/urfave/cli/v2"
 
+       "github.com/apache/skywalking-cli/internal/model"
        "github.com/apache/skywalking-cli/pkg/display/graph/gauge"
        "github.com/apache/skywalking-cli/pkg/display/graph/heatmap"
        "github.com/apache/skywalking-cli/pkg/display/graph/linear"
@@ -88,6 +89,7 @@ var template *dashboard.GlobalTemplate
 var allWidgets *widgets
 
 var initStartStr string
+var initStep = api.StepMinute
 var initEndStr string
 
 var curStartTime time.Time
@@ -131,7 +133,7 @@ func newLayoutButtons(c *container.Container) 
([]*button.Button, error) {
                        return nil, err
                }
 
-               buttons[int(lt)] = b
+               buttons[lt] = b
        }
 
        return buttons, nil
@@ -317,11 +319,15 @@ func refresh(con context.Context, ctx *cli.Context, 
interval time.Duration) {
        initStartStr = ctx.String("start")
        initEndStr = ctx.String("end")
 
-       _, start, err := interceptor.TryParseTime(initStartStr)
+       if s := ctx.Generic("step"); s != nil {
+               initStep = s.(*model.StepEnumValue).Selected
+       }
+
+       _, start, err := interceptor.TryParseTime(initStartStr, initStep)
        if err != nil {
                return
        }
-       _, end, err := interceptor.TryParseTime(initEndStr)
+       _, end, err := interceptor.TryParseTime(initEndStr, initStep)
        if err != nil {
                return
        }
@@ -355,7 +361,7 @@ func refresh(con context.Context, ctx *cli.Context, 
interval time.Duration) {
 // If the duration doesn't change, an error will be returned, and the 
dashboard will not refresh.
 // Otherwise, a new duration will be returned, which is used to get the latest 
global data.
 func updateDuration(interval time.Duration) (api.Duration, error) {
-       step, _, err := interceptor.TryParseTime(initStartStr)
+       step, _, err := interceptor.TryParseTime(initStartStr, initStep)
        if err != nil {
                return api.Duration{}, err
        }

Reply via email to