Compare commits
9 Commits
v0.51.0-cl
...
issue_2705
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f43292c10b | ||
|
|
ea51320e4f | ||
|
|
436009ec20 | ||
|
|
785a872ee0 | ||
|
|
9ab3a03bfe | ||
|
|
8a3688b74d | ||
|
|
5a686f3be1 | ||
|
|
da31b2f7df | ||
|
|
a25189eca7 |
@@ -5,6 +5,7 @@ import { Button } from 'antd';
|
||||
import { Tag } from 'antd/lib';
|
||||
import Input from 'components/Input';
|
||||
import { Check, X } from 'lucide-react';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
import React, { Dispatch, SetStateAction, useState } from 'react';
|
||||
|
||||
function Tags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
@@ -45,19 +46,41 @@ function Tags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
func(value);
|
||||
};
|
||||
|
||||
const forMap = (tag: string): React.ReactElement => (
|
||||
<span key={tag} style={{ display: 'inline-block' }}>
|
||||
<Tag
|
||||
closable
|
||||
onClose={(e): void => {
|
||||
e.preventDefault();
|
||||
handleClose(tag);
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</Tag>
|
||||
</span>
|
||||
);
|
||||
|
||||
const tagChild = tags.map(forMap);
|
||||
|
||||
const renderTagsAnimated = (): React.ReactElement => (
|
||||
<TweenOneGroup
|
||||
appear={false}
|
||||
className="tags"
|
||||
enter={{ scale: 0.8, opacity: 0, type: 'from', duration: 100 }}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
onEnd={(e): void => {
|
||||
if (e.type === 'appear' || e.type === 'enter') {
|
||||
(e.target as any).style = 'display: inline-block';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tagChild}
|
||||
</TweenOneGroup>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="tags-container">
|
||||
{tags.map<React.ReactNode>((tag) => (
|
||||
<Tag
|
||||
key={tag}
|
||||
closable
|
||||
style={{ userSelect: 'none' }}
|
||||
onClose={(): void => handleClose(tag)}
|
||||
>
|
||||
<span>{tag}</span>
|
||||
</Tag>
|
||||
))}
|
||||
|
||||
{renderTagsAnimated()}
|
||||
{inputVisible && (
|
||||
<div className="add-tag-container">
|
||||
<Input
|
||||
|
||||
@@ -125,9 +125,10 @@ function GridCardGraph({
|
||||
offset: 0,
|
||||
limit: updatedQuery.builder.queryData[0].limit || 0,
|
||||
},
|
||||
// we do not need select columns in case of logs
|
||||
selectColumns:
|
||||
initialDataSource === DataSource.TRACES && widget.selectedTracesFields,
|
||||
initialDataSource === DataSource.LOGS
|
||||
? widget.selectedLogFields
|
||||
: widget.selectedTracesFields,
|
||||
},
|
||||
fillGaps: widget.fillSpans,
|
||||
};
|
||||
|
||||
@@ -940,50 +940,3 @@
|
||||
border-color: var(--bg-vanilla-300) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.mt-24 {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.mb-24 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.ingestion-setup-details-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
background: rgba(113, 144, 249, 0.1);
|
||||
color: var(--bg-robin-300, #95acfb);
|
||||
|
||||
.learn-more {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-decoration: underline;
|
||||
|
||||
color: var(--bg-robin-300, #95acfb);
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.ingestion-setup-details-links {
|
||||
background: rgba(113, 144, 249, 0.1);
|
||||
color: var(--bg-robin-500);
|
||||
|
||||
.learn-more {
|
||||
color: var(--bg-robin-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,10 @@ import { useGetAllIngestionsKeys } from 'hooks/IngestionKeys/useGetAllIngestionK
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
CalendarClock,
|
||||
Check,
|
||||
Copy,
|
||||
Infinity,
|
||||
Info,
|
||||
Minus,
|
||||
PenLine,
|
||||
Plus,
|
||||
@@ -877,35 +875,10 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
return (
|
||||
<div className="ingestion-key-container">
|
||||
<div className="ingestion-key-content">
|
||||
<div className="ingestion-setup-details-links">
|
||||
<Info size={14} />
|
||||
|
||||
<span>
|
||||
Find your ingestion URL and learn more about sending data to SigNoz{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/ingestion/signoz-cloud/overview/"
|
||||
target="_blank"
|
||||
className="learn-more"
|
||||
rel="noreferrer"
|
||||
>
|
||||
here <ArrowUpRight size={14} />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<Typography.Title className="title"> Ingestion Keys </Typography.Title>
|
||||
<Typography.Text className="subtitle">
|
||||
Create and manage ingestion keys for the SigNoz Cloud{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/ingestion/signoz-cloud/keys/"
|
||||
target="_blank"
|
||||
className="learn-more"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{' '}
|
||||
Learn more <ArrowUpRight size={14} />
|
||||
</a>
|
||||
Create and manage ingestion keys for the SigNoz Cloud
|
||||
</Typography.Text>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
|
||||
import MultiIngestionSettings from '../MultiIngestionSettings';
|
||||
|
||||
describe('MultiIngestionSettings Page', () => {
|
||||
beforeEach(() => {
|
||||
render(<MultiIngestionSettings />);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders MultiIngestionSettings page without crashing', () => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Find your ingestion URL and learn more about sending data to SigNoz',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Ingestion Keys')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByText('Create and manage ingestion keys for the SigNoz Cloud'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const overviewLink = screen.getByRole('link', { name: /here/i });
|
||||
expect(overviewLink).toHaveAttribute(
|
||||
'href',
|
||||
'https://signoz.io/docs/ingestion/signoz-cloud/overview/',
|
||||
);
|
||||
expect(overviewLink).toHaveAttribute('target', '_blank');
|
||||
expect(overviewLink).toHaveClass('learn-more');
|
||||
expect(overviewLink).toHaveAttribute('rel', 'noreferrer');
|
||||
|
||||
const aboutKeyslink = screen.getByRole('link', { name: /Learn more/i });
|
||||
expect(aboutKeyslink).toHaveAttribute(
|
||||
'href',
|
||||
'https://signoz.io/docs/ingestion/signoz-cloud/keys/',
|
||||
);
|
||||
expect(aboutKeyslink).toHaveAttribute('target', '_blank');
|
||||
expect(aboutKeyslink).toHaveClass('learn-more');
|
||||
expect(aboutKeyslink).toHaveAttribute('rel', 'noreferrer');
|
||||
});
|
||||
});
|
||||
@@ -309,7 +309,6 @@ function ExplorerColumnsRenderer({
|
||||
>
|
||||
<Button
|
||||
className="action-btn"
|
||||
data-testid="add-columns-button"
|
||||
icon={
|
||||
<PlusCircle
|
||||
size={16}
|
||||
|
||||
@@ -563,11 +563,11 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
if (selectedGraph === PANEL_TYPES.LIST) {
|
||||
const initialDataSource = currentQuery.builder.queryData[0].dataSource;
|
||||
if (initialDataSource === DataSource.LOGS) {
|
||||
// we do not need selected log columns in the request data as the entire response contains all the necessary data
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
tableParams: {
|
||||
...prev.tableParams,
|
||||
selectColumns: selectedLogFields,
|
||||
},
|
||||
}));
|
||||
} else if (initialDataSource === DataSource.TRACES) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Card, Typography } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
import Card from 'antd/es/card/Card';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Card)`
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import './TraceDetails.styles.scss';
|
||||
|
||||
import { FilterOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Layout, Typography } from 'antd';
|
||||
import { Button, Col, Typography } from 'antd';
|
||||
import Sider from 'antd/es/layout/Sider';
|
||||
import cx from 'classnames';
|
||||
import {
|
||||
StyledCol,
|
||||
@@ -41,8 +42,6 @@ import {
|
||||
INTERVAL_UNITS,
|
||||
} from './utils';
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
||||
const spanServiceColors = useMemo(
|
||||
() => spanServiceNameToColorMapping(response[0].events),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Card, Col } from 'antd';
|
||||
import { Col } from 'antd';
|
||||
import Card from 'antd/es/card/Card';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Card)`
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
export const explorerView = {
|
||||
status: 'success',
|
||||
data: [
|
||||
{
|
||||
uuid: 'test-uuid-1',
|
||||
name: 'Table View',
|
||||
category: '',
|
||||
createdAt: '2023-08-29T18:04:10.906310033Z',
|
||||
createdBy: 'test-user-1',
|
||||
updatedAt: '2024-01-29T10:42:47.346331133Z',
|
||||
updatedBy: 'test-user-1',
|
||||
sourcePage: 'traces',
|
||||
tags: [''],
|
||||
compositeQuery: {
|
||||
builderQueries: {
|
||||
A: {
|
||||
queryName: 'A',
|
||||
stepInterval: 60,
|
||||
dataSource: 'traces',
|
||||
aggregateOperator: 'count',
|
||||
aggregateAttribute: {
|
||||
key: 'component',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
filters: {
|
||||
op: 'AND',
|
||||
items: [
|
||||
{
|
||||
key: {
|
||||
key: 'component',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
value: 'test-component',
|
||||
op: '!=',
|
||||
},
|
||||
],
|
||||
},
|
||||
groupBy: [
|
||||
{
|
||||
key: 'component',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
{
|
||||
key: 'client-uuid',
|
||||
dataType: 'string',
|
||||
type: 'resource',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
},
|
||||
],
|
||||
expression: 'A',
|
||||
disabled: false,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
pageSize: 0,
|
||||
orderBy: [
|
||||
{
|
||||
columnName: 'timestamp',
|
||||
order: 'desc',
|
||||
},
|
||||
],
|
||||
reduceTo: 'sum',
|
||||
ShiftBy: 0,
|
||||
},
|
||||
},
|
||||
panelType: 'table',
|
||||
queryType: 'builder',
|
||||
},
|
||||
extraData: '{"color":"#00ffd0"}',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -2,7 +2,6 @@ import { rest } from 'msw';
|
||||
|
||||
import { billingSuccessResponse } from './__mockdata__/billing';
|
||||
import { dashboardSuccessResponse } from './__mockdata__/dashboards';
|
||||
import { explorerView } from './__mockdata__/explorer_views';
|
||||
import { inviteUser } from './__mockdata__/invite_user';
|
||||
import { licensesSuccessResponse } from './__mockdata__/licenses';
|
||||
import { membersResponse } from './__mockdata__/members';
|
||||
@@ -56,51 +55,6 @@ export const handlers = [
|
||||
const metricName = req.url.searchParams.get('metricName');
|
||||
const tagKey = req.url.searchParams.get('tagKey');
|
||||
|
||||
const attributeKey = req.url.searchParams.get('attributeKey');
|
||||
|
||||
if (attributeKey === 'serviceName') {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
status: 'success',
|
||||
data: {
|
||||
stringAttributeValues: [
|
||||
'customer',
|
||||
'demo-app',
|
||||
'driver',
|
||||
'frontend',
|
||||
'mysql',
|
||||
'redis',
|
||||
'route',
|
||||
'go-grpc-otel-server',
|
||||
'test',
|
||||
],
|
||||
numberAttributeValues: null,
|
||||
boolAttributeValues: null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (attributeKey === 'name') {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
status: 'success',
|
||||
data: {
|
||||
stringAttributeValues: [
|
||||
'HTTP GET',
|
||||
'HTTP GET /customer',
|
||||
'HTTP GET /dispatch',
|
||||
'HTTP GET /route',
|
||||
],
|
||||
numberAttributeValues: null,
|
||||
boolAttributeValues: null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
metricName === 'signoz_calls_total' &&
|
||||
tagKey === 'resource_signoz_collector_id'
|
||||
@@ -148,31 +102,4 @@ export const handlers = [
|
||||
rest.post('http://localhost/api/v1/invite', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(inviteUser)),
|
||||
),
|
||||
|
||||
rest.get(
|
||||
'http://localhost/api/v3/autocomplete/aggregate_attributes',
|
||||
(req, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
status: 'success',
|
||||
data: { attributeKeys: null },
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
rest.get('http://localhost/api/v1/explorer/views', (req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(explorerView)),
|
||||
),
|
||||
|
||||
rest.post('http://localhost/api/v1/event', (req, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
payload: 'Event Processed Successfully',
|
||||
}),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -26,10 +26,7 @@ export const getRoutes = (
|
||||
settings.push(...organizationSettings(t));
|
||||
}
|
||||
|
||||
if (
|
||||
isGatewayEnabled &&
|
||||
(userRole === USER_ROLES.ADMIN || userRole === USER_ROLES.EDITOR)
|
||||
) {
|
||||
if (isGatewayEnabled && userRole === USER_ROLES.ADMIN) {
|
||||
settings.push(...multiIngestionSettings(t));
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,6 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
||||
className="min-max-input"
|
||||
onChange={onChangeMinHandler}
|
||||
value={preMin}
|
||||
data-testid="min-input"
|
||||
addonAfter="ms"
|
||||
/>
|
||||
<Input
|
||||
@@ -119,7 +118,6 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
||||
className="min-max-input"
|
||||
onChange={onChangeMaxHandler}
|
||||
value={preMax}
|
||||
data-testid="max-input"
|
||||
addonAfter="ms"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -224,18 +224,13 @@ export function Filter(props: FilterProps): JSX.Element {
|
||||
<Button
|
||||
onClick={(): void => handleRun({ resetAll: true })}
|
||||
className="sync-icon"
|
||||
data-testid="reset-filters"
|
||||
>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
<Tooltip title="Collapse" placement="right">
|
||||
<Button
|
||||
onClick={(): void => setOpen(false)}
|
||||
className="arrow-icon"
|
||||
data-testid="toggle-filter-panel"
|
||||
>
|
||||
<Button onClick={(): void => setOpen(false)} className="arrow-icon">
|
||||
<VerticalAlignTopOutlined rotate={270} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -64,7 +64,7 @@ export function Section(props: SectionProps): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Divider plain className="divider" />
|
||||
<div className="section-body-header" data-testid={`collapse-${panelName}`}>
|
||||
<div className="section-body-header">
|
||||
<Collapse
|
||||
bordered={false}
|
||||
className="collapseContainer"
|
||||
@@ -96,11 +96,7 @@ export function Section(props: SectionProps): JSX.Element {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={onClearHandler}
|
||||
data-testid={`collapse-${panelName}-clearBtn`}
|
||||
>
|
||||
<Button type="link" onClick={onClearHandler}>
|
||||
Clear All
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -145,7 +145,6 @@ export function SectionBody(props: SectionBodyProps): JSX.Element {
|
||||
key={`${type}-${item}`}
|
||||
onChange={(e): void => onCheckHandler(e, item)}
|
||||
checked={checkboxMatcher(item)}
|
||||
data-testid={`${type}-${item}`}
|
||||
>
|
||||
<div className="checkbox-label">
|
||||
<div className={labelClassname(item)} />
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {
|
||||
initialQueriesMap,
|
||||
initialQueryBuilderFormValues,
|
||||
} from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import { QueryBuilderContext } from 'providers/QueryBuilder';
|
||||
import { fireEvent, render, screen, waitFor, within } from 'tests/test-utils';
|
||||
import { render } from 'tests/test-utils';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import TracesExplorer from '..';
|
||||
import { Filter } from '../Filter/Filter';
|
||||
import { AllTraceFilterKeyValue } from '../Filter/filterUtils';
|
||||
|
||||
@@ -40,48 +37,6 @@ jest.mock('uplot', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'container/TopNav/DateTimeSelectionV2/index.tsx',
|
||||
() =>
|
||||
function MockDateTimeSelection(): JSX.Element {
|
||||
return <div>MockDateTimeSelection</div>;
|
||||
},
|
||||
);
|
||||
|
||||
function checkIfSectionIsOpen(
|
||||
getByTestId: (testId: string) => HTMLElement,
|
||||
panelName: string,
|
||||
): void {
|
||||
const section = getByTestId(`collapse-${panelName}`);
|
||||
expect(section.querySelector('.ant-collapse-item-active')).not.toBeNull();
|
||||
}
|
||||
|
||||
function checkIfSectionIsNotOpen(
|
||||
getByTestId: (testId: string) => HTMLElement,
|
||||
panelName: string,
|
||||
): void {
|
||||
const section = getByTestId(`collapse-${panelName}`);
|
||||
expect(section.querySelector('.ant-collapse-item-active')).toBeNull();
|
||||
}
|
||||
|
||||
const defaultOpenSections = ['hasError', 'durationNano', 'serviceName'];
|
||||
|
||||
const defaultClosedSections = Object.keys(AllTraceFilterKeyValue).filter(
|
||||
(section) =>
|
||||
![...defaultOpenSections, 'durationNanoMin', 'durationNanoMax'].includes(
|
||||
section,
|
||||
),
|
||||
);
|
||||
|
||||
async function checkForSectionContent(values: string[]): Promise<void> {
|
||||
for (const val of values) {
|
||||
const sectionContent = await screen.findByText(val);
|
||||
await waitFor(() => expect(sectionContent).toBeInTheDocument());
|
||||
}
|
||||
}
|
||||
|
||||
const redirectWithQueryBuilderData = jest.fn();
|
||||
|
||||
const compositeQuery: Query = {
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
@@ -126,157 +81,6 @@ const compositeQuery: Query = {
|
||||
};
|
||||
|
||||
describe('TracesExplorer - ', () => {
|
||||
// Initial filter panel rendering
|
||||
// Test the initial state like which filters section are opened, default state of duration slider, etc.
|
||||
it('should render the Trace filter', async () => {
|
||||
const { getByText, getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||
|
||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||
expect(getByText(filter)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Check default state of duration slider
|
||||
const minDuration = getByTestId('min-input') as HTMLInputElement;
|
||||
const maxDuration = getByTestId('max-input') as HTMLInputElement;
|
||||
expect(minDuration).toHaveValue(null);
|
||||
expect(minDuration).toHaveProperty('placeholder', '0');
|
||||
expect(maxDuration).toHaveValue(null);
|
||||
expect(maxDuration).toHaveProperty('placeholder', '100000000');
|
||||
|
||||
// Check which all filter section are opened by default
|
||||
defaultOpenSections.forEach((section) =>
|
||||
checkIfSectionIsOpen(getByTestId, section),
|
||||
);
|
||||
|
||||
// Check which all filter section are closed by default
|
||||
defaultClosedSections.forEach((section) =>
|
||||
checkIfSectionIsNotOpen(getByTestId, section),
|
||||
);
|
||||
|
||||
// check for the status section content
|
||||
await checkForSectionContent(['Ok', 'Error']);
|
||||
|
||||
// check for the service name section content from API response
|
||||
await checkForSectionContent([
|
||||
'customer',
|
||||
'demo-app',
|
||||
'driver',
|
||||
'frontend',
|
||||
'mysql',
|
||||
'redis',
|
||||
'route',
|
||||
'go-grpc-otel-server',
|
||||
'test',
|
||||
]);
|
||||
});
|
||||
|
||||
// test the filter panel actions like opening and closing the sections, etc.
|
||||
it('filter panel actions', async () => {
|
||||
const { getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||
|
||||
// Check if the section is closed
|
||||
checkIfSectionIsNotOpen(getByTestId, 'name');
|
||||
// Open the section
|
||||
const name = getByTestId('collapse-name');
|
||||
expect(name).toBeInTheDocument();
|
||||
|
||||
userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name));
|
||||
await waitFor(() => checkIfSectionIsOpen(getByTestId, 'name'));
|
||||
|
||||
await checkForSectionContent([
|
||||
'HTTP GET',
|
||||
'HTTP GET /customer',
|
||||
'HTTP GET /dispatch',
|
||||
'HTTP GET /route',
|
||||
]);
|
||||
|
||||
// Close the section
|
||||
userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name));
|
||||
await waitFor(() => checkIfSectionIsNotOpen(getByTestId, 'name'));
|
||||
});
|
||||
|
||||
it('checking filters should update the query', async () => {
|
||||
const { getByText } = render(
|
||||
<QueryBuilderContext.Provider
|
||||
value={
|
||||
{
|
||||
currentQuery: {
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [initialQueryBuilderFormValues],
|
||||
},
|
||||
},
|
||||
redirectWithQueryBuilderData,
|
||||
} as any
|
||||
}
|
||||
>
|
||||
<Filter setOpen={jest.fn()} />
|
||||
</QueryBuilderContext.Provider>,
|
||||
);
|
||||
|
||||
const okCheckbox = getByText('Ok');
|
||||
fireEvent.click(okCheckbox);
|
||||
expect(
|
||||
redirectWithQueryBuilderData.mock.calls[
|
||||
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||
][0].builder.queryData[0].filters.items,
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
key: {
|
||||
id: expect.any(String),
|
||||
key: 'hasError',
|
||||
type: 'tag',
|
||||
dataType: 'bool',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
op: 'in',
|
||||
value: ['false'],
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// Check if the query is updated when the error checkbox is clicked
|
||||
const errorCheckbox = getByText('Error');
|
||||
fireEvent.click(errorCheckbox);
|
||||
expect(
|
||||
redirectWithQueryBuilderData.mock.calls[
|
||||
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||
][0].builder.queryData[0].filters.items,
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
key: {
|
||||
id: expect.any(String),
|
||||
key: 'hasError',
|
||||
type: 'tag',
|
||||
dataType: 'bool',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
op: 'in',
|
||||
value: ['false', 'true'],
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the trace filter with the given query', async () => {
|
||||
jest
|
||||
.spyOn(compositeQueryHook, 'useGetCompositeQueryParam')
|
||||
.mockReturnValue(compositeQuery);
|
||||
|
||||
const { findByText, getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||
|
||||
// check if the default query is applied - composite query has filters - serviceName : demo-app and name : HTTP GET /customer
|
||||
expect(await findByText('demo-app')).toBeInTheDocument();
|
||||
expect(getByTestId('serviceName-demo-app')).toBeChecked();
|
||||
expect(await findByText('HTTP GET /customer')).toBeInTheDocument();
|
||||
expect(getByTestId('name-HTTP GET /customer')).toBeChecked();
|
||||
});
|
||||
|
||||
it('test edge cases of undefined filters', async () => {
|
||||
jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({
|
||||
...compositeQuery,
|
||||
@@ -294,6 +98,7 @@ describe('TracesExplorer - ', () => {
|
||||
|
||||
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
||||
|
||||
// we should have all the filters
|
||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||
expect(getByText(filter)).toBeInTheDocument();
|
||||
});
|
||||
@@ -319,141 +124,9 @@ describe('TracesExplorer - ', () => {
|
||||
|
||||
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
||||
|
||||
// we should have all the filters
|
||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||
expect(getByText(filter)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should clear filter on clear & reset button click', async () => {
|
||||
const { getByText, getByTestId } = render(
|
||||
<QueryBuilderContext.Provider
|
||||
value={
|
||||
{
|
||||
currentQuery: {
|
||||
...initialQueriesMap.traces,
|
||||
builder: {
|
||||
...initialQueriesMap.traces.builder,
|
||||
queryData: [initialQueryBuilderFormValues],
|
||||
},
|
||||
},
|
||||
redirectWithQueryBuilderData,
|
||||
} as any
|
||||
}
|
||||
>
|
||||
<Filter setOpen={jest.fn()} />
|
||||
</QueryBuilderContext.Provider>,
|
||||
);
|
||||
|
||||
// check for the status section content
|
||||
await checkForSectionContent(['Ok', 'Error']);
|
||||
|
||||
// check for the service name section content from API response
|
||||
await checkForSectionContent([
|
||||
'customer',
|
||||
'demo-app',
|
||||
'driver',
|
||||
'frontend',
|
||||
'mysql',
|
||||
'redis',
|
||||
'route',
|
||||
'go-grpc-otel-server',
|
||||
'test',
|
||||
]);
|
||||
|
||||
const okCheckbox = getByText('Ok');
|
||||
fireEvent.click(okCheckbox);
|
||||
|
||||
const frontendCheckbox = getByText('frontend');
|
||||
fireEvent.click(frontendCheckbox);
|
||||
|
||||
// check if checked and present in query
|
||||
expect(
|
||||
redirectWithQueryBuilderData.mock.calls[
|
||||
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||
][0].builder.queryData[0].filters.items,
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
key: {
|
||||
id: expect.any(String),
|
||||
key: 'hasError',
|
||||
type: 'tag',
|
||||
dataType: 'bool',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
op: 'in',
|
||||
value: ['false'],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
key: {
|
||||
key: 'serviceName',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
id: expect.any(String),
|
||||
},
|
||||
op: 'in',
|
||||
value: ['frontend'],
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
const clearButton = getByTestId('collapse-serviceName-clearBtn');
|
||||
expect(clearButton).toBeInTheDocument();
|
||||
fireEvent.click(clearButton);
|
||||
|
||||
// check if cleared and not present in query
|
||||
expect(
|
||||
redirectWithQueryBuilderData.mock.calls[
|
||||
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||
][0].builder.queryData[0].filters.items,
|
||||
).not.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
key: {
|
||||
key: 'serviceName',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
id: expect.any(String),
|
||||
},
|
||||
op: 'in',
|
||||
value: ['frontend'],
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// check if reset button is present
|
||||
const resetButton = getByTestId('reset-filters');
|
||||
expect(resetButton).toBeInTheDocument();
|
||||
fireEvent.click(resetButton);
|
||||
|
||||
// check if reset id done
|
||||
expect(
|
||||
redirectWithQueryBuilderData.mock.calls[
|
||||
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||
][0].builder.queryData[0].filters.items,
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('filter panel should collapse & uncollapsed', async () => {
|
||||
const { getByText, getByTestId } = render(<TracesExplorer />);
|
||||
|
||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||
expect(getByText(filter)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Filter panel should collapse
|
||||
const collapseButton = getByTestId('toggle-filter-panel');
|
||||
expect(collapseButton).toBeInTheDocument();
|
||||
fireEvent.click(collapseButton);
|
||||
|
||||
// uncollapse btn should be present
|
||||
expect(
|
||||
await screen.findByTestId('filter-uncollapse-btn'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -251,7 +251,6 @@ function TracesExplorer(): JSX.Element {
|
||||
<Button
|
||||
onClick={(): void => setOpen(!isOpen)}
|
||||
className="filter-outlined-btn"
|
||||
data-testid="filter-uncollapse-btn"
|
||||
>
|
||||
<FilterOutlined />
|
||||
</Button>
|
||||
|
||||
@@ -63,7 +63,7 @@ func EnrichmentRequired(params *v3.QueryRangeParamsV3) bool {
|
||||
|
||||
func isEnriched(field v3.AttributeKey) bool {
|
||||
// if it is timestamp/id dont check
|
||||
if field.Key == "timestamp" || field.Key == "id" || field.Key == constants.SigNozOrderByValue {
|
||||
if field.Key == "timestamp" || field.Key == "id" || field.Key == constants.SigNozOrderByValue || field.Type == v3.AttributeKeyTypeInstrumentationScope {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ const (
|
||||
ARRAY_INT64 = "Array(Int64)"
|
||||
ARRAY_FLOAT64 = "Array(Float64)"
|
||||
ARRAY_BOOL = "Array(Bool)"
|
||||
NGRAM_SIZE = 4
|
||||
)
|
||||
|
||||
var dataTypeMapping = map[string]string{
|
||||
@@ -73,7 +72,6 @@ func getPath(keyArr []string) string {
|
||||
|
||||
func getJSONFilterKey(key v3.AttributeKey, op v3.FilterOperator, isArray bool) (string, error) {
|
||||
keyArr := strings.Split(key.Key, ".")
|
||||
// i.e it should be at least body.name, and not something like body
|
||||
if len(keyArr) < 2 {
|
||||
return "", fmt.Errorf("incorrect key, should contain at least 2 parts")
|
||||
}
|
||||
@@ -108,29 +106,6 @@ func getJSONFilterKey(key v3.AttributeKey, op v3.FilterOperator, isArray bool) (
|
||||
return keyname, nil
|
||||
}
|
||||
|
||||
// takes the path and the values and generates where clauses for better usage of index
|
||||
func getPathIndexFilter(path string) string {
|
||||
filters := []string{}
|
||||
keyArr := strings.Split(path, ".")
|
||||
if len(keyArr) < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for i, key := range keyArr {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSuffix(key, "[*]")
|
||||
if len(key) >= NGRAM_SIZE {
|
||||
filters = append(filters, strings.ToLower(key))
|
||||
}
|
||||
}
|
||||
if len(filters) > 0 {
|
||||
return fmt.Sprintf("lower(body) like lower('%%%s%%')", strings.Join(filters, "%"))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetJSONFilter(item v3.FilterItem) (string, error) {
|
||||
|
||||
dataType := item.Key.DataType
|
||||
@@ -179,28 +154,11 @@ func GetJSONFilter(item v3.FilterItem) (string, error) {
|
||||
return "", fmt.Errorf("unsupported operator: %s", op)
|
||||
}
|
||||
|
||||
filters := []string{}
|
||||
|
||||
pathFilter := getPathIndexFilter(item.Key.Key)
|
||||
if pathFilter != "" {
|
||||
filters = append(filters, pathFilter)
|
||||
}
|
||||
if op == v3.FilterOperatorContains ||
|
||||
op == v3.FilterOperatorEqual ||
|
||||
op == v3.FilterOperatorHas {
|
||||
val, ok := item.Value.(string)
|
||||
if ok && len(val) >= NGRAM_SIZE {
|
||||
filters = append(filters, fmt.Sprintf("lower(body) like lower('%%%s%%')", utils.QuoteEscapedString(strings.ToLower(val))))
|
||||
}
|
||||
}
|
||||
|
||||
// add exists check for non array items as default values of int/float/bool will corrupt the results
|
||||
if !isArray && !(item.Operator == v3.FilterOperatorExists || item.Operator == v3.FilterOperatorNotExists) {
|
||||
existsFilter := fmt.Sprintf("JSON_EXISTS(body, '$.%s')", getPath(strings.Split(item.Key.Key, ".")[1:]))
|
||||
filter = fmt.Sprintf("%s AND %s", existsFilter, filter)
|
||||
}
|
||||
|
||||
filters = append(filters, filter)
|
||||
|
||||
return strings.Join(filters, " AND "), nil
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "has",
|
||||
Value: "index_service",
|
||||
},
|
||||
Filter: "lower(body) like lower('%requestor_list%') AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service')",
|
||||
Filter: "has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service')",
|
||||
},
|
||||
{
|
||||
Name: "Array membership int64",
|
||||
@@ -181,7 +181,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "has",
|
||||
Value: 2,
|
||||
},
|
||||
Filter: "lower(body) like lower('%int_numbers%') AND has(JSONExtract(JSON_QUERY(body, '$.\"int_numbers\"[*]'), '" + ARRAY_INT64 + "'), 2)",
|
||||
Filter: "has(JSONExtract(JSON_QUERY(body, '$.\"int_numbers\"[*]'), '" + ARRAY_INT64 + "'), 2)",
|
||||
},
|
||||
{
|
||||
Name: "Array membership float64",
|
||||
@@ -194,7 +194,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "nhas",
|
||||
Value: 2.2,
|
||||
},
|
||||
Filter: "lower(body) like lower('%nested_num%float_nums%') AND NOT has(JSONExtract(JSON_QUERY(body, '$.\"nested_num\"[*].\"float_nums\"[*]'), '" + ARRAY_FLOAT64 + "'), 2.200000)",
|
||||
Filter: "NOT has(JSONExtract(JSON_QUERY(body, '$.\"nested_num\"[*].\"float_nums\"[*]'), '" + ARRAY_FLOAT64 + "'), 2.200000)",
|
||||
},
|
||||
{
|
||||
Name: "Array membership bool",
|
||||
@@ -207,7 +207,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "has",
|
||||
Value: true,
|
||||
},
|
||||
Filter: "lower(body) like lower('%bool%') AND has(JSONExtract(JSON_QUERY(body, '$.\"bool\"[*]'), '" + ARRAY_BOOL + "'), true)",
|
||||
Filter: "has(JSONExtract(JSON_QUERY(body, '$.\"bool\"[*]'), '" + ARRAY_BOOL + "'), true)",
|
||||
},
|
||||
{
|
||||
Name: "eq operator",
|
||||
@@ -220,7 +220,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "=",
|
||||
Value: "hello",
|
||||
},
|
||||
Filter: "lower(body) like lower('%message%') AND lower(body) like lower('%hello%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') = 'hello'",
|
||||
Filter: "JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') = 'hello'",
|
||||
},
|
||||
{
|
||||
Name: "eq operator number",
|
||||
@@ -233,7 +233,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "=",
|
||||
Value: 1,
|
||||
},
|
||||
Filter: "lower(body) like lower('%status%') AND JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + INT64 + "') = 1",
|
||||
Filter: "JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + INT64 + "') = 1",
|
||||
},
|
||||
{
|
||||
Name: "neq operator number",
|
||||
@@ -246,7 +246,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "=",
|
||||
Value: 1.1,
|
||||
},
|
||||
Filter: "lower(body) like lower('%status%') AND JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + FLOAT64 + "') = 1.100000",
|
||||
Filter: "JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + FLOAT64 + "') = 1.100000",
|
||||
},
|
||||
{
|
||||
Name: "eq operator bool",
|
||||
@@ -259,7 +259,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "=",
|
||||
Value: true,
|
||||
},
|
||||
Filter: "lower(body) like lower('%boolkey%') AND JSON_EXISTS(body, '$.\"boolkey\"') AND JSONExtract(JSON_VALUE(body, '$.\"boolkey\"'), '" + BOOL + "') = true",
|
||||
Filter: "JSON_EXISTS(body, '$.\"boolkey\"') AND JSONExtract(JSON_VALUE(body, '$.\"boolkey\"'), '" + BOOL + "') = true",
|
||||
},
|
||||
{
|
||||
Name: "greater than operator",
|
||||
@@ -272,7 +272,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: ">",
|
||||
Value: 1,
|
||||
},
|
||||
Filter: "lower(body) like lower('%status%') AND JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + INT64 + "') > 1",
|
||||
Filter: "JSON_EXISTS(body, '$.\"status\"') AND JSONExtract(JSON_VALUE(body, '$.\"status\"'), '" + INT64 + "') > 1",
|
||||
},
|
||||
{
|
||||
Name: "regex operator",
|
||||
@@ -285,7 +285,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "regex",
|
||||
Value: "a*",
|
||||
},
|
||||
Filter: "lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"') AND match(JSON_VALUE(body, '$.\"message\"'), 'a*')",
|
||||
Filter: "JSON_EXISTS(body, '$.\"message\"') AND match(JSON_VALUE(body, '$.\"message\"'), 'a*')",
|
||||
},
|
||||
{
|
||||
Name: "contains operator",
|
||||
@@ -298,7 +298,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "contains",
|
||||
Value: "a",
|
||||
},
|
||||
Filter: "lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%'",
|
||||
Filter: "JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%'",
|
||||
},
|
||||
{
|
||||
Name: "contains operator with quotes",
|
||||
@@ -311,7 +311,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "contains",
|
||||
Value: "hello 'world'",
|
||||
},
|
||||
Filter: "lower(body) like lower('%message%') AND lower(body) like lower('%hello \\'world\\'%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%hello \\'world\\'%'",
|
||||
Filter: "JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%hello \\'world\\'%'",
|
||||
},
|
||||
{
|
||||
Name: "exists",
|
||||
@@ -324,7 +324,7 @@ var testGetJSONFilterData = []struct {
|
||||
Operator: "exists",
|
||||
Value: "",
|
||||
},
|
||||
Filter: "lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"')",
|
||||
Filter: "JSON_EXISTS(body, '$.\"message\"')",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -51,12 +51,13 @@ var logOperators = map[v3.FilterOperator]string{
|
||||
v3.FilterOperatorNotExists: "not has(%s_%s_key, '%s')",
|
||||
}
|
||||
|
||||
const BODY = "body"
|
||||
|
||||
func getClickhouseLogsColumnType(columnType v3.AttributeKeyType) string {
|
||||
if columnType == v3.AttributeKeyTypeTag {
|
||||
return "attributes"
|
||||
}
|
||||
if columnType == v3.AttributeKeyTypeInstrumentationScope {
|
||||
return "scope"
|
||||
}
|
||||
return "resources"
|
||||
}
|
||||
|
||||
@@ -195,24 +196,10 @@ func buildLogsTimeSeriesFilterQuery(fs *v3.FilterSet, groupBy []v3.AttributeKey,
|
||||
case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
|
||||
columnName := getClickhouseColumnName(item.Key)
|
||||
val := utils.QuoteEscapedString(fmt.Sprintf("%v", item.Value))
|
||||
if columnName == BODY {
|
||||
logsOp = strings.Replace(logsOp, "ILIKE", "LIKE", 1) // removing i from ilike and not ilike
|
||||
conditions = append(conditions, fmt.Sprintf("lower(%s) %s lower('%%%s%%')", columnName, logsOp, val))
|
||||
} else {
|
||||
conditions = append(conditions, fmt.Sprintf("%s %s '%%%s%%'", columnName, logsOp, val))
|
||||
}
|
||||
conditions = append(conditions, fmt.Sprintf("%s %s '%%%s%%'", columnName, logsOp, val))
|
||||
default:
|
||||
columnName := getClickhouseColumnName(item.Key)
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
|
||||
// for use lower for like and ilike
|
||||
if op == v3.FilterOperatorLike || op == v3.FilterOperatorNotLike {
|
||||
if columnName == BODY {
|
||||
logsOp = strings.Replace(logsOp, "ILIKE", "LIKE", 1) // removing i from ilike and not ilike
|
||||
columnName = fmt.Sprintf("lower(%s)", columnName)
|
||||
fmtVal = fmt.Sprintf("lower(%s)", fmtVal)
|
||||
}
|
||||
}
|
||||
conditions = append(conditions, fmt.Sprintf("%s %s %s", columnName, logsOp, fmtVal))
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -53,6 +53,11 @@ var testGetClickhouseColumnNameData = []struct {
|
||||
AttributeKey: v3.AttributeKey{Key: "test-attr", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag, IsColumn: true},
|
||||
ExpectedColumnName: "`attribute_string_test-attr`",
|
||||
},
|
||||
{
|
||||
Name: "instrumentation scope attribute",
|
||||
AttributeKey: v3.AttributeKey{Key: "version", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeInstrumentationScope, IsColumn: false},
|
||||
ExpectedColumnName: "scope_string_value[indexOf(scope_string_key, 'version')]",
|
||||
},
|
||||
}
|
||||
|
||||
func TestGetClickhouseColumnName(t *testing.T) {
|
||||
@@ -131,12 +136,11 @@ var timeSeriesFilterQueryData = []struct {
|
||||
ExpectedFilter: "attributes_string_value[indexOf(attributes_string_key, 'user_name')] = 'john' AND resources_string_value[indexOf(resources_string_key, 'k8s_namespace')] != 'my_service'",
|
||||
},
|
||||
{
|
||||
Name: "Test attribute and resource attribute with different case",
|
||||
Name: "Test instrumentation scope attribute",
|
||||
FilterSet: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||||
{Key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "%JoHn%", Operator: "like"},
|
||||
{Key: v3.AttributeKey{Key: "k8s_namespace", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}, Value: "%MyService%", Operator: "nlike"},
|
||||
{Key: v3.AttributeKey{Key: "version", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeInstrumentationScope}, Value: "v1", Operator: "="},
|
||||
}},
|
||||
ExpectedFilter: "attributes_string_value[indexOf(attributes_string_key, 'user_name')] ILIKE '%JoHn%' AND resources_string_value[indexOf(resources_string_key, 'k8s_namespace')] NOT ILIKE '%MyService%'",
|
||||
ExpectedFilter: "scope_string_value[indexOf(scope_string_key, 'version')] = 'v1'",
|
||||
},
|
||||
{
|
||||
Name: "Test materialized column",
|
||||
@@ -295,22 +299,6 @@ var timeSeriesFilterQueryData = []struct {
|
||||
}},
|
||||
ExpectedFilter: "`attribute_int64_status_exists`=false",
|
||||
},
|
||||
{
|
||||
Name: "Test for body contains and ncontains",
|
||||
FilterSet: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||||
{Key: v3.AttributeKey{Key: "body", DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: "contains", Value: "test"},
|
||||
{Key: v3.AttributeKey{Key: "body", DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: "ncontains", Value: "test1"},
|
||||
}},
|
||||
ExpectedFilter: "lower(body) LIKE lower('%test%') AND lower(body) NOT LIKE lower('%test1%')",
|
||||
},
|
||||
{
|
||||
Name: "Test for body like and nlike",
|
||||
FilterSet: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||||
{Key: v3.AttributeKey{Key: "body", DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: "like", Value: "test"},
|
||||
{Key: v3.AttributeKey{Key: "body", DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: "nlike", Value: "test1"},
|
||||
}},
|
||||
ExpectedFilter: "lower(body) LIKE lower('test') AND lower(body) NOT LIKE lower('test1')",
|
||||
},
|
||||
}
|
||||
|
||||
func TestBuildLogsTimeSeriesFilterQuery(t *testing.T) {
|
||||
@@ -758,10 +746,11 @@ var testBuildLogsQueryData = []struct {
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
|
||||
},
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
"CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64," +
|
||||
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string " +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, " +
|
||||
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by timestamp DESC",
|
||||
},
|
||||
{
|
||||
@@ -777,10 +766,11 @@ var testBuildLogsQueryData = []struct {
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
|
||||
OrderBy: []v3.OrderBy{{ColumnName: "method", DataType: v3.AttributeKeyDataTypeString, Order: "ASC", IsColumn: true}},
|
||||
},
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
"CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64," +
|
||||
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string " +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, " +
|
||||
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by `method` ASC",
|
||||
},
|
||||
{
|
||||
@@ -797,12 +787,35 @@ var testBuildLogsQueryData = []struct {
|
||||
{Key: v3.AttributeKey{Key: "severity_number", DataType: v3.AttributeKeyDataTypeInt64, IsColumn: true}, Operator: "!=", Value: 0},
|
||||
}},
|
||||
},
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
"CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64," +
|
||||
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string " +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, " +
|
||||
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND severity_number != 0 order by timestamp DESC",
|
||||
},
|
||||
{
|
||||
Name: "Test Noop with scope filter",
|
||||
PanelType: v3.PanelTypeList,
|
||||
Start: 1680066360726210000,
|
||||
End: 1680066458000000000,
|
||||
BuilderQuery: &v3.BuilderQuery{
|
||||
SelectColumns: []v3.AttributeKey{},
|
||||
QueryName: "A",
|
||||
AggregateOperator: v3.AggregateOperatorNoOp,
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||||
{Key: v3.AttributeKey{Key: "scope_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified, IsColumn: true}, Operator: "=", Value: "app"},
|
||||
{Key: v3.AttributeKey{Key: "scope_version", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified, IsColumn: true}, Operator: "=", Value: "version"},
|
||||
}},
|
||||
},
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
"CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64," +
|
||||
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, " +
|
||||
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND scope_name = 'app' AND scope_version = 'version' order by timestamp DESC",
|
||||
},
|
||||
{
|
||||
Name: "Test aggregate with having clause",
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
@@ -875,7 +888,7 @@ var testBuildLogsQueryData = []struct {
|
||||
},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) LIKE lower('%test%') AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
|
||||
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND body ILIKE '%test%' AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
|
||||
},
|
||||
{
|
||||
Name: "Test attribute with same name as top level key",
|
||||
@@ -1005,7 +1018,7 @@ var testBuildLogsQueryData = []struct {
|
||||
},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%' AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
|
||||
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%' AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
|
||||
},
|
||||
{
|
||||
Name: "TABLE: Test count with JSON Filter Array, groupBy, orderBy",
|
||||
@@ -1039,7 +1052,7 @@ var testBuildLogsQueryData = []struct {
|
||||
},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%requestor_list%') AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
|
||||
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1332,9 +1345,13 @@ var testPrepLogsQueryData = []struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body," +
|
||||
"CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') " +
|
||||
"as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
},
|
||||
{
|
||||
Name: "Live Tail Query with contains",
|
||||
@@ -1351,9 +1368,12 @@ var testPrepLogsQueryData = []struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] ILIKE '%GET%' AND ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] ILIKE '%GET%' AND ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
},
|
||||
{
|
||||
Name: "Live Tail Query W/O filter",
|
||||
@@ -1367,9 +1387,12 @@ var testPrepLogsQueryData = []struct {
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where ",
|
||||
Options: Options{IsLivetailQuery: true},
|
||||
},
|
||||
{
|
||||
Name: "Table query w/o limit",
|
||||
@@ -1506,8 +1529,11 @@ var testPrepLogsQueryLimitOffsetData = []struct {
|
||||
Offset: 0,
|
||||
PageSize: 5,
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by `timestamp` desc LIMIT 1",
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by `timestamp` desc LIMIT 1",
|
||||
},
|
||||
{
|
||||
Name: "Test limit greater than pageSize - order by ts",
|
||||
@@ -1527,8 +1553,11 @@ var testPrepLogsQueryLimitOffsetData = []struct {
|
||||
Offset: 10,
|
||||
PageSize: 10,
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by `timestamp` desc LIMIT 10",
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by `timestamp` desc LIMIT 10",
|
||||
},
|
||||
{
|
||||
Name: "Test limit less than pageSize - order by custom",
|
||||
@@ -1546,8 +1575,11 @@ var testPrepLogsQueryLimitOffsetData = []struct {
|
||||
Offset: 0,
|
||||
PageSize: 5,
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 1 OFFSET 0",
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 1 OFFSET 0",
|
||||
},
|
||||
{
|
||||
Name: "Test limit greater than pageSize - order by custom",
|
||||
@@ -1567,8 +1599,11 @@ var testPrepLogsQueryLimitOffsetData = []struct {
|
||||
Offset: 50,
|
||||
PageSize: 50,
|
||||
},
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 50 OFFSET 50",
|
||||
TableName: "logs",
|
||||
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') " +
|
||||
"as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') " +
|
||||
"as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
|
||||
"from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 50 OFFSET 50",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -296,12 +296,13 @@ var StaticSelectedLogFields = []model.LogField{
|
||||
|
||||
const (
|
||||
LogsSQLSelect = "SELECT " +
|
||||
"timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body," +
|
||||
"timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body," +
|
||||
"CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string," +
|
||||
"CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64," +
|
||||
"CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64," +
|
||||
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string "
|
||||
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string, " +
|
||||
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope "
|
||||
TracesExplorerViewSQLSelectWithSubQuery = "WITH subQuery AS (SELECT distinct on (traceID) traceID, durationNano, " +
|
||||
"serviceName, name FROM %s.%s WHERE parentSpanID = '' AND %s %s ORDER BY durationNano DESC "
|
||||
TracesExplorerViewSQLSelectQuery = "SELECT subQuery.serviceName, subQuery.name, count() AS " +
|
||||
@@ -366,6 +367,18 @@ var StaticFieldsLogsV3 = map[string]v3.AttributeKey{
|
||||
Type: v3.AttributeKeyTypeUnspecified,
|
||||
IsColumn: true,
|
||||
},
|
||||
"scope_name": {
|
||||
Key: "scope_name",
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeUnspecified,
|
||||
IsColumn: true,
|
||||
},
|
||||
"scope_version": {
|
||||
Key: "scope_version",
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeUnspecified,
|
||||
IsColumn: true,
|
||||
},
|
||||
}
|
||||
|
||||
const SigNozOrderByValue = "#SIGNOZ_VALUE"
|
||||
|
||||
@@ -229,13 +229,14 @@ type AggregateAttributeRequest struct {
|
||||
type TagType string
|
||||
|
||||
const (
|
||||
TagTypeTag TagType = "tag"
|
||||
TagTypeResource TagType = "resource"
|
||||
TagTypeTag TagType = "tag"
|
||||
TagTypeResource TagType = "resource"
|
||||
TagTypeInstrumentationScope TagType = "scope"
|
||||
)
|
||||
|
||||
func (q TagType) Validate() error {
|
||||
switch q {
|
||||
case TagTypeTag, TagTypeResource:
|
||||
case TagTypeTag, TagTypeResource, TagTypeInstrumentationScope:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid tag type: %s", q)
|
||||
@@ -300,9 +301,10 @@ type FilterAttributeKeyResponse struct {
|
||||
type AttributeKeyType string
|
||||
|
||||
const (
|
||||
AttributeKeyTypeUnspecified AttributeKeyType = ""
|
||||
AttributeKeyTypeTag AttributeKeyType = "tag"
|
||||
AttributeKeyTypeResource AttributeKeyType = "resource"
|
||||
AttributeKeyTypeUnspecified AttributeKeyType = ""
|
||||
AttributeKeyTypeTag AttributeKeyType = "tag"
|
||||
AttributeKeyTypeResource AttributeKeyType = "resource"
|
||||
AttributeKeyTypeInstrumentationScope AttributeKeyType = "scope"
|
||||
)
|
||||
|
||||
type AttributeKey struct {
|
||||
@@ -327,7 +329,7 @@ func (a AttributeKey) Validate() error {
|
||||
|
||||
if a.IsColumn {
|
||||
switch a.Type {
|
||||
case AttributeKeyTypeResource, AttributeKeyTypeTag, AttributeKeyTypeUnspecified:
|
||||
case AttributeKeyTypeResource, AttributeKeyTypeTag, AttributeKeyTypeUnspecified, AttributeKeyTypeInstrumentationScope:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("invalid attribute type: %s", a.Type)
|
||||
@@ -907,8 +909,7 @@ const (
|
||||
FilterOperatorNotContains FilterOperator = "ncontains"
|
||||
FilterOperatorRegex FilterOperator = "regex"
|
||||
FilterOperatorNotRegex FilterOperator = "nregex"
|
||||
// (I)LIKE is faster than REGEX
|
||||
// ilike doesn't support index so internally we use lower(body) like for query
|
||||
// (I)LIKE is faster than REGEX and supports index
|
||||
FilterOperatorLike FilterOperator = "like"
|
||||
FilterOperatorNotLike FilterOperator = "nlike"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user