-
Notifications
You must be signed in to change notification settings - Fork 148
Expand file tree
/
Copy pathfunction.go
More file actions
167 lines (147 loc) · 5.54 KB
/
function.go
File metadata and controls
167 lines (147 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package timeShift
import (
"context"
"fmt"
"strconv"
"github.com/lomik/zapwriter"
"github.com/spf13/viper"
"go.uber.org/zap"
"github.com/go-graphite/carbonapi/expr/helper"
"github.com/go-graphite/carbonapi/expr/interfaces"
"github.com/go-graphite/carbonapi/expr/types"
"github.com/go-graphite/carbonapi/pkg/parser"
)
type timeShift struct {
config timeShiftConfig
}
func GetOrder() interfaces.Order {
return interfaces.Any
}
type timeShiftConfig struct {
ResetEndDefaultValue *bool
}
func New(configFile string) []interfaces.FunctionMetadata {
logger := zapwriter.Logger("functionInit").With(zap.String("function", "timeShift"))
res := make([]interfaces.FunctionMetadata, 0)
f := &timeShift{}
functions := []string{"timeShift"}
for _, n := range functions {
res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
}
cfg := timeShiftConfig{}
v := viper.New()
v.SetConfigFile(configFile)
err := v.ReadInConfig()
if err != nil {
logger.Info("failed to read config file, using default",
zap.Error(err),
)
} else {
err = v.Unmarshal(&cfg)
if err != nil {
logger.Fatal("failed to parse config",
zap.Error(err),
)
return nil
}
f.config = cfg
}
if cfg.ResetEndDefaultValue == nil {
// TODO(civil): Change default value in 0.15
v := false
f.config.ResetEndDefaultValue = &v
logger.Warn("timeShift function in graphite-web have a default value for resetEnd set to true." +
"carbonapi currently forces this to be false. This behavior will change in next major release (0.15)" +
"to be compatible with graphite-web. Please change your dashboards to explicitly pass resetEnd parameter" +
"or create a config file for this function that sets it to false." +
"Please see https://github.com/go-graphite/carbonapi/blob/main/doc/configuration.md#example-for-timeshift")
}
return res
}
// timeShift(seriesList, timeShift, resetEnd=True)
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) {
// FIXME(civil): support alignDst
if e.ArgsLen() < 2 {
return nil, parser.ErrMissingArgument
}
offs, err := e.GetIntervalArg(1, -1)
if err != nil {
return nil, err
}
offsStr := strconv.Itoa(int(offs))
resetEnd, err := e.GetBoolArgDefault(2, *f.config.ResetEndDefaultValue)
if err != nil {
return nil, err
}
resetEndStr := strconv.FormatBool(resetEnd)
arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from+int64(offs), until+int64(offs), values)
if err != nil {
return nil, err
}
results := make([]*types.MetricData, 0, len(arg))
for _, a := range arg {
r := a.CopyLink()
r.Name = "timeShift(" + a.Name + ",'" + offsStr + "'," + resetEndStr + ")"
r.StartTime = a.StartTime - int64(offs)
r.StopTime = a.StopTime - int64(offs)
if resetEnd && r.StopTime > until {
r.StopTime = until
}
length := int((r.StopTime - r.StartTime) / r.StepTime)
if length < 0 {
continue
}
r.Values = r.Values[:length]
r.Tags["timeshift"] = fmt.Sprintf("%d", offs)
results = append(results, r)
}
return results, nil
}
// Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
func (f *timeShift) Description() map[string]types.FunctionDescription {
return map[string]types.FunctionDescription{
"timeShift": {
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\")",
Function: "timeShift(seriesList, timeShift, resetEnd=True, alignDST=False)",
Group: "Transform",
Module: "graphite.render.functions",
Name: "timeShift",
Params: []types.FunctionParam{
{
Name: "seriesList",
Required: true,
Type: types.SeriesList,
},
{
Name: "timeShift",
Required: true,
Suggestions: types.NewSuggestions(
"1h",
"6h",
"12h",
"1d",
"2d",
"7d",
"14d",
"30d",
),
Type: types.Interval,
},
{
Default: types.NewSuggestion(*f.config.ResetEndDefaultValue),
Name: "resetEnd",
Type: types.Boolean,
},
/*
{
Default: types.NewSuggestion(false),
Name: "alignDst",
Type: types.Boolean,
},
*/
},
NameChange: true, // name changed
ValuesChange: true, // values changed
},
}
}