InfluxDB: Support cardinality queries with backend mode (#87264)

support cardinality queries
This commit is contained in:
ismail simsek 2024-05-03 12:50:00 +02:00 committed by GitHub
parent 253ab5efd3
commit 3317691615
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 540 additions and 65 deletions

View File

@ -330,18 +330,22 @@ func newFrameWithoutTimeField(row models.Row, query models.Query) *data.Frame {
var values []*string
for _, valuePair := range row.Values {
if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES")) {
if len(valuePair) >= 2 {
values = append(values, util.ParseString(valuePair[1]))
}
} else if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW DIAGNOSTICS")) {
// https://docs.influxdata.com/platform/monitoring/influxdata-platform/tools/show-diagnostics/
for _, vp := range valuePair {
values = append(values, util.ParseString(vp))
}
if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("CARDINALITY")) {
values = append(values, util.ParseString(valuePair[0]))
} else {
if len(valuePair) >= 1 {
values = append(values, util.ParseString(valuePair[0]))
if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES")) {
if len(valuePair) >= 2 {
values = append(values, util.ParseString(valuePair[1]))
}
} else if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW DIAGNOSTICS")) {
// https://docs.influxdata.com/platform/monitoring/influxdata-platform/tools/show-diagnostics/
for _, vp := range valuePair {
values = append(values, util.ParseString(vp))
}
} else {
if len(valuePair) >= 1 {
values = append(values, util.ParseString(valuePair[0]))
}
}
}
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"testing"
@ -34,9 +33,9 @@ func readJsonFile(filePath string) io.ReadCloser {
return io.NopCloser(strings.NewReader(string(bytes)))
}
func generateQuery(resFormat string, alias string) *models.Query {
func generateQuery(query, resFormat, alias string) *models.Query {
return &models.Query{
RawQuery: "Test raw query",
RawQuery: query,
UseRawQuery: true,
Alias: alias,
ResultFormat: resFormat,
@ -83,25 +82,43 @@ func TestReadInfluxAsTable(t *testing.T) {
func runScenario(tf string, resultFormat string) func(t *testing.T) {
return func(t *testing.T) {
f, err := os.Open(path.Join(testPath, filepath.Clean(tf+".json")))
require.NoError(t, err)
f := readJsonFile(tf)
query := generateQuery(resultFormat, "")
query := generateQuery("Test raw query", resultFormat, "")
rsp := ResponseParse(io.NopCloser(f), 200, query)
require.NoError(t, rsp.Error)
fname := tf + "." + resultFormat + ".golden"
experimental.CheckGoldenJSONResponse(t, testPath, fname, rsp, shouldUpdate)
runQuery(t, f, tf, resultFormat, query)
}
}
func runQuery(t *testing.T, f io.ReadCloser, tf string, rf string, query *models.Query) {
rsp := ResponseParse(f, 200, query)
if strings.Contains(tf, "error") {
require.Error(t, rsp.Error)
return
}
require.NoError(t, rsp.Error)
fname := tf + "." + rf + ".golden"
experimental.CheckGoldenJSONResponse(t, testPath, fname, rsp, shouldUpdate)
}
func TestParsingAsTimeSeriesWithoutTimeColumn(t *testing.T) {
t.Run("cardinality", func(t *testing.T) {
f := readJsonFile("cardinality")
query := generateQuery(`SHOW TAG VALUES CARDINALITY with key = "host"`, "time_series", "")
runQuery(t, f, "cardinality", "time_series", query)
})
}
func TestInfluxdbResponseParser(t *testing.T) {
t.Run("Influxdb response parser should handle invalid JSON", func(t *testing.T) {
result := ResponseParse(
io.NopCloser(strings.NewReader(`{ invalid }`)),
200,
generateQuery("time_series", ""),
generateQuery("Test raw query", "time_series", ""),
)
require.Nil(t, result.Frames)
@ -132,7 +149,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
)
t.Run("should parse aliases", func(t *testing.T) {
result := ResponseParse(readJsonFile("response"), 200, generateQuery("time_sereies", "alias $m $measurement"))
result := ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_sereies", "alias $m $measurement"))
name := "alias cpu.upc cpu.upc"
testFrame.Name = name
@ -141,7 +158,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
query := generateQuery("time_series", "alias $col")
query := generateQuery("Test raw query", "time_series", "alias $col")
query.Measurement = "10m"
result = ResponseParse(readJsonFile("response"), 200, query)
name = "alias mean"
@ -162,7 +179,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $tag_datacenter"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $tag_datacenter"))
name = "alias America"
testFrame.Name = name
newField = data.NewField("Value", labels, []*float64{
@ -174,7 +191,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $tag_datacenter/$tag_datacenter"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $tag_datacenter/$tag_datacenter"))
name = "alias America/America"
testFrame.Name = name
newField = data.NewField("Value", labels, []*float64{
@ -186,7 +203,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
query = generateQuery("time_series", "alias [[col]]")
query = generateQuery("Test raw query", "time_series", "alias [[col]]")
query.Measurement = "10m"
result = ResponseParse(readJsonFile("response"), 200, query)
name = "alias mean"
@ -196,7 +213,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $0 $1 $2 $3 $4"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $0 $1 $2 $3 $4"))
name = "alias cpu upc $2 $3 $4"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -204,7 +221,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $0, $1 - $2 - $3, $4: something"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $0, $1 - $2 - $3, $4: something"))
name = "alias cpu, upc - $2 - $3, $4: something"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -212,7 +229,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $1"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $1"))
name = "alias upc"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -220,7 +237,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias $5"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias $5"))
name = "alias $5"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -228,7 +245,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "series alias"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "series alias"))
name = "series alias"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -236,7 +253,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
query = generateQuery("time_series", "alias [[m]] [[measurement]]")
query = generateQuery("Test raw query", "time_series", "alias [[m]] [[measurement]]")
query.Measurement = "10m"
result = ResponseParse(readJsonFile("response"), 200, query)
name = "alias cpu.upc cpu.upc"
@ -246,7 +263,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias [[tag_datacenter]]"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias [[tag_datacenter]]"))
name = "alias America"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -254,7 +271,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias [[tag_dc.region.name]]"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias [[tag_dc.region.name]]"))
name = "alias Northeast"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -262,7 +279,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias [[tag_cluster-name]]"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias [[tag_cluster-name]]"))
name = "alias Cluster"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -270,7 +287,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias [[tag_/cluster/name/]]"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias [[tag_/cluster/name/]]"))
name = "alias Cluster/"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -278,7 +295,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias [[tag_@cluster@name@]]"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias [[tag_@cluster@name@]]"))
name = "alias Cluster@"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -288,7 +305,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
})
t.Run("shouldn't parse aliases", func(t *testing.T) {
result := ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias words with no brackets"))
result := ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias words with no brackets"))
name := "alias words with no brackets"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -296,7 +313,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias Test 1.5"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias Test 1.5"))
name = "alias Test 1.5"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -304,7 +321,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
t.Errorf("Result mismatch (-want +got):\n%s", diff)
}
result = ResponseParse(readJsonFile("response"), 200, generateQuery("time_series", "alias Test -1"))
result = ResponseParse(readJsonFile("response"), 200, generateQuery("Test raw query", "time_series", "alias Test -1"))
name = "alias Test -1"
testFrame.Name = name
testFrame.Fields[1].Config.DisplayNameFromDS = name
@ -315,13 +332,13 @@ func TestInfluxdbResponseParser(t *testing.T) {
})
t.Run("Influxdb response parser with errors", func(t *testing.T) {
result := ResponseParse(readJsonFile("error_response"), 200, generateQuery("time_series", ""))
result := ResponseParse(readJsonFile("error_response"), 200, generateQuery("Test raw query", "time_series", ""))
require.EqualError(t, result.Error, "query-timeout limit exceeded")
})
t.Run("Influxdb response parser with top-level error", func(t *testing.T) {
result := ResponseParse(readJsonFile("error_on_top_level_response"), 200, generateQuery("time_series", ""))
result := ResponseParse(readJsonFile("error_on_top_level_response"), 200, generateQuery("Test raw query", "time_series", ""))
require.Nil(t, result.Frames)
require.EqualError(t, result.Error, "error parsing query: found THING")
})
@ -356,7 +373,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
)
testFrame.Meta = &data.FrameMeta{PreferredVisualization: util.GraphVisType, ExecutedQueryString: "Test raw query"}
result := ResponseParse(readJsonFile("invalid_timestamp_format"), 200, generateQuery("time_series", ""))
result := ResponseParse(readJsonFile("invalid_timestamp_format"), 200, generateQuery("Test raw query", "time_series", ""))
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
t.Errorf("Result mismatch (-want +got):\n%s", diff)

View File

@ -2,6 +2,7 @@ package converter
import (
"fmt"
"strconv"
"strings"
"time"
@ -359,6 +360,18 @@ func handleTimeSeriesFormatWithTimeColumn(valueFields data.Fields, tags map[stri
func handleTimeSeriesFormatWithoutTimeColumn(valueFields data.Fields, columns []string, measurement string, query *models.Query) *data.Frame {
// Frame without time column
if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("CARDINALITY")) {
var stringArray []*string
for _, v := range valueFields {
if f, ok := v.At(0).(*float64); ok {
str := strconv.FormatFloat(*f, 'f', -1, 64)
stringArray = append(stringArray, util.ParseString(str))
} else {
stringArray = append(stringArray, util.ParseString(v.At(0)))
}
}
return data.NewFrame(measurement, data.NewField("Value", nil, stringArray))
}
if len(columns) >= 2 && strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES")) {
return data.NewFrame(measurement, valueFields[1])
}

View File

@ -1,13 +1,14 @@
package querydata
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/stretchr/testify/require"
@ -19,6 +20,24 @@ const (
testPath = "../testdata"
)
func readJsonFile(filePath string) io.ReadCloser {
bytes, err := os.ReadFile(filepath.Join(testPath, filepath.Clean(filePath)+".json"))
if err != nil {
panic(fmt.Sprintf("cannot read the file: %s", filePath))
}
return io.NopCloser(strings.NewReader(string(bytes)))
}
func generateQuery(query, resFormat, alias string) *models.Query {
return &models.Query{
RawQuery: query,
UseRawQuery: true,
Alias: alias,
ResultFormat: resFormat,
}
}
var testFiles = []string{
"all_values_are_null",
"influx_select_all_from_cpu",
@ -55,26 +74,34 @@ func TestReadInfluxAsTable(t *testing.T) {
func runScenario(tf string, resultFormat string) func(t *testing.T) {
return func(t *testing.T) {
f, err := os.Open(path.Join(testPath, filepath.Clean(tf+".json")))
require.NoError(t, err)
f := readJsonFile(tf)
var rsp *backend.DataResponse
query := generateQuery("Test raw query", resultFormat, "")
query := &models.Query{
RawQuery: "Test raw query",
UseRawQuery: true,
ResultFormat: resultFormat,
}
rsp = ResponseParse(f, 200, query)
if strings.Contains(tf, "error") {
require.Error(t, rsp.Error)
return
}
require.NoError(t, rsp.Error)
fname := tf + "." + resultFormat + ".golden"
experimental.CheckGoldenJSONResponse(t, testPath, fname, rsp, shouldUpdate)
runQuery(t, f, tf, resultFormat, query)
}
}
func runQuery(t *testing.T, f io.ReadCloser, tf string, rf string, query *models.Query) {
rsp := ResponseParse(f, 200, query)
if strings.Contains(tf, "error") {
require.Error(t, rsp.Error)
return
}
require.NoError(t, rsp.Error)
fname := tf + "." + rf + ".golden"
experimental.CheckGoldenJSONResponse(t, testPath, fname, rsp, shouldUpdate)
}
func TestParsingAsTimeSeriesWithoutTimeColumn(t *testing.T) {
t.Run("cardinality", func(t *testing.T) {
f, err := os.Open(path.Join(testPath, filepath.Clean("cardinality.json")))
require.NoError(t, err)
query := generateQuery(`SHOW TAG VALUES CARDINALITY with key = "host"`, "time_series", "")
runQuery(t, f, "cardinality", "time_series", query)
})
}

View File

@ -0,0 +1 @@
{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[1]]},{"name":"disk","columns":["count"],"values":[[1]]},{"name":"diskio","columns":["count"],"values":[[1]]},{"name":"kernel","columns":["count"],"values":[[1]]},{"name":"logs","columns":["count"],"values":[[1]]},{"name":"mem","columns":["count"],"values":[[1]]},{"name":"processes","columns":["count"],"values":[[1]]},{"name":"swap","columns":["count"],"values":[[1]]},{"name":"system","columns":["count"],"values":[[1]]}]}]}

View File

@ -0,0 +1,76 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "preferredVisualisationType": "table",
// "executedQueryString": "Test raw query"
// }
// Name: cpu
// Dimensions: 1 Fields by 9 Rows
// +------------------+
// | Name: count |
// | Labels: |
// | Type: []*float64 |
// +------------------+
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// | 1 |
// +------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"name": "cpu",
"meta": {
"typeVersion": [
0,
0
],
"preferredVisualisationType": "table",
"executedQueryString": "Test raw query"
},
"fields": [
{
"name": "count",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"config": {
"displayNameFromDS": "count"
}
}
]
},
"data": {
"values": [
[
1,
1,
1,
1,
1,
1,
1,
1,
1
]
]
}
}
]
}

View File

@ -0,0 +1,337 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "preferredVisualisationType": "graph",
// "executedQueryString": "SHOW TAG VALUES CARDINALITY with key = \"host\""
// }
// Name: cpu
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[1]
// Name: disk
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[2]
// Name: diskio
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[3]
// Name: kernel
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[4]
// Name: logs
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[5]
// Name: mem
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[6]
// Name: processes
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[7]
// Name: swap
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
//
// Frame[8]
// Name: system
// Dimensions: 1 Fields by 1 Rows
// +-----------------+
// | Name: Value |
// | Labels: |
// | Type: []*string |
// +-----------------+
// | 1 |
// +-----------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"name": "cpu",
"meta": {
"typeVersion": [
0,
0
],
"preferredVisualisationType": "graph",
"executedQueryString": "SHOW TAG VALUES CARDINALITY with key = \"host\""
},
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "disk",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "diskio",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "kernel",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "logs",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "mem",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "processes",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "swap",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
},
{
"schema": {
"name": "system",
"fields": [
{
"name": "Value",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"1"
]
]
}
}
]
}