import { FieldColorModeId, getFrameDisplayName } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import { SceneFlexLayout, SceneByFrameRepeater, SceneTimePicker, VizPanel, EmbeddedScene, SceneDataNode, SceneTimeRange, VariableValueSelectors, SceneQueryRunner, SceneVariableSet, QueryVariable, SceneControlsSpacer, SceneDataTransformer, SceneRefreshPicker, SceneFlexItem, } from '@grafana/scenes'; import { PromQuery } from 'app/plugins/datasource/prometheus/types'; import { SceneRadioToggle } from './SceneRadioToggle'; import { SceneSearchBox } from './SceneSearchBox'; import { getTableFilterTransform, getTimeSeriesFilterTransform } from './transforms'; import { getLinkUrlWithAppUrlState } from './utils'; export function getHttpHandlerListScene(): EmbeddedScene { const searchBox = new SceneSearchBox({ value: '' }); const httpHandlerQueries = getInstantQuery({ expr: 'sort_desc(avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval]) * 1e3)) ', }); const httpHandlerQueriesFiltered = new SceneDataTransformer({ $data: httpHandlerQueries, transformations: [getTableFilterTransform('')], }); httpHandlerQueriesFiltered.addActivationHandler(() => { const sub = searchBox.subscribeToState((state) => { // Update transform and re-process them httpHandlerQueriesFiltered.setState({ transformations: [getTableFilterTransform(state.value)] }); httpHandlerQueriesFiltered.reprocessTransformations(); }); return () => sub.unsubscribe(); }); const httpHandlersTable = new VizPanel({ $data: httpHandlerQueriesFiltered, pluginId: 'table', title: '', options: { footer: { enablePagination: true, }, }, fieldConfig: { defaults: {}, overrides: [ { matcher: { id: 'byRegexp', options: '.*', }, properties: [{ id: 'filterable', value: false }], }, { matcher: { id: 'byName', options: 'Time', }, properties: [{ id: 'custom.hidden', value: true }], }, { matcher: { id: 'byName', options: 'Value', }, properties: [{ id: 'displayName', value: 'Duration (Avg)' }], }, { matcher: { id: 'byName', options: 'handler', }, properties: [ { id: 'links', value: [ { title: 'Go to handler drilldown view', onBuildUrl: () => { const params = locationService.getSearchObject(); return getLinkUrlWithAppUrlState( '/scenes/grafana-monitoring/handlers/${__value.text:percentencode}', params ); }, }, ], }, ], }, ], }, }); const reqDurationTimeSeries = new SceneQueryRunner({ datasource: { uid: 'gdev-prometheus' }, queries: [ { refId: 'A', //expr: ``, expr: 'topk(20, avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval])) * 1e3)', range: true, format: 'time_series', legendFormat: '{{method}} {{handler}} (status = {{status_code}})', maxDataPoints: 500, }, ], }); const reqDurationTimeSeriesFiltered = new SceneDataTransformer({ $data: reqDurationTimeSeries, transformations: [getTimeSeriesFilterTransform('')], }); reqDurationTimeSeriesFiltered.addActivationHandler(() => { const sub = searchBox.subscribeToState((state) => { // Update transform and re-process them reqDurationTimeSeriesFiltered.setState({ transformations: [getTimeSeriesFilterTransform(state.value)] }); reqDurationTimeSeriesFiltered.reprocessTransformations(); }); return () => sub.unsubscribe(); }); const graphsScene = new SceneByFrameRepeater({ $data: reqDurationTimeSeriesFiltered, body: new SceneFlexLayout({ direction: 'column', children: [], }), getLayoutChild: (data, frame, frameIndex) => { return new SceneFlexItem({ key: `panel-${frameIndex}`, minHeight: 200, $data: new SceneDataNode({ data: { ...data, series: [frame], }, }), body: new SceneFlexLayout({ direction: 'row', children: [ new SceneFlexItem({ body: new VizPanel({ pluginId: 'timeseries', // titleLink: { // path: `/scenes/grafana-monitoring/handlers/${encodeURIComponent(frame.fields[1].labels.handler)}`, // queryKeys: ['from', 'to', 'var-instance'], // }, title: getFrameDisplayName(frame), options: { legend: { displayMode: 'hidden' }, }, }), }), new SceneFlexItem({ width: 200, body: new VizPanel({ title: 'Last', pluginId: 'stat', fieldConfig: { defaults: { displayName: 'Last', links: [ { title: 'Go to handler drilldown view', url: ``, onBuildUrl: () => { const params = locationService.getSearchObject(); return getLinkUrlWithAppUrlState( '/scenes/grafana-monitoring/handlers/${__field.labels.handler:percentencode}', params ); }, }, ], }, overrides: [], }, options: { graphMode: 'none', textMode: 'value', }, }), }), ], }), }); }, }); const layout = new SceneFlexLayout({ children: [new SceneFlexItem({ body: httpHandlersTable })], }); const sceneToggle = new SceneRadioToggle({ options: [ { value: 'table', label: 'Table' }, { value: 'graphs', label: 'Graphs' }, ], value: 'table', onChange: (value) => { if (value === 'table') { layout.setState({ children: [new SceneFlexItem({ body: httpHandlersTable })] }); } else { layout.setState({ children: [graphsScene] }); } }, }); const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), $data: httpHandlerQueries, $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), controls: [ new VariableValueSelectors({}), searchBox, new SceneControlsSpacer(), sceneToggle, new SceneTimePicker({ isOnCanvas: true }), new SceneRefreshPicker({ isOnCanvas: true }), ], body: layout, }); return scene; } export function getHandlerDetailsScene(handler: string): EmbeddedScene { const reqDurationTimeSeries = getTimeSeriesQuery({ expr: `avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum{handler="${handler}"}[$__rate_interval])) * 1e3`, legendFormat: '{{method}} {{handler}} (status = {{status_code}})', }); const reqCountTimeSeries = getTimeSeriesQuery({ expr: `sum without(job, instance) (rate(grafana_http_request_duration_seconds_count{handler="${handler}"}[$__rate_interval])) `, legendFormat: '{{method}} {{handler}} (status = {{status_code}})', }); const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), controls: [ new VariableValueSelectors({}), new SceneControlsSpacer(), new SceneTimePicker({ isOnCanvas: true }), new SceneRefreshPicker({ isOnCanvas: true }), ], body: new SceneFlexLayout({ direction: 'column', children: [ new SceneFlexItem({ body: new VizPanel({ $data: reqDurationTimeSeries, pluginId: 'timeseries', title: 'Request duration avg (ms)', //displayMode: 'transparent', options: {}, }), }), new SceneFlexItem({ body: new VizPanel({ $data: reqCountTimeSeries, pluginId: 'timeseries', title: 'Request count/s', //displayMode: 'transparent', options: {}, }), }), ], }), }); return scene; } function getInstantQuery(query: Partial): SceneQueryRunner { return new SceneQueryRunner({ datasource: { uid: 'gdev-prometheus' }, queries: [ { refId: 'A', instant: true, format: 'table', maxDataPoints: 500, ...query, }, ], }); } function getTimeSeriesQuery(query: Partial): SceneQueryRunner { return new SceneQueryRunner({ datasource: { uid: 'gdev-prometheus' }, queries: [ { refId: 'A', range: true, format: 'time_series', maxDataPoints: 500, ...query, }, ], }); } export function getOverviewScene(): EmbeddedScene { const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), controls: [ new VariableValueSelectors({}), new SceneControlsSpacer(), new SceneTimePicker({ isOnCanvas: true }), new SceneRefreshPicker({ isOnCanvas: true }), ], body: new SceneFlexLayout({ direction: 'column', children: [ new SceneFlexItem({ height: 150, body: new SceneFlexLayout({ children: [ new SceneFlexItem({ body: getInstantStatPanel('grafana_stat_totals_dashboard', 'Dashboards'), }), new SceneFlexItem({ body: getInstantStatPanel('grafana_stat_total_users', 'Users'), }), new SceneFlexItem({ body: getInstantStatPanel('sum(grafana_stat_totals_datasource)', 'Data sources'), }), new SceneFlexItem({ body: getInstantStatPanel('grafana_stat_total_service_account_tokens', 'Service account tokens'), }), ], }), }), new SceneFlexItem({ body: new VizPanel({ $data: new SceneQueryRunner({ datasource: { uid: 'gdev-prometheus' }, queries: [ { refId: 'A', expr: `sum(process_resident_memory_bytes{job="grafana", instance=~"$instance"})`, range: true, format: 'time_series', maxDataPoints: 500, }, ], }), pluginId: 'timeseries', title: 'Memory usage', options: { legend: { showLegend: false, }, }, fieldConfig: { defaults: { unit: 'bytes', min: 0, custom: { lineWidth: 2, fillOpacity: 6, //gradientMode: 'opacity', }, }, overrides: [], }, }), }), new SceneFlexItem({ body: new VizPanel({ $data: new SceneQueryRunner({ datasource: { uid: 'gdev-prometheus' }, queries: [ { refId: 'A', expr: `sum(go_goroutines{job="grafana", instance=~"$instance"})`, range: true, format: 'time_series', maxDataPoints: 500, }, ], }), pluginId: 'timeseries', title: 'Go routines', options: { legend: { showLegend: false, }, }, fieldConfig: { defaults: { min: 0, custom: { lineWidth: 2, fillOpacity: 6, //gradientMode: 'opacity', }, }, overrides: [], }, }), }), ], }), }); return scene; } function getVariablesDefinitions() { return new SceneVariableSet({ variables: [ new QueryVariable({ name: 'instance', datasource: { uid: 'gdev-prometheus' }, query: { query: 'label_values(grafana_http_request_duration_seconds_sum, instance)' }, }), ], }); } function getInstantStatPanel(query: string, title: string) { return new VizPanel({ $data: getInstantQuery({ expr: query }), pluginId: 'stat', title, options: {}, fieldConfig: { defaults: { color: { fixedColor: 'text', mode: FieldColorModeId.Fixed }, }, overrides: [], }, }); } export function getHandlerLogsScene(handler: string): EmbeddedScene { const logsQuery = new SceneQueryRunner({ datasource: { uid: 'gdev-loki' }, queries: [ { refId: 'A', expr: `{job="grafana"} | logfmt | handler=\`${handler}\` | __error__=\`\``, queryType: 'range', maxDataPoints: 5000, }, ], }); const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), controls: [ new VariableValueSelectors({}), new SceneControlsSpacer(), new SceneTimePicker({ isOnCanvas: true }), new SceneRefreshPicker({ isOnCanvas: true }), ], body: new SceneFlexLayout({ direction: 'column', children: [ new SceneFlexItem({ body: new VizPanel({ pluginId: 'text', title: '', options: { mode: 'markdown', content: ` [mupp](/scenes/grafana-monitoring/handlers/${encodeURIComponent(handler)}/logs/mupp) [mapp](/scenes/grafana-monitoring/handlers/${encodeURIComponent(handler)}/logs/mapp) `, }, }), }), new SceneFlexItem({ body: new VizPanel({ $data: logsQuery, pluginId: 'logs', title: '', options: { showTime: true, showLabels: false, showCommonLabels: false, wrapLogMessage: true, prettifyLogMessage: false, enableLogDetails: true, dedupStrategy: 'none', sortOrder: 'Descending', }, }), }), ], }), }); return scene; } export function getOverviewLogsScene(): EmbeddedScene { const logsQuery = new SceneQueryRunner({ datasource: { uid: 'gdev-loki' }, queries: [ { refId: 'A', expr: `{job="grafana"} | logfmt | __error__=\`\``, queryType: 'range', maxDataPoints: 5000, }, ], }); const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), controls: [ new VariableValueSelectors({}), new SceneControlsSpacer(), new SceneTimePicker({ isOnCanvas: true }), new SceneRefreshPicker({ isOnCanvas: true }), ], body: new SceneFlexLayout({ direction: 'column', children: [ new SceneFlexItem({ body: new VizPanel({ $data: logsQuery, pluginId: 'logs', title: '', options: { showTime: true, showLabels: false, showCommonLabels: false, wrapLogMessage: true, prettifyLogMessage: false, enableLogDetails: true, dedupStrategy: 'none', sortOrder: 'Descending', }, }), }), ], }), }); return scene; }