Compare commits

..

1 Commits

Author SHA1 Message Date
SagarRajput-7
1b073e03cb fix: dummy test pr for stylus build failure test 2025-07-23 13:00:29 +05:30
37 changed files with 158 additions and 744 deletions

View File

@@ -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"
}
}

View File

@@ -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],
);

View File

@@ -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],
);

View File

@@ -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],
);

View File

@@ -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}

View File

@@ -47,5 +47,4 @@ export enum QueryParams {
destination = 'destination',
kindString = 'kindString',
tab = 'tab',
thresholds = 'thresholds',
}

View File

@@ -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 =

View File

@@ -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;

View File

@@ -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(() => {

View File

@@ -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}

View File

@@ -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');
});
});

View File

@@ -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);

View File

@@ -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,
};
};

View File

@@ -196,6 +196,7 @@ function GridCardGraph({
[requestData.query],
);
console.log('requestData', requestData);
const queryResponse = useGetQueryRange(
{
...requestData,

View File

@@ -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]);

View File

@@ -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 });

View File

@@ -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('&lt;script&gt;');
expect(result).toContain('&lt;/script&gt;');
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 = '& < > " \' &amp; &lt; &gt;';
const result = getSanitizedLogBody(input, { shouldEscapeHtml: true });
// Should escape HTML entities
expect(result).toContain('&amp;');
expect(result).toContain('&lt;');
expect(result).toContain('&gt;');
expect(result).toContain('&quot;');
});
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('&lt;div&gt;');
expect(result2).toContain('&lt;/div&gt;');
});
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('&lt;script&gt;');
expect(result).toContain('World');
expect(result).toContain('&lt;/script&gt;');
});
});
//

View File

@@ -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 '{}';
}
};

View File

@@ -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];

View File

@@ -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({

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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",

View File

@@ -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
}
}
}
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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%') ))",

View File

@@ -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",
},
}

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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",

View File

@@ -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,
},

View File

@@ -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,
},