2022-04-26 11:43:18 -06:00
import { css } from '@emotion/css' ;
import React , { useEffect , useState } from 'react' ;
2021-11-11 14:27:59 +01:00
import useAsync from 'react-use/lib/useAsync' ;
2022-04-22 14:33:13 +01:00
2022-04-26 11:43:18 -06:00
import { GrafanaTheme2 } from '@grafana/data' ;
import { Alert , InlineField , InlineFieldRow , useStyles2 } from '@grafana/ui' ;
2022-04-22 14:33:13 +01:00
2024-01-16 11:36:40 +01:00
import { AdHocFilter } from './_importedDependencies/components/AdHocFilter/AdHocFilter' ;
import { PrometheusDatasource } from './_importedDependencies/datasources/prometheus/types' ;
import { AdHocVariableFilter } from './_importedDependencies/types' ;
2023-07-24 16:26:10 +01:00
import { TempoQuery } from './types' ;
2022-04-22 14:33:13 +01:00
import { getDS } from './utils' ;
2021-11-11 14:27:59 +01:00
export function ServiceGraphSection ( {
graphDatasourceUid ,
query ,
onChange ,
} : {
graphDatasourceUid? : string ;
query : TempoQuery ;
onChange : ( value : TempoQuery ) = > void ;
} ) {
2022-04-26 11:43:18 -06:00
const styles = useStyles2 ( getStyles ) ;
2021-11-11 14:27:59 +01:00
const dsState = useAsync ( ( ) = > getDS ( graphDatasourceUid ) , [ graphDatasourceUid ] ) ;
2022-04-26 11:43:18 -06:00
// Check if service graph metrics are being collected. If not, displays a warning
const [ hasKeys , setHasKeys ] = useState < boolean | undefined > ( undefined ) ;
useEffect ( ( ) = > {
async function fn ( ds : PrometheusDatasource ) {
2023-04-13 15:05:20 -05:00
const keys = await ds . getTagKeys ( {
2023-09-19 08:24:45 +02:00
filters : [
{
key : '__name__' ,
operator : '=~' ,
value :
'traces_service_graph_request_server_seconds_sum|traces_service_graph_request_total|traces_service_graph_request_failed_total' ,
condition : '' ,
} ,
2022-04-26 11:43:18 -06:00
] ,
} ) ;
setHasKeys ( Boolean ( keys . length ) ) ;
}
if ( ! dsState . loading && dsState . value ) {
fn ( dsState . value as PrometheusDatasource ) ;
}
} , [ dsState ] ) ;
2021-11-11 14:27:59 +01:00
if ( dsState . loading ) {
return null ;
}
2023-11-14 09:17:29 +00:00
const ds = dsState . value ;
2021-11-11 14:27:59 +01:00
if ( ! graphDatasourceUid ) {
2023-10-11 09:43:05 +01:00
return getWarning (
'No service graph datasource selected' ,
'Please set up a service graph datasource in the datasource settings' ,
styles
) ;
2021-11-11 14:27:59 +01:00
}
if ( graphDatasourceUid && ! ds ) {
2023-10-11 09:43:05 +01:00
return getWarning (
'No service graph data found' ,
'Service graph datasource is configured but the data source no longer exists. Please configure existing data source to use the service graph functionality' ,
styles
2021-11-11 14:27:59 +01:00
) ;
}
2023-09-19 08:24:45 +02:00
2021-11-11 14:27:59 +01:00
const filters = queryToFilter ( query . serviceMapQuery || '' ) ;
return (
< div >
< InlineFieldRow >
< InlineField label = "Filter" labelWidth = { 14 } grow >
< AdHocFilter
datasource = { { uid : graphDatasourceUid } }
filters = { filters }
2023-09-19 08:24:45 +02:00
baseFilters = { [
{
key : '__name__' ,
operator : '=~' ,
value : 'traces_service_graph_request_total|traces_spanmetrics_calls_total' ,
condition : '' ,
} ,
] }
2021-11-11 14:27:59 +01:00
addFilter = { ( filter : AdHocVariableFilter ) = > {
onChange ( {
. . . query ,
serviceMapQuery : filtersToQuery ( [ . . . filters , filter ] ) ,
} ) ;
} }
removeFilter = { ( index : number ) = > {
const newFilters = [ . . . filters ] ;
newFilters . splice ( index , 1 ) ;
onChange ( { . . . query , serviceMapQuery : filtersToQuery ( newFilters ) } ) ;
} }
changeFilter = { ( index : number , filter : AdHocVariableFilter ) = > {
const newFilters = [ . . . filters ] ;
newFilters . splice ( index , 1 , filter ) ;
onChange ( { . . . query , serviceMapQuery : filtersToQuery ( newFilters ) } ) ;
} }
/ >
< / InlineField >
< / InlineFieldRow >
2023-10-11 09:43:05 +01:00
{ hasKeys === false
? getWarning (
'No service graph data found' ,
'Please ensure that service graph metrics are set up correctly' ,
styles
)
: null }
2021-11-11 14:27:59 +01:00
< / div >
) ;
}
2023-10-11 09:43:05 +01:00
function getWarning ( title : string , description : string , styles : { alert : string ; link : string } ) {
return (
< Alert title = { title } severity = "info" className = { styles . alert } >
{ description } according to the { ' ' }
< a
target = "_blank"
rel = "noreferrer noopener"
href = "https://grafana.com/docs/grafana/latest/datasources/tempo/service-graph/"
className = { styles . link }
>
Tempo documentation
< / a >
.
< / Alert >
) ;
}
2021-11-11 14:27:59 +01:00
function queryToFilter ( query : string ) : AdHocVariableFilter [ ] {
let match ;
let filters : AdHocVariableFilter [ ] = [ ] ;
const re = /([\w_]+)(=|!=|<|>|=~|!~)"(.*?)"/g ;
while ( ( match = re . exec ( query ) ) !== null ) {
filters . push ( {
key : match [ 1 ] ,
operator : match [ 2 ] ,
value : match [ 3 ] ,
condition : '' ,
} ) ;
}
return filters ;
}
function filtersToQuery ( filters : AdHocVariableFilter [ ] ) : string {
return ` { ${ filters . map ( ( f ) = > ` ${ f . key } ${ f . operator } " ${ f . value } " ` ) . join ( ',' ) } } ` ;
}
2022-04-26 11:43:18 -06:00
const getStyles = ( theme : GrafanaTheme2 ) = > ( {
alert : css `
max - width : 75ch ;
margin - top : $ { theme . spacing ( 2 ) } ;
` ,
2023-06-28 10:34:11 +01:00
link : css `
color : $ { theme . colors . text . link } ;
text - decoration : underline ;
` ,
2022-04-26 11:43:18 -06:00
} ) ;