2021-08-19 06:38:31 +02:00
import { lastValueFrom , Observable , of } from 'rxjs' ;
2022-04-22 14:33:13 +01:00
2021-08-05 15:13:44 +02:00
import {
DataFrame ,
dataFrameToJSON ,
DataSourceInstanceSettings ,
2022-10-21 20:08:10 +05:30
dateTime ,
2021-08-05 15:13:44 +02:00
FieldType ,
2021-08-17 15:48:29 +02:00
getDefaultTimeRange ,
LoadingState ,
2023-05-01 19:41:40 -07:00
createDataFrame ,
2021-08-05 15:13:44 +02:00
PluginType ,
2023-05-15 14:27:27 +01:00
CoreApp ,
2021-08-05 15:13:44 +02:00
} from '@grafana/data' ;
2024-01-16 11:36:40 +01:00
import {
BackendDataSourceResponse ,
FetchResponse ,
setBackendSrv ,
setDataSourceSrv ,
TemplateSrv ,
} from '@grafana/runtime' ;
2023-03-22 08:37:44 +00:00
import { BarGaugeDisplayMode , TableCellDisplayMode } from '@grafana/schema' ;
2022-04-22 14:33:13 +01:00
2023-08-30 13:45:39 +02:00
import { TempoVariableQueryType } from './VariableQueryEditor' ;
2024-01-16 11:36:40 +01:00
import { createFetchResponse } from './_importedDependencies/test/helpers/createFetchResponse' ;
2023-08-28 15:02:12 +01:00
import { TraceqlSearchScope } from './dataquery.gen' ;
2022-06-09 17:56:15 +01:00
import {
DEFAULT_LIMIT ,
TempoDatasource ,
buildExpr ,
buildLinkExpr ,
getRateAlignedValues ,
2023-03-22 08:37:44 +00:00
makeServiceGraphViewRequest ,
2022-06-09 17:56:15 +01:00
makeTempoLink ,
2022-07-25 10:03:57 +01:00
getFieldConfig ,
2023-05-11 17:21:26 +03:00
getEscapedSpanNames ,
2022-06-09 17:56:15 +01:00
} from './datasource' ;
2021-08-05 15:13:44 +02:00
import mockJson from './mockJsonResponse.json' ;
2022-06-07 06:37:19 -06:00
import mockServiceGraph from './mockServiceGraph.json' ;
2023-08-30 13:45:39 +02:00
import { createMetadataRequest , createTempoDatasource } from './mocks' ;
2024-01-16 11:36:40 +01:00
import { initTemplateSrv } from './test_utils' ;
2022-09-05 15:46:10 +01:00
import { TempoJsonData , TempoQuery } from './types' ;
2021-03-05 14:28:17 +01:00
2022-09-02 11:17:36 +01:00
let mockObservable : ( ) = > Observable < any > ;
2022-06-01 10:28:45 +01:00
jest . mock ( '@grafana/runtime' , ( ) = > {
return {
. . . jest . requireActual ( '@grafana/runtime' ) ,
2022-09-02 11:17:36 +01:00
getBackendSrv : ( ) = > ( {
fetch : mockObservable ,
_request : mockObservable ,
} ) ,
2022-06-01 10:28:45 +01:00
} ;
} ) ;
2021-03-05 14:28:17 +01:00
describe ( 'Tempo data source' , ( ) = > {
2022-02-17 08:34:16 -07:00
// Mock the console error so that running the test suite doesnt throw the error
const origError = console . error ;
const consoleErrorMock = jest . fn ( ) ;
afterEach ( ( ) = > ( console . error = origError ) ) ;
beforeEach ( ( ) = > ( console . error = consoleErrorMock ) ) ;
2022-01-05 19:34:09 +01:00
it ( 'returns empty response when traceId is empty' , async ( ) = > {
2023-01-02 14:23:50 +00:00
const templateSrv : TemplateSrv = { replace : jest.fn ( ) } as unknown as TemplateSrv ;
const ds = new TempoDatasource ( defaultSettings , templateSrv ) ;
2022-01-05 19:34:09 +01:00
const response = await lastValueFrom (
2022-12-13 13:27:45 +00:00
ds . query ( { targets : [ { refId : 'refid1' , queryType : 'traceql' , query : '' } as Partial < TempoQuery > ] } as any ) ,
2022-01-05 19:34:09 +01:00
{ defaultValue : 'empty' }
) ;
expect ( response ) . toBe ( 'empty' ) ;
} ) ;
2022-05-03 17:42:36 +01:00
describe ( 'Variables should be interpolated correctly' , ( ) = > {
function getQuery ( ) : TempoQuery {
return {
refId : 'x' ,
2022-12-13 13:27:45 +00:00
queryType : 'traceql' ,
2022-05-03 17:42:36 +01:00
linkedQuery : {
refId : 'linked' ,
expr : '{instance="$interpolationVar"}' ,
} ,
2023-06-19 13:28:56 +01:00
query : '$interpolationVarWithPipe' ,
spanName : '$interpolationVar' ,
serviceName : '$interpolationVar' ,
2022-05-03 17:42:36 +01:00
search : '$interpolationVar' ,
minDuration : '$interpolationVar' ,
maxDuration : '$interpolationVar' ,
2023-12-15 09:03:00 +00:00
serviceMapQuery : '$interpolationVar' ,
2023-03-06 16:31:08 +00:00
filters : [ ] ,
2022-05-03 17:42:36 +01:00
} ;
}
2023-06-19 13:28:56 +01:00
let templateSrv : TemplateSrv ;
const text = 'interpolationText' ;
const textWithPipe = 'interpolationTextOne|interpolationTextTwo' ;
beforeEach ( ( ) = > {
2024-01-16 11:36:40 +01:00
const expectedValues = {
interpolationVar : 'scopedInterpolationText' ,
interpolationText : 'interpolationText' ,
interpolationVarWithPipe : 'interpolationTextOne|interpolationTextTwo' ,
scopedInterpolationText : 'scopedInterpolationText' ,
} ;
templateSrv = initTemplateSrv ( [ { name : 'templateVariable1' } , { name : 'templateVariable2' } ] , expectedValues ) ;
2023-06-19 13:28:56 +01:00
} ) ;
2022-05-03 17:42:36 +01:00
it ( 'when traceId query for dashboard->explore' , async ( ) = > {
2024-01-16 11:36:40 +01:00
const expectedValues = {
interpolationVar : 'interpolationText' ,
interpolationText : 'interpolationText' ,
interpolationVarWithPipe : 'interpolationTextOne|interpolationTextTwo' ,
scopedInterpolationText : 'scopedInterpolationText' ,
} ;
templateSrv = initTemplateSrv ( [ { name : 'templateVariable1' } , { name : 'templateVariable2' } ] , expectedValues ) ;
2022-05-03 17:42:36 +01:00
const ds = new TempoDatasource ( defaultSettings , templateSrv ) ;
2023-06-19 13:28:56 +01:00
const queries = ds . interpolateVariablesInQueries ( [ getQuery ( ) ] , { } ) ;
expect ( queries [ 0 ] . linkedQuery ? . expr ) . toBe ( ` {instance= \ " ${ text } \ "} ` ) ;
expect ( queries [ 0 ] . query ) . toBe ( textWithPipe ) ;
2022-07-18 08:08:35 +01:00
expect ( queries [ 0 ] . serviceName ) . toBe ( text ) ;
expect ( queries [ 0 ] . spanName ) . toBe ( text ) ;
2022-05-03 17:42:36 +01:00
expect ( queries [ 0 ] . search ) . toBe ( text ) ;
expect ( queries [ 0 ] . minDuration ) . toBe ( text ) ;
expect ( queries [ 0 ] . maxDuration ) . toBe ( text ) ;
2023-12-15 09:03:00 +00:00
expect ( queries [ 0 ] . serviceMapQuery ) . toBe ( text ) ;
2022-05-03 17:42:36 +01:00
} ) ;
it ( 'when traceId query for template variable' , async ( ) = > {
2023-06-19 13:28:56 +01:00
const scopedText = 'scopedInterpolationText' ;
2022-05-03 17:42:36 +01:00
const ds = new TempoDatasource ( defaultSettings , templateSrv ) ;
const resp = ds . applyTemplateVariables ( getQuery ( ) , {
2023-06-19 13:28:56 +01:00
interpolationVar : { text : scopedText , value : scopedText } ,
2022-05-03 17:42:36 +01:00
} ) ;
2023-06-19 13:28:56 +01:00
expect ( resp . linkedQuery ? . expr ) . toBe ( ` {instance= \ " ${ scopedText } \ "} ` ) ;
expect ( resp . query ) . toBe ( textWithPipe ) ;
expect ( resp . serviceName ) . toBe ( scopedText ) ;
expect ( resp . spanName ) . toBe ( scopedText ) ;
expect ( resp . search ) . toBe ( scopedText ) ;
expect ( resp . minDuration ) . toBe ( scopedText ) ;
expect ( resp . maxDuration ) . toBe ( scopedText ) ;
2022-05-03 17:42:36 +01:00
} ) ;
} ) ;
2021-03-22 19:09:15 +01:00
it ( 'parses json fields from backend' , async ( ) = > {
setupBackendSrv (
2023-05-01 19:41:40 -07:00
createDataFrame ( {
2021-03-22 19:09:15 +01:00
fields : [
2021-05-05 20:51:43 +02:00
{ name : 'traceID' , values : [ '04450900759028499335' ] } ,
{ name : 'spanID' , values : [ '4322526419282105830' ] } ,
{ name : 'parentSpanID' , values : [ '' ] } ,
{ name : 'operationName' , values : [ 'store.validateQueryTimeRange' ] } ,
{ name : 'startTime' , values : [ 1619712655875.4539 ] } ,
{ name : 'duration' , values : [ 14.984 ] } ,
2022-07-21 10:00:47 -06:00
{ name : 'serviceTags' , values : [ { key : 'servicetag1' , value : 'service' } ] } ,
{ name : 'logs' , values : [ { timestamp : 12345 , fields : [ { key : 'count' , value : 1 } ] } ] } ,
{ name : 'tags' , values : [ { key : 'tag1' , value : 'val1' } ] } ,
2021-03-22 19:09:15 +01:00
{ name : 'serviceName' , values : [ 'service' ] } ,
] ,
} )
) ;
2022-05-03 17:42:36 +01:00
const templateSrv : any = { replace : jest.fn ( ) } ;
const ds = new TempoDatasource ( defaultSettings , templateSrv ) ;
2022-01-05 19:34:09 +01:00
const response = await lastValueFrom ( ds . query ( { targets : [ { refId : 'refid1' , query : '12345' } ] } as any ) ) ;
2021-05-05 20:51:43 +02:00
expect (
( response . data [ 0 ] as DataFrame ) . fields . map ( ( f ) = > ( {
name : f.name ,
2023-04-20 09:59:18 -05:00
values : f.values ,
2021-05-05 20:51:43 +02:00
} ) )
) . toMatchObject ( [
{ name : 'traceID' , values : [ '04450900759028499335' ] } ,
{ name : 'spanID' , values : [ '4322526419282105830' ] } ,
{ name : 'parentSpanID' , values : [ '' ] } ,
{ name : 'operationName' , values : [ 'store.validateQueryTimeRange' ] } ,
{ name : 'startTime' , values : [ 1619712655875.4539 ] } ,
{ name : 'duration' , values : [ 14.984 ] } ,
{ name : 'serviceTags' , values : [ { key : 'servicetag1' , value : 'service' } ] } ,
{ name : 'logs' , values : [ { timestamp : 12345 , fields : [ { key : 'count' , value : 1 } ] } ] } ,
{ name : 'tags' , values : [ { key : 'tag1' , value : 'val1' } ] } ,
{ name : 'serviceName' , values : [ 'service' ] } ,
] ) ;
expect (
( response . data [ 1 ] as DataFrame ) . fields . map ( ( f ) = > ( {
name : f.name ,
2023-04-20 09:59:18 -05:00
values : f.values ,
2021-05-05 20:51:43 +02:00
} ) )
) . toMatchObject ( [
{ name : 'id' , values : [ '4322526419282105830' ] } ,
{ name : 'title' , values : [ 'service' ] } ,
2022-04-13 08:18:40 -07:00
{ name : 'subtitle' , values : [ 'store.validateQueryTimeRange' ] } ,
{ name : 'mainstat' , values : [ '14.98ms (100%)' ] } ,
{ name : 'secondarystat' , values : [ '14.98ms (100%)' ] } ,
2021-05-05 20:51:43 +02:00
{ name : 'color' , values : [ 1.000007560204647 ] } ,
] ) ;
expect (
( response . data [ 2 ] as DataFrame ) . fields . map ( ( f ) = > ( {
name : f.name ,
2023-04-20 09:59:18 -05:00
values : f.values ,
2021-05-05 20:51:43 +02:00
} ) )
) . toMatchObject ( [
{ name : 'id' , values : [ ] } ,
{ name : 'target' , values : [ ] } ,
{ name : 'source' , values : [ ] } ,
] ) ;
2020-10-13 19:12:49 +02:00
} ) ;
2021-08-05 15:13:44 +02:00
it ( 'should handle json file upload' , async ( ) = > {
const ds = new TempoDatasource ( defaultSettings ) ;
ds . uploadedJson = JSON . stringify ( mockJson ) ;
2021-08-19 06:38:31 +02:00
const response = await lastValueFrom (
ds . query ( {
2021-08-05 15:13:44 +02:00
targets : [ { queryType : 'upload' , refId : 'A' } ] ,
} as any )
2021-08-19 06:38:31 +02:00
) ;
2021-08-05 15:13:44 +02:00
const field = response . data [ 0 ] . fields [ 0 ] ;
expect ( field . name ) . toBe ( 'traceID' ) ;
expect ( field . type ) . toBe ( FieldType . string ) ;
2023-04-20 09:59:18 -05:00
expect ( field . values [ 0 ] ) . toBe ( '60ba2abb44f13eae' ) ;
2021-08-05 15:13:44 +02:00
expect ( field . values . length ) . toBe ( 6 ) ;
} ) ;
2021-08-19 09:15:46 -06:00
2021-08-25 10:08:46 -06:00
it ( 'should fail on invalid json file upload' , async ( ) = > {
const ds = new TempoDatasource ( defaultSettings ) ;
ds . uploadedJson = JSON . stringify ( mockInvalidJson ) ;
const response = await lastValueFrom (
ds . query ( {
targets : [ { queryType : 'upload' , refId : 'A' } ] ,
} as any )
) ;
expect ( response . error ? . message ) . toBeDefined ( ) ;
expect ( response . data . length ) . toBe ( 0 ) ;
} ) ;
2022-06-07 06:37:19 -06:00
it ( 'should handle service graph upload' , async ( ) = > {
const ds = new TempoDatasource ( defaultSettings ) ;
ds . uploadedJson = JSON . stringify ( mockServiceGraph ) ;
const response = await lastValueFrom (
ds . query ( {
targets : [ { queryType : 'upload' , refId : 'A' } ] ,
} as any )
) ;
expect ( response . data ) . toHaveLength ( 2 ) ;
const nodesFrame = response . data [ 0 ] ;
expect ( nodesFrame . name ) . toBe ( 'Nodes' ) ;
2023-10-11 18:04:54 +02:00
expect ( nodesFrame . meta ? . preferredVisualisationType ) . toBe ( 'nodeGraph' ) ;
2022-06-07 06:37:19 -06:00
const edgesFrame = response . data [ 1 ] ;
expect ( edgesFrame . name ) . toBe ( 'Edges' ) ;
2023-10-11 18:04:54 +02:00
expect ( edgesFrame . meta ? . preferredVisualisationType ) . toBe ( 'nodeGraph' ) ;
2022-06-07 06:37:19 -06:00
} ) ;
2021-08-19 09:15:46 -06:00
it ( 'should build search query correctly' , ( ) = > {
2022-05-03 17:42:36 +01:00
const templateSrv : any = { replace : jest.fn ( ) } ;
const ds = new TempoDatasource ( defaultSettings , templateSrv ) ;
const duration = '10ms' ;
templateSrv . replace . mockReturnValue ( duration ) ;
2021-08-19 09:15:46 -06:00
const tempoQuery : TempoQuery = {
queryType : 'search' ,
refId : 'A' ,
query : '' ,
serviceName : 'frontend' ,
spanName : '/config' ,
search : 'root.http.status_code=500' ,
2022-05-03 17:42:36 +01:00
minDuration : '$interpolationVar' ,
maxDuration : '$interpolationVar' ,
2021-08-19 09:15:46 -06:00
limit : 10 ,
2023-03-06 16:31:08 +00:00
filters : [ ] ,
2021-08-19 09:15:46 -06:00
} ;
const builtQuery = ds . buildSearchQuery ( tempoQuery ) ;
expect ( builtQuery ) . toStrictEqual ( {
2021-12-14 07:41:46 -07:00
tags : 'root.http.status_code=500 service.name="frontend" name="/config"' ,
2022-05-03 17:42:36 +01:00
minDuration : duration ,
maxDuration : duration ,
2021-08-19 09:15:46 -06:00
limit : 10 ,
} ) ;
} ) ;
2023-08-28 15:02:12 +01:00
it ( 'should format metrics summary query correctly' , ( ) = > {
const ds = new TempoDatasource ( defaultSettings , { } as TemplateSrv ) ;
const queryGroupBy = [
{ id : '1' , scope : TraceqlSearchScope.Unscoped , tag : 'component' } ,
{ id : '2' , scope : TraceqlSearchScope.Span , tag : 'name' } ,
{ id : '3' , scope : TraceqlSearchScope.Resource , tag : 'service.name' } ,
{ id : '4' , scope : TraceqlSearchScope.Intrinsic , tag : 'kind' } ,
] ;
const groupBy = ds . formatGroupBy ( queryGroupBy ) ;
expect ( groupBy ) . toEqual ( '.component, span.name, resource.service.name, kind' ) ;
} ) ;
2021-10-06 08:43:13 -06:00
it ( 'should include a default limit' , ( ) = > {
2021-09-28 10:52:57 -06:00
const ds = new TempoDatasource ( defaultSettings ) ;
const tempoQuery : TempoQuery = {
queryType : 'search' ,
refId : 'A' ,
query : '' ,
search : '' ,
2023-03-06 16:31:08 +00:00
filters : [ ] ,
2021-09-28 10:52:57 -06:00
} ;
const builtQuery = ds . buildSearchQuery ( tempoQuery ) ;
expect ( builtQuery ) . toStrictEqual ( {
2021-12-14 07:41:46 -07:00
tags : '' ,
2021-10-06 08:43:13 -06:00
limit : DEFAULT_LIMIT ,
2021-09-28 10:52:57 -06:00
} ) ;
} ) ;
2022-01-10 07:38:40 -07:00
it ( 'should include time range if provided' , ( ) = > {
const ds = new TempoDatasource ( defaultSettings ) ;
const tempoQuery : TempoQuery = {
queryType : 'search' ,
refId : 'A' ,
query : '' ,
search : '' ,
2023-03-06 16:31:08 +00:00
filters : [ ] ,
2022-01-10 07:38:40 -07:00
} ;
const timeRange = { startTime : 0 , endTime : 1000 } ;
const builtQuery = ds . buildSearchQuery ( tempoQuery , timeRange ) ;
expect ( builtQuery ) . toStrictEqual ( {
tags : '' ,
limit : DEFAULT_LIMIT ,
start : timeRange.startTime ,
end : timeRange.endTime ,
} ) ;
} ) ;
2021-09-16 08:04:15 -06:00
it ( 'formats native search query history correctly' , ( ) = > {
const ds = new TempoDatasource ( defaultSettings ) ;
const tempoQuery : TempoQuery = {
2023-03-06 16:31:08 +00:00
filters : [ ] ,
2021-09-16 08:04:15 -06:00
queryType : 'nativeSearch' ,
refId : 'A' ,
query : '' ,
serviceName : 'frontend' ,
spanName : '/config' ,
search : 'root.http.status_code=500' ,
minDuration : '1ms' ,
maxDuration : '100s' ,
limit : 10 ,
} ;
const result = ds . getQueryDisplayText ( tempoQuery ) ;
expect ( result ) . toBe (
'Service Name: frontend, Span Name: /config, Search: root.http.status_code=500, Min Duration: 1ms, Max Duration: 100s, Limit: 10'
) ;
} ) ;
2022-03-17 11:23:15 -06:00
it ( 'should get loki search datasource' , ( ) = > {
// 1. Get lokiSearch.datasource if present
const ds1 = new TempoDatasource ( {
. . . defaultSettings ,
jsonData : {
lokiSearch : {
datasourceUid : 'loki-1' ,
} ,
} ,
} ) ;
const lokiDS1 = ds1 . getLokiSearchDS ( ) ;
expect ( lokiDS1 ) . toBe ( 'loki-1' ) ;
// 2. Get traceToLogs.datasource
const ds2 = new TempoDatasource ( {
. . . defaultSettings ,
jsonData : {
tracesToLogs : {
lokiSearch : true ,
datasourceUid : 'loki-2' ,
} ,
} ,
} ) ;
const lokiDS2 = ds2 . getLokiSearchDS ( ) ;
expect ( lokiDS2 ) . toBe ( 'loki-2' ) ;
// 3. Return undefined if neither is available
const ds3 = new TempoDatasource ( defaultSettings ) ;
const lokiDS3 = ds3 . getLokiSearchDS ( ) ;
expect ( lokiDS3 ) . toBe ( undefined ) ;
// 4. Return undefined if lokiSearch is undefined, even if traceToLogs is present
// since this indicates the user cleared the fallback setting
const ds4 = new TempoDatasource ( {
. . . defaultSettings ,
jsonData : {
tracesToLogs : {
lokiSearch : true ,
datasourceUid : 'loki-2' ,
} ,
lokiSearch : {
datasourceUid : undefined ,
} ,
} ,
} ) ;
const lokiDS4 = ds4 . getLokiSearchDS ( ) ;
expect ( lokiDS4 ) . toBe ( undefined ) ;
} ) ;
2022-09-02 11:17:36 +01:00
describe ( 'test the testDatasource function' , ( ) = > {
it ( 'should return a success msg if response.ok is true' , async ( ) = > {
mockObservable = ( ) = > of ( { ok : true } ) ;
const ds = new TempoDatasource ( defaultSettings ) ;
const response = await ds . testDatasource ( ) ;
expect ( response . status ) . toBe ( 'success' ) ;
} ) ;
} ) ;
describe ( 'test the metadataRequest function' , ( ) = > {
it ( 'should return the last value from the observed stream' , async ( ) = > {
mockObservable = ( ) = > of ( '321' , '123' , '456' ) ;
const ds = new TempoDatasource ( defaultSettings ) ;
const response = await ds . metadataRequest ( '/api/search/tags' ) ;
expect ( response ) . toBe ( '456' ) ;
} ) ;
} ) ;
2022-10-21 20:08:10 +05:30
it ( 'should include time shift when querying for traceID' , ( ) = > {
const ds = new TempoDatasource ( {
. . . defaultSettings ,
2022-11-03 11:01:22 +00:00
jsonData : { traceQuery : { timeShiftEnabled : true , spanStartTimeShift : '2m' , spanEndTimeShift : '4m' } } ,
2022-10-21 20:08:10 +05:30
} ) ;
const request = ds . traceIdQueryRequest (
{
requestId : 'test' ,
interval : '' ,
intervalMs : 5 ,
scopedVars : { } ,
targets : [ ] ,
timezone : '' ,
app : '' ,
startTime : 0 ,
range : {
from : dateTime ( new Date ( 2022 , 8 , 13 , 16 , 0 , 0 , 0 ) ) ,
to : dateTime ( new Date ( 2022 , 8 , 13 , 16 , 15 , 0 , 0 ) ) ,
raw : { from : '15m' , to : 'now' } ,
} ,
} ,
2022-12-13 13:27:45 +00:00
[ { refId : 'refid1' , queryType : 'traceql' , query : '' } as TempoQuery ]
2022-10-21 20:08:10 +05:30
) ;
expect ( request . range . from . unix ( ) ) . toBe ( dateTime ( new Date ( 2022 , 8 , 13 , 15 , 58 , 0 , 0 ) ) . unix ( ) ) ;
expect ( request . range . to . unix ( ) ) . toBe ( dateTime ( new Date ( 2022 , 8 , 13 , 16 , 19 , 0 , 0 ) ) . unix ( ) ) ;
} ) ;
2022-11-03 11:01:22 +00:00
it ( 'should not include time shift when querying for traceID and time shift config is off' , ( ) = > {
const ds = new TempoDatasource ( {
. . . defaultSettings ,
jsonData : { traceQuery : { timeShiftEnabled : false , spanStartTimeShift : '2m' , spanEndTimeShift : '4m' } } ,
} ) ;
const request = ds . traceIdQueryRequest (
{
requestId : 'test' ,
interval : '' ,
intervalMs : 5 ,
scopedVars : { } ,
targets : [ ] ,
timezone : '' ,
app : '' ,
startTime : 0 ,
range : {
from : dateTime ( new Date ( 2022 , 8 , 13 , 16 , 0 , 0 , 0 ) ) ,
to : dateTime ( new Date ( 2022 , 8 , 13 , 16 , 15 , 0 , 0 ) ) ,
raw : { from : '15m' , to : 'now' } ,
} ,
} ,
2022-12-13 13:27:45 +00:00
[ { refId : 'refid1' , queryType : 'traceql' , query : '' } as TempoQuery ]
2022-11-03 11:01:22 +00:00
) ;
expect ( request . range . from . unix ( ) ) . toBe ( dateTime ( 0 ) . unix ( ) ) ;
expect ( request . range . to . unix ( ) ) . toBe ( dateTime ( 0 ) . unix ( ) ) ;
} ) ;
2020-10-13 19:12:49 +02:00
} ) ;
2023-03-22 08:37:44 +00:00
describe ( 'Tempo service graph view' , ( ) = > {
2022-06-09 17:56:15 +01:00
it ( 'runs service graph queries' , async ( ) = > {
const ds = new TempoDatasource ( {
. . . defaultSettings ,
jsonData : {
serviceMap : {
datasourceUid : 'prom' ,
} ,
} ,
} ) ;
setDataSourceSrv ( backendSrvWithPrometheus as any ) ;
const response = await lastValueFrom (
2023-05-15 14:27:27 +01:00
ds . query ( { targets : [ { queryType : 'serviceMap' } ] , range : getDefaultTimeRange ( ) , app : CoreApp.Explore } as any )
2022-06-09 17:56:15 +01:00
) ;
expect ( response . data ) . toHaveLength ( 3 ) ;
expect ( response . state ) . toBe ( LoadingState . Done ) ;
2023-03-22 08:37:44 +00:00
// Service Graph view
2022-06-09 17:56:15 +01:00
expect ( response . data [ 0 ] . fields [ 0 ] . name ) . toBe ( 'Name' ) ;
2023-04-20 09:59:18 -05:00
expect ( response . data [ 0 ] . fields [ 0 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 0 ] . values [ 0 ] ) . toBe ( 'HTTP Client' ) ;
expect ( response . data [ 0 ] . fields [ 0 ] . values [ 1 ] ) . toBe ( 'HTTP GET - root' ) ;
2022-06-09 17:56:15 +01:00
expect ( response . data [ 0 ] . fields [ 1 ] . name ) . toBe ( 'Rate' ) ;
2023-04-20 09:59:18 -05:00
expect ( response . data [ 0 ] . fields [ 1 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 1 ] . values [ 0 ] ) . toBe ( 12.75164671814457 ) ;
expect ( response . data [ 0 ] . fields [ 1 ] . values [ 1 ] ) . toBe ( 12.121331111401608 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . decimals ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . links ? . [ 0 ] ? . title ) . toBe ( 'Rate' ) ;
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . expr ) . toBe (
2022-06-20 09:56:45 +01:00
'sum(rate(traces_spanmetrics_calls_total{span_name="${__data.fields[0]}"}[$__rate_interval]))'
2022-06-09 17:56:15 +01:00
) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . range ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . exemplar ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 1 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . instant ) . toBe ( false ) ;
2022-06-09 17:56:15 +01:00
2023-04-20 09:59:18 -05:00
expect ( response . data [ 0 ] . fields [ 2 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 2 ] . values [ 0 ] ) . toBe ( 12.75164671814457 ) ;
expect ( response . data [ 0 ] . fields [ 2 ] . values [ 1 ] ) . toBe ( 12.121331111401608 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 2 ] ? . config ? . color ? . mode ) . toBe ( 'continuous-BlPu' ) ;
expect ( response . data [ 0 ] . fields [ 2 ] ? . config ? . custom . cellOptions . mode ) . toBe ( BarGaugeDisplayMode . Lcd ) ;
expect ( response . data [ 0 ] . fields [ 2 ] ? . config ? . custom . cellOptions . type ) . toBe ( TableCellDisplayMode . Gauge ) ;
expect ( response . data [ 0 ] . fields [ 2 ] ? . config ? . decimals ) . toBe ( 3 ) ;
2022-06-09 17:56:15 +01:00
expect ( response . data [ 0 ] . fields [ 3 ] . name ) . toBe ( 'Error Rate' ) ;
expect ( response . data [ 0 ] . fields [ 3 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 3 ] . values [ 0 ] ) . toBe ( 3.75164671814457 ) ;
expect ( response . data [ 0 ] . fields [ 3 ] . values [ 1 ] ) . toBe ( 3.121331111401608 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . decimals ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . links ? . [ 0 ] ? . title ) . toBe ( 'Error Rate' ) ;
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . expr ) . toBe (
2022-06-24 10:19:26 +01:00
'sum(rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR",span_name="${__data.fields[0]}"}[$__rate_interval]))'
2022-06-09 17:56:15 +01:00
) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . range ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . exemplar ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 3 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . instant ) . toBe ( false ) ;
2022-06-09 17:56:15 +01:00
expect ( response . data [ 0 ] . fields [ 4 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 4 ] . values [ 0 ] ) . toBe ( 3.75164671814457 ) ;
expect ( response . data [ 0 ] . fields [ 4 ] . values [ 1 ] ) . toBe ( 3.121331111401608 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 4 ] ? . config ? . color ? . mode ) . toBe ( 'continuous-RdYlGr' ) ;
expect ( response . data [ 0 ] . fields [ 4 ] ? . config ? . custom . cellOptions . mode ) . toBe ( BarGaugeDisplayMode . Lcd ) ;
expect ( response . data [ 0 ] . fields [ 4 ] ? . config ? . custom . cellOptions . type ) . toBe ( TableCellDisplayMode . Gauge ) ;
expect ( response . data [ 0 ] . fields [ 4 ] ? . config ? . decimals ) . toBe ( 3 ) ;
2022-06-09 17:56:15 +01:00
expect ( response . data [ 0 ] . fields [ 5 ] . name ) . toBe ( 'Duration (p90)' ) ;
expect ( response . data [ 0 ] . fields [ 5 ] . values . length ) . toBe ( 2 ) ;
expect ( response . data [ 0 ] . fields [ 5 ] . values [ 0 ] ) . toBe ( '0' ) ;
expect ( response . data [ 0 ] . fields [ 5 ] . values [ 1 ] ) . toBe ( 0.12003505696757232 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . unit ) . toBe ( 's' ) ;
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . links ? . [ 0 ] ? . title ) . toBe ( 'Duration' ) ;
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . expr ) . toBe (
2022-06-24 10:19:26 +01:00
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{span_name="${__data.fields[0]}"}[$__rate_interval])) by (le))'
2022-06-09 17:56:15 +01:00
) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . range ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . exemplar ) . toBe ( true ) ;
expect ( response . data [ 0 ] . fields [ 5 ] ? . config ? . links ? . [ 0 ] ? . internal ? . query . instant ) . toBe ( false ) ;
2022-06-09 17:56:15 +01:00
2023-10-11 18:04:54 +02:00
expect ( response . data [ 0 ] . fields [ 6 ] ? . config ? . links ? . [ 0 ] . url ) . toBe ( '' ) ;
expect ( response . data [ 0 ] . fields [ 6 ] ? . config ? . links ? . [ 0 ] . title ) . toBe ( 'Tempo' ) ;
expect ( response . data [ 0 ] . fields [ 6 ] ? . config ? . links ? . [ 0 ] . internal . query . queryType ) . toBe ( 'traceqlSearch' ) ;
expect ( response . data [ 0 ] . fields [ 6 ] ? . config ? . links ? . [ 0 ] . internal . query . filters [ 0 ] . value ) . toBe ( '${__data.fields[0]}' ) ;
2022-06-09 17:56:15 +01:00
// Service graph
expect ( response . data [ 1 ] . name ) . toBe ( 'Nodes' ) ;
expect ( response . data [ 1 ] . fields [ 0 ] . values . length ) . toBe ( 3 ) ;
2023-10-11 18:04:54 +02:00
expect ( response . data [ 1 ] . fields [ 0 ] ? . config ? . links ? . length ) . toBeGreaterThan ( 0 ) ;
expect ( response . data [ 1 ] . fields [ 0 ] ? . config ? . links ) . toEqual ( serviceGraphLinks ) ;
2022-06-09 17:56:15 +01:00
expect ( response . data [ 2 ] . name ) . toBe ( 'Edges' ) ;
expect ( response . data [ 2 ] . fields [ 0 ] . values . length ) . toBe ( 2 ) ;
} ) ;
it ( 'should build expr correctly' , ( ) = > {
let targets = { targets : [ { queryType : 'serviceMap' } ] } as any ;
let builtQuery = buildExpr (
{ expr : 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' , params : [ ] } ,
'' ,
targets
) ;
expect ( builtQuery ) . toBe ( 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' ) ;
builtQuery = buildExpr (
{
expr : 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' ,
2022-06-24 10:19:26 +01:00
params : [ 'status_code="STATUS_CODE_ERROR"' ] ,
2022-06-09 17:56:15 +01:00
} ,
'span_name=~"HTTP Client|HTTP GET|HTTP GET - root|HTTP POST|HTTP POST - post"' ,
targets
) ;
expect ( builtQuery ) . toBe (
2022-06-24 10:19:26 +01:00
'topk(5, sum(rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR",span_name=~"HTTP Client|HTTP GET|HTTP GET - root|HTTP POST|HTTP POST - post"}[$__range])) by (span_name))'
2022-06-09 17:56:15 +01:00
) ;
builtQuery = buildExpr (
{
2022-06-24 10:19:26 +01:00
expr : 'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{}[$__range])) by (le))' ,
params : [ 'status_code="STATUS_CODE_ERROR"' ] ,
2022-06-09 17:56:15 +01:00
} ,
'span_name=~"HTTP Client"' ,
targets
) ;
expect ( builtQuery ) . toBe (
2022-06-24 10:19:26 +01:00
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{status_code="STATUS_CODE_ERROR",span_name=~"HTTP Client"}[$__range])) by (le))'
2022-06-09 17:56:15 +01:00
) ;
targets = { targets : [ { queryType : 'serviceMap' , serviceMapQuery : '{client="app",service="app"}' } ] } as any ;
builtQuery = buildExpr (
{ expr : 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' , params : [ ] } ,
'' ,
targets
) ;
expect ( builtQuery ) . toBe (
'topk(5, sum(rate(traces_spanmetrics_calls_total{service="app",service="app"}[$__range])) by (span_name))'
) ;
2023-01-04 16:18:41 +00:00
targets = { targets : [ { queryType : 'serviceMap' , serviceMapQuery : '{client="${app}",service="$app"}' } ] } as any ;
builtQuery = buildExpr (
{ expr : 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' , params : [ ] } ,
'' ,
targets
) ;
expect ( builtQuery ) . toBe (
'topk(5, sum(rate(traces_spanmetrics_calls_total{service="${app}",service="$app"}[$__range])) by (span_name))'
) ;
2022-06-09 17:56:15 +01:00
} ) ;
it ( 'should build link expr correctly' , ( ) = > {
let builtQuery = buildLinkExpr ( 'topk(5, sum(rate(traces_spanmetrics_calls_total{}[$__range])) by (span_name))' ) ;
2022-06-20 09:56:45 +01:00
expect ( builtQuery ) . toBe ( 'sum(rate(traces_spanmetrics_calls_total{}[$__rate_interval]))' ) ;
2022-06-09 17:56:15 +01:00
} ) ;
2023-05-11 17:21:26 +03:00
it ( 'should escape span names correctly' , ( ) = > {
const spanNames = [
'/actuator/health/**' ,
'$type + [test]|HTTP POST - post' ,
'server.cluster.local:9090^/sample.test(.*)?' ,
] ;
let escaped = getEscapedSpanNames ( spanNames ) ;
expect ( escaped ) . toEqual ( [
'/actuator/health/\\\\*\\\\*' ,
'\\\\$type \\\\+ \\\\[test\\\\]\\\\|HTTP POST - post' ,
'server\\\\.cluster\\\\.local:9090\\\\^/sample\\\\.test\\\\(\\\\.\\\\*\\\\)\\\\?' ,
] ) ;
} ) ;
2022-07-27 15:48:09 +01:00
it ( 'should get field config correctly' , ( ) = > {
2022-07-25 10:03:57 +01:00
let datasourceUid = 's4Jvz8Qnk' ;
let tempoDatasourceUid = 'EbPO1fYnz' ;
let targetField = '__data.fields.target' ;
let tempoField = '__data.fields.target' ;
let sourceField = '__data.fields.source' ;
let fieldConfig = getFieldConfig ( datasourceUid , tempoDatasourceUid , targetField , tempoField , sourceField ) ;
let resultObj = {
links : [
{
url : '' ,
title : 'Request rate' ,
internal : {
query : {
expr : 'sum by (client, server)(rate(traces_service_graph_request_total{client="${__data.fields.source}",server="${__data.fields.target}"}[$__rate_interval]))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'Request histogram' ,
internal : {
query : {
expr : 'histogram_quantile(0.9, sum(rate(traces_service_graph_request_server_seconds_bucket{client="${__data.fields.source}",server="${__data.fields.target}"}[$__rate_interval])) by (le, client, server))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'Failed request rate' ,
internal : {
query : {
expr : 'sum by (client, server)(rate(traces_service_graph_request_failed_total{client="${__data.fields.source}",server="${__data.fields.target}"}[$__rate_interval]))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'View traces' ,
internal : {
query : {
2023-10-09 14:22:39 +01:00
refId : 'A' ,
queryType : 'traceqlSearch' ,
filters : [
{
id : 'service-name' ,
operator : '=' ,
scope : 'resource' ,
tag : 'service.name' ,
value : '${__data.fields.target}' ,
valueType : 'string' ,
} ,
] ,
2023-09-13 10:17:31 +02:00
} ,
datasourceUid : 'EbPO1fYnz' ,
datasourceName : '' ,
} ,
} ,
] ,
} ;
expect ( fieldConfig ) . toStrictEqual ( resultObj ) ;
} ) ;
it ( 'should get field config correctly when namespaces are present' , ( ) = > {
let datasourceUid = 's4Jvz8Qnk' ;
let tempoDatasourceUid = 'EbPO1fYnz' ;
let targetField = '__data.fields.targetName' ;
let tempoField = '__data.fields.target' ;
let sourceField = '__data.fields.sourceName' ;
let namespaceFields = {
targetNamespace : '__data.fields.targetNamespace' ,
sourceNamespace : '__data.fields.sourceNamespace' ,
} ;
let fieldConfig = getFieldConfig (
datasourceUid ,
tempoDatasourceUid ,
targetField ,
tempoField ,
sourceField ,
namespaceFields
) ;
let resultObj = {
links : [
{
url : '' ,
title : 'Request rate' ,
internal : {
query : {
expr : 'sum by (client, server, server_service_namespace, client_service_namespace)(rate(traces_service_graph_request_total{client="${__data.fields.sourceName}",client_service_namespace="${__data.fields.sourceNamespace}",server="${__data.fields.targetName}",server_service_namespace="${__data.fields.targetNamespace}"}[$__rate_interval]))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'Request histogram' ,
internal : {
query : {
expr : 'histogram_quantile(0.9, sum(rate(traces_service_graph_request_server_seconds_bucket{client="${__data.fields.sourceName}",client_service_namespace="${__data.fields.sourceNamespace}",server="${__data.fields.targetName}",server_service_namespace="${__data.fields.targetNamespace}"}[$__rate_interval])) by (le, client, server, server_service_namespace, client_service_namespace))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'Failed request rate' ,
internal : {
query : {
expr : 'sum by (client, server, server_service_namespace, client_service_namespace)(rate(traces_service_graph_request_failed_total{client="${__data.fields.sourceName}",client_service_namespace="${__data.fields.sourceNamespace}",server="${__data.fields.targetName}",server_service_namespace="${__data.fields.targetNamespace}"}[$__rate_interval]))' ,
range : true ,
exemplar : true ,
instant : false ,
} ,
datasourceUid : 's4Jvz8Qnk' ,
datasourceName : '' ,
} ,
} ,
{
url : '' ,
title : 'View traces' ,
internal : {
query : {
2023-10-09 14:22:39 +01:00
queryType : 'traceqlSearch' ,
refId : 'A' ,
filters : [
{
id : 'service-name' ,
operator : '=' ,
scope : 'resource' ,
tag : 'service.name' ,
value : '${__data.fields.target}' ,
valueType : 'string' ,
} ,
] ,
2022-07-25 10:03:57 +01:00
} ,
datasourceUid : 'EbPO1fYnz' ,
datasourceName : '' ,
} ,
} ,
] ,
} ;
expect ( fieldConfig ) . toStrictEqual ( resultObj ) ;
} ) ;
2022-06-09 17:56:15 +01:00
it ( 'should get rate aligned values correctly' , ( ) = > {
const resp = [
{
refId :
'topk(5, sum(rate(traces_spanmetrics_calls_total{service="app",service="app"}[$__range])) by (span_name))' ,
fields : [
{
name : 'Time' ,
2023-10-11 18:04:54 +02:00
type : FieldType . time ,
2022-06-09 17:56:15 +01:00
config : { } ,
values : [ 1653828275000 , 1653828275000 , 1653828275000 , 1653828275000 , 1653828275000 ] ,
} ,
{
name : 'span_name' ,
config : {
filterable : true ,
} ,
2023-10-11 18:04:54 +02:00
type : FieldType . string ,
2023-04-20 09:59:18 -05:00
values : [ 'HTTP Client' , 'HTTP GET' , 'HTTP GET - root' , 'HTTP POST' , 'HTTP POST - post' ] ,
2022-06-09 17:56:15 +01:00
} ,
] ,
2023-10-11 18:04:54 +02:00
values : [ ] ,
2022-06-09 17:56:15 +01:00
} ,
] ;
2023-10-11 18:04:54 +02:00
2022-06-09 17:56:15 +01:00
const objToAlign = {
'HTTP GET - root' : {
2023-01-04 16:18:41 +00:00
value : '0.1234' ,
2022-06-09 17:56:15 +01:00
} ,
'HTTP GET' : {
2023-01-04 16:18:41 +00:00
value : '0.6789' ,
2022-06-09 17:56:15 +01:00
} ,
'HTTP POST - post' : {
2023-01-04 16:18:41 +00:00
value : '0.4321' ,
2022-06-09 17:56:15 +01:00
} ,
} ;
2023-01-04 16:18:41 +00:00
let value = getRateAlignedValues ( resp , objToAlign ) ;
2022-07-25 18:59:43 +01:00
expect ( value . toString ( ) ) . toBe ( '0,0.6789,0.1234,0,0.4321' ) ;
2022-06-09 17:56:15 +01:00
} ) ;
2023-03-22 08:37:44 +00:00
it ( 'should make service graph view request correctly' , ( ) = > {
const request = makeServiceGraphViewRequest ( [
2022-06-09 17:56:15 +01:00
'topk(5, sum(rate(traces_spanmetrics_calls_total{service="app"}[$__range])) by (span_name))"' ,
2022-06-24 10:19:26 +01:00
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{status_code="STATUS_CODE_ERROR",service="app",service="app",span_name=~"HTTP Client"}[$__range])) by (le))' ,
2022-06-09 17:56:15 +01:00
] ) ;
2023-03-22 08:37:44 +00:00
expect ( request ) . toEqual ( [
2022-06-09 17:56:15 +01:00
{
refId : 'topk(5, sum(rate(traces_spanmetrics_calls_total{service="app"}[$__range])) by (span_name))"' ,
expr : 'topk(5, sum(rate(traces_spanmetrics_calls_total{service="app"}[$__range])) by (span_name))"' ,
instant : true ,
} ,
{
refId :
2022-06-24 10:19:26 +01:00
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{status_code="STATUS_CODE_ERROR",service="app",service="app",span_name=~"HTTP Client"}[$__range])) by (le))' ,
expr : 'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{status_code="STATUS_CODE_ERROR",service="app",service="app",span_name=~"HTTP Client"}[$__range])) by (le))' ,
2022-06-09 17:56:15 +01:00
instant : true ,
} ,
] ) ;
} ) ;
it ( 'should make tempo link correctly' , ( ) = > {
const tempoLink = makeTempoLink ( 'Tempo' , '' , '"${__data.fields[0]}"' , 'gdev-tempo' ) ;
expect ( tempoLink ) . toEqual ( {
url : '' ,
title : 'Tempo' ,
internal : {
query : {
2023-10-09 14:22:39 +01:00
queryType : 'traceqlSearch' ,
refId : 'A' ,
filters : [
{
id : 'span-name' ,
operator : '=' ,
scope : 'span' ,
tag : 'name' ,
value : '"${__data.fields[0]}"' ,
valueType : 'string' ,
} ,
] ,
2022-06-09 17:56:15 +01:00
} ,
datasourceUid : 'gdev-tempo' ,
datasourceName : 'Tempo' ,
} ,
} ) ;
} ) ;
} ) ;
2023-08-30 13:45:39 +02:00
describe ( 'label names - v2 tags' , ( ) = > {
let datasource : TempoDatasource ;
beforeEach ( ( ) = > {
datasource = createTempoDatasource ( ) ;
jest . spyOn ( datasource , 'metadataRequest' ) . mockImplementation (
createMetadataRequest ( {
data : {
scopes : [ { name : 'span' , tags : [ 'label1' , 'label2' ] } ] ,
} ,
} )
) ;
} ) ;
it ( 'get label names' , async ( ) = > {
// label_names()
const response = await datasource . executeVariableQuery ( { refId : 'test' , type : TempoVariableQueryType . LabelNames } ) ;
expect ( response ) . toEqual ( [ { text : 'label1' } , { text : 'label2' } ] ) ;
} ) ;
} ) ;
describe ( 'label names - v1 tags' , ( ) = > {
let datasource : TempoDatasource ;
beforeEach ( ( ) = > {
datasource = createTempoDatasource ( ) ;
jest
. spyOn ( datasource , 'metadataRequest' )
. mockImplementationOnce ( ( ) = > {
throw Error ;
} )
. mockImplementation (
createMetadataRequest ( {
data : {
tagNames : [ 'label1' , 'label2' ] ,
} ,
} )
) ;
} ) ;
it ( 'get label names' , async ( ) = > {
// label_names()
const response = await datasource . executeVariableQuery ( { refId : 'test' , type : TempoVariableQueryType . LabelNames } ) ;
expect ( response ) . toEqual ( [ { text : 'label1' } , { text : 'label2' } , { text : 'status.code' } ] ) ;
} ) ;
} ) ;
describe ( 'label values' , ( ) = > {
let datasource : TempoDatasource ;
beforeEach ( ( ) = > {
datasource = createTempoDatasource ( ) ;
jest . spyOn ( datasource , 'metadataRequest' ) . mockImplementation (
createMetadataRequest ( {
data : {
tagValues : [
{
type : 'value1' ,
value : 'value1' ,
label : 'value1' ,
} ,
{
type : 'value2' ,
value : 'value2' ,
label : 'value2' ,
} ,
] ,
} ,
} )
) ;
} ) ;
it ( 'get label values for given label' , async ( ) = > {
// label_values("label")
const response = await datasource . executeVariableQuery ( {
refId : 'test' ,
type : TempoVariableQueryType . LabelValues ,
label : 'label' ,
} ) ;
expect ( response ) . toEqual ( [
{ text : { type : 'value1' , value : 'value1' , label : 'value1' } } ,
{ text : { type : 'value2' , value : 'value2' , label : 'value2' } } ,
] ) ;
} ) ;
it ( 'do not raise error when label is not set' , async ( ) = > {
// label_values()
const response = await datasource . executeVariableQuery ( {
refId : 'test' ,
type : TempoVariableQueryType . LabelValues ,
label : undefined ,
} ) ;
expect ( response ) . toEqual ( [ ] ) ;
} ) ;
} ) ;
2021-08-17 15:48:29 +02:00
const backendSrvWithPrometheus = {
async get ( uid : string ) {
if ( uid === 'prom' ) {
return {
query() {
2022-06-09 17:56:15 +01:00
return of ( {
2023-10-10 17:57:05 +03:00
data : [
rateMetric ,
errorRateMetric ,
durationMetric ,
emptyDurationMetric ,
totalsPromMetric ,
secondsPromMetric ,
failedPromMetric ,
] ,
2022-06-09 17:56:15 +01:00
} ) ;
2021-08-17 15:48:29 +02:00
} ,
} ;
}
throw new Error ( 'unexpected uid' ) ;
} ,
2024-01-16 11:36:40 +01:00
getInstanceSettings ( uid : string ) {
2022-07-18 15:36:16 +01:00
if ( uid === 'prom' ) {
return { name : 'Prometheus' } ;
} else if ( uid === 'gdev-tempo' ) {
return { name : 'Tempo' } ;
}
return '' ;
} ,
2021-08-17 15:48:29 +02:00
} ;
2021-03-22 19:09:15 +01:00
function setupBackendSrv ( frame : DataFrame ) {
setBackendSrv ( {
fetch ( ) : Observable < FetchResponse < BackendDataSourceResponse > > {
return of (
createFetchResponse ( {
results : {
refid1 : {
2021-04-01 10:30:08 -07:00
frames : [ dataFrameToJSON ( frame ) ] ,
2021-03-22 19:09:15 +01:00
} ,
} ,
} )
) ;
} ,
} as any ) ;
}
2020-10-13 19:12:49 +02:00
2023-08-28 15:02:12 +01:00
export const defaultSettings : DataSourceInstanceSettings < TempoJsonData > = {
2020-10-13 19:12:49 +02:00
id : 0 ,
2022-07-18 15:36:16 +01:00
uid : 'gdev-tempo' ,
2020-10-13 19:12:49 +02:00
type : 'tracing' ,
2021-08-17 15:48:29 +02:00
name : 'tempo' ,
2021-08-06 03:24:35 -04:00
access : 'proxy' ,
2020-10-13 19:12:49 +02:00
meta : {
2021-08-17 15:48:29 +02:00
id : 'tempo' ,
name : 'tempo' ,
2020-10-13 19:12:49 +02:00
type : PluginType . datasource ,
info : { } as any ,
module : '' ,
baseUrl : '' ,
} ,
2021-10-06 13:39:14 -06:00
jsonData : {
nodeGraph : {
enabled : true ,
} ,
} ,
2022-08-26 11:27:28 +01:00
readOnly : false ,
2020-10-13 19:12:49 +02:00
} ;
2021-08-17 15:48:29 +02:00
2023-05-01 19:41:40 -07:00
const rateMetric = createDataFrame ( {
2022-07-27 15:48:09 +01:00
refId : 'topk(5, sum(rate(traces_spanmetrics_calls_total{span_kind="SPAN_KIND_SERVER"}[$__range])) by (span_name))' ,
2022-06-09 17:56:15 +01:00
fields : [
{ name : 'Time' , values : [ 1653725618609 , 1653725618609 ] } ,
{ name : 'span_name' , values : [ 'HTTP Client' , 'HTTP GET - root' ] } ,
{
2022-07-27 15:48:09 +01:00
name : 'Value #topk(5, sum(rate(traces_spanmetrics_calls_total{span_kind="SPAN_KIND_SERVER"}[$__range])) by (span_name))' ,
2022-06-09 17:56:15 +01:00
values : [ 12.75164671814457 , 12.121331111401608 ] ,
} ,
] ,
} ) ;
2023-05-01 19:41:40 -07:00
const errorRateMetric = createDataFrame ( {
2022-06-09 17:56:15 +01:00
refId :
2022-06-24 10:19:26 +01:00
'topk(5, sum(rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR",span_name=~"HTTP Client|HTTP GET - root"}[$__range])) by (span_name))' ,
2022-06-09 17:56:15 +01:00
fields : [
{ name : 'Time' , values : [ 1653725618609 , 1653725618609 ] } ,
{ name : 'span_name' , values : [ 'HTTP Client' , 'HTTP GET - root' ] } ,
{
2022-06-24 10:19:26 +01:00
name : 'Value #topk(5, sum(rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR"}[$__range])) by (span_name))' ,
2022-06-09 17:56:15 +01:00
values : [ 3.75164671814457 , 3.121331111401608 ] ,
} ,
] ,
} ) ;
2023-05-01 19:41:40 -07:00
const durationMetric = createDataFrame ( {
2022-06-09 17:56:15 +01:00
refId :
2022-06-24 10:19:26 +01:00
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{span_name=~"HTTP GET - root"}[$__range])) by (le))' ,
2022-06-09 17:56:15 +01:00
fields : [
{ name : 'Time' , values : [ 1653725618609 ] } ,
{
2022-06-24 10:19:26 +01:00
name : 'Value #histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{span_name=~"HTTP GET - root"}[$__range])) by (le))' ,
2022-06-09 17:56:15 +01:00
values : [ 0.12003505696757232 ] ,
} ,
] ,
} ) ;
2023-10-10 17:57:05 +03:00
const emptyDurationMetric = createDataFrame ( {
refId :
'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{span_name=~"HTTP GET - root"}[$__range])) by (le))' ,
fields : [ ] ,
} ) ;
2023-05-01 19:41:40 -07:00
const totalsPromMetric = createDataFrame ( {
2021-10-19 11:58:21 +02:00
refId : 'traces_service_graph_request_total' ,
2021-08-17 15:48:29 +02:00
fields : [
{ name : 'Time' , values : [ 1628169788000 , 1628169788000 ] } ,
{ name : 'client' , values : [ 'app' , 'lb' ] } ,
{ name : 'instance' , values : [ '127.0.0.1:12345' , '127.0.0.1:12345' ] } ,
{ name : 'job' , values : [ 'local_scrape' , 'local_scrape' ] } ,
{ name : 'server' , values : [ 'db' , 'app' ] } ,
{ name : 'tempo_config' , values : [ 'default' , 'default' ] } ,
2021-10-19 11:58:21 +02:00
{ name : 'Value #traces_service_graph_request_total' , values : [ 10 , 20 ] } ,
2021-08-17 15:48:29 +02:00
] ,
} ) ;
2023-05-01 19:41:40 -07:00
const secondsPromMetric = createDataFrame ( {
2021-10-19 11:58:21 +02:00
refId : 'traces_service_graph_request_server_seconds_sum' ,
2021-08-17 15:48:29 +02:00
fields : [
{ name : 'Time' , values : [ 1628169788000 , 1628169788000 ] } ,
{ name : 'client' , values : [ 'app' , 'lb' ] } ,
{ name : 'instance' , values : [ '127.0.0.1:12345' , '127.0.0.1:12345' ] } ,
{ name : 'job' , values : [ 'local_scrape' , 'local_scrape' ] } ,
{ name : 'server' , values : [ 'db' , 'app' ] } ,
{ name : 'tempo_config' , values : [ 'default' , 'default' ] } ,
2021-10-19 11:58:21 +02:00
{ name : 'Value #traces_service_graph_request_server_seconds_sum' , values : [ 10 , 40 ] } ,
2021-08-17 15:48:29 +02:00
] ,
} ) ;
2021-08-25 10:08:46 -06:00
2023-05-01 19:41:40 -07:00
const failedPromMetric = createDataFrame ( {
2021-10-22 16:37:41 +02:00
refId : 'traces_service_graph_request_failed_total' ,
fields : [
{ name : 'Time' , values : [ 1628169788000 , 1628169788000 ] } ,
{ name : 'client' , values : [ 'app' , 'lb' ] } ,
{ name : 'instance' , values : [ '127.0.0.1:12345' , '127.0.0.1:12345' ] } ,
{ name : 'job' , values : [ 'local_scrape' , 'local_scrape' ] } ,
{ name : 'server' , values : [ 'db' , 'app' ] } ,
{ name : 'tempo_config' , values : [ 'default' , 'default' ] } ,
{ name : 'Value #traces_service_graph_request_failed_total' , values : [ 2 , 15 ] } ,
] ,
} ) ;
2021-08-25 10:08:46 -06:00
const mockInvalidJson = {
batches : [
{
resource : {
attributes : [ ] ,
} ,
instrumentation_library_spans : [
{
instrumentation_library : { } ,
spans : [
{
trace_id : 'AAAAAAAAAABguiq7RPE+rg==' ,
span_id : 'cmteMBAvwNA=' ,
parentSpanId : 'OY8PIaPbma4=' ,
name : 'HTTP GET - root' ,
kind : 'SPAN_KIND_SERVER' ,
startTimeUnixNano : '1627471657255809000' ,
endTimeUnixNano : '1627471657256268000' ,
attributes : [
{ key : 'http.status_code' , value : { intValue : '200' } } ,
{ key : 'http.method' , value : { stringValue : 'GET' } } ,
{ key : 'http.url' , value : { stringValue : '/' } } ,
{ key : 'component' , value : { stringValue : 'net/http' } } ,
] ,
status : { } ,
} ,
] ,
} ,
] ,
} ,
] ,
} ;
2022-01-13 08:28:20 -07:00
const serviceGraphLinks = [
{
url : '' ,
title : 'Request rate' ,
internal : {
query : {
2022-06-19 19:11:59 -06:00
expr : 'sum by (client, server)(rate(traces_service_graph_request_total{server="${__data.fields.id}"}[$__rate_interval]))' ,
2022-06-09 17:56:15 +01:00
instant : false ,
range : true ,
exemplar : true ,
2022-01-13 08:28:20 -07:00
} ,
datasourceUid : 'prom' ,
datasourceName : 'Prometheus' ,
} ,
} ,
2022-02-01 10:41:14 -07:00
{
url : '' ,
title : 'Request histogram' ,
internal : {
query : {
2022-02-11 07:58:10 -07:00
expr : 'histogram_quantile(0.9, sum(rate(traces_service_graph_request_server_seconds_bucket{server="${__data.fields.id}"}[$__rate_interval])) by (le, client, server))' ,
2022-06-09 17:56:15 +01:00
instant : false ,
range : true ,
exemplar : true ,
2022-02-01 10:41:14 -07:00
} ,
datasourceUid : 'prom' ,
datasourceName : 'Prometheus' ,
} ,
} ,
2022-01-13 08:28:20 -07:00
{
url : '' ,
title : 'Failed request rate' ,
internal : {
query : {
2022-06-19 19:11:59 -06:00
expr : 'sum by (client, server)(rate(traces_service_graph_request_failed_total{server="${__data.fields.id}"}[$__rate_interval]))' ,
2022-06-09 17:56:15 +01:00
instant : false ,
range : true ,
exemplar : true ,
2022-01-13 08:28:20 -07:00
} ,
datasourceUid : 'prom' ,
datasourceName : 'Prometheus' ,
} ,
} ,
2022-06-03 10:38:13 +01:00
{
url : '' ,
title : 'View traces' ,
internal : {
query : {
2023-10-09 14:22:39 +01:00
refId : 'A' ,
queryType : 'traceqlSearch' ,
filters : [
{
id : 'service-name' ,
operator : '=' ,
scope : 'resource' ,
tag : 'service.name' ,
value : '${__data.fields[0]}' ,
valueType : 'string' ,
} ,
] ,
2022-06-03 10:38:13 +01:00
} as TempoQuery ,
2022-07-18 15:36:16 +01:00
datasourceUid : 'gdev-tempo' ,
2022-06-03 10:38:13 +01:00
datasourceName : 'Tempo' ,
} ,
} ,
2022-01-13 08:28:20 -07:00
] ;