github.com/go-graphite/carbonapi@v0.17.0/expr/functions/timeShift/function.go (about)

     1  package timeShift
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  
     8  	"github.com/lomik/zapwriter"
     9  	"github.com/spf13/viper"
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/go-graphite/carbonapi/expr/helper"
    13  	"github.com/go-graphite/carbonapi/expr/interfaces"
    14  	"github.com/go-graphite/carbonapi/expr/types"
    15  	"github.com/go-graphite/carbonapi/pkg/parser"
    16  )
    17  
    18  type timeShift struct {
    19  	config timeShiftConfig
    20  }
    21  
    22  func GetOrder() interfaces.Order {
    23  	return interfaces.Any
    24  }
    25  
    26  type timeShiftConfig struct {
    27  	ResetEndDefaultValue *bool
    28  }
    29  
    30  func New(configFile string) []interfaces.FunctionMetadata {
    31  	logger := zapwriter.Logger("functionInit").With(zap.String("function", "timeShift"))
    32  	res := make([]interfaces.FunctionMetadata, 0)
    33  	f := &timeShift{}
    34  	functions := []string{"timeShift"}
    35  	for _, n := range functions {
    36  		res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
    37  	}
    38  
    39  	cfg := timeShiftConfig{}
    40  	v := viper.New()
    41  	v.SetConfigFile(configFile)
    42  	err := v.ReadInConfig()
    43  	if err != nil {
    44  		logger.Info("failed to read config file, using default",
    45  			zap.Error(err),
    46  		)
    47  	} else {
    48  		err = v.Unmarshal(&cfg)
    49  		if err != nil {
    50  			logger.Fatal("failed to parse config",
    51  				zap.Error(err),
    52  			)
    53  			return nil
    54  		}
    55  
    56  		f.config = cfg
    57  	}
    58  
    59  	if cfg.ResetEndDefaultValue == nil {
    60  		// TODO(civil): Change default value in 0.15
    61  		v := false
    62  		f.config.ResetEndDefaultValue = &v
    63  		logger.Warn("timeShift function in graphite-web have a default value for resetEnd set to true." +
    64  			"carbonapi currently forces this to be false. This behavior will change in next major release (0.15)" +
    65  			"to be compatible with graphite-web. Please change your dashboards to explicitly pass resetEnd parameter" +
    66  			"or create a config file for this function that sets it to false." +
    67  			"Please see https://github.com/go-graphite/carbonapi/blob/main/doc/configuration.md#example-for-timeshift")
    68  	}
    69  
    70  	return res
    71  }
    72  
    73  // timeShift(seriesList, timeShift, resetEnd=True)
    74  func (f *timeShift) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    75  	// FIXME(civil): support alignDst
    76  	if e.ArgsLen() < 2 {
    77  		return nil, parser.ErrMissingArgument
    78  	}
    79  
    80  	offs, err := e.GetIntervalArg(1, -1)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	offsStr := strconv.Itoa(int(offs))
    85  
    86  	resetEnd, err := e.GetBoolArgDefault(2, *f.config.ResetEndDefaultValue)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	resetEndStr := strconv.FormatBool(resetEnd)
    91  
    92  	arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from+int64(offs), until+int64(offs), values)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	results := make([]*types.MetricData, len(arg))
    97  
    98  	for n, a := range arg {
    99  		r := a.CopyLink()
   100  		r.Name = "timeShift(" + a.Name + ",'" + offsStr + "'," + resetEndStr + ")"
   101  		r.StartTime = a.StartTime - int64(offs)
   102  		r.StopTime = a.StopTime - int64(offs)
   103  		if resetEnd && r.StopTime > until {
   104  			r.StopTime = until
   105  		}
   106  		length := int((r.StopTime - r.StartTime) / r.StepTime)
   107  		if length < 0 {
   108  			continue
   109  		}
   110  		r.Values = r.Values[:length]
   111  
   112  		r.Tags["timeshift"] = fmt.Sprintf("%d", offs)
   113  		results[n] = r
   114  
   115  	}
   116  
   117  	return results, nil
   118  }
   119  
   120  // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
   121  func (f *timeShift) Description() map[string]types.FunctionDescription {
   122  	return map[string]types.FunctionDescription{
   123  		"timeShift": {
   124  			Description: "Takes one metric or a wildcard seriesList, followed by a quoted string with the\nlength of time (See ``from / until`` in the render\\_api_ for examples of time formats).\n\nDraws the selected metrics shifted in time. If no sign is given, a minus sign ( - ) is\nimplied which will shift the metric back in time. If a plus sign ( + ) is given, the\nmetric will be shifted forward in time.\n\nWill reset the end date range automatically to the end of the base stat unless\nresetEnd is False. Example case is when you timeshift to last week and have the graph\ndate range set to include a time in the future, will limit this timeshift to pretend\nending at the current time. If resetEnd is False, will instead draw full range including\nfuture time.\n\nBecause time is shifted by a fixed number of seconds, comparing a time period with DST to\na time period without DST, and vice-versa, will result in an apparent misalignment. For\nexample, 8am might be overlaid with 7am. To compensate for this, use the alignDST option.\n\nUseful for comparing a metric against itself at a past periods or correcting data\nstored at an offset.\n\nExample:\n\n.. code-block:: none\n\n  &target=timeShift(Sales.widgets.largeBlue,\"7d\")\n  &target=timeShift(Sales.widgets.largeBlue,\"-7d\")\n  &target=timeShift(Sales.widgets.largeBlue,\"+1h\")",
   125  			Function:    "timeShift(seriesList, timeShift, resetEnd=True, alignDST=False)",
   126  			Group:       "Transform",
   127  			Module:      "graphite.render.functions",
   128  			Name:        "timeShift",
   129  			Params: []types.FunctionParam{
   130  				{
   131  					Name:     "seriesList",
   132  					Required: true,
   133  					Type:     types.SeriesList,
   134  				},
   135  				{
   136  					Name:     "timeShift",
   137  					Required: true,
   138  					Suggestions: types.NewSuggestions(
   139  						"1h",
   140  						"6h",
   141  						"12h",
   142  						"1d",
   143  						"2d",
   144  						"7d",
   145  						"14d",
   146  						"30d",
   147  					),
   148  					Type: types.Interval,
   149  				},
   150  				{
   151  					Default: types.NewSuggestion(*f.config.ResetEndDefaultValue),
   152  					Name:    "resetEnd",
   153  					Type:    types.Boolean,
   154  				},
   155  				/*
   156  					{
   157  						Default: types.NewSuggestion(false),
   158  						Name:    "alignDst",
   159  						Type:    types.Boolean,
   160  					},
   161  				*/
   162  			},
   163  			NameChange:   true, // name changed
   164  			ValuesChange: true, // values changed
   165  		},
   166  	}
   167  }