mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-20 15:20:31 +01:00
Compare commits
1 Commits
v0.91.0
...
dummy-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b073e03cb |
@@ -258,7 +258,6 @@
|
||||
"cookie": "^0.7.1",
|
||||
"serialize-javascript": "6.0.2",
|
||||
"prismjs": "1.30.0",
|
||||
"got": "11.8.5",
|
||||
"stylus": "0.0.1-security"
|
||||
"got": "11.8.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import './LogDetails.styles.scss';
|
||||
|
||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||
import Convert from 'ansi-to-html';
|
||||
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
|
||||
import { RadioChangeEvent } from 'antd/lib';
|
||||
import cx from 'classnames';
|
||||
@@ -16,10 +17,12 @@ import JSONView from 'container/LogDetailedView/JsonView';
|
||||
import Overview from 'container/LogDetailedView/Overview';
|
||||
import {
|
||||
aggregateAttributesResourcesToString,
|
||||
getSanitizedLogBody,
|
||||
escapeHtml,
|
||||
removeEscapeCharacters,
|
||||
unescapeString,
|
||||
} from 'container/LogDetailedView/utils';
|
||||
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||
import dompurify from 'dompurify';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
@@ -43,11 +46,14 @@ import { AppState } from 'store/reducers';
|
||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
import { RESOURCE_KEYS, VIEW_TYPES, VIEWS } from './constants';
|
||||
import { LogDetailProps } from './LogDetail.interfaces';
|
||||
import QueryBuilderSearchWrapper from './QueryBuilderSearchWrapper';
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
function LogDetail({
|
||||
log,
|
||||
onClose,
|
||||
@@ -112,7 +118,11 @@ function LogDetail({
|
||||
|
||||
const htmlBody = useMemo(
|
||||
() => ({
|
||||
__html: getSanitizedLogBody(log?.body || '', { shouldEscapeHtml: true }),
|
||||
__html: convert.toHtml(
|
||||
dompurify.sanitize(unescapeString(escapeHtml(log?.body || '')), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
),
|
||||
}),
|
||||
[log?.body],
|
||||
);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import './ListLogView.styles.scss';
|
||||
|
||||
import { blue } from '@ant-design/colors';
|
||||
import Convert from 'ansi-to-html';
|
||||
import { Typography } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import dompurify from 'dompurify';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -18,6 +20,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
// interfaces
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
// components
|
||||
import AddToQueryHOC, { AddToQueryHOCProps } from '../AddToQueryHOC';
|
||||
@@ -34,6 +37,8 @@ import {
|
||||
} from './styles';
|
||||
import { isValidLogField } from './util';
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
interface LogFieldProps {
|
||||
fieldKey: string;
|
||||
fieldValue: string;
|
||||
@@ -52,7 +57,11 @@ function LogGeneralField({
|
||||
}: LogFieldProps): JSX.Element {
|
||||
const html = useMemo(
|
||||
() => ({
|
||||
__html: getSanitizedLogBody(fieldValue, { shouldEscapeHtml: true }),
|
||||
__html: convert.toHtml(
|
||||
dompurify.sanitize(unescapeString(escapeHtml(fieldValue)), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
),
|
||||
}),
|
||||
[fieldValue],
|
||||
);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import './RawLogView.styles.scss';
|
||||
|
||||
import Convert from 'ansi-to-html';
|
||||
import { DrawerProps } from 'antd';
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import { escapeHtml, unescapeString } from 'container/LogDetailedView/utils';
|
||||
import LogsExplorerContext from 'container/LogsExplorerContext';
|
||||
import dompurify from 'dompurify';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
// hooks
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
import LogLinesActionButtons from '../LogLinesActionButtons/LogLinesActionButtons';
|
||||
import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
|
||||
@@ -29,6 +32,8 @@ import { getLogIndicatorType } from '../LogStateIndicator/utils';
|
||||
import { RawLogContent, RawLogViewContainer } from './styles';
|
||||
import { RawLogViewProps } from './types';
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
function RawLogView({
|
||||
isActiveLog,
|
||||
isReadOnly,
|
||||
@@ -171,7 +176,11 @@ function RawLogView({
|
||||
|
||||
const html = useMemo(
|
||||
() => ({
|
||||
__html: getSanitizedLogBody(text, { shouldEscapeHtml: true }),
|
||||
__html: convert.toHtml(
|
||||
dompurify.sanitize(unescapeString(escapeHtml(text)), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
),
|
||||
}),
|
||||
[text],
|
||||
);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import './useTableView.styles.scss';
|
||||
|
||||
import Convert from 'ansi-to-html';
|
||||
import { Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import cx from 'classnames';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import { unescapeString } from 'container/LogDetailedView/utils';
|
||||
import dompurify from 'dompurify';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { useMemo } from 'react';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
|
||||
import { getLogIndicatorTypeForTable } from '../LogStateIndicator/utils';
|
||||
@@ -24,6 +27,8 @@ import {
|
||||
UseTableViewResult,
|
||||
} from './types';
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
||||
const {
|
||||
logs,
|
||||
@@ -144,7 +149,11 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
||||
children: (
|
||||
<TableBodyContent
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getSanitizedLogBody(field as string),
|
||||
__html: convert.toHtml(
|
||||
dompurify.sanitize(unescapeString(field as string), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
),
|
||||
}}
|
||||
fontSize={fontSize}
|
||||
linesPerRow={linesPerRow}
|
||||
|
||||
@@ -47,5 +47,4 @@ export enum QueryParams {
|
||||
destination = 'destination',
|
||||
kindString = 'kindString',
|
||||
tab = 'tab',
|
||||
thresholds = 'thresholds',
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
import * as usePrefillAlertConditions from 'container/FormAlertRules/usePrefillAlertConditions';
|
||||
import CreateAlertPage from 'pages/CreateAlert';
|
||||
import { MemoryRouter, Route } from 'react-router-dom';
|
||||
import { act, fireEvent, render } from 'tests/test-utils';
|
||||
@@ -34,15 +33,6 @@ jest.mock('hooks/useSafeNavigate', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest
|
||||
.spyOn(usePrefillAlertConditions, 'usePrefillAlertConditions')
|
||||
.mockReturnValue({
|
||||
matchType: '3',
|
||||
op: '1',
|
||||
target: 100,
|
||||
targetUnit: 'rpm',
|
||||
});
|
||||
|
||||
let mockWindowOpen: jest.Mock;
|
||||
|
||||
window.ResizeObserver =
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
import * as usePrefillAlertConditions from 'container/FormAlertRules/usePrefillAlertConditions';
|
||||
import CreateAlertPage from 'pages/CreateAlert';
|
||||
import { MemoryRouter, Route } from 'react-router-dom';
|
||||
import { act, fireEvent, render } from 'tests/test-utils';
|
||||
@@ -42,15 +41,6 @@ jest.mock('hooks/useSafeNavigate', () => ({
|
||||
safeNavigate: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
jest
|
||||
.spyOn(usePrefillAlertConditions, 'usePrefillAlertConditions')
|
||||
.mockReturnValue({
|
||||
matchType: '3',
|
||||
op: '1',
|
||||
target: 100,
|
||||
targetUnit: 'rpm',
|
||||
});
|
||||
|
||||
describe('Anomaly Alert Documentation Redirection', () => {
|
||||
let mockWindowOpen: jest.Mock;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import logEvent from 'api/common/logEvent';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import FormAlertRules, { AlertDetectionTypes } from 'container/FormAlertRules';
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import history from 'lib/history';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -33,12 +32,6 @@ function CreateRules(): JSX.Element {
|
||||
? AlertTypes.ANOMALY_BASED_ALERT
|
||||
: queryParams.get(QueryParams.alertType);
|
||||
|
||||
const { thresholds } = (location.state as {
|
||||
thresholds: ThresholdProps[];
|
||||
}) || {
|
||||
thresholds: null,
|
||||
};
|
||||
|
||||
const compositeQuery = useGetCompositeQueryParam();
|
||||
function getAlertTypeFromDataSource(): AlertTypes | null {
|
||||
if (!compositeQuery) {
|
||||
@@ -103,9 +96,7 @@ function CreateRules(): JSX.Element {
|
||||
}
|
||||
|
||||
const generatedUrl = `${location.pathname}?${queryParams.toString()}`;
|
||||
history.replace(generatedUrl, {
|
||||
thresholds,
|
||||
});
|
||||
history.replace(generatedUrl);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -390,7 +390,7 @@ function RuleOptions({
|
||||
<Space direction="vertical" size="large">
|
||||
{ruleType !== AlertDetectionTypes.ANOMALY_DETECTION_ALERT && (
|
||||
<Space direction="horizontal" align="center">
|
||||
<Form.Item noStyle>
|
||||
<Form.Item noStyle name={['condition', 'target']}>
|
||||
<InputNumber
|
||||
addonBefore={t('field_threshold')}
|
||||
value={alertDef?.condition?.target}
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
import { usePrefillAlertConditions } from '../usePrefillAlertConditions';
|
||||
|
||||
const TEST_MAPPINGS = {
|
||||
op: {
|
||||
'>': '1',
|
||||
'<': '2',
|
||||
'=': '3',
|
||||
},
|
||||
matchType: {
|
||||
avg: '3',
|
||||
sum: '4',
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('react-router-dom-v5-compat', () => {
|
||||
const mockThreshold1 = {
|
||||
index: '0d11f426-a02e-48da-867c-b79c6ef1ff06',
|
||||
isEditEnabled: false,
|
||||
keyIndex: 1,
|
||||
selectedGraph: 'graph',
|
||||
thresholdColor: 'Orange',
|
||||
thresholdFormat: 'Text',
|
||||
thresholdLabel: 'Caution',
|
||||
thresholdOperator: '>',
|
||||
thresholdTableOptions: 'A',
|
||||
thresholdUnit: 'rpm',
|
||||
thresholdValue: 800,
|
||||
};
|
||||
const mockThreshold2 = {
|
||||
index: 'edbe8ef2-fa54-4cb9-b343-7afe883bb714',
|
||||
isEditEnabled: false,
|
||||
keyIndex: 0,
|
||||
selectedGraph: 'graph',
|
||||
thresholdColor: 'Red',
|
||||
thresholdFormat: 'Text',
|
||||
thresholdLabel: 'Danger',
|
||||
thresholdOperator: '<',
|
||||
thresholdTableOptions: 'A',
|
||||
thresholdUnit: 'rpm',
|
||||
thresholdValue: 900,
|
||||
};
|
||||
return {
|
||||
...jest.requireActual('react-router-dom-v5-compat'),
|
||||
useLocation: jest.fn().mockReturnValue({
|
||||
state: {
|
||||
thresholds: [mockThreshold1, mockThreshold2],
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mockStagedQuery = {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('usePrefillAlertConditions', () => {
|
||||
it('returns the correct matchType for a single query', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePrefillAlertConditions(mockStagedQuery as any),
|
||||
);
|
||||
expect(result.current.matchType).toBe(TEST_MAPPINGS.matchType.avg);
|
||||
});
|
||||
|
||||
it('returns null matchType for a single query with unsupported time aggregation', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePrefillAlertConditions({
|
||||
builder: { queryData: [{ reduceTo: 'p90' }] },
|
||||
} as any),
|
||||
);
|
||||
expect(result.current.matchType).toBe(null);
|
||||
});
|
||||
|
||||
it('returns the correct matchType for multiple queries with same time aggregation', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePrefillAlertConditions({
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
{
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any),
|
||||
);
|
||||
expect(result.current.matchType).toBe(TEST_MAPPINGS.matchType.avg);
|
||||
});
|
||||
|
||||
it('returns null matchType for multiple queries with different time aggregation', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePrefillAlertConditions({
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
{
|
||||
reduceTo: 'sum',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any),
|
||||
);
|
||||
expect(result.current.matchType).toBe(null);
|
||||
});
|
||||
|
||||
it('returns the correct op, target, targetUnit from the higher priority threshold for multiple thresholds', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePrefillAlertConditions(mockStagedQuery as any),
|
||||
);
|
||||
expect(result.current.op).toBe(TEST_MAPPINGS.op['<']);
|
||||
expect(result.current.target).toBe(900);
|
||||
expect(result.current.targetUnit).toBe('rpm');
|
||||
});
|
||||
});
|
||||
@@ -57,7 +57,6 @@ import {
|
||||
StepContainer,
|
||||
StepHeading,
|
||||
} from './styles';
|
||||
import { usePrefillAlertConditions } from './usePrefillAlertConditions';
|
||||
import { getSelectedQueryOptions } from './utils';
|
||||
|
||||
export enum AlertDetectionTypes {
|
||||
@@ -114,9 +113,6 @@ function FormAlertRules({
|
||||
handleSetConfig,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
const { matchType, op, target, targetUnit } = usePrefillAlertConditions(
|
||||
stagedQuery,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
handleSetConfig(panelType || PANEL_TYPES.TIME_SERIES, dataSource);
|
||||
@@ -270,13 +266,6 @@ function FormAlertRules({
|
||||
...initialValue,
|
||||
broadcastToAll: !broadcastToSpecificChannels,
|
||||
ruleType,
|
||||
condition: {
|
||||
...initialValue.condition,
|
||||
matchType: matchType || initialValue.condition.matchType,
|
||||
op: op || initialValue.condition.op,
|
||||
target: target || initialValue.condition.target,
|
||||
targetUnit: targetUnit || initialValue.condition.targetUnit,
|
||||
},
|
||||
});
|
||||
|
||||
setDetectionMethod(ruleType);
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom-v5-compat';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
const THRESHOLD_COLORS_SORTING_ORDER = ['Red', 'Orange', 'Green', 'Blue'];
|
||||
|
||||
export const usePrefillAlertConditions = (
|
||||
stagedQuery: Query | null,
|
||||
): {
|
||||
matchType: string | null;
|
||||
op: string | null;
|
||||
target: number | undefined;
|
||||
targetUnit: string | undefined;
|
||||
} => {
|
||||
const location = useLocation();
|
||||
|
||||
// Extract and set match type
|
||||
const reduceTo = useMemo(() => {
|
||||
if (!stagedQuery) return null;
|
||||
const isSameTimeAggregation = stagedQuery.builder.queryData.every(
|
||||
(queryData) =>
|
||||
queryData.reduceTo === stagedQuery.builder.queryData[0].reduceTo,
|
||||
);
|
||||
return isSameTimeAggregation
|
||||
? stagedQuery.builder.queryData[0].reduceTo
|
||||
: null;
|
||||
}, [stagedQuery]);
|
||||
|
||||
const matchType = useMemo(() => {
|
||||
switch (reduceTo) {
|
||||
case 'avg':
|
||||
return '3';
|
||||
case 'sum':
|
||||
return '4';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, [reduceTo]);
|
||||
|
||||
// Extract and set threshold operator, value and unit
|
||||
const threshold = useMemo(() => {
|
||||
const { thresholds } = (location.state as {
|
||||
thresholds: ThresholdProps[];
|
||||
}) || {
|
||||
thresholds: null,
|
||||
};
|
||||
if (!thresholds || thresholds.length === 0) return null;
|
||||
const sortedThresholds = thresholds.sort((a, b) => {
|
||||
const aIndex = THRESHOLD_COLORS_SORTING_ORDER.indexOf(
|
||||
a.thresholdColor || '',
|
||||
);
|
||||
const bIndex = THRESHOLD_COLORS_SORTING_ORDER.indexOf(
|
||||
b.thresholdColor || '',
|
||||
);
|
||||
return aIndex - bIndex;
|
||||
});
|
||||
return sortedThresholds[0];
|
||||
}, [location.state]);
|
||||
|
||||
const thresholdOperator = useMemo(() => {
|
||||
const op = threshold?.thresholdOperator;
|
||||
switch (op) {
|
||||
case '>':
|
||||
case '>=':
|
||||
return '1';
|
||||
case '<':
|
||||
case '<=':
|
||||
return '2';
|
||||
case '=':
|
||||
return '3';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, [threshold]);
|
||||
|
||||
const thresholdUnit = useMemo(() => threshold?.thresholdUnit, [threshold]);
|
||||
|
||||
const thresholdValue = useMemo(() => threshold?.thresholdValue, [threshold]);
|
||||
|
||||
return {
|
||||
matchType,
|
||||
op: thresholdOperator,
|
||||
target: thresholdValue,
|
||||
targetUnit: thresholdUnit,
|
||||
};
|
||||
};
|
||||
@@ -196,6 +196,7 @@ function GridCardGraph({
|
||||
[requestData.query],
|
||||
);
|
||||
|
||||
console.log('requestData', requestData);
|
||||
const queryResponse = useGetQueryRange(
|
||||
{
|
||||
...requestData,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import './TableViewActions.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import Convert from 'ansi-to-html';
|
||||
import { Button, Popover, Spin, Tooltip, Tree } from 'antd';
|
||||
import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
|
||||
import cx from 'classnames';
|
||||
@@ -11,19 +12,22 @@ import { OPERATORS } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
|
||||
import { MetricsType } from 'container/MetricsApplication/constant';
|
||||
import dompurify from 'dompurify';
|
||||
import { ArrowDownToDot, ArrowUpFromDot, Ellipsis } from 'lucide-react';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
import { DataType } from '../TableView';
|
||||
import {
|
||||
escapeHtml,
|
||||
filterKeyForField,
|
||||
getFieldAttributes,
|
||||
getSanitizedLogBody,
|
||||
parseFieldValue,
|
||||
removeEscapeCharacters,
|
||||
unescapeString,
|
||||
} from '../utils';
|
||||
import useAsyncJSONProcessing from './useAsyncJSONProcessing';
|
||||
|
||||
@@ -47,6 +51,8 @@ interface ITableViewActionsProps {
|
||||
) => () => void;
|
||||
}
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
// Memoized Tree Component
|
||||
const MemoizedTree = React.memo<{ treeData: any[] }>(({ treeData }) => (
|
||||
<Tree
|
||||
@@ -140,7 +146,11 @@ export default function TableViewActions(
|
||||
if (record.field !== 'body') return { __html: '' };
|
||||
|
||||
return {
|
||||
__html: getSanitizedLogBody(record.value, { shouldEscapeHtml: true }),
|
||||
__html: convert.toHtml(
|
||||
dompurify.sanitize(unescapeString(escapeHtml(record.value)), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
),
|
||||
};
|
||||
}, [record.field, record.value]);
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { jsonToDataNodes, recursiveParseJSON } from '../utils';
|
||||
|
||||
const MAX_BODY_BYTES = 100 * 1024; // 100 KB
|
||||
|
||||
// Hook for async JSON processing
|
||||
const useAsyncJSONProcessing = (
|
||||
value: string,
|
||||
@@ -33,12 +31,6 @@ const useAsyncJSONProcessing = (
|
||||
return (): void => {};
|
||||
}
|
||||
|
||||
// Avoid processing if the json is too large
|
||||
const byteSize = new Blob([value]).size;
|
||||
if (byteSize > MAX_BODY_BYTES) {
|
||||
return (): void => {};
|
||||
}
|
||||
|
||||
processingRef.current = true;
|
||||
setJsonState({ isLoading: true, treeData: null, error: null });
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import {
|
||||
flattenObject,
|
||||
getDataTypes,
|
||||
getSanitizedLogBody,
|
||||
recursiveParseJSON,
|
||||
} from './utils';
|
||||
import { flattenObject, getDataTypes, recursiveParseJSON } from './utils';
|
||||
|
||||
describe('recursiveParseJSON', () => {
|
||||
it('should return an empty object if the input is not valid JSON', () => {
|
||||
@@ -190,146 +185,3 @@ describe('Get Data Types utils', () => {
|
||||
expect(getDataTypes([2.5, 3, 1])).toBe(DataTypes.ArrayFloat64);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSanitizedLogBody', () => {
|
||||
it('should return sanitized HTML with default options (shouldEscapeHtml: false)', () => {
|
||||
const input = '<script>alert("xss")</script>Hello World';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should remove script tags and return sanitized HTML
|
||||
expect(result).not.toContain('<script>');
|
||||
expect(result).toContain('Hello World');
|
||||
});
|
||||
|
||||
it('should escape HTML when shouldEscapeHtml is true', () => {
|
||||
const input = '<script>alert("xss")</script>Hello World';
|
||||
const result = getSanitizedLogBody(input, { shouldEscapeHtml: true });
|
||||
|
||||
// Should escape HTML entities
|
||||
expect(result).toContain('<script>');
|
||||
expect(result).toContain('</script>');
|
||||
expect(result).toContain('Hello World');
|
||||
});
|
||||
|
||||
it('should handle ANSI color codes correctly', () => {
|
||||
const input = '\x1b[32mHello\x1b[0m World';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should convert ANSI codes to HTML spans
|
||||
expect(result).toContain('<span');
|
||||
expect(result).toContain('Hello');
|
||||
expect(result).toContain('World');
|
||||
});
|
||||
|
||||
it('should handle unescaped strings correctly', () => {
|
||||
const input = 'Hello\\nWorld\\tTab';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should unescape the string
|
||||
expect(result).toContain('Hello');
|
||||
expect(result).toContain('World');
|
||||
});
|
||||
|
||||
it('should handle empty string input', () => {
|
||||
const result = getSanitizedLogBody('');
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should handle null/undefined input gracefully', () => {
|
||||
const result1 = getSanitizedLogBody(null as any);
|
||||
const result2 = getSanitizedLogBody(undefined as any);
|
||||
|
||||
expect(result1).toBe('');
|
||||
expect(result2).toBe('');
|
||||
});
|
||||
|
||||
it('should handle special characters and entities', () => {
|
||||
const input = '& < > " \' & < >';
|
||||
const result = getSanitizedLogBody(input, { shouldEscapeHtml: true });
|
||||
|
||||
// Should escape HTML entities
|
||||
expect(result).toContain('&');
|
||||
expect(result).toContain('<');
|
||||
expect(result).toContain('>');
|
||||
expect(result).toContain('"');
|
||||
});
|
||||
|
||||
it('should handle complex HTML with mixed content', () => {
|
||||
const input =
|
||||
'<div><p>Hello <strong>World</strong></p><script>alert("xss")</script></div>';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should keep safe HTML but remove script tags
|
||||
expect(result).toContain('<div>');
|
||||
expect(result).toContain('<p>');
|
||||
expect(result).toContain('<strong>');
|
||||
expect(result).toContain('Hello');
|
||||
expect(result).toContain('World');
|
||||
expect(result).not.toContain('<script>');
|
||||
});
|
||||
|
||||
it('should handle JSON-like strings', () => {
|
||||
const input = '{"key": "value", "nested": {"inner": "data"}}';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should preserve JSON structure
|
||||
expect(result).toContain('{');
|
||||
expect(result).toContain('}');
|
||||
expect(result).toContain('key');
|
||||
expect(result).toContain('value');
|
||||
});
|
||||
|
||||
it('should handle URLs and links', () => {
|
||||
const input = 'Visit https://example.com for more info';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should preserve the URL text
|
||||
expect(result).toContain('https://example.com');
|
||||
expect(result).toContain('Visit');
|
||||
expect(result).toContain('info');
|
||||
});
|
||||
|
||||
it('should handle error cases and return fallback', () => {
|
||||
// Mock console.error to avoid noise in tests
|
||||
const originalConsoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
|
||||
// Create a scenario that might cause an error
|
||||
const input = 'Normal text';
|
||||
const result = getSanitizedLogBody(input);
|
||||
|
||||
// Should return the processed text normally
|
||||
expect(result).toContain('Normal text');
|
||||
|
||||
// Restore console.error
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
|
||||
it('should handle different escape scenarios correctly', () => {
|
||||
const input1 = '<div>Hello</div>';
|
||||
const result1 = getSanitizedLogBody(input1, { shouldEscapeHtml: false });
|
||||
const result2 = getSanitizedLogBody(input1, { shouldEscapeHtml: true });
|
||||
|
||||
// Without escaping, should keep HTML structure
|
||||
expect(result1).toContain('<div>');
|
||||
expect(result1).toContain('</div>');
|
||||
|
||||
// With escaping, should escape HTML entities
|
||||
expect(result2).toContain('<div>');
|
||||
expect(result2).toContain('</div>');
|
||||
});
|
||||
|
||||
it('should handle ANSI codes with HTML escaping', () => {
|
||||
const input = '\x1b[32mHello\x1b[0m <script>World</script>';
|
||||
const result = getSanitizedLogBody(input, { shouldEscapeHtml: true });
|
||||
|
||||
// Should handle both ANSI codes and HTML escaping
|
||||
expect(result).toContain('<span');
|
||||
expect(result).toContain('Hello');
|
||||
expect(result).toContain('<script>');
|
||||
expect(result).toContain('World');
|
||||
expect(result).toContain('</script>');
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import Convert from 'ansi-to-html';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
import { MetricsType } from 'container/MetricsApplication/constant';
|
||||
import dompurify from 'dompurify';
|
||||
import { uniqueId } from 'lodash-es';
|
||||
import { ILog, ILogAggregateAttributesResources } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
|
||||
|
||||
import BodyTitleRenderer from './BodyTitleRenderer';
|
||||
import { typeToArrayTypeMapper } from './config';
|
||||
import { AnyObject, IFieldAttributes } from './LogDetailedView.types';
|
||||
|
||||
const convertInstance = new Convert();
|
||||
|
||||
export const recursiveParseJSON = (obj: string): Record<string, unknown> => {
|
||||
try {
|
||||
const value = JSON.parse(obj);
|
||||
@@ -341,21 +336,3 @@ export function findKeyPath(
|
||||
});
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
export const getSanitizedLogBody = (
|
||||
text: string,
|
||||
options: { shouldEscapeHtml?: boolean } = {},
|
||||
): string => {
|
||||
const { shouldEscapeHtml = false } = options;
|
||||
const escapedText = shouldEscapeHtml ? escapeHtml(text) : text;
|
||||
try {
|
||||
return convertInstance.toHtml(
|
||||
dompurify.sanitize(unescapeString(escapedText), {
|
||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error sanitizing text', error, text);
|
||||
return '{}';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -130,11 +130,7 @@ function RightContainer({
|
||||
const selectedGraphType =
|
||||
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
||||
|
||||
const onCreateAlertsHandler = useCreateAlerts(
|
||||
selectedWidget,
|
||||
'panelView',
|
||||
thresholds,
|
||||
);
|
||||
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
|
||||
|
||||
const allowThreshold = panelTypeVsThreshold[selectedGraph];
|
||||
const allowSoftMinMax = panelTypeVsSoftMinMax[selectedGraph];
|
||||
|
||||
@@ -5,7 +5,6 @@ import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants';
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { prepareQueryRangePayload } from 'lib/dashboard/prepareQueryRangePayload';
|
||||
@@ -20,11 +19,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
|
||||
const useCreateAlerts = (
|
||||
widget?: Widgets,
|
||||
caller?: string,
|
||||
thresholds?: ThresholdProps[],
|
||||
): VoidFunction => {
|
||||
const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||
const queryRangeMutation = useMutation(getQueryRangeFormat);
|
||||
|
||||
const { selectedTime: globalSelectedInterval } = useSelector<
|
||||
@@ -71,17 +66,13 @@ const useCreateAlerts = (
|
||||
widget?.query,
|
||||
);
|
||||
|
||||
const url = `${ROUTES.ALERTS_NEW}?${
|
||||
QueryParams.compositeQuery
|
||||
}=${encodeURIComponent(JSON.stringify(updatedQuery))}&${
|
||||
QueryParams.panelTypes
|
||||
}=${widget.panelTypes}&version=${
|
||||
selectedDashboard?.data.version || DEFAULT_ENTITY_VERSION
|
||||
}`;
|
||||
|
||||
history.push(url, {
|
||||
thresholds,
|
||||
});
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||
JSON.stringify(updatedQuery),
|
||||
)}&${QueryParams.panelTypes}=${widget.panelTypes}&version=${
|
||||
selectedDashboard?.data.version || DEFAULT_ENTITY_VERSION
|
||||
}`,
|
||||
);
|
||||
},
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
|
||||
@@ -7486,7 +7486,7 @@ debounce@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
||||
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
|
||||
|
||||
debug@2.6.9, debug@4, debug@4.3.4, debug@^3.2.6, debug@^3.2.7, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4, debug@^4.3.6, debug@ngokevin/debug#noTimestamp:
|
||||
debug@2.6.9, debug@4, debug@4.3.4, debug@^3.2.6, debug@^3.2.7, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.6, debug@ngokevin/debug#noTimestamp:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@@ -9122,7 +9122,7 @@ glob-to-regexp@^0.4.1:
|
||||
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@@ -15249,7 +15249,7 @@ sass@1.66.1, sass@^1.58.3:
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
|
||||
sax@>=0.6.0, sax@^1.2.4:
|
||||
sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
@@ -16016,10 +16016,16 @@ stylis@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4"
|
||||
integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==
|
||||
|
||||
stylus@0.0.1-security, stylus@^0.59.0:
|
||||
version "0.0.1-security"
|
||||
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.0.1-security.tgz#7fbf8df07e6c1202fce29d57fed2ecbf960e6a3b"
|
||||
integrity sha512-qTLX5NJwuj5dqdJyi1eAjXGE4HfjWDoPLbSIpYWn/rAEdiyZnsngS/Yj0BRjM7wC41e/+spK4QCXFqz7LM0fFQ==
|
||||
stylus@^0.59.0:
|
||||
version "0.59.0"
|
||||
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.59.0.tgz#a344d5932787142a141946536d6e24e6a6be7aa6"
|
||||
integrity sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==
|
||||
dependencies:
|
||||
"@adobe/css-tools" "^4.0.1"
|
||||
debug "^4.3.2"
|
||||
glob "^7.1.6"
|
||||
sax "~1.2.4"
|
||||
source-map "^0.7.3"
|
||||
|
||||
super-animejs@^3.1.0:
|
||||
version "3.1.0"
|
||||
|
||||
@@ -6054,90 +6054,3 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc
|
||||
|
||||
return &searchSpansResult, nil
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetNormalizedStatus(
|
||||
ctx context.Context,
|
||||
orgID valuer.UUID,
|
||||
metricNames []string,
|
||||
) (map[string]bool, error) {
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
result := make(map[string]bool, len(metricNames))
|
||||
buildKey := func(name string) string {
|
||||
return constants.NormalizedMetricsMapCacheKey + ":" + name
|
||||
}
|
||||
|
||||
uncached := make([]string, 0, len(metricNames))
|
||||
for _, m := range metricNames {
|
||||
var status model.MetricsNormalizedMap
|
||||
if err := r.cache.Get(ctx, orgID, buildKey(m), &status, true); err == nil {
|
||||
result[m] = status.IsUnNormalized
|
||||
} else {
|
||||
uncached = append(uncached, m)
|
||||
}
|
||||
}
|
||||
if len(uncached) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
placeholders := "'" + strings.Join(uncached, "', '") + "'"
|
||||
|
||||
q := fmt.Sprintf(
|
||||
`SELECT metric_name, toUInt8(__normalized)
|
||||
FROM %s.%s
|
||||
WHERE metric_name IN (%s)
|
||||
GROUP BY metric_name, __normalized`,
|
||||
signozMetricDBName, signozTSTableNameV41Day, placeholders,
|
||||
)
|
||||
|
||||
rows, err := r.db.Query(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// tmp[m] collects the set {0,1} for a metric name, truth table
|
||||
tmp := make(map[string]map[uint8]struct{}, len(uncached))
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
name string
|
||||
normalized uint8
|
||||
)
|
||||
if err := rows.Scan(&name, &normalized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := tmp[name]; !ok {
|
||||
tmp[name] = make(map[uint8]struct{}, 2)
|
||||
}
|
||||
tmp[name][normalized] = struct{}{}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, m := range uncached {
|
||||
set := tmp[m]
|
||||
switch {
|
||||
case len(set) == 0:
|
||||
return nil, fmt.Errorf("metric %q not found in ClickHouse", m)
|
||||
|
||||
case len(set) == 2:
|
||||
result[m] = true
|
||||
|
||||
default:
|
||||
_, hasUnnorm := set[0]
|
||||
result[m] = hasUnnorm
|
||||
}
|
||||
status := model.MetricsNormalizedMap{
|
||||
MetricName: m,
|
||||
IsUnNormalized: result[m],
|
||||
}
|
||||
_ = r.cache.Set(ctx, orgID, buildKey(m), &status, 0)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -862,7 +862,7 @@ func Test_buildLogsQuery(t *testing.T) {
|
||||
"from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
|
||||
"AND attributes_number['duration'] > 1000.000000 AND mapContains(attributes_number, 'duration') AND mapContains(attributes_number, 'duration') AND " +
|
||||
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
|
||||
"AND simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name\":\"test%' AND ( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
|
||||
"AND simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name%test%' AND ( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
|
||||
"group by `host`,ts order by `host` desc",
|
||||
},
|
||||
}
|
||||
@@ -962,7 +962,7 @@ func TestPrepareLogsQuery(t *testing.T) {
|
||||
"where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
|
||||
"AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND (resource_fingerprint GLOBAL IN " +
|
||||
"(SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'app' " +
|
||||
"AND labels like '%service.name\":\"app%')) group by `user` order by value DESC) LIMIT 10",
|
||||
"AND labels like '%service.name%app%')) group by `user` order by value DESC) LIMIT 10",
|
||||
},
|
||||
{
|
||||
name: "Test TS with limit- second",
|
||||
@@ -991,7 +991,7 @@ func TestPrepareLogsQuery(t *testing.T) {
|
||||
"from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
|
||||
"attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND " +
|
||||
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'app' AND labels like '%service.name\":\"app%')) AND (`user`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `user`,ts order by value DESC",
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'app' AND labels like '%service.name%app%')) AND (`user`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `user`,ts order by value DESC",
|
||||
},
|
||||
{
|
||||
name: "Live Tail Query",
|
||||
|
||||
@@ -3,7 +3,6 @@ package v2
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -277,59 +276,3 @@ func (q *querier) runBuilderQuery(
|
||||
Series: resultSeries,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateMetricNames function is used to print all those queries who are still using old normalized metrics and not new metrics.
|
||||
func (q *querier) ValidateMetricNames(ctx context.Context, query *v3.CompositeQuery, orgID valuer.UUID) {
|
||||
var metricNames []string
|
||||
switch query.QueryType {
|
||||
case v3.QueryTypePromQL:
|
||||
for _, query := range query.PromQueries {
|
||||
expr, err := parser.ParseExpr(query.Query)
|
||||
if err != nil {
|
||||
zap.L().Debug("error parsing promQL expression", zap.String("query", query.Query), zap.Error(err))
|
||||
continue
|
||||
}
|
||||
parser.Inspect(expr, func(node parser.Node, path []parser.Node) error {
|
||||
if vs, ok := node.(*parser.VectorSelector); ok {
|
||||
for _, m := range vs.LabelMatchers {
|
||||
if m.Name == "__name__" {
|
||||
metricNames = append(metricNames, m.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
metrics, err := q.reader.GetNormalizedStatus(ctx, orgID, metricNames)
|
||||
if err != nil {
|
||||
zap.L().Debug("error getting corresponding normalized metrics", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for metricName, metricPresent := range metrics {
|
||||
if metricPresent {
|
||||
continue
|
||||
} else {
|
||||
zap.L().Warn("using normalized metric name", zap.String("metrics", metricName))
|
||||
continue
|
||||
}
|
||||
}
|
||||
case v3.QueryTypeBuilder:
|
||||
for _, query := range query.BuilderQueries {
|
||||
metricName := query.AggregateAttribute.Key
|
||||
metricNames = append(metricNames, metricName)
|
||||
}
|
||||
metrics, err := q.reader.GetNormalizedStatus(ctx, orgID, metricNames)
|
||||
if err != nil {
|
||||
zap.L().Debug("error getting corresponding normalized metrics", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for metricName, metricPresent := range metrics {
|
||||
if metricPresent {
|
||||
continue
|
||||
} else {
|
||||
zap.L().Warn("using normalized metric name", zap.String("metrics", metricName))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,9 +506,6 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, params *v3.
|
||||
var results []*v3.Result
|
||||
var err error
|
||||
var errQueriesByName map[string]error
|
||||
if !q.testingMode && q.reader != nil {
|
||||
q.ValidateMetricNames(ctx, params.CompositeQuery, orgID)
|
||||
}
|
||||
if params.CompositeQuery != nil {
|
||||
switch params.CompositeQuery.QueryType {
|
||||
case v3.QueryTypeBuilder:
|
||||
|
||||
@@ -117,19 +117,17 @@ func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{
|
||||
// add index filters
|
||||
switch op {
|
||||
case v3.FilterOperatorEqual:
|
||||
return fmt.Sprintf("labels like '%%%s\":\"%s%%'", key, fmtValEscapedForContains)
|
||||
return fmt.Sprintf("labels like '%%%s%%%s%%'", key, fmtValEscapedForContains)
|
||||
case v3.FilterOperatorNotEqual:
|
||||
return fmt.Sprintf("labels not like '%%%s\":\"%s%%'", key, fmtValEscapedForContains)
|
||||
return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, fmtValEscapedForContains)
|
||||
case v3.FilterOperatorLike:
|
||||
return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, fmtValEscapedLower)
|
||||
case v3.FilterOperatorNotLike:
|
||||
// cannot apply not contains x%y as y can be somewhere else
|
||||
return ""
|
||||
return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, fmtValEscapedLower)
|
||||
case v3.FilterOperatorContains:
|
||||
return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, fmtValEscapedForContainsLower)
|
||||
case v3.FilterOperatorNotContains:
|
||||
// cannot apply not contains x%y as y can be somewhere else
|
||||
return ""
|
||||
return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, fmtValEscapedForContainsLower)
|
||||
case v3.FilterOperatorExists:
|
||||
return fmt.Sprintf("lower(labels) like '%%%s%%'", key)
|
||||
case v3.FilterOperatorNotExists:
|
||||
|
||||
@@ -164,22 +164,31 @@ func Test_buildResourceIndexFilter(t *testing.T) {
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "test eq",
|
||||
name: "test contains",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorEqual,
|
||||
value: `Application"`,
|
||||
op: v3.FilterOperatorContains,
|
||||
value: "application",
|
||||
},
|
||||
want: `labels like '%service.name":"Application\\\\"%'`,
|
||||
want: `lower(labels) like '%service.name%application%'`,
|
||||
},
|
||||
{
|
||||
name: "test not eq",
|
||||
name: "test not contains",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorNotEqual,
|
||||
value: `Application"`,
|
||||
op: v3.FilterOperatorNotContains,
|
||||
value: "application",
|
||||
},
|
||||
want: `labels not like '%service.name":"Application\\\\"%'`,
|
||||
want: `lower(labels) not like '%service.name%application%'`,
|
||||
},
|
||||
{
|
||||
name: "test contains with % and _",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorNotContains,
|
||||
value: "application%_test",
|
||||
},
|
||||
want: `lower(labels) not like '%service.name%application\%\_test%'`,
|
||||
},
|
||||
{
|
||||
name: "test like with % and _",
|
||||
@@ -199,43 +208,6 @@ func Test_buildResourceIndexFilter(t *testing.T) {
|
||||
},
|
||||
want: `lower(labels) like '%service.name%application%_test%'`,
|
||||
},
|
||||
{
|
||||
name: "test not like with % and _",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorLike,
|
||||
value: "application%_test",
|
||||
},
|
||||
want: `lower(labels) like '%service.name%application%_test%'`,
|
||||
},
|
||||
{
|
||||
name: "test contains",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorContains,
|
||||
value: "application",
|
||||
},
|
||||
want: `lower(labels) like '%service.name%application%'`,
|
||||
},
|
||||
{
|
||||
name: "test not contains",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorNotContains,
|
||||
value: "application",
|
||||
},
|
||||
want: ``,
|
||||
},
|
||||
{
|
||||
name: "test contains with % and _",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorNotContains,
|
||||
value: "application%_test",
|
||||
},
|
||||
want: ``,
|
||||
},
|
||||
|
||||
{
|
||||
name: "test not regex",
|
||||
args: args{
|
||||
@@ -254,6 +226,15 @@ func Test_buildResourceIndexFilter(t *testing.T) {
|
||||
},
|
||||
want: `(labels not like '%"service.name":"Application"%' AND labels not like '%"service.name":"Test"%')`,
|
||||
},
|
||||
{
|
||||
name: "test eq",
|
||||
args: args{
|
||||
key: "service.name",
|
||||
op: v3.FilterOperatorEqual,
|
||||
value: `Application"`,
|
||||
},
|
||||
want: `labels like '%service.name%Application\\\\"%'`,
|
||||
},
|
||||
{
|
||||
name: "test exists",
|
||||
args: args{
|
||||
@@ -329,7 +310,7 @@ func Test_buildResourceFiltersFromFilterItems(t *testing.T) {
|
||||
},
|
||||
want: []string{
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'test'",
|
||||
"labels like '%service.name\":\"test%'",
|
||||
"labels like '%service.name%test%'",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -361,7 +342,7 @@ func Test_buildResourceFiltersFromFilterItems(t *testing.T) {
|
||||
},
|
||||
want: []string{
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'test'",
|
||||
"labels like '%service.name\":\"test%'",
|
||||
"labels like '%service.name%test%'",
|
||||
`simpleJSONExtractString(lower(labels), 'namespace') LIKE '%test1"%'`,
|
||||
`lower(labels) like '%namespace%test1\\\\"%'`,
|
||||
},
|
||||
@@ -523,7 +504,7 @@ func Test_buildResourceSubQuery(t *testing.T) {
|
||||
},
|
||||
want: "(SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE " +
|
||||
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name\":\"test%' " +
|
||||
"simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name%test%' " +
|
||||
"AND simpleJSONExtractString(lower(labels), 'namespace') LIKE '%test1%' AND lower(labels) like '%namespace%test1%' " +
|
||||
"AND (simpleJSONHas(labels, 'cluster.name') AND labels like '%cluster.name%') AND " +
|
||||
"( (simpleJSONHas(labels, 'host.name') AND labels like '%host.name%') ))",
|
||||
|
||||
@@ -532,7 +532,7 @@ func Test_buildTracesQuery(t *testing.T) {
|
||||
want: "SELECT resources_number['host'] as `host`, toFloat64(count()) as value from signoz_traces.distributed_signoz_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
|
||||
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_number['bytes'] > 100 AND " +
|
||||
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND " +
|
||||
"(seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%' AND " +
|
||||
"(seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name%myService%' AND " +
|
||||
"( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
|
||||
"group by `host` order by `host` ASC",
|
||||
},
|
||||
@@ -614,7 +614,7 @@ func Test_buildTracesQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: "SELECT timestamp as timestamp_datetime, spanID, traceID, name as `name` from signoz_traces.distributed_signoz_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
|
||||
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'cartservice' AND labels like '%service.name\":\"cartservice%')) AND parent_span_id = '' order by timestamp ASC",
|
||||
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'cartservice' AND labels like '%service.name%cartservice%')) AND parent_span_id = '' order by timestamp ASC",
|
||||
},
|
||||
{
|
||||
name: "test noop list view-without ts",
|
||||
@@ -649,7 +649,7 @@ func Test_buildTracesQuery(t *testing.T) {
|
||||
},
|
||||
want: "SELECT subQuery.serviceName as `subQuery.serviceName`, subQuery.name as `subQuery.name`, count() AS span_count, subQuery.durationNano as `subQuery.durationNano`, subQuery.traceID FROM " +
|
||||
"(SELECT traceID AS dist_traceID, timestamp, ts_bucket_start FROM signoz_traces.distributed_signoz_index_v3 WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
|
||||
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%'))) as dist_table " +
|
||||
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name%myService%'))) as dist_table " +
|
||||
"INNER JOIN " +
|
||||
"( SELECT * FROM (SELECT traceID, durationNano, serviceName, name FROM signoz_traces.signoz_index_v3 WHERE parentSpanID = '' AND (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
|
||||
"ORDER BY durationNano DESC LIMIT 1 BY traceID) AS inner_subquery ) AS subQuery ON dist_table.dist_traceID = subQuery.traceID " +
|
||||
@@ -676,7 +676,7 @@ func Test_buildTracesQuery(t *testing.T) {
|
||||
want: "SELECT serviceName, name, subQuery.span_count as span_count, durationNano, trace_id as traceID from signoz_traces.distributed_signoz_index_v3 GLOBAL INNER JOIN " +
|
||||
"( SELECT * FROM (SELECT trace_id, count() as span_count FROM signoz_traces.signoz_index_v3 WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
|
||||
"AND attributes_string['method'] = 'GET' AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE " +
|
||||
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%')) " +
|
||||
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name%myService%')) " +
|
||||
"GROUP BY trace_id ORDER BY span_count DESC LIMIT 1 BY trace_id LIMIT 100) AS inner_subquery ) AS subQuery ON signoz_traces.distributed_signoz_index_v3.trace_id = subQuery.trace_id " +
|
||||
"WHERE parent_span_id = '' AND (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
|
||||
"ORDER BY subQuery.span_count DESC LIMIT 100 settings distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
@@ -863,7 +863,7 @@ func TestPrepareTracesQuery(t *testing.T) {
|
||||
want: "SELECT `function`,`service.name` from (SELECT `attribute_string_function` as `function`, `resource_string_service$$name` as `service.name`, toFloat64(count(distinct(name))) as value " +
|
||||
"from signoz_traces.distributed_signoz_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
|
||||
"AND attributes_number['line'] = 100 AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE " +
|
||||
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname\":\"server1%' AND " +
|
||||
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname%server1%' AND " +
|
||||
"( (simpleJSONHas(labels, 'service.name') AND labels like '%service.name%') ))) group by `function`,`service.name` order by value DESC) LIMIT 10",
|
||||
},
|
||||
{
|
||||
@@ -905,7 +905,7 @@ func TestPrepareTracesQuery(t *testing.T) {
|
||||
want: "SELECT `attribute_string_function` as `function`, serviceName as `serviceName`, toFloat64(count(distinct(name))) as value from signoz_traces.distributed_signoz_index_v3 " +
|
||||
"where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_number['line'] = 100 " +
|
||||
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
|
||||
"AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname\":\"server1%')) AND (`function`,`serviceName`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `function`,`serviceName` order by value DESC",
|
||||
"AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname%server1%')) AND (`function`,`serviceName`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `function`,`serviceName` order by value DESC",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package constants
|
||||
import (
|
||||
"maps"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -34,12 +33,6 @@ var InviteEmailTemplate = GetOrDefaultEnv("INVITE_EMAIL_TEMPLATE", "/root/templa
|
||||
var MetricsExplorerClickhouseThreads = GetOrDefaultEnvInt("METRICS_EXPLORER_CLICKHOUSE_THREADS", 8)
|
||||
var UpdatedMetricsMetadataCachePrefix = GetOrDefaultEnv("METRICS_UPDATED_METADATA_CACHE_KEY", "UPDATED_METRICS_METADATA")
|
||||
|
||||
const NormalizedMetricsMapCacheKey = "NORMALIZED_METRICS_MAP_CACHE_KEY"
|
||||
const NormalizedMetricsMapQueryThreads = 10
|
||||
|
||||
var NormalizedMetricsMapRegex = regexp.MustCompile(`[^a-zA-Z0-9]`)
|
||||
var NormalizedMetricsMapQuantileRegex = regexp.MustCompile(`(?i)([._-]?quantile.*)$`)
|
||||
|
||||
// TODO(srikanthccv): remove after backfilling is done
|
||||
func UseMetricsPreAggregation() bool {
|
||||
return GetOrDefaultEnv("USE_METRICS_PRE_AGGREGATION", "true") == "true"
|
||||
|
||||
@@ -130,7 +130,6 @@ type Reader interface {
|
||||
GetUpdatedMetricsMetadata(ctx context.Context, orgID valuer.UUID, metricNames ...string) (map[string]*model.UpdateMetricsMetadata, *model.ApiError)
|
||||
|
||||
CheckForLabelsInMetric(ctx context.Context, metricName string, labels []string) (bool, *model.ApiError)
|
||||
GetNormalizedStatus(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]bool, error)
|
||||
}
|
||||
|
||||
type Querier interface {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package model
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type MetricsNormalizedMap struct {
|
||||
MetricName string `json:"metricName"`
|
||||
IsUnNormalized bool `json:"isUnNormalized"`
|
||||
}
|
||||
|
||||
func (c *MetricsNormalizedMap) MarshalBinary() (data []byte, err error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
func (c *MetricsNormalizedMap) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, c)
|
||||
}
|
||||
@@ -283,7 +283,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID,
|
||||
if hasLogsQuery {
|
||||
// check if any enrichment is required for logs if yes then enrich them
|
||||
if logsv3.EnrichmentRequired(params) {
|
||||
logsFields, err := r.reader.GetLogFieldsFromNames(ctx, logsv3.GetFieldNames(params.CompositeQuery))
|
||||
logsFields, err := r.reader.GetLogFields(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1320,6 +1320,7 @@ func TestThresholdRuleNoData(t *testing.T) {
|
||||
for idx, c := range cases {
|
||||
rows := cmock.NewRows(cols, c.values)
|
||||
|
||||
|
||||
// We are testing the eval logic after the query is run
|
||||
// so we don't care about the query string here
|
||||
queryString := "SELECT any"
|
||||
@@ -1513,11 +1514,11 @@ func TestThresholdRuleLogsLink(t *testing.T) {
|
||||
|
||||
attrMetaCols := make([]cmock.ColumnType, 0)
|
||||
attrMetaCols = append(attrMetaCols, cmock.ColumnType{Name: "name", Type: "String"})
|
||||
attrMetaCols = append(attrMetaCols, cmock.ColumnType{Name: "datatype", Type: "String"})
|
||||
attrMetaCols = append(attrMetaCols, cmock.ColumnType{Name: "dataType", Type: "String"})
|
||||
|
||||
resourceMetaCols := make([]cmock.ColumnType, 0)
|
||||
resourceMetaCols = append(resourceMetaCols, cmock.ColumnType{Name: "name", Type: "String"})
|
||||
resourceMetaCols = append(resourceMetaCols, cmock.ColumnType{Name: "datatype", Type: "String"})
|
||||
resourceMetaCols = append(resourceMetaCols, cmock.ColumnType{Name: "dataType", Type: "String"})
|
||||
|
||||
createTableCols := make([]cmock.ColumnType, 0)
|
||||
createTableCols = append(createTableCols, cmock.ColumnType{Name: "statement", Type: "String"})
|
||||
@@ -1530,12 +1531,12 @@ func TestThresholdRuleLogsLink(t *testing.T) {
|
||||
for idx, c := range testCases {
|
||||
attrMetaRows := cmock.NewRows(attrMetaCols, c.attrMetaValues)
|
||||
telemetryStore.Mock().
|
||||
ExpectSelect("SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_attribute_keys where name in ('component','k8s.container.name') group by name, datatype").
|
||||
ExpectSelect("SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_attribute_keys group by name, datatype").
|
||||
WillReturnRows(attrMetaRows)
|
||||
|
||||
resourceMetaRows := cmock.NewRows(resourceMetaCols, c.resourceMetaValues)
|
||||
telemetryStore.Mock().
|
||||
ExpectSelect("SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_resource_keys where name in ('component','k8s.container.name') group by name, datatype").
|
||||
ExpectSelect("SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_resource_keys group by name, datatype").
|
||||
WillReturnRows(resourceMetaRows)
|
||||
|
||||
createTableRows := cmock.NewRows(createTableCols, c.createTableValues)
|
||||
|
||||
@@ -19,18 +19,14 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *defaultConditionBuilder {
|
||||
return &defaultConditionBuilder{fm: fm}
|
||||
}
|
||||
|
||||
func valueForIndexFilter(op qbtypes.FilterOperator, key *telemetrytypes.TelemetryFieldKey, value any) any {
|
||||
func valueForIndexFilter(key *telemetrytypes.TelemetryFieldKey, value any) any {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
if op == qbtypes.FilterOperatorEqual || op == qbtypes.FilterOperatorNotEqual {
|
||||
return fmt.Sprintf(`%%%s":"%s%%`, key.Name, v)
|
||||
}
|
||||
return fmt.Sprintf(`%%%s%%%s%%`, key.Name, v)
|
||||
case []any:
|
||||
// assuming array will always be for in and not in
|
||||
values := make([]string, 0, len(v))
|
||||
for _, v := range v {
|
||||
values = append(values, fmt.Sprintf(`%%%s":"%s%%`, key.Name, v))
|
||||
values = append(values, fmt.Sprintf(`%%%s%%%s%%`, key.Name, v))
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -59,7 +55,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
}
|
||||
|
||||
keyIdxFilter := sb.Like(column.Name, keyIndexFilter(key))
|
||||
valueForIndexFilter := valueForIndexFilter(op, key, value)
|
||||
valueForIndexFilter := valueForIndexFilter(key, value)
|
||||
|
||||
fieldName, err := b.fm.FieldFor(ctx, key)
|
||||
if err != nil {
|
||||
@@ -94,9 +90,9 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
sb.ILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotLike, qbtypes.FilterOperatorNotILike:
|
||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||
return sb.And(
|
||||
sb.NotILike(fieldName, value),
|
||||
sb.NotILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
|
||||
case qbtypes.FilterOperatorBetween:
|
||||
@@ -183,9 +179,9 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
sb.ILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotContains:
|
||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||
return sb.And(
|
||||
sb.NotILike(fieldName, fmt.Sprintf(`%%%s%%`, value)),
|
||||
sb.NotILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
}
|
||||
return "", qbtypes.ErrUnsupportedOperator
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestConditionBuilder(t *testing.T) {
|
||||
op: querybuildertypesv5.FilterOperatorEqual,
|
||||
value: "watch",
|
||||
expected: "simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?",
|
||||
expectedArgs: []any{"watch", "%k8s.namespace.name%", `%k8s.namespace.name":"watch%`},
|
||||
expectedArgs: []any{"watch", "%k8s.namespace.name%", `%k8s.namespace.name%watch%`},
|
||||
},
|
||||
{
|
||||
name: "string_not_equal",
|
||||
@@ -42,7 +42,7 @@ func TestConditionBuilder(t *testing.T) {
|
||||
op: querybuildertypesv5.FilterOperatorNotEqual,
|
||||
value: "redis",
|
||||
expected: "simpleJSONExtractString(labels, 'k8s.namespace.name') <> ? AND labels NOT LIKE ?",
|
||||
expectedArgs: []any{"redis", `%k8s.namespace.name":"redis%`},
|
||||
expectedArgs: []any{"redis", `%k8s.namespace.name%redis%`},
|
||||
},
|
||||
{
|
||||
name: "string_like",
|
||||
@@ -63,8 +63,8 @@ func TestConditionBuilder(t *testing.T) {
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorNotLike,
|
||||
value: "_mango%",
|
||||
expected: "LOWER(simpleJSONExtractString(labels, 'k8s.namespace.name')) NOT LIKE LOWER(?)",
|
||||
expectedArgs: []any{"_mango%"},
|
||||
expected: "LOWER(simpleJSONExtractString(labels, 'k8s.namespace.name')) NOT LIKE LOWER(?) AND LOWER(labels) NOT LIKE LOWER(?)",
|
||||
expectedArgs: []any{"_mango%", `%k8s.namespace.name%_mango%%`},
|
||||
},
|
||||
{
|
||||
name: "string_contains",
|
||||
@@ -85,8 +85,8 @@ func TestConditionBuilder(t *testing.T) {
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorNotContains,
|
||||
value: "banana",
|
||||
expected: "LOWER(simpleJSONExtractString(labels, 'k8s.namespace.name')) NOT LIKE LOWER(?)",
|
||||
expectedArgs: []any{"%banana%"},
|
||||
expected: "LOWER(simpleJSONExtractString(labels, 'k8s.namespace.name')) NOT LIKE LOWER(?) AND LOWER(labels) NOT LIKE LOWER(?)",
|
||||
expectedArgs: []any{"%banana%", `%k8s.namespace.name%banana%`},
|
||||
},
|
||||
{
|
||||
name: "string_in",
|
||||
@@ -97,7 +97,7 @@ func TestConditionBuilder(t *testing.T) {
|
||||
op: querybuildertypesv5.FilterOperatorIn,
|
||||
value: []any{"watch", "redis"},
|
||||
expected: "(simpleJSONExtractString(labels, 'k8s.namespace.name') = ? OR simpleJSONExtractString(labels, 'k8s.namespace.name') = ?) AND labels LIKE ? AND (labels LIKE ? OR labels LIKE ?)",
|
||||
expectedArgs: []any{"watch", "redis", "%k8s.namespace.name%", "%k8s.namespace.name\":\"watch%", "%k8s.namespace.name\":\"redis%"},
|
||||
expectedArgs: []any{"watch", "redis", "%k8s.namespace.name%", "%k8s.namespace.name%watch%", "%k8s.namespace.name%redis%"},
|
||||
},
|
||||
{
|
||||
name: "string_not_in",
|
||||
@@ -108,7 +108,7 @@ func TestConditionBuilder(t *testing.T) {
|
||||
op: querybuildertypesv5.FilterOperatorNotIn,
|
||||
value: []any{"watch", "redis"},
|
||||
expected: "(simpleJSONExtractString(labels, 'k8s.namespace.name') <> ? AND simpleJSONExtractString(labels, 'k8s.namespace.name') <> ?) AND (labels NOT LIKE ? AND labels NOT LIKE ?)",
|
||||
expectedArgs: []any{"watch", "redis", "%k8s.namespace.name\":\"watch%", "%k8s.namespace.name\":\"redis%"},
|
||||
expectedArgs: []any{"watch", "redis", "%k8s.namespace.name%watch%", "%k8s.namespace.name%redis%"},
|
||||
},
|
||||
{
|
||||
name: "string_exists",
|
||||
|
||||
@@ -69,7 +69,7 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name%cartservice%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -108,7 +108,7 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name%cartservice%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -173,7 +173,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name%cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -201,7 +201,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY `attribute_string_materialized$$key$$name` AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name%cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -91,7 +91,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`) GLOBAL IN (SELECT `httpRoute` FROM __limit_cte) GROUP BY ts, `httpRoute`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -158,7 +158,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -187,7 +187,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(resources_string, 'service.name') = ?, resources_string['service.name'], NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -218,7 +218,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -249,7 +249,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -344,7 +344,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resources_string['service.name'] AS `service.name`, duration_nano AS `duration_nano`, `attribute_number_cart$$items_count` AS `cart.items_count`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -373,7 +373,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, `resource_string_service$$name` AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY attributes_string['user.id'] AS `user.id` desc LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -417,7 +417,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, response_status_code AS `responseStatusCode`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name%redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user