DataFrame: move labels to field (#19926)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
|
import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
|
||||||
import { KeyValue, QueryResultMeta, Labels } from '../types/data';
|
import { KeyValue, QueryResultMeta } from '../types/data';
|
||||||
import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
|
import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
|
||||||
import isArray from 'lodash/isArray';
|
import isArray from 'lodash/isArray';
|
||||||
import isString from 'lodash/isString';
|
import isString from 'lodash/isString';
|
||||||
@@ -16,7 +16,6 @@ export const MISSING_VALUE: any = null;
|
|||||||
|
|
||||||
export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
|
export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
|
||||||
name?: string;
|
name?: string;
|
||||||
labels?: Labels;
|
|
||||||
refId?: string;
|
refId?: string;
|
||||||
meta?: QueryResultMeta;
|
meta?: QueryResultMeta;
|
||||||
|
|
||||||
@@ -36,13 +35,10 @@ export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
|
|||||||
|
|
||||||
// Copy values from
|
// Copy values from
|
||||||
if (source) {
|
if (source) {
|
||||||
const { name, labels, refId, meta, fields } = source;
|
const { name, refId, meta, fields } = source;
|
||||||
if (name) {
|
if (name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
if (labels) {
|
|
||||||
this.labels = labels;
|
|
||||||
}
|
|
||||||
if (refId) {
|
if (refId) {
|
||||||
this.refId = refId;
|
this.refId = refId;
|
||||||
}
|
}
|
||||||
@@ -116,6 +112,7 @@ export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
|
|||||||
type,
|
type,
|
||||||
config: f.config || {},
|
config: f.config || {},
|
||||||
values: this.creator(buffer),
|
values: this.creator(buffer),
|
||||||
|
labels: f.labels,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type === FieldType.other) {
|
if (type === FieldType.other) {
|
||||||
|
|||||||
@@ -57,6 +57,13 @@ function convertTableToDataFrame(table: TableData): DataFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
|
function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
|
||||||
|
const times: number[] = [];
|
||||||
|
const values: TimeSeriesValue[] = [];
|
||||||
|
for (const point of timeSeries.datapoints) {
|
||||||
|
values.push(point[0]);
|
||||||
|
times.push(point[1] as number);
|
||||||
|
}
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
name: timeSeries.target || 'Value',
|
name: timeSeries.target || 'Value',
|
||||||
@@ -64,30 +71,23 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
|
|||||||
config: {
|
config: {
|
||||||
unit: timeSeries.unit,
|
unit: timeSeries.unit,
|
||||||
},
|
},
|
||||||
values: new ArrayVector<TimeSeriesValue>(),
|
values: new ArrayVector<TimeSeriesValue>(values),
|
||||||
|
labels: timeSeries.tags,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Time',
|
name: 'Time',
|
||||||
type: FieldType.time,
|
type: FieldType.time,
|
||||||
config: {
|
config: {},
|
||||||
unit: 'dateTimeAsIso',
|
values: new ArrayVector<number>(times),
|
||||||
},
|
|
||||||
values: new ArrayVector<number>(),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const point of timeSeries.datapoints) {
|
|
||||||
fields[0].values.buffer.push(point[0]);
|
|
||||||
fields[1].values.buffer.push(point[1] as number);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: timeSeries.target,
|
name: timeSeries.target,
|
||||||
labels: timeSeries.tags,
|
|
||||||
refId: timeSeries.refId,
|
refId: timeSeries.refId,
|
||||||
meta: timeSeries.meta,
|
meta: timeSeries.meta,
|
||||||
fields,
|
fields,
|
||||||
length: timeSeries.datapoints.length,
|
length: values.length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ function convertJSONDocumentDataToDataFrame(timeSeries: TimeSeries): DataFrame {
|
|||||||
{
|
{
|
||||||
name: timeSeries.target,
|
name: timeSeries.target,
|
||||||
type: FieldType.other,
|
type: FieldType.other,
|
||||||
|
labels: timeSeries.tags,
|
||||||
config: {
|
config: {
|
||||||
unit: timeSeries.unit,
|
unit: timeSeries.unit,
|
||||||
filterable: (timeSeries as any).filterable,
|
filterable: (timeSeries as any).filterable,
|
||||||
@@ -146,7 +147,6 @@ function convertJSONDocumentDataToDataFrame(timeSeries: TimeSeries): DataFrame {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: timeSeries.target,
|
name: timeSeries.target,
|
||||||
labels: timeSeries.tags,
|
|
||||||
refId: timeSeries.target,
|
refId: timeSeries.target,
|
||||||
meta: { json: true },
|
meta: { json: true },
|
||||||
fields,
|
fields,
|
||||||
@@ -445,6 +445,7 @@ export function toDataFrameDTO(data: DataFrame): DataFrameDTO {
|
|||||||
type: f.type,
|
type: f.type,
|
||||||
config: f.config,
|
config: f.config,
|
||||||
values: f.values.toArray(),
|
values: f.values.toArray(),
|
||||||
|
labels: f.labels,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -453,6 +454,5 @@ export function toDataFrameDTO(data: DataFrame): DataFrameDTO {
|
|||||||
refId: data.refId,
|
refId: data.refId,
|
||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
labels: data.labels,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -5,6 +5,7 @@ Object {
|
|||||||
"fields": Array [
|
"fields": Array [
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Field",
|
"name": "Field",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -16,6 +17,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"title": "First",
|
"title": "First",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "first",
|
"name": "first",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -27,6 +29,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"title": "Min",
|
"title": "Min",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "min",
|
"name": "min",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -38,6 +41,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"title": "Max",
|
"title": "Max",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "max",
|
"name": "max",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -49,6 +53,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"title": "Delta",
|
"title": "Delta",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "delta",
|
"name": "delta",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -57,7 +62,6 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": Object {
|
"meta": Object {
|
||||||
"transformations": Array [
|
"transformations": Array [
|
||||||
"reduce",
|
"reduce",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export interface Field<T = any, V = Vector<T>> {
|
|||||||
type: FieldType;
|
type: FieldType;
|
||||||
config: FieldConfig;
|
config: FieldConfig;
|
||||||
values: V; // The raw field values
|
values: V; // The raw field values
|
||||||
|
labels?: Labels;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of reduced values
|
* Cache of reduced values
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +77,6 @@ export interface Field<T = any, V = Vector<T>> {
|
|||||||
export interface DataFrame extends QueryResultBase {
|
export interface DataFrame extends QueryResultBase {
|
||||||
name?: string;
|
name?: string;
|
||||||
fields: Field[]; // All fields of equal length
|
fields: Field[]; // All fields of equal length
|
||||||
labels?: Labels;
|
|
||||||
|
|
||||||
// The number of rows
|
// The number of rows
|
||||||
length: number;
|
length: number;
|
||||||
@@ -89,6 +90,7 @@ export interface FieldDTO<T = any> {
|
|||||||
type?: FieldType;
|
type?: FieldType;
|
||||||
config?: FieldConfig;
|
config?: FieldConfig;
|
||||||
values?: Vector<T> | T[]; // toJSON will always be T[], input could be either
|
values?: Vector<T> | T[]; // toJSON will always be T[], input could be either
|
||||||
|
labels?: Labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +98,5 @@ export interface FieldDTO<T = any> {
|
|||||||
*/
|
*/
|
||||||
export interface DataFrameDTO extends QueryResultBase {
|
export interface DataFrameDTO extends QueryResultBase {
|
||||||
name?: string;
|
name?: string;
|
||||||
labels?: Labels;
|
|
||||||
fields: Array<FieldDTO | Field>;
|
fields: Array<FieldDTO | Field>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Object {
|
|||||||
"fields": Array [
|
"fields": Array [
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Field 1",
|
"name": "Field 1",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -16,6 +17,7 @@ Object {
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Field 2",
|
"name": "Field 2",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -27,6 +29,7 @@ Object {
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Field 3",
|
"name": "Field 3",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -38,6 +41,7 @@ Object {
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Field 4",
|
"name": "Field 4",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -48,7 +52,6 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": undefined,
|
"meta": undefined,
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"refId": undefined,
|
"refId": undefined,
|
||||||
@@ -60,6 +63,7 @@ Object {
|
|||||||
"fields": Array [
|
"fields": Array [
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -69,6 +73,7 @@ Object {
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "b",
|
"name": "b",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -78,6 +83,7 @@ Object {
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "c",
|
"name": "c",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -86,7 +92,6 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": undefined,
|
"meta": undefined,
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"refId": undefined,
|
"refId": undefined,
|
||||||
@@ -100,6 +105,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"unit": "ms",
|
"unit": "ms",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -113,6 +119,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"unit": "lengthm",
|
"unit": "lengthm",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "b",
|
"name": "b",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -126,6 +133,7 @@ Object {
|
|||||||
"config": Object {
|
"config": Object {
|
||||||
"unit": "s",
|
"unit": "s",
|
||||||
},
|
},
|
||||||
|
"labels": undefined,
|
||||||
"name": "c",
|
"name": "c",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
@@ -136,7 +144,6 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": undefined,
|
"meta": undefined,
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"refId": undefined,
|
"refId": undefined,
|
||||||
|
|||||||
@@ -6,18 +6,16 @@ describe('getRowContexts', () => {
|
|||||||
it('then the result should be in correct format', async () => {
|
it('then the result should be in correct format', async () => {
|
||||||
const firstResult = new MutableDataFrame({
|
const firstResult = new MutableDataFrame({
|
||||||
refId: 'B',
|
refId: 'B',
|
||||||
labels: {},
|
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'ts', type: FieldType.time, values: [3, 2, 1] },
|
{ name: 'ts', type: FieldType.time, values: [3, 2, 1] },
|
||||||
{ name: 'line', type: FieldType.string, values: ['3', '2', '1'] },
|
{ name: 'line', type: FieldType.string, values: ['3', '2', '1'], labels: {} },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const secondResult = new MutableDataFrame({
|
const secondResult = new MutableDataFrame({
|
||||||
refId: 'B',
|
refId: 'B',
|
||||||
labels: {},
|
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'ts', type: FieldType.time, values: [6, 5, 4] },
|
{ name: 'ts', type: FieldType.time, values: [6, 5, 4] },
|
||||||
{ name: 'line', type: FieldType.string, values: ['6', '5', '4'] },
|
{ name: 'line', type: FieldType.string, values: ['6', '5', '4'], labels: {} },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const row: LogRowModel = {
|
const row: LogRowModel = {
|
||||||
|
|||||||
@@ -189,10 +189,6 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
it('given one series should return expected logs model', () => {
|
it('given one series should return expected logs model', () => {
|
||||||
const series: DataFrame[] = [
|
const series: DataFrame[] = [
|
||||||
new MutableDataFrame({
|
new MutableDataFrame({
|
||||||
labels: {
|
|
||||||
filename: '/var/log/grafana/grafana.log',
|
|
||||||
job: 'grafana',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'time',
|
name: 'time',
|
||||||
@@ -206,6 +202,10 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
|
't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
|
||||||
't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
|
't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
|
||||||
],
|
],
|
||||||
|
labels: {
|
||||||
|
filename: '/var/log/grafana/grafana.log',
|
||||||
|
job: 'grafana',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -244,7 +244,7 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
expect(logsModel.meta).toHaveLength(2);
|
expect(logsModel.meta).toHaveLength(2);
|
||||||
expect(logsModel.meta[0]).toMatchObject({
|
expect(logsModel.meta[0]).toMatchObject({
|
||||||
label: 'Common labels',
|
label: 'Common labels',
|
||||||
value: series[0].labels,
|
value: series[0].fields[1].labels,
|
||||||
kind: LogsMetaKind.LabelsMap,
|
kind: LogsMetaKind.LabelsMap,
|
||||||
});
|
});
|
||||||
expect(logsModel.meta[1]).toMatchObject({
|
expect(logsModel.meta[1]).toMatchObject({
|
||||||
@@ -291,11 +291,6 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
it('given multiple series with unique times should return expected logs model', () => {
|
it('given multiple series with unique times should return expected logs model', () => {
|
||||||
const series: DataFrame[] = [
|
const series: DataFrame[] = [
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
labels: {
|
|
||||||
foo: 'bar',
|
|
||||||
baz: '1',
|
|
||||||
level: 'dbug',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'ts',
|
name: 'ts',
|
||||||
@@ -306,16 +301,16 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
name: 'line',
|
name: 'line',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: ['WARN boooo'],
|
values: ['WARN boooo'],
|
||||||
|
labels: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: '1',
|
||||||
|
level: 'dbug',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
name: 'logs',
|
name: 'logs',
|
||||||
labels: {
|
|
||||||
foo: 'bar',
|
|
||||||
baz: '2',
|
|
||||||
level: 'err',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'time',
|
name: 'time',
|
||||||
@@ -326,6 +321,11 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
name: 'message',
|
name: 'message',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: ['INFO 1', 'INFO 2'],
|
values: ['INFO 1', 'INFO 2'],
|
||||||
|
labels: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: '2',
|
||||||
|
level: 'err',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -367,11 +367,6 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
it('given multiple series with equal times should return expected logs model', () => {
|
it('given multiple series with equal times should return expected logs model', () => {
|
||||||
const series: DataFrame[] = [
|
const series: DataFrame[] = [
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
labels: {
|
|
||||||
foo: 'bar',
|
|
||||||
baz: '1',
|
|
||||||
level: 'dbug',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'ts',
|
name: 'ts',
|
||||||
@@ -382,15 +377,15 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
name: 'line',
|
name: 'line',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: ['WARN boooo 1'],
|
values: ['WARN boooo 1'],
|
||||||
|
labels: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: '1',
|
||||||
|
level: 'dbug',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
labels: {
|
|
||||||
foo: 'bar',
|
|
||||||
baz: '2',
|
|
||||||
level: 'dbug',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'ts',
|
name: 'ts',
|
||||||
@@ -401,16 +396,16 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
name: 'line',
|
name: 'line',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: ['WARN boooo 2'],
|
values: ['WARN boooo 2'],
|
||||||
|
labels: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: '2',
|
||||||
|
level: 'dbug',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
name: 'logs',
|
name: 'logs',
|
||||||
labels: {
|
|
||||||
foo: 'bar',
|
|
||||||
baz: '2',
|
|
||||||
level: 'err',
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'time',
|
name: 'time',
|
||||||
@@ -421,6 +416,11 @@ describe('dataFrameToLogsModel', () => {
|
|||||||
name: 'message',
|
name: 'message',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: ['INFO 1', 'INFO 2'],
|
values: ['INFO 1', 'INFO 2'],
|
||||||
|
labels: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: '2',
|
||||||
|
level: 'err',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -244,6 +244,15 @@ function separateLogsAndMetrics(dataFrame: DataFrame[]) {
|
|||||||
|
|
||||||
const logTimeFormat = 'YYYY-MM-DD HH:mm:ss';
|
const logTimeFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
|
interface LogFields {
|
||||||
|
series: DataFrame;
|
||||||
|
|
||||||
|
timeField: FieldWithIndex;
|
||||||
|
stringField: FieldWithIndex;
|
||||||
|
logLevelField?: FieldWithIndex;
|
||||||
|
idField?: FieldWithIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts dataFrames into LogsModel. This involves merging them into one list, sorting them and computing metadata
|
* Converts dataFrames into LogsModel. This involves merging them into one list, sorting them and computing metadata
|
||||||
* like common labels.
|
* like common labels.
|
||||||
@@ -252,29 +261,43 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
|||||||
if (logSeries.length === 0) {
|
if (logSeries.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const commonLabels = findCommonLabelsFromDataFrames(logSeries);
|
const allLabels: Labels[] = [];
|
||||||
|
|
||||||
|
// Find the fields we care about and collect all labels
|
||||||
|
const allSeries: LogFields[] = logSeries.map(series => {
|
||||||
|
const fieldCache = new FieldCache(series);
|
||||||
|
|
||||||
|
// Assume the first string field in the dataFrame is the message. This was right so far but probably needs some
|
||||||
|
// more explicit checks.
|
||||||
|
const stringField = fieldCache.getFirstFieldOfType(FieldType.string);
|
||||||
|
if (stringField.labels) {
|
||||||
|
allLabels.push(stringField.labels);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
series,
|
||||||
|
timeField: fieldCache.getFirstFieldOfType(FieldType.time),
|
||||||
|
stringField,
|
||||||
|
logLevelField: fieldCache.getFieldByName('level'),
|
||||||
|
idField: getIdField(fieldCache),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const commonLabels = allLabels.length > 0 ? findCommonLabels(allLabels) : {};
|
||||||
|
|
||||||
const rows: LogRowModel[] = [];
|
const rows: LogRowModel[] = [];
|
||||||
let hasUniqueLabels = false;
|
let hasUniqueLabels = false;
|
||||||
|
|
||||||
for (let i = 0; i < logSeries.length; i++) {
|
for (const info of allSeries) {
|
||||||
const series = logSeries[i];
|
const { timeField, stringField, logLevelField, idField, series } = info;
|
||||||
const fieldCache = new FieldCache(series);
|
const labels = stringField.labels;
|
||||||
const uniqueLabels = findUniqueLabels(series.labels, commonLabels);
|
const uniqueLabels = findUniqueLabels(labels, commonLabels);
|
||||||
if (Object.keys(uniqueLabels).length > 0) {
|
if (Object.keys(uniqueLabels).length > 0) {
|
||||||
hasUniqueLabels = true;
|
hasUniqueLabels = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeField = fieldCache.getFirstFieldOfType(FieldType.time);
|
|
||||||
// Assume the first string field in the dataFrame is the message. This was right so far but probably needs some
|
|
||||||
// more explicit checks.
|
|
||||||
const stringField = fieldCache.getFirstFieldOfType(FieldType.string);
|
|
||||||
const logLevelField = fieldCache.getFieldByName('level');
|
|
||||||
const idField = getIdField(fieldCache);
|
|
||||||
|
|
||||||
let seriesLogLevel: LogLevel | undefined = undefined;
|
let seriesLogLevel: LogLevel | undefined = undefined;
|
||||||
if (series.labels && Object.keys(series.labels).indexOf('level') !== -1) {
|
if (labels && Object.keys(labels).indexOf('level') !== -1) {
|
||||||
seriesLogLevel = getLogLevelFromKey(series.labels['level']);
|
seriesLogLevel = getLogLevelFromKey(labels['level']);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let j = 0; j < series.length; j++) {
|
for (let j = 0; j < series.length; j++) {
|
||||||
@@ -311,7 +334,7 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
|||||||
searchWords,
|
searchWords,
|
||||||
entry: hasAnsi ? ansicolor.strip(message) : message,
|
entry: hasAnsi ? ansicolor.strip(message) : message,
|
||||||
raw: message,
|
raw: message,
|
||||||
labels: series.labels,
|
labels: stringField.labels,
|
||||||
timestamp: ts,
|
timestamp: ts,
|
||||||
uid: idField ? idField.values.get(j) : j.toString(),
|
uid: idField ? idField.values.get(j) : j.toString(),
|
||||||
});
|
});
|
||||||
@@ -345,21 +368,6 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function findCommonLabelsFromDataFrames(logSeries: DataFrame[]): Labels {
|
|
||||||
const allLabels: Labels[] = [];
|
|
||||||
for (let n = 0; n < logSeries.length; n++) {
|
|
||||||
const series = logSeries[n];
|
|
||||||
if (series.labels) {
|
|
||||||
allLabels.push(series.labels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allLabels.length > 0) {
|
|
||||||
return findCommonLabels(allLabels);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIdField(fieldCache: FieldCache): FieldWithIndex | undefined {
|
function getIdField(fieldCache: FieldCache): FieldWithIndex | undefined {
|
||||||
const idFieldNames = ['id'];
|
const idFieldNames = ['id'];
|
||||||
for (const fieldName of idFieldNames) {
|
for (const fieldName of idFieldNames) {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -191,7 +191,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -315,7 +315,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -426,7 +426,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -521,7 +521,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -469,7 +469,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -706,7 +706,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
@@ -943,7 +943,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
|||||||
],
|
],
|
||||||
"refresh": undefined,
|
"refresh": undefined,
|
||||||
"revision": undefined,
|
"revision": undefined,
|
||||||
"schemaVersion": 20,
|
"schemaVersion": 21,
|
||||||
"snapshot": undefined,
|
"snapshot": undefined,
|
||||||
"style": "dark",
|
"style": "dark",
|
||||||
"tags": Array [],
|
"tags": Array [],
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dashboard schema version should be set to latest', () => {
|
it('dashboard schema version should be set to latest', () => {
|
||||||
expect(model.schemaVersion).toBe(20);
|
expect(model.schemaVersion).toBe(21);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('graph thresholds should be migrated', () => {
|
it('graph thresholds should be migrated', () => {
|
||||||
@@ -506,6 +506,51 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when migrating labels from DataFrame to Field', () => {
|
||||||
|
let model: any;
|
||||||
|
beforeEach(() => {
|
||||||
|
model = new DashboardModel({
|
||||||
|
panels: [
|
||||||
|
{
|
||||||
|
//graph panel
|
||||||
|
options: {
|
||||||
|
dataLinks: [
|
||||||
|
{
|
||||||
|
url: 'http://mylink.com?series=${__series.labels}&${__series.labels.a}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// panel with field options
|
||||||
|
options: {
|
||||||
|
fieldOptions: {
|
||||||
|
defaults: {
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
url: 'http://mylink.com?series=${__series.labels}&${__series.labels.x}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('data links', () => {
|
||||||
|
it('should replace __series.label variable with __field.label', () => {
|
||||||
|
expect(model.panels[0].options.dataLinks[0].url).toBe(
|
||||||
|
'http://mylink.com?series=${__field.labels}&${__field.labels.a}'
|
||||||
|
);
|
||||||
|
expect(model.panels[1].options.fieldOptions.defaults.links[0].url).toBe(
|
||||||
|
'http://mylink.com?series=${__field.labels}&${__field.labels.x}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function createRow(options: any, panelDescriptions: any[]) {
|
function createRow(options: any, panelDescriptions: any[]) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class DashboardMigrator {
|
|||||||
let i, j, k, n;
|
let i, j, k, n;
|
||||||
const oldVersion = this.dashboard.schemaVersion;
|
const oldVersion = this.dashboard.schemaVersion;
|
||||||
const panelUpgrades = [];
|
const panelUpgrades = [];
|
||||||
this.dashboard.schemaVersion = 20;
|
this.dashboard.schemaVersion = 21;
|
||||||
|
|
||||||
if (oldVersion === this.dashboard.schemaVersion) {
|
if (oldVersion === this.dashboard.schemaVersion) {
|
||||||
return;
|
return;
|
||||||
@@ -463,6 +463,28 @@ export class DashboardMigrator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 21) {
|
||||||
|
const updateLinks = (link: DataLink) => {
|
||||||
|
return {
|
||||||
|
...link,
|
||||||
|
url: link.url.replace(/__series.labels/g, '__field.labels'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
panelUpgrades.push((panel: any) => {
|
||||||
|
// For graph panel
|
||||||
|
if (panel.options && panel.options.dataLinks && _.isArray(panel.options.dataLinks)) {
|
||||||
|
panel.options.dataLinks = panel.options.dataLinks.map(updateLinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For panel with fieldOptions
|
||||||
|
if (panel.options && panel.options.fieldOptions && panel.options.fieldOptions.defaults) {
|
||||||
|
if (panel.options.fieldOptions.defaults.links && _.isArray(panel.options.fieldOptions.defaults.links)) {
|
||||||
|
panel.options.fieldOptions.defaults.links = panel.options.fieldOptions.defaults.links.map(updateLinks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (panelUpgrades.length === 0) {
|
if (panelUpgrades.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Array [
|
|||||||
"fields": Array [
|
"fields": Array [
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Time",
|
"name": "Time",
|
||||||
"type": "time",
|
"type": "time",
|
||||||
"values": Int32Array [
|
"values": Int32Array [
|
||||||
@@ -39,6 +40,7 @@ Array [
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "",
|
"name": "",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Float64Array [
|
"values": Float64Array [
|
||||||
@@ -58,7 +60,6 @@ Array [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": undefined,
|
"meta": undefined,
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"refId": undefined,
|
"refId": undefined,
|
||||||
@@ -67,6 +68,7 @@ Array [
|
|||||||
"fields": Array [
|
"fields": Array [
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "Time",
|
"name": "Time",
|
||||||
"type": "time",
|
"type": "time",
|
||||||
"values": Int32Array [
|
"values": Int32Array [
|
||||||
@@ -100,6 +102,7 @@ Array [
|
|||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"config": Object {},
|
"config": Object {},
|
||||||
|
"labels": undefined,
|
||||||
"name": "GB-series",
|
"name": "GB-series",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"values": Float64Array [
|
"values": Float64Array [
|
||||||
@@ -119,7 +122,6 @@ Array [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"labels": undefined,
|
|
||||||
"meta": undefined,
|
"meta": undefined,
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"refId": undefined,
|
"refId": undefined,
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ describe('GEL Utils', () => {
|
|||||||
test('should parse output with dataframe', () => {
|
test('should parse output with dataframe', () => {
|
||||||
const frames = gelResponseToDataFrames(resp);
|
const frames = gelResponseToDataFrames(resp);
|
||||||
for (const frame of frames) {
|
for (const frame of frames) {
|
||||||
console.log('Frame', frame.refId + ' // ' + frame.labels);
|
console.log('Frame', frame.refId);
|
||||||
for (const field of frame.fields) {
|
for (const field of frame.fields) {
|
||||||
console.log(' > ', field.name, field.values.toArray());
|
console.log(' > ', field.name, field.labels);
|
||||||
|
console.log(' (values)= ', field.values.toArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ import { getLinkSrv } from './link_srv';
|
|||||||
|
|
||||||
interface SeriesVars {
|
interface SeriesVars {
|
||||||
name?: string;
|
name?: string;
|
||||||
labels?: Labels;
|
|
||||||
refId?: string;
|
refId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FieldVars {
|
interface FieldVars {
|
||||||
name: string;
|
name: string;
|
||||||
|
labels?: Labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ValueVars {
|
interface ValueVars {
|
||||||
@@ -54,7 +54,6 @@ export const getFieldLinksSupplier = (value: FieldDisplay): LinkModelSupplier<Fi
|
|||||||
scopedVars['__series'] = {
|
scopedVars['__series'] = {
|
||||||
value: {
|
value: {
|
||||||
name: dataFrame.name,
|
name: dataFrame.name,
|
||||||
labels: dataFrame.labels,
|
|
||||||
refId: dataFrame.refId,
|
refId: dataFrame.refId,
|
||||||
},
|
},
|
||||||
text: 'Series',
|
text: 'Series',
|
||||||
@@ -66,6 +65,7 @@ export const getFieldLinksSupplier = (value: FieldDisplay): LinkModelSupplier<Fi
|
|||||||
scopedVars['__field'] = {
|
scopedVars['__field'] = {
|
||||||
value: {
|
value: {
|
||||||
name: field.name,
|
name: field.name,
|
||||||
|
labels: field.labels,
|
||||||
},
|
},
|
||||||
text: 'Field',
|
text: 'Field',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ const timeRangeVars = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const fieldVars = [
|
const seriesVars = [
|
||||||
{
|
{
|
||||||
value: `${DataLinkBuiltInVars.fieldName}`,
|
value: `${DataLinkBuiltInVars.seriesName}`,
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
documentation: 'Field name of the clicked datapoint (in ms epoch)',
|
documentation: 'Name of the series',
|
||||||
origin: VariableOrigin.Field,
|
origin: VariableOrigin.Series,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -76,41 +76,51 @@ export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
|
|||||||
...timeRangeVars,
|
...timeRangeVars,
|
||||||
];
|
];
|
||||||
|
|
||||||
const getSeriesVars = (dataFrames: DataFrame[]) => {
|
const getFieldVars = (dataFrames: DataFrame[]) => {
|
||||||
const labels = _.chain(dataFrames.map(df => Object.keys(df.labels || {})))
|
const all = [];
|
||||||
|
for (const df of dataFrames) {
|
||||||
|
for (const f of df.fields) {
|
||||||
|
if (f.labels) {
|
||||||
|
for (const k of Object.keys(f.labels)) {
|
||||||
|
all.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const labels = _.chain(all)
|
||||||
.flatten()
|
.flatten()
|
||||||
.uniq()
|
.uniq()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
value: `${DataLinkBuiltInVars.seriesName}`,
|
value: `${DataLinkBuiltInVars.fieldName}`,
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
documentation: 'Name of the series',
|
documentation: 'Field name of the clicked datapoint (in ms epoch)',
|
||||||
origin: VariableOrigin.Series,
|
origin: VariableOrigin.Field,
|
||||||
},
|
},
|
||||||
...labels.map(label => ({
|
...labels.map(label => ({
|
||||||
value: `__series.labels${buildLabelPath(label)}`,
|
value: `__field.labels${buildLabelPath(label)}`,
|
||||||
label: `labels.${label}`,
|
label: `labels.${label}`,
|
||||||
documentation: `${label} label value`,
|
documentation: `${label} label value`,
|
||||||
origin: VariableOrigin.Series,
|
origin: VariableOrigin.Field,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
export const getDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
|
export const getDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
|
||||||
const seriesVars = getSeriesVars(dataFrames);
|
const fieldVars = getFieldVars(dataFrames);
|
||||||
const valueTimeVar = {
|
const valueTimeVar = {
|
||||||
value: `${DataLinkBuiltInVars.valueTime}`,
|
value: `${DataLinkBuiltInVars.valueTime}`,
|
||||||
label: 'Time',
|
label: 'Time',
|
||||||
documentation: 'Time value of the clicked datapoint (in ms epoch)',
|
documentation: 'Time value of the clicked datapoint (in ms epoch)',
|
||||||
origin: VariableOrigin.Value,
|
origin: VariableOrigin.Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [...seriesVars, ...fieldVars, ...valueVars, valueTimeVar, ...getPanelLinksVariableSuggestions()];
|
return [...seriesVars, ...fieldVars, ...valueVars, valueTimeVar, ...getPanelLinksVariableSuggestions()];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
|
export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
|
||||||
const seriesVars = getSeriesVars(dataFrames);
|
const fieldVars = getFieldVars(dataFrames);
|
||||||
const valueCalcVar = {
|
const valueCalcVar = {
|
||||||
value: `${DataLinkBuiltInVars.valueCalc}`,
|
value: `${DataLinkBuiltInVars.valueCalc}`,
|
||||||
label: 'Calculation name',
|
label: 'Calculation name',
|
||||||
|
|||||||
@@ -530,7 +530,6 @@ export class ElasticResponse {
|
|||||||
|
|
||||||
for (let y = 0; y < tmpSeriesList.length; y++) {
|
for (let y = 0; y < tmpSeriesList.length; y++) {
|
||||||
const series = toDataFrame(tmpSeriesList[y]);
|
const series = toDataFrame(tmpSeriesList[y]);
|
||||||
series.labels = {};
|
|
||||||
dataFrame.push(series);
|
dataFrame.push(series);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,7 +410,12 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
|||||||
const annotations: AnnotationEvent[] = [];
|
const annotations: AnnotationEvent[] = [];
|
||||||
|
|
||||||
for (const frame of data) {
|
for (const frame of data) {
|
||||||
const tags = Object.values(frame.labels) as string[];
|
const tags: string[] = [];
|
||||||
|
for (const field of frame.fields) {
|
||||||
|
if (field.labels) {
|
||||||
|
tags.push.apply(tags, Object.values(field.labels));
|
||||||
|
}
|
||||||
|
}
|
||||||
const view = new DataFrameView<{ ts: string; line: string }>(frame);
|
const view = new DataFrameView<{ ts: string; line: string }>(frame);
|
||||||
|
|
||||||
view.forEachRow(row => {
|
view.forEachRow(row => {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe('Live Stream Tests', () => {
|
|||||||
const tests = [
|
const tests = [
|
||||||
(val: DataFrame[]) => {
|
(val: DataFrame[]) => {
|
||||||
expect(val[0].length).toEqual(7);
|
expect(val[0].length).toEqual(7);
|
||||||
expect(val[0].labels).toEqual(labels);
|
expect(val[0].fields[1].labels).toEqual(labels);
|
||||||
},
|
},
|
||||||
(val: DataFrame[]) => {
|
(val: DataFrame[]) => {
|
||||||
expect(val[0].length).toEqual(8);
|
expect(val[0].length).toEqual(8);
|
||||||
|
|||||||
@@ -27,10 +27,9 @@ export class LiveStreams {
|
|||||||
let stream = this.streams[target.url];
|
let stream = this.streams[target.url];
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
const data = new CircularDataFrame({ capacity: target.size });
|
const data = new CircularDataFrame({ capacity: target.size });
|
||||||
data.labels = parseLabels(target.query);
|
|
||||||
data.addField({ name: 'ts', type: FieldType.time, config: { title: 'Time' } });
|
data.addField({ name: 'ts', type: FieldType.time, config: { title: 'Time' } });
|
||||||
data.addField({ name: 'line', type: FieldType.string });
|
data.addField({ name: 'line', type: FieldType.string }).labels = parseLabels(target.query);
|
||||||
data.addField({ name: 'labels', type: FieldType.other });
|
data.addField({ name: 'labels', type: FieldType.other }); // The labels for each line
|
||||||
data.addField({ name: 'id', type: FieldType.string });
|
data.addField({ name: 'id', type: FieldType.string });
|
||||||
|
|
||||||
stream = webSocket(target.url).pipe(
|
stream = webSocket(target.url).pipe(
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe('logStreamToDataFrame', () => {
|
|||||||
const data = streams.map(stream => logStreamToDataFrame(stream));
|
const data = streams.map(stream => logStreamToDataFrame(stream));
|
||||||
|
|
||||||
expect(data.length).toBe(2);
|
expect(data.length).toBe(2);
|
||||||
expect(data[0].labels['foo']).toEqual('bar');
|
expect(data[0].fields[1].labels['foo']).toEqual('bar');
|
||||||
expect(data[0].fields[0].values.get(0)).toEqual(streams[0].entries[0].ts);
|
expect(data[0].fields[0].values.get(0)).toEqual(streams[0].entries[0].ts);
|
||||||
expect(data[0].fields[1].values.get(0)).toEqual(streams[0].entries[0].line);
|
expect(data[0].fields[1].values.get(0)).toEqual(streams[0].entries[0].line);
|
||||||
expect(data[0].fields[2].values.get(0)).toEqual('1970-01-01T00:00:00Z_{foo="bar"}');
|
expect(data[0].fields[2].values.get(0)).toEqual('1970-01-01T00:00:00Z_{foo="bar"}');
|
||||||
|
|||||||
@@ -35,10 +35,9 @@ export function logStreamToDataFrame(stream: LokiLogsStream, reverse?: boolean,
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
refId,
|
refId,
|
||||||
labels,
|
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'ts', type: FieldType.time, config: { title: 'Time' }, values: times }, // Time
|
{ name: 'ts', type: FieldType.time, config: { title: 'Time' }, values: times }, // Time
|
||||||
{ name: 'line', type: FieldType.string, config: {}, values: lines }, // Line
|
{ name: 'line', type: FieldType.string, config: {}, values: lines, labels }, // Line
|
||||||
{ name: 'id', type: FieldType.string, config: {}, values: uids },
|
{ name: 'id', type: FieldType.string, config: {}, values: uids },
|
||||||
],
|
],
|
||||||
length: times.length,
|
length: times.length,
|
||||||
@@ -57,18 +56,29 @@ export function appendResponseToBufferedData(response: LokiResponse, data: Mutab
|
|||||||
|
|
||||||
const streams: LokiLogsStream[] = response.streams;
|
const streams: LokiLogsStream[] = response.streams;
|
||||||
if (streams && streams.length) {
|
if (streams && streams.length) {
|
||||||
|
const { values } = data;
|
||||||
|
let baseLabels: Labels = {};
|
||||||
|
for (const f of data.fields) {
|
||||||
|
if (f.type === FieldType.string) {
|
||||||
|
if (f.labels) {
|
||||||
|
baseLabels = f.labels;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const stream of streams) {
|
for (const stream of streams) {
|
||||||
// Find unique labels
|
// Find unique labels
|
||||||
const labels = parseLabels(stream.labels);
|
const labels = parseLabels(stream.labels);
|
||||||
const unique = findUniqueLabels(labels, data.labels || {});
|
const unique = findUniqueLabels(labels, baseLabels);
|
||||||
|
|
||||||
// Add each line
|
// Add each line
|
||||||
for (const entry of stream.entries) {
|
for (const entry of stream.entries) {
|
||||||
const ts = entry.ts || entry.timestamp;
|
const ts = entry.ts || entry.timestamp;
|
||||||
data.values.ts.add(ts);
|
values.ts.add(ts);
|
||||||
data.values.line.add(entry.line);
|
values.line.add(entry.line);
|
||||||
data.values.labels.add(unique);
|
values.labels.add(unique);
|
||||||
data.values.id.add(`${ts}_${stream.labels}`);
|
values.id.add(`${ts}_${stream.labels}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user