123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { DataQueryResponse, DataFrame, isDataFrame, FieldType, QueryResultMeta, DataQueryError } from '@grafana/data';
- import { getDerivedFields } from './getDerivedFields';
- import { makeTableFrames } from './makeTableFrames';
- import { formatQuery, getHighlighterExpressionsFromQuery } from './query_utils';
- import { dataFrameHasLokiError } from './responseUtils';
- import { DerivedFieldConfig, LokiQuery, LokiQueryType } from './types';
- function isMetricFrame(frame: DataFrame): boolean {
- return frame.fields.every((field) => field.type === FieldType.time || field.type === FieldType.number);
- }
- // returns a new frame, with meta shallow merged with it's original meta
- function setFrameMeta(frame: DataFrame, meta: QueryResultMeta): DataFrame {
- const { meta: oldMeta, ...rest } = frame;
- // meta maybe be undefined, we need to handle that
- const newMeta = { ...oldMeta, ...meta };
- return {
- ...rest,
- meta: newMeta,
- };
- }
- function processStreamFrame(
- frame: DataFrame,
- query: LokiQuery | undefined,
- derivedFieldConfigs: DerivedFieldConfig[]
- ): DataFrame {
- const custom: Record<string, string> = {
- ...frame.meta?.custom, // keep the original meta.custom
- // used by logs_model
- lokiQueryStatKey: 'Summary: total bytes processed',
- };
- if (dataFrameHasLokiError(frame)) {
- custom.error = 'Error when parsing some of the logs';
- }
- const meta: QueryResultMeta = {
- preferredVisualisationType: 'logs',
- limit: query?.maxLines,
- searchWords: query !== undefined ? getHighlighterExpressionsFromQuery(formatQuery(query.expr)) : undefined,
- custom,
- };
- const newFrame = setFrameMeta(frame, meta);
- const derivedFields = getDerivedFields(newFrame, derivedFieldConfigs);
- return {
- ...newFrame,
- fields: [...newFrame.fields, ...derivedFields],
- };
- }
- function processStreamsFrames(
- frames: DataFrame[],
- queryMap: Map<string, LokiQuery>,
- derivedFieldConfigs: DerivedFieldConfig[]
- ): DataFrame[] {
- return frames.map((frame) => {
- const query = frame.refId !== undefined ? queryMap.get(frame.refId) : undefined;
- return processStreamFrame(frame, query, derivedFieldConfigs);
- });
- }
- function processMetricInstantFrames(frames: DataFrame[]): DataFrame[] {
- return frames.length > 0 ? makeTableFrames(frames) : [];
- }
- function processMetricRangeFrames(frames: DataFrame[]): DataFrame[] {
- const meta: QueryResultMeta = { preferredVisualisationType: 'graph' };
- return frames.map((frame) => setFrameMeta(frame, meta));
- }
- // we split the frames into 3 groups, because we will handle
- // each group slightly differently
- function groupFrames(
- frames: DataFrame[],
- queryMap: Map<string, LokiQuery>
- ): {
- streamsFrames: DataFrame[];
- metricInstantFrames: DataFrame[];
- metricRangeFrames: DataFrame[];
- } {
- const streamsFrames: DataFrame[] = [];
- const metricInstantFrames: DataFrame[] = [];
- const metricRangeFrames: DataFrame[] = [];
- frames.forEach((frame) => {
- if (!isMetricFrame(frame)) {
- streamsFrames.push(frame);
- } else {
- const isInstantFrame = frame.refId != null && queryMap.get(frame.refId)?.queryType === LokiQueryType.Instant;
- if (isInstantFrame) {
- metricInstantFrames.push(frame);
- } else {
- metricRangeFrames.push(frame);
- }
- }
- });
- return { streamsFrames, metricInstantFrames, metricRangeFrames };
- }
- function improveError(error: DataQueryError | undefined, queryMap: Map<string, LokiQuery>): DataQueryError | undefined {
- // many things are optional in an error-object, we need an error-message to exist,
- // and we need to find the loki-query, based on the refId in the error-object.
- if (error === undefined) {
- return error;
- }
- const { refId, message } = error;
- if (refId === undefined || message === undefined) {
- return error;
- }
- const query = queryMap.get(refId);
- if (query === undefined) {
- return error;
- }
- if (message.includes('escape') && query.expr.includes('\\')) {
- return {
- ...error,
- message: `${message}. Make sure that all special characters are escaped with \\. For more information on escaping of special characters visit LogQL documentation at https://grafana.com/docs/loki/latest/logql/.`,
- };
- }
- return error;
- }
- export function transformBackendResult(
- response: DataQueryResponse,
- queries: LokiQuery[],
- derivedFieldConfigs: DerivedFieldConfig[]
- ): DataQueryResponse {
- const { data, error, ...rest } = response;
- // in the typescript type, data is an array of basically anything.
- // we do know that they have to be dataframes, so we make a quick check,
- // this way we can be sure, and also typescript is happy.
- const dataFrames = data.map((d) => {
- if (!isDataFrame(d)) {
- throw new Error('transformation only supports dataframe responses');
- }
- return d;
- });
- const queryMap = new Map(queries.map((query) => [query.refId, query]));
- const { streamsFrames, metricInstantFrames, metricRangeFrames } = groupFrames(dataFrames, queryMap);
- return {
- ...rest,
- error: improveError(error, queryMap),
- data: [
- ...processMetricRangeFrames(metricRangeFrames),
- ...processMetricInstantFrames(metricInstantFrames),
- ...processStreamsFrames(streamsFrames, queryMap, derivedFieldConfigs),
- ],
- };
- }
|