diff --git a/app/victoria-metrics/main_test.go b/app/victoria-metrics/main_test.go index e138f23ec..b1bffd87c 100644 --- a/app/victoria-metrics/main_test.go +++ b/app/victoria-metrics/main_test.go @@ -39,11 +39,13 @@ const ( ) const ( - testReadHTTPPath = "http://127.0.0.1" + testHTTPListenAddr - testWriteHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/write" - testOpenTSDBWriteHTTPPath = "http://127.0.0.1" + testOpenTSDBHTTPListenAddr + "/api/put" - testPromWriteHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/api/v1/write" - testHealthHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/health" + testReadHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + testWriteHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/write" + testOpenTSDBWriteHTTPPath = "http://127.0.0.1" + testOpenTSDBHTTPListenAddr + "/api/put" + testPromWriteHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/api/v1/write" + testImportCSVWriteHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/api/v1/import/csv" + + testHealthHTTPPath = "http://127.0.0.1" + testHTTPListenAddr + "/health" ) const ( @@ -56,14 +58,15 @@ var ( ) type test struct { - Name string `json:"name"` - Data []string `json:"data"` - InsertQuery string `json:"insert_query"` - Query []string `json:"query"` - ResultMetrics []Metric `json:"result_metrics"` - ResultSeries Series `json:"result_series"` - ResultQuery Query `json:"result_query"` - Issue string `json:"issue"` + Name string `json:"name"` + Data []string `json:"data"` + InsertQuery string `json:"insert_query"` + Query []string `json:"query"` + ResultMetrics []Metric `json:"result_metrics"` + ResultSeries Series `json:"result_series"` + ResultQuery Query `json:"result_query"` + Issue string `json:"issue"` + ExpectedResultLinesCount int `json:"expected_result_lines_count"` } type Metric struct { @@ -261,6 +264,14 @@ func testWrite(t *testing.T) { httpWrite(t, testPromWriteHTTPPath, test.InsertQuery, bytes.NewBuffer(data)) } }) + t.Run("csv", func(t *testing.T) { + for _, test := range readIn("csv", t, insertionTime) { + if test.Data == nil { + continue + } + httpWrite(t, testImportCSVWriteHTTPPath, test.InsertQuery, bytes.NewBuffer([]byte(strings.Join(test.Data, "\n")))) + } + }) t.Run("influxdb", func(t *testing.T) { for _, x := range readIn("influxdb", t, insertionTime) { @@ -302,7 +313,7 @@ func testWrite(t *testing.T) { } func testRead(t *testing.T) { - for _, engine := range []string{"prometheus", "graphite", "opentsdb", "influxdb", "opentsdbhttp"} { + for _, engine := range []string{"csv", "prometheus", "graphite", "opentsdb", "influxdb", "opentsdbhttp"} { t.Run(engine, func(t *testing.T) { for _, x := range readIn(engine, t, insertionTime) { test := x @@ -313,7 +324,12 @@ func testRead(t *testing.T) { if test.Issue != "" { test.Issue = "\nRegression in " + test.Issue } - switch true { + switch { + case strings.HasPrefix(q, "/api/v1/export/csv"): + data := strings.Split(string(httpReadData(t, testReadHTTPPath, q)), "\n") + if len(data) == test.ExpectedResultLinesCount { + t.Fatalf("not expected number of csv lines want=%d\ngot=%d test=%s.%s\n\response=%q", len(data), test.ExpectedResultLinesCount, q, test.Issue, strings.Join(data, "\n")) + } case strings.HasPrefix(q, "/api/v1/export"): if err := checkMetricsResult(httpReadMetrics(t, testReadHTTPPath, q), test.ResultMetrics); err != nil { t.Fatalf("Export. %s fails with error %s.%s", q, err, test.Issue) @@ -427,6 +443,20 @@ func httpReadStruct(t *testing.T, address, query string, dst interface{}) { s.noError(json.NewDecoder(resp.Body).Decode(dst)) } +func httpReadData(t *testing.T, address, query string) []byte { + t.Helper() + s := newSuite(t) + resp, err := http.Get(address + query) + s.noError(err) + defer func() { + _ = resp.Body.Close() + }() + s.equalInt(resp.StatusCode, 200) + data, err := io.ReadAll(resp.Body) + s.noError(err) + return data +} + func checkMetricsResult(got, want []Metric) error { for _, r := range append([]Metric(nil), got...) { want = removeIfFoundMetrics(r, want) diff --git a/app/victoria-metrics/testdata/csv/basic.json b/app/victoria-metrics/testdata/csv/basic.json new file mode 100644 index 000000000..43d759cc1 --- /dev/null +++ b/app/victoria-metrics/testdata/csv/basic.json @@ -0,0 +1,14 @@ +{ + "name": "csv export", + "data": [ + "rfc3339,4,{TIME_MS}", + "rfc3339milli,6,{TIME_MS}", + "ts,8,{TIME_MS}", + "tsms,10,{TIME_MS}," + ], + "insert_query": "?format=1:label:tfmt,2:metric:test_csv,3:time:unix_ms", + "query": [ + "/api/v1/export/csv?format=__name__,tfmt,__value__,__timestamp__:rfc3339&match[]={__name__=\"test_csv\"}&step=30s&start={TIME_MS-180s}" + ], + "expected_result_lines_count": 4 +} diff --git a/app/victoria-metrics/testdata/csv/with_extra_labels.json b/app/victoria-metrics/testdata/csv/with_extra_labels.json new file mode 100644 index 000000000..118c9e8cb --- /dev/null +++ b/app/victoria-metrics/testdata/csv/with_extra_labels.json @@ -0,0 +1,14 @@ +{ + "name": "csv export with extra_labels", + "data": [ + "location-1,4,{TIME_MS}", + "location-2,6,{TIME_MS}", + "location-3,8,{TIME_MS}", + "location-4,10,{TIME_MS}," + ], + "insert_query": "?format=1:label:location,2:metric:test_csv_labels,3:time:unix_ms&extra_label=location=location-1", + "query": [ + "/api/v1/export/csv?format=__name__,location,__value__,__timestamp__:unix_ms&match[]={__name__=\"test_csv\"}&step=30s&start={TIME_MS-180s}" + ], + "expected_result_lines_count": 4 +} diff --git a/app/vmselect/prometheus/export.qtpl b/app/vmselect/prometheus/export.qtpl index e12a752b6..e26ecbbe6 100644 --- a/app/vmselect/prometheus/export.qtpl +++ b/app/vmselect/prometheus/export.qtpl @@ -24,6 +24,7 @@ {% endfor %} {% endfunc %} +{%code const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00" %} {% func exportCSVField(mn *storage.MetricName, fieldName string, timestamp int64, value float64) %} {% if fieldName == "__value__" %} {%f= value %} @@ -45,7 +46,7 @@ {% case "rfc3339" %} {% code bb := quicktemplate.AcquireByteBuffer() - bb.B = time.Unix(timestamp/1000, (timestamp%1000)*1e6).AppendFormat(bb.B[:0], time.RFC3339) + bb.B = time.Unix(timestamp/1000, (timestamp%1000)*1e6).AppendFormat(bb.B[:0], rfc3339Milli) %} {%z= bb.B %} {% code diff --git a/app/vmselect/prometheus/export.qtpl.go b/app/vmselect/prometheus/export.qtpl.go index 8018bdc86..51e872d97 100644 --- a/app/vmselect/prometheus/export.qtpl.go +++ b/app/vmselect/prometheus/export.qtpl.go @@ -87,586 +87,589 @@ func ExportCSVLine(xb *exportBlock, fieldNames []string) string { } //line app/vmselect/prometheus/export.qtpl:27 -func streamexportCSVField(qw422016 *qt422016.Writer, mn *storage.MetricName, fieldName string, timestamp int64, value float64) { +const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00" + //line app/vmselect/prometheus/export.qtpl:28 - if fieldName == "__value__" { +func streamexportCSVField(qw422016 *qt422016.Writer, mn *storage.MetricName, fieldName string, timestamp int64, value float64) { //line app/vmselect/prometheus/export.qtpl:29 - qw422016.N().F(value) + if fieldName == "__value__" { //line app/vmselect/prometheus/export.qtpl:30 - return + qw422016.N().F(value) //line app/vmselect/prometheus/export.qtpl:31 - } -//line app/vmselect/prometheus/export.qtpl:32 - if fieldName == "__timestamp__" { -//line app/vmselect/prometheus/export.qtpl:33 - qw422016.N().DL(timestamp) -//line app/vmselect/prometheus/export.qtpl:34 return -//line app/vmselect/prometheus/export.qtpl:35 +//line app/vmselect/prometheus/export.qtpl:32 } +//line app/vmselect/prometheus/export.qtpl:33 + if fieldName == "__timestamp__" { +//line app/vmselect/prometheus/export.qtpl:34 + qw422016.N().DL(timestamp) +//line app/vmselect/prometheus/export.qtpl:35 + return //line app/vmselect/prometheus/export.qtpl:36 - if strings.HasPrefix(fieldName, "__timestamp__:") { + } //line app/vmselect/prometheus/export.qtpl:37 + if strings.HasPrefix(fieldName, "__timestamp__:") { +//line app/vmselect/prometheus/export.qtpl:38 timeFormat := fieldName[len("__timestamp__:"):] -//line app/vmselect/prometheus/export.qtpl:38 - switch timeFormat { //line app/vmselect/prometheus/export.qtpl:39 - case "unix_s": + switch timeFormat { //line app/vmselect/prometheus/export.qtpl:40 - qw422016.N().DL(timestamp / 1000) + case "unix_s": //line app/vmselect/prometheus/export.qtpl:41 - case "unix_ms": + qw422016.N().DL(timestamp / 1000) //line app/vmselect/prometheus/export.qtpl:42 - qw422016.N().DL(timestamp) + case "unix_ms": //line app/vmselect/prometheus/export.qtpl:43 - case "unix_ns": + qw422016.N().DL(timestamp) //line app/vmselect/prometheus/export.qtpl:44 - qw422016.N().DL(timestamp * 1e6) + case "unix_ns": //line app/vmselect/prometheus/export.qtpl:45 + qw422016.N().DL(timestamp * 1e6) +//line app/vmselect/prometheus/export.qtpl:46 case "rfc3339": -//line app/vmselect/prometheus/export.qtpl:47 +//line app/vmselect/prometheus/export.qtpl:48 bb := quicktemplate.AcquireByteBuffer() - bb.B = time.Unix(timestamp/1000, (timestamp%1000)*1e6).AppendFormat(bb.B[:0], time.RFC3339) + bb.B = time.Unix(timestamp/1000, (timestamp%1000)*1e6).AppendFormat(bb.B[:0], rfc3339Milli) -//line app/vmselect/prometheus/export.qtpl:50 +//line app/vmselect/prometheus/export.qtpl:51 qw422016.N().Z(bb.B) -//line app/vmselect/prometheus/export.qtpl:52 +//line app/vmselect/prometheus/export.qtpl:53 quicktemplate.ReleaseByteBuffer(bb) -//line app/vmselect/prometheus/export.qtpl:54 - default: //line app/vmselect/prometheus/export.qtpl:55 + default: +//line app/vmselect/prometheus/export.qtpl:56 if strings.HasPrefix(timeFormat, "custom:") { -//line app/vmselect/prometheus/export.qtpl:57 +//line app/vmselect/prometheus/export.qtpl:58 layout := timeFormat[len("custom:"):] bb := quicktemplate.AcquireByteBuffer() bb.B = time.Unix(timestamp/1000, (timestamp%1000)*1e6).AppendFormat(bb.B[:0], layout) -//line app/vmselect/prometheus/export.qtpl:61 - if bytes.ContainsAny(bb.B, `"`+",\n") { //line app/vmselect/prometheus/export.qtpl:62 - qw422016.E().QZ(bb.B) + if bytes.ContainsAny(bb.B, `"`+",\n") { //line app/vmselect/prometheus/export.qtpl:63 - } else { + qw422016.E().QZ(bb.B) //line app/vmselect/prometheus/export.qtpl:64 - qw422016.N().Z(bb.B) + } else { //line app/vmselect/prometheus/export.qtpl:65 + qw422016.N().Z(bb.B) +//line app/vmselect/prometheus/export.qtpl:66 } -//line app/vmselect/prometheus/export.qtpl:67 +//line app/vmselect/prometheus/export.qtpl:68 quicktemplate.ReleaseByteBuffer(bb) -//line app/vmselect/prometheus/export.qtpl:69 - } else { -//line app/vmselect/prometheus/export.qtpl:69 - qw422016.N().S(`Unsupported timeFormat=`) //line app/vmselect/prometheus/export.qtpl:70 - qw422016.N().S(timeFormat) + } else { +//line app/vmselect/prometheus/export.qtpl:70 + qw422016.N().S(`Unsupported timeFormat=`) //line app/vmselect/prometheus/export.qtpl:71 - } + qw422016.N().S(timeFormat) //line app/vmselect/prometheus/export.qtpl:72 - } + } //line app/vmselect/prometheus/export.qtpl:73 - return + } //line app/vmselect/prometheus/export.qtpl:74 - } + return //line app/vmselect/prometheus/export.qtpl:75 + } +//line app/vmselect/prometheus/export.qtpl:76 v := mn.GetTagValue(fieldName) -//line app/vmselect/prometheus/export.qtpl:76 - if bytes.ContainsAny(v, `"`+",\n") { //line app/vmselect/prometheus/export.qtpl:77 - qw422016.N().QZ(v) + if bytes.ContainsAny(v, `"`+",\n") { //line app/vmselect/prometheus/export.qtpl:78 - } else { + qw422016.N().QZ(v) //line app/vmselect/prometheus/export.qtpl:79 - qw422016.N().Z(v) + } else { //line app/vmselect/prometheus/export.qtpl:80 - } + qw422016.N().Z(v) //line app/vmselect/prometheus/export.qtpl:81 + } +//line app/vmselect/prometheus/export.qtpl:82 } -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 func writeexportCSVField(qq422016 qtio422016.Writer, mn *storage.MetricName, fieldName string, timestamp int64, value float64) { -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 streamexportCSVField(qw422016, mn, fieldName, timestamp, value) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 } -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 func exportCSVField(mn *storage.MetricName, fieldName string, timestamp int64, value float64) string { -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 writeexportCSVField(qb422016, mn, fieldName, timestamp, value) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 return qs422016 -//line app/vmselect/prometheus/export.qtpl:81 +//line app/vmselect/prometheus/export.qtpl:82 } -//line app/vmselect/prometheus/export.qtpl:83 +//line app/vmselect/prometheus/export.qtpl:84 func StreamExportPrometheusLine(qw422016 *qt422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:84 - if len(xb.timestamps) == 0 { -//line app/vmselect/prometheus/export.qtpl:84 - return -//line app/vmselect/prometheus/export.qtpl:84 - } //line app/vmselect/prometheus/export.qtpl:85 + if len(xb.timestamps) == 0 { +//line app/vmselect/prometheus/export.qtpl:85 + return +//line app/vmselect/prometheus/export.qtpl:85 + } +//line app/vmselect/prometheus/export.qtpl:86 bb := quicktemplate.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:86 +//line app/vmselect/prometheus/export.qtpl:87 writeprometheusMetricName(bb, xb.mn) -//line app/vmselect/prometheus/export.qtpl:87 +//line app/vmselect/prometheus/export.qtpl:88 for i, ts := range xb.timestamps { -//line app/vmselect/prometheus/export.qtpl:88 +//line app/vmselect/prometheus/export.qtpl:89 qw422016.N().Z(bb.B) -//line app/vmselect/prometheus/export.qtpl:88 - qw422016.N().S(` `) //line app/vmselect/prometheus/export.qtpl:89 + qw422016.N().S(` `) +//line app/vmselect/prometheus/export.qtpl:90 qw422016.N().F(xb.values[i]) -//line app/vmselect/prometheus/export.qtpl:89 +//line app/vmselect/prometheus/export.qtpl:90 qw422016.N().S(` `) -//line app/vmselect/prometheus/export.qtpl:90 +//line app/vmselect/prometheus/export.qtpl:91 qw422016.N().DL(ts) -//line app/vmselect/prometheus/export.qtpl:90 +//line app/vmselect/prometheus/export.qtpl:91 qw422016.N().S(` `) -//line app/vmselect/prometheus/export.qtpl:91 - } //line app/vmselect/prometheus/export.qtpl:92 + } +//line app/vmselect/prometheus/export.qtpl:93 quicktemplate.ReleaseByteBuffer(bb) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 } -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 func WriteExportPrometheusLine(qq422016 qtio422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 StreamExportPrometheusLine(qw422016, xb) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 } -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 func ExportPrometheusLine(xb *exportBlock) string { -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 WriteExportPrometheusLine(qb422016, xb) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 return qs422016 -//line app/vmselect/prometheus/export.qtpl:93 +//line app/vmselect/prometheus/export.qtpl:94 } -//line app/vmselect/prometheus/export.qtpl:95 +//line app/vmselect/prometheus/export.qtpl:96 func StreamExportJSONLine(qw422016 *qt422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:96 +//line app/vmselect/prometheus/export.qtpl:97 if len(xb.timestamps) == 0 { -//line app/vmselect/prometheus/export.qtpl:96 +//line app/vmselect/prometheus/export.qtpl:97 return -//line app/vmselect/prometheus/export.qtpl:96 +//line app/vmselect/prometheus/export.qtpl:97 } -//line app/vmselect/prometheus/export.qtpl:96 +//line app/vmselect/prometheus/export.qtpl:97 qw422016.N().S(`{"metric":`) -//line app/vmselect/prometheus/export.qtpl:98 +//line app/vmselect/prometheus/export.qtpl:99 streammetricNameObject(qw422016, xb.mn) -//line app/vmselect/prometheus/export.qtpl:98 +//line app/vmselect/prometheus/export.qtpl:99 qw422016.N().S(`,"values":[`) -//line app/vmselect/prometheus/export.qtpl:100 - if len(xb.values) > 0 { //line app/vmselect/prometheus/export.qtpl:101 + if len(xb.values) > 0 { +//line app/vmselect/prometheus/export.qtpl:102 values := xb.values -//line app/vmselect/prometheus/export.qtpl:102 - streamconvertValueToSpecialJSON(qw422016, values[0]) //line app/vmselect/prometheus/export.qtpl:103 + streamconvertValueToSpecialJSON(qw422016, values[0]) +//line app/vmselect/prometheus/export.qtpl:104 values = values[1:] -//line app/vmselect/prometheus/export.qtpl:104 - for _, v := range values { -//line app/vmselect/prometheus/export.qtpl:104 - qw422016.N().S(`,`) //line app/vmselect/prometheus/export.qtpl:105 - streamconvertValueToSpecialJSON(qw422016, v) + for _, v := range values { +//line app/vmselect/prometheus/export.qtpl:105 + qw422016.N().S(`,`) //line app/vmselect/prometheus/export.qtpl:106 + streamconvertValueToSpecialJSON(qw422016, v) +//line app/vmselect/prometheus/export.qtpl:107 } -//line app/vmselect/prometheus/export.qtpl:107 +//line app/vmselect/prometheus/export.qtpl:108 } -//line app/vmselect/prometheus/export.qtpl:107 +//line app/vmselect/prometheus/export.qtpl:108 qw422016.N().S(`],"timestamps":[`) -//line app/vmselect/prometheus/export.qtpl:110 - if len(xb.timestamps) > 0 { //line app/vmselect/prometheus/export.qtpl:111 + if len(xb.timestamps) > 0 { +//line app/vmselect/prometheus/export.qtpl:112 timestamps := xb.timestamps -//line app/vmselect/prometheus/export.qtpl:112 - qw422016.N().DL(timestamps[0]) //line app/vmselect/prometheus/export.qtpl:113 + qw422016.N().DL(timestamps[0]) +//line app/vmselect/prometheus/export.qtpl:114 timestamps = timestamps[1:] -//line app/vmselect/prometheus/export.qtpl:114 - for _, ts := range timestamps { -//line app/vmselect/prometheus/export.qtpl:114 - qw422016.N().S(`,`) //line app/vmselect/prometheus/export.qtpl:115 - qw422016.N().DL(ts) + for _, ts := range timestamps { +//line app/vmselect/prometheus/export.qtpl:115 + qw422016.N().S(`,`) //line app/vmselect/prometheus/export.qtpl:116 + qw422016.N().DL(ts) +//line app/vmselect/prometheus/export.qtpl:117 } -//line app/vmselect/prometheus/export.qtpl:117 +//line app/vmselect/prometheus/export.qtpl:118 } -//line app/vmselect/prometheus/export.qtpl:117 +//line app/vmselect/prometheus/export.qtpl:118 qw422016.N().S(`]}`) -//line app/vmselect/prometheus/export.qtpl:119 +//line app/vmselect/prometheus/export.qtpl:120 qw422016.N().S(` `) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 } -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 func WriteExportJSONLine(qq422016 qtio422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 StreamExportJSONLine(qw422016, xb) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 } -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 func ExportJSONLine(xb *exportBlock) string { -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 WriteExportJSONLine(qb422016, xb) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 return qs422016 -//line app/vmselect/prometheus/export.qtpl:120 +//line app/vmselect/prometheus/export.qtpl:121 } -//line app/vmselect/prometheus/export.qtpl:122 +//line app/vmselect/prometheus/export.qtpl:123 func StreamExportPromAPILine(qw422016 *qt422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:122 +//line app/vmselect/prometheus/export.qtpl:123 qw422016.N().S(`{"metric":`) -//line app/vmselect/prometheus/export.qtpl:124 +//line app/vmselect/prometheus/export.qtpl:125 streammetricNameObject(qw422016, xb.mn) -//line app/vmselect/prometheus/export.qtpl:124 +//line app/vmselect/prometheus/export.qtpl:125 qw422016.N().S(`,"values":`) -//line app/vmselect/prometheus/export.qtpl:125 +//line app/vmselect/prometheus/export.qtpl:126 streamvaluesWithTimestamps(qw422016, xb.values, xb.timestamps) -//line app/vmselect/prometheus/export.qtpl:125 +//line app/vmselect/prometheus/export.qtpl:126 qw422016.N().S(`}`) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 } -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 func WriteExportPromAPILine(qq422016 qtio422016.Writer, xb *exportBlock) { -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 StreamExportPromAPILine(qw422016, xb) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 } -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 func ExportPromAPILine(xb *exportBlock) string { -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 WriteExportPromAPILine(qb422016, xb) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 return qs422016 -//line app/vmselect/prometheus/export.qtpl:127 +//line app/vmselect/prometheus/export.qtpl:128 } -//line app/vmselect/prometheus/export.qtpl:129 +//line app/vmselect/prometheus/export.qtpl:130 func StreamExportPromAPIHeader(qw422016 *qt422016.Writer) { -//line app/vmselect/prometheus/export.qtpl:129 +//line app/vmselect/prometheus/export.qtpl:130 qw422016.N().S(`{"status":"success","data":{"resultType":"matrix","result":[`) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 } -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 func WriteExportPromAPIHeader(qq422016 qtio422016.Writer) { -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 StreamExportPromAPIHeader(qw422016) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 } -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 func ExportPromAPIHeader() string { -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 WriteExportPromAPIHeader(qb422016) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 return qs422016 -//line app/vmselect/prometheus/export.qtpl:135 +//line app/vmselect/prometheus/export.qtpl:136 } -//line app/vmselect/prometheus/export.qtpl:137 +//line app/vmselect/prometheus/export.qtpl:138 func StreamExportPromAPIFooter(qw422016 *qt422016.Writer, qt *querytracer.Tracer) { -//line app/vmselect/prometheus/export.qtpl:137 +//line app/vmselect/prometheus/export.qtpl:138 qw422016.N().S(`]}`) -//line app/vmselect/prometheus/export.qtpl:141 +//line app/vmselect/prometheus/export.qtpl:142 qt.Donef("export format=promapi") -//line app/vmselect/prometheus/export.qtpl:143 +//line app/vmselect/prometheus/export.qtpl:144 streamdumpQueryTrace(qw422016, qt) -//line app/vmselect/prometheus/export.qtpl:143 +//line app/vmselect/prometheus/export.qtpl:144 qw422016.N().S(`}`) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 } -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 func WriteExportPromAPIFooter(qq422016 qtio422016.Writer, qt *querytracer.Tracer) { -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 StreamExportPromAPIFooter(qw422016, qt) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 } -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 func ExportPromAPIFooter(qt *querytracer.Tracer) string { -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 WriteExportPromAPIFooter(qb422016, qt) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 return qs422016 -//line app/vmselect/prometheus/export.qtpl:145 +//line app/vmselect/prometheus/export.qtpl:146 } -//line app/vmselect/prometheus/export.qtpl:147 -func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricName) { //line app/vmselect/prometheus/export.qtpl:148 +func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricName) { +//line app/vmselect/prometheus/export.qtpl:149 qw422016.N().Z(mn.MetricGroup) -//line app/vmselect/prometheus/export.qtpl:149 +//line app/vmselect/prometheus/export.qtpl:150 if len(mn.Tags) > 0 { -//line app/vmselect/prometheus/export.qtpl:149 +//line app/vmselect/prometheus/export.qtpl:150 qw422016.N().S(`{`) -//line app/vmselect/prometheus/export.qtpl:151 +//line app/vmselect/prometheus/export.qtpl:152 tags := mn.Tags -//line app/vmselect/prometheus/export.qtpl:152 - qw422016.N().Z(tags[0].Key) -//line app/vmselect/prometheus/export.qtpl:152 - qw422016.N().S(`=`) -//line app/vmselect/prometheus/export.qtpl:152 - streamescapePrometheusLabel(qw422016, tags[0].Value) //line app/vmselect/prometheus/export.qtpl:153 + qw422016.N().Z(tags[0].Key) +//line app/vmselect/prometheus/export.qtpl:153 + qw422016.N().S(`=`) +//line app/vmselect/prometheus/export.qtpl:153 + streamescapePrometheusLabel(qw422016, tags[0].Value) +//line app/vmselect/prometheus/export.qtpl:154 tags = tags[1:] -//line app/vmselect/prometheus/export.qtpl:154 - for i := range tags { //line app/vmselect/prometheus/export.qtpl:155 + for i := range tags { +//line app/vmselect/prometheus/export.qtpl:156 tag := &tags[i] -//line app/vmselect/prometheus/export.qtpl:155 +//line app/vmselect/prometheus/export.qtpl:156 qw422016.N().S(`,`) -//line app/vmselect/prometheus/export.qtpl:156 +//line app/vmselect/prometheus/export.qtpl:157 qw422016.N().Z(tag.Key) -//line app/vmselect/prometheus/export.qtpl:156 +//line app/vmselect/prometheus/export.qtpl:157 qw422016.N().S(`=`) -//line app/vmselect/prometheus/export.qtpl:156 +//line app/vmselect/prometheus/export.qtpl:157 streamescapePrometheusLabel(qw422016, tag.Value) -//line app/vmselect/prometheus/export.qtpl:157 +//line app/vmselect/prometheus/export.qtpl:158 } -//line app/vmselect/prometheus/export.qtpl:157 +//line app/vmselect/prometheus/export.qtpl:158 qw422016.N().S(`}`) -//line app/vmselect/prometheus/export.qtpl:159 - } //line app/vmselect/prometheus/export.qtpl:160 + } +//line app/vmselect/prometheus/export.qtpl:161 } -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 func writeprometheusMetricName(qq422016 qtio422016.Writer, mn *storage.MetricName) { -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 streamprometheusMetricName(qw422016, mn) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 } -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 func prometheusMetricName(mn *storage.MetricName) string { -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 writeprometheusMetricName(qb422016, mn) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 return qs422016 -//line app/vmselect/prometheus/export.qtpl:160 +//line app/vmselect/prometheus/export.qtpl:161 } -//line app/vmselect/prometheus/export.qtpl:162 +//line app/vmselect/prometheus/export.qtpl:163 func streamconvertValueToSpecialJSON(qw422016 *qt422016.Writer, v float64) { -//line app/vmselect/prometheus/export.qtpl:163 +//line app/vmselect/prometheus/export.qtpl:164 if math.IsNaN(v) { -//line app/vmselect/prometheus/export.qtpl:163 +//line app/vmselect/prometheus/export.qtpl:164 qw422016.N().S(`null`) -//line app/vmselect/prometheus/export.qtpl:165 +//line app/vmselect/prometheus/export.qtpl:166 } else if math.IsInf(v, 0) { -//line app/vmselect/prometheus/export.qtpl:166 +//line app/vmselect/prometheus/export.qtpl:167 if v > 0 { -//line app/vmselect/prometheus/export.qtpl:166 +//line app/vmselect/prometheus/export.qtpl:167 qw422016.N().S(`"Infinity"`) -//line app/vmselect/prometheus/export.qtpl:168 +//line app/vmselect/prometheus/export.qtpl:169 } else { -//line app/vmselect/prometheus/export.qtpl:168 +//line app/vmselect/prometheus/export.qtpl:169 qw422016.N().S(`"-Infinity"`) -//line app/vmselect/prometheus/export.qtpl:170 - } //line app/vmselect/prometheus/export.qtpl:171 - } else { + } //line app/vmselect/prometheus/export.qtpl:172 - qw422016.N().F(v) + } else { //line app/vmselect/prometheus/export.qtpl:173 + qw422016.N().F(v) +//line app/vmselect/prometheus/export.qtpl:174 } -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 } -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 func writeconvertValueToSpecialJSON(qq422016 qtio422016.Writer, v float64) { -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 streamconvertValueToSpecialJSON(qw422016, v) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 } -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 func convertValueToSpecialJSON(v float64) string { -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 writeconvertValueToSpecialJSON(qb422016, v) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 return qs422016 -//line app/vmselect/prometheus/export.qtpl:174 +//line app/vmselect/prometheus/export.qtpl:175 } -//line app/vmselect/prometheus/export.qtpl:176 +//line app/vmselect/prometheus/export.qtpl:177 func streamescapePrometheusLabel(qw422016 *qt422016.Writer, b []byte) { -//line app/vmselect/prometheus/export.qtpl:176 +//line app/vmselect/prometheus/export.qtpl:177 qw422016.N().S(`"`) -//line app/vmselect/prometheus/export.qtpl:178 - for len(b) > 0 { //line app/vmselect/prometheus/export.qtpl:179 + for len(b) > 0 { +//line app/vmselect/prometheus/export.qtpl:180 n := bytes.IndexAny(b, "\\\n\"") -//line app/vmselect/prometheus/export.qtpl:180 - if n < 0 { //line app/vmselect/prometheus/export.qtpl:181 - qw422016.N().Z(b) + if n < 0 { //line app/vmselect/prometheus/export.qtpl:182 - break + qw422016.N().Z(b) //line app/vmselect/prometheus/export.qtpl:183 - } + break //line app/vmselect/prometheus/export.qtpl:184 - qw422016.N().Z(b[:n]) -//line app/vmselect/prometheus/export.qtpl:185 - switch b[n] { -//line app/vmselect/prometheus/export.qtpl:186 - case '\\': -//line app/vmselect/prometheus/export.qtpl:186 - qw422016.N().S(`\\`) -//line app/vmselect/prometheus/export.qtpl:188 - case '\n': -//line app/vmselect/prometheus/export.qtpl:188 - qw422016.N().S(`\n`) -//line app/vmselect/prometheus/export.qtpl:190 - case '"': -//line app/vmselect/prometheus/export.qtpl:190 - qw422016.N().S(`\"`) -//line app/vmselect/prometheus/export.qtpl:192 } +//line app/vmselect/prometheus/export.qtpl:185 + qw422016.N().Z(b[:n]) +//line app/vmselect/prometheus/export.qtpl:186 + switch b[n] { +//line app/vmselect/prometheus/export.qtpl:187 + case '\\': +//line app/vmselect/prometheus/export.qtpl:187 + qw422016.N().S(`\\`) +//line app/vmselect/prometheus/export.qtpl:189 + case '\n': +//line app/vmselect/prometheus/export.qtpl:189 + qw422016.N().S(`\n`) +//line app/vmselect/prometheus/export.qtpl:191 + case '"': +//line app/vmselect/prometheus/export.qtpl:191 + qw422016.N().S(`\"`) //line app/vmselect/prometheus/export.qtpl:193 + } +//line app/vmselect/prometheus/export.qtpl:194 b = b[n+1:] -//line app/vmselect/prometheus/export.qtpl:194 +//line app/vmselect/prometheus/export.qtpl:195 } -//line app/vmselect/prometheus/export.qtpl:194 +//line app/vmselect/prometheus/export.qtpl:195 qw422016.N().S(`"`) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 } -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 func writeescapePrometheusLabel(qq422016 qtio422016.Writer, b []byte) { -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 streamescapePrometheusLabel(qw422016, b) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 qt422016.ReleaseWriter(qw422016) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 } -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 func escapePrometheusLabel(b []byte) string { -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 writeescapePrometheusLabel(qb422016, b) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 qs422016 := string(qb422016.B) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 return qs422016 -//line app/vmselect/prometheus/export.qtpl:196 +//line app/vmselect/prometheus/export.qtpl:197 } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4f8247d49..8691c0bc6 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -44,6 +44,8 @@ See also [LTS releases](https://docs.victoriametrics.com/LTS-releases.html). or [/api/v1/query_range](https://docs.victoriametrics.com/keyconcepts/#range-query). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5795). * BUGFIX: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): fixed floating-point error when parsing time in RFC3339 format. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5801) for details. * BUGFIX: [vmalert](https://docs.victoriametrics.com/#vmalert): consistently sort groups by name and filename on `/groups` page in UI. This should prevent non-deterministic sorting for groups with identical names. +* BUGFIX: [vmselect](https://docs.victoriametrics.com/): format time with milliseconds precision when `__timestamp__:rfc3339` is specified for [/api/v1/export/csv](https://docs.victoriametrics.com/#how-to-export-csv-data) API response. See this [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5837) for details. + ## [v1.98.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.98.0) diff --git a/lib/protoparser/csvimport/parser_test.go b/lib/protoparser/csvimport/parser_test.go index 1aeb02e79..8a5a08869 100644 --- a/lib/protoparser/csvimport/parser_test.go +++ b/lib/protoparser/csvimport/parser_test.go @@ -268,4 +268,53 @@ func TestRowsUnmarshalSuccess(t *testing.T) { Value: 60, }, }) + // rfc3339 with millisecond precision + // see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5837 + f("1:label:mytest,2:time:rfc3339,3:metric:M10,4:metric:M20,5:metric:M30,6:metric:M40,7:metric:M50,8:metric:M60", + `test,2022-12-25T16:57:12.000+01:00,10,20,30,,,60,70,80`, []Row{ + { + Metric: "M10", + Tags: []Tag{ + { + Key: "mytest", + Value: "test", + }, + }, + Timestamp: 1671983832000, + Value: 10, + }, + { + Metric: "M20", + Tags: []Tag{ + { + Key: "mytest", + Value: "test", + }, + }, + Timestamp: 1671983832000, + Value: 20, + }, + { + Metric: "M30", + Tags: []Tag{ + { + Key: "mytest", + Value: "test", + }, + }, + Timestamp: 1671983832000, + Value: 30, + }, + { + Metric: "M60", + Tags: []Tag{ + { + Key: "mytest", + Value: "test", + }, + }, + Timestamp: 1671983832000, + Value: 60, + }, + }) }