From 6b03bbb2189b85ebf9bd16b585d298531bf08d18 Mon Sep 17 00:00:00 2001 From: Robert Pocklington Date: Tue, 8 Mar 2022 14:00:18 +1100 Subject: [PATCH] WIP --- go-bin-rpm-amd64.deb | 0 output/csv/config.go | 14 ++++++++++++ output/csv/config_test.go | 13 +++++++++++ output/csv/output.go | 18 ++++++++++++--- output/csv/output_test.go | 46 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 go-bin-rpm-amd64.deb diff --git a/go-bin-rpm-amd64.deb b/go-bin-rpm-amd64.deb new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/output/csv/config.go b/output/csv/config.go index 8ab6f03d2e10..a9fa4576d982 100644 --- a/output/csv/config.go +++ b/output/csv/config.go @@ -23,6 +23,7 @@ package csv import ( "encoding/json" "fmt" + "strconv" "strings" "time" @@ -38,6 +39,7 @@ type Config struct { // Samples. FileName null.String `json:"file_name" envconfig:"K6_CSV_FILENAME"` SaveInterval types.NullDuration `json:"save_interval" envconfig:"K6_CSV_SAVE_INTERVAL"` + useISO8601 null.Bool `json:"use_iso8601" envconfig:"K6_CSV_USE_ISO_8601"` } // NewConfig creates a new Config instance with default values for some fields. @@ -45,6 +47,7 @@ func NewConfig() Config { return Config{ FileName: null.StringFrom("file.csv"), SaveInterval: types.NullDurationFrom(1 * time.Second), + useISO8601: null.BoolFrom(false), } } @@ -56,6 +59,9 @@ func (c Config) Apply(cfg Config) Config { if cfg.SaveInterval.Valid { c.SaveInterval = cfg.SaveInterval } + if cfg.useISO8601.Valid { + c.useISO8601 = cfg.useISO8601 + } return c } @@ -66,6 +72,7 @@ func ParseArg(arg string, logger *logrus.Logger) (Config, error) { if !strings.Contains(arg, "=") { c.FileName = null.StringFrom(arg) c.SaveInterval = types.NullDurationFrom(1 * time.Second) + c.useISO8601 = null.BoolFrom(false) return c, nil } @@ -89,6 +96,13 @@ func ParseArg(arg string, logger *logrus.Logger) (Config, error) { fallthrough case "fileName": c.FileName = null.StringFrom(r[1]) + case "useISO8601": + useISO8601, err := strconv.ParseBool(r[1]) + if err != nil { + return c, err + } + c.useISO8601 = null.BoolFrom(useISO8601) + default: return c, fmt.Errorf("unknown key %q as argument for csv output", r[0]) } diff --git a/output/csv/config_test.go b/output/csv/config_test.go index cb53cd7c81eb..ff2be7fd5594 100644 --- a/output/csv/config_test.go +++ b/output/csv/config_test.go @@ -39,6 +39,7 @@ func TestNewConfig(t *testing.T) { config := NewConfig() assert.Equal(t, "file.csv", config.FileName.String) assert.Equal(t, "1s", config.SaveInterval.String()) + assert.Equal(t, null.BoolFrom(false), config.useISO8601) } func TestApply(t *testing.T) { @@ -46,23 +47,28 @@ func TestApply(t *testing.T) { { FileName: null.StringFrom(""), SaveInterval: types.NullDurationFrom(2 * time.Second), + useISO8601: null.BoolFrom(false), }, { FileName: null.StringFrom("newPath"), SaveInterval: types.NewNullDuration(time.Duration(1), false), + useISO8601: null.BoolFrom(true), }, } expected := []struct { FileName string SaveInterval string + useISO8601 null.Bool }{ { FileName: "", SaveInterval: "2s", + useISO8601: null.BoolFrom(false), }, { FileName: "newPath", SaveInterval: "1s", + useISO8601: null.BoolFrom(true), }, } @@ -75,6 +81,7 @@ func TestApply(t *testing.T) { assert.Equal(t, expected.FileName, baseConfig.FileName.String) assert.Equal(t, expected.SaveInterval, baseConfig.SaveInterval.String()) + assert.Equal(t, expected.useISO8601, baseConfig.useISO8601) }) } } @@ -126,6 +133,12 @@ func TestParseArg(t *testing.T) { "filename=test.csv,save_interval=5s": { expectedErr: true, }, + "fileName=test.csv,useISO8601=true": { + config: Config{ + FileName: null.StringFrom("test.csv"), + useISO8601: null.BoolFrom(true), + }, + }, } for arg, testCase := range cases { diff --git a/output/csv/output.go b/output/csv/output.go index 1a3f754a0c05..68c96fbace00 100644 --- a/output/csv/output.go +++ b/output/csv/output.go @@ -54,6 +54,7 @@ type Output struct { ignoredTags []string row []string saveInterval time.Duration + useISO8601 bool } // New Creates new instance of CSV output @@ -87,6 +88,7 @@ func newOutput(params output.Params) (*Output, error) { saveInterval := config.SaveInterval.TimeDuration() fname := config.FileName.String + useISO8601 := config.useISO8601.Bool if fname == "" || fname == "-" { stdoutWriter := csv.NewWriter(os.Stdout) @@ -97,6 +99,7 @@ func newOutput(params output.Params) (*Output, error) { csvWriter: stdoutWriter, row: make([]string, 3+len(resTags)+1), saveInterval: saveInterval, + useISO8601: useISO8601, closeFn: func() error { return nil }, logger: logger, params: params, @@ -114,6 +117,7 @@ func newOutput(params output.Params) (*Output, error) { ignoredTags: ignoredTags, row: make([]string, 3+len(resTags)+1), saveInterval: saveInterval, + useISO8601: useISO8601, logger: logger, params: params, } @@ -182,7 +186,7 @@ func (o *Output) flushMetrics() { for _, sc := range samples { for _, sample := range sc.GetSamples() { sample := sample - row := SampleToRow(&sample, o.resTags, o.ignoredTags, o.row) + row := SampleToRow(&sample, o.resTags, o.ignoredTags, o.row, o.useISO8601) err := o.csvWriter.Write(row) if err != nil { o.logger.WithField("filename", o.fname).Error("CSV: Error writing to file") @@ -200,9 +204,17 @@ func MakeHeader(tags []string) []string { } // SampleToRow converts sample into array of strings -func SampleToRow(sample *stats.Sample, resTags []string, ignoredTags []string, row []string) []string { +func SampleToRow(sample *stats.Sample, resTags []string, ignoredTags []string, row []string, useISO8601 bool) []string { + + // func SampleToRow(sample *stats.Sample, resTags []string, ignoredTags []string, row []string) []string { row[0] = sample.Metric.Name - row[1] = fmt.Sprintf("%d", sample.Time.Unix()) + + if useISO8601 { + row[1] = fmt.Sprintf("%s", sample.Time.Format(time.RFC3339)) + } else { + row[1] = fmt.Sprintf("%d", sample.Time.Unix()) + } + row[2] = fmt.Sprintf("%f", sample.Value) sampleTags := sample.Tags.CloneTags() diff --git a/output/csv/output_test.go b/output/csv/output_test.go index 4c3e7ff88ba0..58c57ade3cce 100644 --- a/output/csv/output_test.go +++ b/output/csv/output_test.go @@ -70,6 +70,7 @@ func TestSampleToRow(t *testing.T) { sample *stats.Sample resTags []string ignoredTags []string + useISO8601 bool }{ { testname: "One res tag, one ignored tag, one extra tag", @@ -85,6 +86,7 @@ func TestSampleToRow(t *testing.T) { }, resTags: []string{"tag1"}, ignoredTags: []string{"tag2"}, + useISO8601: false, }, { testname: "Two res tags, three extra tags", @@ -102,9 +104,10 @@ func TestSampleToRow(t *testing.T) { }, resTags: []string{"tag1", "tag2"}, ignoredTags: []string{}, + useISO8601: false, }, { - testname: "Two res tags, two ignored", + testname: "Two res tags, two ignored, with ISO8601", sample: &stats.Sample{ Time: time.Unix(1562324644, 0), Metric: stats.New("my_metric", stats.Gauge), @@ -120,6 +123,7 @@ func TestSampleToRow(t *testing.T) { }, resTags: []string{"tag1", "tag3"}, ignoredTags: []string{"tag4", "tag6"}, + useISO8601: true, }, } @@ -155,7 +159,7 @@ func TestSampleToRow(t *testing.T) { { baseRow: []string{ "my_metric", - "1562324644", + "2019-07-05T21:04:04+10:00", "1.000000", "val1", "val3", @@ -170,10 +174,11 @@ func TestSampleToRow(t *testing.T) { for i := range testData { testname, sample := testData[i].testname, testData[i].sample resTags, ignoredTags := testData[i].resTags, testData[i].ignoredTags + useISO8601 := testData[i].useISO8601 expectedRow := expected[i] t.Run(testname, func(t *testing.T) { - row := SampleToRow(sample, resTags, ignoredTags, make([]string, 3+len(resTags)+1)) + row := SampleToRow(sample, resTags, ignoredTags, make([]string, 3+len(resTags)+1), useISO8601) for ind, cell := range expectedRow.baseRow { assert.Equal(t, cell, row[ind]) } @@ -218,6 +223,7 @@ func TestRun(t *testing.T) { samples []stats.SampleContainer fileName string fileReaderFunc func(fileName string, fs afero.Fs) string + useISO8601 bool outputContent string }{ { @@ -246,6 +252,7 @@ func TestRun(t *testing.T) { }, fileName: "test", fileReaderFunc: readUnCompressedFile, + useISO8601: false, outputContent: "metric_name,timestamp,metric_value,check,error,extra_tags\n" + "my_metric,1562324643,1.000000,val1,val3,url=val2\n" + "my_metric,1562324644,1.000000,val1,val3,tag4=val4&url=val2\n", }, { @@ -274,8 +281,38 @@ func TestRun(t *testing.T) { }, fileName: "test.gz", fileReaderFunc: readCompressedFile, + useISO8601: false, outputContent: "metric_name,timestamp,metric_value,check,error,extra_tags\n" + "my_metric,1562324643,1.000000,val1,val3,url=val2\n" + "my_metric,1562324644,1.000000,val1,val3,name=val4&url=val2\n", }, + { + samples: []stats.SampleContainer{ + stats.Sample{ + Time: time.Unix(1562324644, 0), + Metric: stats.New("my_metric", stats.Gauge), + Value: 1, + Tags: stats.NewSampleTags(map[string]string{ + "check": "val1", + "url": "val2", + "error": "val3", + }), + }, + stats.Sample{ + Time: time.Unix(1562324644, 0), + Metric: stats.New("my_metric", stats.Gauge), + Value: 1, + Tags: stats.NewSampleTags(map[string]string{ + "check": "val1", + "url": "val2", + "error": "val3", + "name": "val4", + }), + }, + }, + fileName: "test", + fileReaderFunc: readUnCompressedFile, + useISO8601: true, + outputContent: "metric_name,timestamp,metric_value,check,error,extra_tags\n" + "my_metric,2019-07-05T21:04:04+10:00,1.000000,val1,val3,url=val2\n" + "my_metric,2019-07-05T21:04:04+10:00,1.000000,val1,val3,name=val4&url=val2\n", + }, } for _, data := range testData { @@ -288,6 +325,9 @@ func TestRun(t *testing.T) { SystemTags: stats.NewSystemTagSet(stats.TagError | stats.TagCheck), }, }) + + output.useISO8601 = data.useISO8601 + require.NoError(t, err) require.NotNil(t, output)