mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-03 12:32:02 +00:00
Compare commits
8 Commits
platform-p
...
issue_4071
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e9628dd72 | ||
|
|
08d52a6a87 | ||
|
|
7a00dac7fc | ||
|
|
84bd91dc7a | ||
|
|
4b84b715b4 | ||
|
|
b3c08ec417 | ||
|
|
0365935c09 | ||
|
|
83cf760a01 |
@@ -29,7 +29,6 @@ export enum LOCALSTORAGE {
|
||||
DONT_SHOW_SLOW_API_WARNING = 'DONT_SHOW_SLOW_API_WARNING',
|
||||
METRICS_LIST_OPTIONS = 'METRICS_LIST_OPTIONS',
|
||||
SHOW_EXCEPTIONS_QUICK_FILTERS = 'SHOW_EXCEPTIONS_QUICK_FILTERS',
|
||||
BANNER_DISMISSED = 'BANNER_DISMISSED',
|
||||
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
||||
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
||||
SPAN_DETAILS_PINNED_ATTRIBUTES = 'SPAN_DETAILS_PINNED_ATTRIBUTES',
|
||||
|
||||
@@ -25,51 +25,6 @@
|
||||
background: var(--bg-slate-500);
|
||||
}
|
||||
|
||||
.home-container-banner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
background-color: var(--bg-robin-500);
|
||||
|
||||
.home-container-banner-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: var(--bg-vanilla-100);
|
||||
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.home-container-banner-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
.home-container-banner-link {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Compass, Dot, House, Plus, Wrench } from '@signozhq/icons';
|
||||
import { Button, Popover } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import listUserPreferences from 'api/v1/user/preferences/list';
|
||||
import updateUserPreferenceAPI from 'api/v1/user/preferences/name/update';
|
||||
import Header from 'components/Header/Header';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { ORG_PREFERENCES } from 'constants/orgPreferences';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
@@ -15,10 +15,8 @@ import ROUTES from 'constants/routes';
|
||||
import { getMetricsListQuery } from 'container/MetricsExplorer/Summary/utils';
|
||||
import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import history from 'lib/history';
|
||||
import cloneDeep from 'lodash-es/cloneDeep';
|
||||
import { CompassIcon, DotIcon, HomeIcon, Plus, Wrench, X } from 'lucide-react';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
import * as motion from 'motion/react-client';
|
||||
import Card from 'periscope/components/Card/Card';
|
||||
@@ -51,8 +49,6 @@ export default function Home(): JSX.Element {
|
||||
const [updatingUserPreferences, setUpdatingUserPreferences] = useState(false);
|
||||
const [loadingUserPreferences, setLoadingUserPreferences] = useState(true);
|
||||
|
||||
const { isCommunityUser, isCommunityEnterpriseUser } = useGetTenantLicense();
|
||||
|
||||
const [checklistItems, setChecklistItems] = useState<ChecklistItem[]>(
|
||||
defaultChecklistItemsState,
|
||||
);
|
||||
@@ -61,13 +57,6 @@ export default function Home(): JSX.Element {
|
||||
false,
|
||||
);
|
||||
|
||||
const [isBannerDismissed, setIsBannerDismissed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const bannerDismissed = localStorage.getItem(LOCALSTORAGE.BANNER_DISMISSED);
|
||||
setIsBannerDismissed(bannerDismissed === 'true');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const now = new Date();
|
||||
const startTime = new Date(now.getTime() - homeInterval);
|
||||
@@ -298,44 +287,13 @@ export default function Home(): JSX.Element {
|
||||
logEvent('Homepage: Visited', {});
|
||||
}, []);
|
||||
|
||||
const hideBanner = (): void => {
|
||||
localStorage.setItem(LOCALSTORAGE.BANNER_DISMISSED, 'true');
|
||||
setIsBannerDismissed(true);
|
||||
};
|
||||
|
||||
const showBanner = useMemo(
|
||||
() => !isBannerDismissed && (isCommunityUser || isCommunityEnterpriseUser),
|
||||
[isBannerDismissed, isCommunityUser, isCommunityEnterpriseUser],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="home-container">
|
||||
<div className="sticky-header">
|
||||
{showBanner && (
|
||||
<div className="home-container-banner">
|
||||
<div className="home-container-banner-content">
|
||||
Big News: SigNoz Community Edition now available with SSO (Google OAuth)
|
||||
and API keys -
|
||||
<a
|
||||
href="https://signoz.io/blog/open-source-signoz-now-available-with-sso-and-api-keys/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="home-container-banner-link"
|
||||
>
|
||||
<i>read more</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="home-container-banner-close">
|
||||
<X size={16} onClick={hideBanner} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Header
|
||||
leftComponent={
|
||||
<div className="home-header-left">
|
||||
<HomeIcon size={14} /> Home
|
||||
<House size={14} /> Home
|
||||
</div>
|
||||
}
|
||||
rightComponent={
|
||||
@@ -400,7 +358,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="active-ingestion-card-content-container">
|
||||
<div className="active-ingestion-card-content">
|
||||
<div className="active-ingestion-card-content-icon">
|
||||
<DotIcon size={16} color={Color.BG_FOREST_500} />
|
||||
<Dot size={16} color={Color.BG_FOREST_500} />
|
||||
</div>
|
||||
|
||||
<div className="active-ingestion-card-content-description">
|
||||
@@ -427,7 +385,7 @@ export default function Home(): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CompassIcon size={12} />
|
||||
<Compass size={12} />
|
||||
Explore Logs
|
||||
</div>
|
||||
</div>
|
||||
@@ -441,7 +399,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="active-ingestion-card-content-container">
|
||||
<div className="active-ingestion-card-content">
|
||||
<div className="active-ingestion-card-content-icon">
|
||||
<DotIcon size={16} color={Color.BG_FOREST_500} />
|
||||
<Dot size={16} color={Color.BG_FOREST_500} />
|
||||
</div>
|
||||
|
||||
<div className="active-ingestion-card-content-description">
|
||||
@@ -468,7 +426,7 @@ export default function Home(): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CompassIcon size={12} />
|
||||
<Compass size={12} />
|
||||
Explore Traces
|
||||
</div>
|
||||
</div>
|
||||
@@ -482,7 +440,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="active-ingestion-card-content-container">
|
||||
<div className="active-ingestion-card-content">
|
||||
<div className="active-ingestion-card-content-icon">
|
||||
<DotIcon size={16} color={Color.BG_FOREST_500} />
|
||||
<Dot size={16} color={Color.BG_FOREST_500} />
|
||||
</div>
|
||||
|
||||
<div className="active-ingestion-card-content-description">
|
||||
@@ -509,7 +467,7 @@ export default function Home(): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CompassIcon size={12} />
|
||||
<Compass size={12} />
|
||||
Explore Metrics
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import {
|
||||
Button,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Input,
|
||||
Menu,
|
||||
Popover,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
@@ -14,148 +14,49 @@ import logEvent from 'api/common/logEvent';
|
||||
import { useGetMetricAttributes } from 'api/generated/services/metrics';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import { DataType } from 'container/LogDetailedView/TableView';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { Compass, Copy, Search } from 'lucide-react';
|
||||
import { Check, Copy, Info, Search, SquareArrowOutUpRight } from 'lucide-react';
|
||||
|
||||
import { PANEL_TYPES } from '../../../constants/queryBuilder';
|
||||
import ROUTES from '../../../constants/routes';
|
||||
import { useHandleExplorerTabChange } from '../../../hooks/useHandleExplorerTabChange';
|
||||
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
|
||||
import MetricDetailsErrorState from './MetricDetailsErrorState';
|
||||
import {
|
||||
AllAttributesEmptyTextProps,
|
||||
AllAttributesProps,
|
||||
AllAttributesValueProps,
|
||||
} from './types';
|
||||
AllAttributesEmptyText,
|
||||
AllAttributesValue,
|
||||
} from './AllAttributesValue';
|
||||
import { AllAttributesProps } from './types';
|
||||
import { getMetricDetailsQuery } from './utils';
|
||||
|
||||
const ALL_ATTRIBUTES_KEY = 'all-attributes';
|
||||
|
||||
function AllAttributesEmptyText({
|
||||
isErrorAttributes,
|
||||
refetchAttributes,
|
||||
}: AllAttributesEmptyTextProps): JSX.Element {
|
||||
if (isErrorAttributes) {
|
||||
return (
|
||||
<div className="all-attributes-error-state">
|
||||
<MetricDetailsErrorState
|
||||
refetch={refetchAttributes}
|
||||
errorMessage="Something went wrong while fetching attributes"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <Typography.Text>No attributes found</Typography.Text>;
|
||||
}
|
||||
|
||||
export function AllAttributesValue({
|
||||
filterKey,
|
||||
filterValue,
|
||||
goToMetricsExploreWithAppliedAttribute,
|
||||
}: AllAttributesValueProps): JSX.Element {
|
||||
const [visibleIndex, setVisibleIndex] = useState(5);
|
||||
const [attributePopoverKey, setAttributePopoverKey] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const handleShowMore = (): void => {
|
||||
setVisibleIndex(visibleIndex + 5);
|
||||
};
|
||||
|
||||
const handleMenuItemClick = useCallback(
|
||||
(key: string, attribute: string): void => {
|
||||
switch (key) {
|
||||
case 'open-in-explorer':
|
||||
goToMetricsExploreWithAppliedAttribute(filterKey, attribute);
|
||||
break;
|
||||
case 'copy-attribute':
|
||||
copyToClipboard(attribute);
|
||||
notifications.success({
|
||||
message: 'Attribute copied!',
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setAttributePopoverKey(null);
|
||||
},
|
||||
[
|
||||
goToMetricsExploreWithAppliedAttribute,
|
||||
filterKey,
|
||||
copyToClipboard,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
const attributePopoverContent = useCallback(
|
||||
(attribute: string) => (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
icon: <Compass size={16} />,
|
||||
label: 'Open in Explorer',
|
||||
key: 'open-in-explorer',
|
||||
},
|
||||
{
|
||||
icon: <Copy size={16} />,
|
||||
label: 'Copy Attribute',
|
||||
key: 'copy-attribute',
|
||||
},
|
||||
]}
|
||||
onClick={(info): void => {
|
||||
handleMenuItemClick(info.key, attribute);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[handleMenuItemClick],
|
||||
);
|
||||
return (
|
||||
<div className="all-attributes-value">
|
||||
{filterValue.slice(0, visibleIndex).map((attribute) => (
|
||||
<Popover
|
||||
key={attribute}
|
||||
content={attributePopoverContent(attribute)}
|
||||
trigger="click"
|
||||
open={attributePopoverKey === `${filterKey}-${attribute}`}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
setAttributePopoverKey(null);
|
||||
} else {
|
||||
setAttributePopoverKey(`${filterKey}-${attribute}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button key={attribute} type="text">
|
||||
<Typography.Text>{attribute}</Typography.Text>
|
||||
</Button>
|
||||
</Popover>
|
||||
))}
|
||||
{visibleIndex < filterValue.length && (
|
||||
<Button type="text" onClick={handleShowMore}>
|
||||
Show More
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const COPY_FEEDBACK_DURATION_MS = 1500;
|
||||
|
||||
function AllAttributes({
|
||||
metricName,
|
||||
metricType,
|
||||
minTime,
|
||||
maxTime,
|
||||
}: AllAttributesProps): JSX.Element {
|
||||
const [searchString, setSearchString] = useState('');
|
||||
const [activeKey, setActiveKey] = useState<string[]>([ALL_ATTRIBUTES_KEY]);
|
||||
const [keyPopoverOpen, setKeyPopoverOpen] = useState<string | null>(null);
|
||||
const [copiedKey, setCopiedKey] = useState<string | null>(null);
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const copyTimerRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
const {
|
||||
data: attributesData,
|
||||
isLoading: isLoadingAttributes,
|
||||
isError: isErrorAttributes,
|
||||
refetch: refetchAttributes,
|
||||
} = useGetMetricAttributes({
|
||||
metricName,
|
||||
});
|
||||
} = useGetMetricAttributes(
|
||||
{
|
||||
metricName,
|
||||
},
|
||||
{
|
||||
start: minTime ? Math.floor(minTime / 1000000) : undefined,
|
||||
end: maxTime ? Math.floor(maxTime / 1000000) : undefined,
|
||||
},
|
||||
);
|
||||
|
||||
const attributes = useMemo(() => attributesData?.data.attributes ?? [], [
|
||||
attributesData,
|
||||
@@ -164,12 +65,14 @@ function AllAttributes({
|
||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||
|
||||
const goToMetricsExplorerwithAppliedSpaceAggregation = useCallback(
|
||||
(groupBy: string) => {
|
||||
(groupBy: string, valueCount?: number) => {
|
||||
const limit = valueCount && valueCount > 250 ? 100 : undefined;
|
||||
const compositeQuery = getMetricDetailsQuery(
|
||||
metricName,
|
||||
metricType,
|
||||
undefined,
|
||||
groupBy,
|
||||
limit,
|
||||
);
|
||||
handleExplorerTabChange(
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
@@ -216,6 +119,28 @@ function AllAttributes({
|
||||
[metricName, metricType, handleExplorerTabChange],
|
||||
);
|
||||
|
||||
const handleKeyMenuItemClick = useCallback(
|
||||
(menuKey: string, attributeKey: string, valueCount?: number): void => {
|
||||
switch (menuKey) {
|
||||
case 'open-in-explorer':
|
||||
goToMetricsExplorerwithAppliedSpaceAggregation(attributeKey, valueCount);
|
||||
break;
|
||||
case 'copy-key':
|
||||
copyToClipboard(attributeKey);
|
||||
setCopiedKey(attributeKey);
|
||||
clearTimeout(copyTimerRef.current);
|
||||
copyTimerRef.current = setTimeout(() => {
|
||||
setCopiedKey(null);
|
||||
}, COPY_FEEDBACK_DURATION_MS);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setKeyPopoverOpen(null);
|
||||
},
|
||||
[goToMetricsExplorerwithAppliedSpaceAggregation, copyToClipboard],
|
||||
);
|
||||
|
||||
const filteredAttributes = useMemo(
|
||||
() =>
|
||||
attributes.filter(
|
||||
@@ -254,21 +179,57 @@ function AllAttributes({
|
||||
width: 50,
|
||||
align: 'left',
|
||||
className: 'metric-metadata-key',
|
||||
render: (field: { label: string; contribution: number }): JSX.Element => (
|
||||
<div className="all-attributes-key">
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(): void =>
|
||||
goToMetricsExplorerwithAppliedSpaceAggregation(field.label)
|
||||
}
|
||||
>
|
||||
<Typography.Text>{field.label}</Typography.Text>
|
||||
</Button>
|
||||
<Typography.Text className="all-attributes-contribution">
|
||||
{field.contribution}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
render: (field: { label: string; contribution: number }): JSX.Element => {
|
||||
const isCopied = copiedKey === field.label;
|
||||
return (
|
||||
<div className="all-attributes-key">
|
||||
<Popover
|
||||
content={
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
icon: <SquareArrowOutUpRight size={14} />,
|
||||
label: 'Open in Metric Explorer',
|
||||
key: 'open-in-explorer',
|
||||
},
|
||||
{
|
||||
icon: <Copy size={14} />,
|
||||
label: 'Copy Key',
|
||||
key: 'copy-key',
|
||||
},
|
||||
]}
|
||||
onClick={(info): void => {
|
||||
handleKeyMenuItemClick(info.key, field.label, field.contribution);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
trigger="click"
|
||||
placement="right"
|
||||
overlayClassName="metric-details-popover attribute-key-popover-overlay"
|
||||
open={keyPopoverOpen === field.label}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
setKeyPopoverOpen(null);
|
||||
} else {
|
||||
setKeyPopoverOpen(field.label);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button type="text">
|
||||
<Typography.Text>{field.label}</Typography.Text>
|
||||
</Button>
|
||||
</Popover>
|
||||
{isCopied && (
|
||||
<span className="copy-feedback">
|
||||
<Check size={12} />
|
||||
</span>
|
||||
)}
|
||||
<Typography.Text className="all-attributes-contribution">
|
||||
{field.contribution}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Value',
|
||||
@@ -291,7 +252,9 @@ function AllAttributes({
|
||||
],
|
||||
[
|
||||
goToMetricsExploreWithAppliedAttribute,
|
||||
goToMetricsExplorerwithAppliedSpaceAggregation,
|
||||
handleKeyMenuItemClick,
|
||||
keyPopoverOpen,
|
||||
copiedKey,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -300,7 +263,12 @@ function AllAttributes({
|
||||
{
|
||||
label: (
|
||||
<div className="metrics-accordion-header">
|
||||
<Typography.Text>All Attributes</Typography.Text>
|
||||
<div className="all-attributes-header-title">
|
||||
<Typography.Text>All Attributes</Typography.Text>
|
||||
<Tooltip title="Showing attributes for the selected time range">
|
||||
<Info size={14} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input
|
||||
className="all-attributes-search-input"
|
||||
placeholder="Search"
|
||||
@@ -329,7 +297,9 @@ function AllAttributes({
|
||||
className="metrics-accordion-content all-attributes-content"
|
||||
scroll={{ y: 600 }}
|
||||
locale={{
|
||||
emptyText: (
|
||||
emptyText: isLoadingAttributes ? (
|
||||
' '
|
||||
) : (
|
||||
<AllAttributesEmptyText
|
||||
isErrorAttributes={isErrorAttributes}
|
||||
refetchAttributes={refetchAttributes}
|
||||
@@ -350,14 +320,6 @@ function AllAttributes({
|
||||
],
|
||||
);
|
||||
|
||||
if (isLoadingAttributes) {
|
||||
return (
|
||||
<div className="all-attributes-skeleton-container">
|
||||
<Skeleton active paragraph={{ rows: 8 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
bordered
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Button, Input, Menu, Popover, Tooltip, Typography } from 'antd';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { Check, Copy, Search, SquareArrowOutUpRight } from 'lucide-react';
|
||||
|
||||
import MetricDetailsErrorState from './MetricDetailsErrorState';
|
||||
import { AllAttributesEmptyTextProps, AllAttributesValueProps } from './types';
|
||||
|
||||
const INITIAL_VISIBLE_COUNT = 5;
|
||||
const COPY_FEEDBACK_DURATION_MS = 1500;
|
||||
|
||||
export function AllAttributesEmptyText({
|
||||
isErrorAttributes,
|
||||
refetchAttributes,
|
||||
}: AllAttributesEmptyTextProps): JSX.Element {
|
||||
if (isErrorAttributes) {
|
||||
return (
|
||||
<div className="all-attributes-error-state">
|
||||
<MetricDetailsErrorState
|
||||
refetch={refetchAttributes}
|
||||
errorMessage="Something went wrong while fetching attributes"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <Typography.Text>No attributes found</Typography.Text>;
|
||||
}
|
||||
|
||||
export function AllAttributesValue({
|
||||
filterKey,
|
||||
filterValue,
|
||||
goToMetricsExploreWithAppliedAttribute,
|
||||
}: AllAttributesValueProps): JSX.Element {
|
||||
const [attributePopoverKey, setAttributePopoverKey] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [allValuesOpen, setAllValuesOpen] = useState(false);
|
||||
const [allValuesSearch, setAllValuesSearch] = useState('');
|
||||
const [copiedValue, setCopiedValue] = useState<string | null>(null);
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const { notifications } = useNotifications();
|
||||
const copyTimerRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
const handleCopyWithFeedback = useCallback(
|
||||
(value: string): void => {
|
||||
copyToClipboard(value);
|
||||
setCopiedValue(value);
|
||||
clearTimeout(copyTimerRef.current);
|
||||
copyTimerRef.current = setTimeout(() => {
|
||||
setCopiedValue(null);
|
||||
}, COPY_FEEDBACK_DURATION_MS);
|
||||
},
|
||||
[copyToClipboard],
|
||||
);
|
||||
|
||||
const handleMenuItemClick = useCallback(
|
||||
(key: string, attribute: string): void => {
|
||||
switch (key) {
|
||||
case 'open-in-explorer':
|
||||
goToMetricsExploreWithAppliedAttribute(filterKey, attribute);
|
||||
break;
|
||||
case 'copy-value':
|
||||
handleCopyWithFeedback(attribute);
|
||||
notifications.success({
|
||||
message: 'Value copied!',
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setAttributePopoverKey(null);
|
||||
},
|
||||
[
|
||||
goToMetricsExploreWithAppliedAttribute,
|
||||
filterKey,
|
||||
handleCopyWithFeedback,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
const attributePopoverContent = useCallback(
|
||||
(attribute: string) => (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
icon: <SquareArrowOutUpRight size={14} />,
|
||||
label: 'Open in Metric Explorer',
|
||||
key: 'open-in-explorer',
|
||||
},
|
||||
{
|
||||
icon: <Copy size={14} />,
|
||||
label: 'Copy Value',
|
||||
key: 'copy-value',
|
||||
},
|
||||
]}
|
||||
onClick={(info): void => {
|
||||
handleMenuItemClick(info.key, attribute);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[handleMenuItemClick],
|
||||
);
|
||||
|
||||
const filteredAllValues = useMemo(
|
||||
() =>
|
||||
allValuesSearch
|
||||
? filterValue.filter((v) =>
|
||||
v.toLowerCase().includes(allValuesSearch.toLowerCase()),
|
||||
)
|
||||
: filterValue,
|
||||
[filterValue, allValuesSearch],
|
||||
);
|
||||
|
||||
const allValuesPopoverContent = (
|
||||
<div className="all-values-popover">
|
||||
<Input
|
||||
placeholder="Search values"
|
||||
size="small"
|
||||
prefix={<Search size={12} />}
|
||||
value={allValuesSearch}
|
||||
onChange={(e): void => setAllValuesSearch(e.target.value)}
|
||||
allowClear
|
||||
/>
|
||||
<div className="all-values-list">
|
||||
{allValuesOpen &&
|
||||
filteredAllValues.map((attribute) => {
|
||||
const isCopied = copiedValue === attribute;
|
||||
return (
|
||||
<div key={attribute} className="all-values-item">
|
||||
<Typography.Text ellipsis className="all-values-item-text">
|
||||
{attribute}
|
||||
</Typography.Text>
|
||||
<div className="all-values-item-actions">
|
||||
<Tooltip title={isCopied ? 'Copied!' : 'Copy value'}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className={isCopied ? 'copy-success' : ''}
|
||||
icon={isCopied ? <Check size={12} /> : <Copy size={12} />}
|
||||
onClick={(): void => {
|
||||
handleCopyWithFeedback(attribute);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Open in Metric Explorer">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<SquareArrowOutUpRight size={12} />}
|
||||
onClick={(): void => {
|
||||
goToMetricsExploreWithAppliedAttribute(filterKey, attribute);
|
||||
setAllValuesOpen(false);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{allValuesOpen && filteredAllValues.length === 0 && (
|
||||
<Typography.Text type="secondary" className="all-values-empty">
|
||||
No values found
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="all-attributes-value">
|
||||
{filterValue.slice(0, INITIAL_VISIBLE_COUNT).map((attribute) => (
|
||||
<Popover
|
||||
key={attribute}
|
||||
content={attributePopoverContent(attribute)}
|
||||
trigger="click"
|
||||
overlayClassName="metric-details-popover attribute-value-popover-overlay"
|
||||
open={attributePopoverKey === `${filterKey}-${attribute}`}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
setAttributePopoverKey(null);
|
||||
} else {
|
||||
setAttributePopoverKey(`${filterKey}-${attribute}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button key={attribute} type="text">
|
||||
<Typography.Text>{attribute}</Typography.Text>
|
||||
</Button>
|
||||
</Popover>
|
||||
))}
|
||||
{filterValue.length > INITIAL_VISIBLE_COUNT && (
|
||||
<Popover
|
||||
content={allValuesPopoverContent}
|
||||
trigger="click"
|
||||
open={allValuesOpen}
|
||||
onOpenChange={(open): void => {
|
||||
setAllValuesOpen(open);
|
||||
if (!open) {
|
||||
setAllValuesSearch('');
|
||||
setCopiedValue(null);
|
||||
}
|
||||
}}
|
||||
overlayClassName="metric-details-popover all-values-popover-overlay"
|
||||
>
|
||||
<Button type="text" className="all-values-button">
|
||||
All values ({filterValue.length})
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Skeleton, Tooltip, Typography } from 'antd';
|
||||
import { Button, Spin, Tooltip, Typography } from 'antd';
|
||||
import { useGetMetricHighlights } from 'api/generated/services/metrics';
|
||||
import { InfoIcon } from 'lucide-react';
|
||||
|
||||
@@ -39,17 +39,6 @@ function Highlights({ metricName }: HighlightsProps): JSX.Element {
|
||||
metricHighlights?.lastReceived,
|
||||
);
|
||||
|
||||
if (isLoadingMetricHighlights) {
|
||||
return (
|
||||
<div
|
||||
className="metric-details-content-grid"
|
||||
data-testid="metric-highlights-loading-state"
|
||||
>
|
||||
<Skeleton title={false} paragraph={{ rows: 2 }} active />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isErrorMetricHighlights) {
|
||||
return (
|
||||
<div className="metric-details-content-grid">
|
||||
@@ -89,32 +78,41 @@ function Highlights({ metricName }: HighlightsProps): JSX.Element {
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div className="values-row">
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-data-points"
|
||||
>
|
||||
<Tooltip title={metricHighlights?.dataPoints?.toLocaleString()}>
|
||||
{formatNumberIntoHumanReadableFormat(metricHighlights?.dataPoints ?? 0)}
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-time-series-total"
|
||||
>
|
||||
<Tooltip
|
||||
title="Active time series are those that have received data points in the last 1
|
||||
hour."
|
||||
placement="top"
|
||||
>
|
||||
<span>{`${timeSeriesTotal} total ⎯ ${timeSeriesActive} active`}</span>
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-last-received"
|
||||
>
|
||||
<Tooltip title={lastReceivedText}>{lastReceivedText}</Tooltip>
|
||||
</Typography.Text>
|
||||
{isLoadingMetricHighlights ? (
|
||||
<div className="metric-highlights-loading-inline">
|
||||
<Spin size="small" />
|
||||
<Typography.Text type="secondary">Loading metric stats</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-data-points"
|
||||
>
|
||||
<Tooltip title={metricHighlights?.dataPoints?.toLocaleString()}>
|
||||
{formatNumberIntoHumanReadableFormat(metricHighlights?.dataPoints ?? 0)}
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-time-series-total"
|
||||
>
|
||||
<Tooltip
|
||||
title="Active time series are those that have received data points in the last 1
|
||||
hour."
|
||||
placement="top"
|
||||
>
|
||||
<span>{`${timeSeriesTotal} total ⎯ ${timeSeriesActive} active`}</span>
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
className="metric-details-grid-value"
|
||||
data-testid="metric-highlights-last-received"
|
||||
>
|
||||
<Tooltip title={lastReceivedText}>{lastReceivedText}</Tooltip>
|
||||
</Typography.Text>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { Button, Collapse, Input, Select, Skeleton, Typography } from 'antd';
|
||||
import { Button, Collapse, Input, Select, Spin, Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
@@ -334,7 +334,7 @@ function Metadata({
|
||||
e.stopPropagation();
|
||||
setIsEditing(true);
|
||||
}}
|
||||
disabled={isUpdatingMetricsMetadata}
|
||||
disabled={isUpdatingMetricsMetadata || isLoadingMetricMetadata}
|
||||
>
|
||||
<Edit2 size={14} />
|
||||
<Typography.Text>Edit</Typography.Text>
|
||||
@@ -345,6 +345,7 @@ function Metadata({
|
||||
isEditing,
|
||||
isErrorMetricMetadata,
|
||||
isUpdatingMetricsMetadata,
|
||||
isLoadingMetricMetadata,
|
||||
cancelEdit,
|
||||
handleSave,
|
||||
]);
|
||||
@@ -359,7 +360,11 @@ function Metadata({
|
||||
</div>
|
||||
),
|
||||
key: 'metric-metadata',
|
||||
children: isErrorMetricMetadata ? (
|
||||
children: isLoadingMetricMetadata ? (
|
||||
<div className="metrics-accordion-loading-state">
|
||||
<Spin size="small" />
|
||||
</div>
|
||||
) : isErrorMetricMetadata ? (
|
||||
<div className="metric-metadata-error-state">
|
||||
<MetricDetailsErrorState
|
||||
refetch={refetchMetricMetadata}
|
||||
@@ -381,20 +386,13 @@ function Metadata({
|
||||
[
|
||||
actionButton,
|
||||
columns,
|
||||
isLoadingMetricMetadata,
|
||||
isErrorMetricMetadata,
|
||||
refetchMetricMetadata,
|
||||
tableData,
|
||||
],
|
||||
);
|
||||
|
||||
if (isLoadingMetricMetadata) {
|
||||
return (
|
||||
<div className="metrics-metadata-skeleton-container">
|
||||
<Skeleton active paragraph={{ rows: 8 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
bordered
|
||||
|
||||
@@ -52,6 +52,13 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-highlights-loading-inline {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.metric-highlights-error-state {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@@ -120,12 +127,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-metadata-skeleton-container {
|
||||
height: 330px;
|
||||
}
|
||||
|
||||
.all-attributes-skeleton-container {
|
||||
height: 600px;
|
||||
.metrics-accordion-loading-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.metrics-accordion {
|
||||
@@ -153,6 +159,18 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
|
||||
.all-attributes-header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.lucide-info {
|
||||
cursor: pointer;
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-typography {
|
||||
font-family: 'Geist Mono';
|
||||
color: var(--bg-robin-400);
|
||||
@@ -186,6 +204,7 @@
|
||||
.all-attributes-key {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.ant-btn {
|
||||
.ant-typography:first-child {
|
||||
font-family: 'Geist Mono';
|
||||
@@ -193,17 +212,15 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.copy-feedback {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: var(--bg-forest-500);
|
||||
animation: fade-in-out 1.5s ease-in-out;
|
||||
}
|
||||
.all-attributes-contribution {
|
||||
font-family: 'Geist Mono';
|
||||
color: var(--bg-vanilla-400);
|
||||
background-color: rgba(171, 189, 255, 0.1);
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,10 +276,8 @@
|
||||
}
|
||||
|
||||
.metric-metadata-key {
|
||||
cursor: pointer;
|
||||
padding-left: 10px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
.field-renderer-container {
|
||||
.label {
|
||||
color: var(--bg-vanilla-400);
|
||||
@@ -448,3 +463,138 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attribute-key-popover-overlay {
|
||||
.ant-popover-inner {
|
||||
padding: 0 !important;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.ant-menu {
|
||||
font-size: 12px;
|
||||
background: transparent;
|
||||
|
||||
.ant-menu-item {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.all-values-popover-overlay {
|
||||
.ant-popover-inner {
|
||||
padding: 0 !important;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
}
|
||||
|
||||
.all-values-popover {
|
||||
width: 320px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
|
||||
.all-values-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-slate-300);
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.all-values-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
gap: 8px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.all-values-item-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-family: 'Geist Mono';
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.all-values-item-actions {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.copy-success {
|
||||
color: var(--bg-forest-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.all-values-empty {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.all-values-button {
|
||||
color: var(--bg-robin-400) !important;
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.attribute-key-popover-overlay,
|
||||
.all-values-popover-overlay {
|
||||
.ant-popover-inner {
|
||||
border: 1px solid var(--bg-vanilla-400);
|
||||
background: var(--bg-vanilla-100) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-out {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
15% {
|
||||
opacity: 1;
|
||||
}
|
||||
85% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Divider, Drawer, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useGetMetricMetadata } from 'api/generated/services/metrics';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { Compass, Crosshair, X } from 'lucide-react';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { PANEL_TYPES } from '../../../constants/queryBuilder';
|
||||
import ROUTES from '../../../constants/routes';
|
||||
@@ -29,6 +33,9 @@ function MetricDetails({
|
||||
}: MetricDetailsProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const {
|
||||
data: metricMetadataResponse,
|
||||
@@ -100,6 +107,21 @@ function MetricDetails({
|
||||
const isActionButtonDisabled =
|
||||
!metricName || isLoadingMetricMetadata || isErrorMetricMetadata;
|
||||
|
||||
const handleDrawerClose = useCallback(
|
||||
(e: React.MouseEvent | React.KeyboardEvent): void => {
|
||||
if ('key' in e && e.key === 'Escape') {
|
||||
const openPopover = document.querySelector(
|
||||
'.metric-details-popover:not(.ant-popover-hidden)',
|
||||
);
|
||||
if (openPopover) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
onClose();
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
width="60%"
|
||||
@@ -137,7 +159,7 @@ function MetricDetails({
|
||||
</div>
|
||||
}
|
||||
placement="right"
|
||||
onClose={onClose}
|
||||
onClose={handleDrawerClose}
|
||||
open={isOpen}
|
||||
style={{
|
||||
overscrollBehavior: 'contain',
|
||||
@@ -157,7 +179,12 @@ function MetricDetails({
|
||||
isLoadingMetricMetadata={isLoadingMetricMetadata}
|
||||
refetchMetricMetadata={refetchMetricMetadata}
|
||||
/>
|
||||
<AllAttributes metricName={metricName} metricType={metadata?.type} />
|
||||
<AllAttributes
|
||||
metricName={metricName}
|
||||
metricType={metadata?.type}
|
||||
minTime={minTime}
|
||||
maxTime={maxTime}
|
||||
/>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import * as reactUseHooks from 'react-use';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import * as metricsExplorerHooks from 'api/generated/services/metrics';
|
||||
import { MetrictypesTypeDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import * as useHandleExplorerTabChange from 'hooks/useHandleExplorerTabChange';
|
||||
import { userEvent } from 'tests/test-utils';
|
||||
|
||||
import ROUTES from '../../../../constants/routes';
|
||||
import AllAttributes, { AllAttributesValue } from '../AllAttributes';
|
||||
import AllAttributes from '../AllAttributes';
|
||||
import { AllAttributesValue } from '../AllAttributesValue';
|
||||
import { getMockMetricAttributesData, MOCK_METRIC_NAME } from './testUtlls';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
@@ -15,17 +14,6 @@ jest.mock('react-router-dom', () => ({
|
||||
pathname: `${ROUTES.METRICS_EXPLORER}`,
|
||||
}),
|
||||
}));
|
||||
const mockHandleExplorerTabChange = jest.fn();
|
||||
jest
|
||||
.spyOn(useHandleExplorerTabChange, 'useHandleExplorerTabChange')
|
||||
.mockReturnValue({
|
||||
handleExplorerTabChange: mockHandleExplorerTabChange,
|
||||
});
|
||||
|
||||
const mockUseCopyToClipboard = jest.fn();
|
||||
jest
|
||||
.spyOn(reactUseHooks, 'useCopyToClipboard')
|
||||
.mockReturnValue([{ value: 'value1' }, mockUseCopyToClipboard] as any);
|
||||
|
||||
const useGetMetricAttributesMock = jest.spyOn(
|
||||
metricsExplorerHooks,
|
||||
@@ -34,12 +22,13 @@ const useGetMetricAttributesMock = jest.spyOn(
|
||||
|
||||
describe('AllAttributes', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
useGetMetricAttributesMock.mockReturnValue({
|
||||
...getMockMetricAttributesData(),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders attributes section with title', () => {
|
||||
it('renders attribute keys, values, and value counts from API data', () => {
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
@@ -47,39 +36,13 @@ describe('AllAttributes', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('All Attributes')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders all attribute keys and values', () => {
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
metricType={MetrictypesTypeDTO.gauge}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Check attribute keys are rendered
|
||||
expect(screen.getByText('attribute1')).toBeInTheDocument();
|
||||
expect(screen.getByText('attribute2')).toBeInTheDocument();
|
||||
|
||||
// Check attribute values are rendered
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
expect(screen.getByText('value2')).toBeInTheDocument();
|
||||
expect(screen.getByText('value3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders value counts correctly', () => {
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
metricType={MetrictypesTypeDTO.gauge}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('2')).toBeInTheDocument(); // For attribute1
|
||||
expect(screen.getByText('1')).toBeInTheDocument(); // For attribute2
|
||||
});
|
||||
|
||||
it('handles empty attributes array', () => {
|
||||
useGetMetricAttributesMock.mockReturnValue({
|
||||
...getMockMetricAttributesData({
|
||||
@@ -100,7 +63,7 @@ describe('AllAttributes', () => {
|
||||
expect(screen.getByText('No attributes found')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clicking on an attribute key opens the explorer with the attribute filter applied', async () => {
|
||||
it('clicking on an attribute key shows popover with Open in Metric Explorer option', async () => {
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
@@ -108,7 +71,8 @@ describe('AllAttributes', () => {
|
||||
/>,
|
||||
);
|
||||
await userEvent.click(screen.getByText('attribute1'));
|
||||
expect(mockHandleExplorerTabChange).toHaveBeenCalled();
|
||||
expect(screen.getByText('Open in Metric Explorer')).toBeInTheDocument();
|
||||
expect(screen.getByText('Copy Key')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('filters attributes based on search input', async () => {
|
||||
@@ -123,26 +87,66 @@ describe('AllAttributes', () => {
|
||||
expect(screen.getByText('attribute1')).toBeInTheDocument();
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows error state when attribute fetching fails', () => {
|
||||
useGetMetricAttributesMock.mockReturnValue({
|
||||
...getMockMetricAttributesData(
|
||||
{
|
||||
data: {
|
||||
attributes: [],
|
||||
totalKeys: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
isError: true,
|
||||
},
|
||||
),
|
||||
});
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
metricType={MetrictypesTypeDTO.gauge}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText('Something went wrong while fetching attributes'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show misleading empty text while loading', () => {
|
||||
useGetMetricAttributesMock.mockReturnValue({
|
||||
...getMockMetricAttributesData(
|
||||
{
|
||||
data: {
|
||||
attributes: [],
|
||||
totalKeys: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
isLoading: true,
|
||||
},
|
||||
),
|
||||
});
|
||||
render(
|
||||
<AllAttributes
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
metricType={MetrictypesTypeDTO.gauge}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('No attributes found')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('AllAttributesValue', () => {
|
||||
const mockGoToMetricsExploreWithAppliedAttribute = jest.fn();
|
||||
|
||||
it('renders all attribute values', () => {
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={['value1', 'value2']}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
expect(screen.getByText('value2')).toBeInTheDocument();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('loads more attributes when show more button is clicked', async () => {
|
||||
it('shows All values button when there are more than 5 values', () => {
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
@@ -153,58 +157,59 @@ describe('AllAttributesValue', () => {
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByText('value6')).not.toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('Show More'));
|
||||
expect(screen.getByText('value6')).toBeInTheDocument();
|
||||
expect(screen.getByText('All values (6)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render show more button when there are no more attributes to show', () => {
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={['value1', 'value2']}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByText('Show More')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('copy button should copy the attribute value to the clipboard', async () => {
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={['value1', 'value2']}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('value1'));
|
||||
expect(screen.getByText('Copy Attribute')).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('Copy Attribute'));
|
||||
expect(mockUseCopyToClipboard).toHaveBeenCalledWith('value1');
|
||||
});
|
||||
|
||||
it('explorer button should go to metrics explore with the attribute filter applied', async () => {
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={['value1', 'value2']}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('value1'));
|
||||
|
||||
expect(screen.getByText('Open in Explorer')).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('Open in Explorer'));
|
||||
expect(mockGoToMetricsExploreWithAppliedAttribute).toHaveBeenCalledWith(
|
||||
'attribute1',
|
||||
it('All values popover shows values beyond the initial 5', async () => {
|
||||
const values = [
|
||||
'value1',
|
||||
'value2',
|
||||
'value3',
|
||||
'value4',
|
||||
'value5',
|
||||
'value6',
|
||||
'value7',
|
||||
];
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={values}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByText('All values (7)'));
|
||||
|
||||
expect(screen.getByText('value6')).toBeInTheDocument();
|
||||
expect(screen.getByText('value7')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('All values popover search filters the value list', async () => {
|
||||
const values = [
|
||||
'alpha',
|
||||
'bravo',
|
||||
'charlie',
|
||||
'delta',
|
||||
'echo',
|
||||
'fig-special',
|
||||
'golf-target',
|
||||
];
|
||||
render(
|
||||
<AllAttributesValue
|
||||
filterKey="attribute1"
|
||||
filterValue={values}
|
||||
goToMetricsExploreWithAppliedAttribute={
|
||||
mockGoToMetricsExploreWithAppliedAttribute
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByText('All values (7)'));
|
||||
await userEvent.type(screen.getByPlaceholderText('Search values'), 'golf');
|
||||
|
||||
expect(screen.getByText('golf-target')).toBeInTheDocument();
|
||||
expect(screen.queryByText('fig-special')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('Highlights', () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading state when data is loading', () => {
|
||||
it('should show labels and loading text but not stale data values while loading', () => {
|
||||
useGetMetricHighlightsMock.mockReturnValue(
|
||||
getMockMetricHighlightsData(
|
||||
{},
|
||||
@@ -60,8 +60,19 @@ describe('Highlights', () => {
|
||||
|
||||
render(<Highlights metricName={MOCK_METRIC_NAME} />);
|
||||
|
||||
expect(screen.getByText('SAMPLES')).toBeInTheDocument();
|
||||
expect(screen.getByText('TIME SERIES')).toBeInTheDocument();
|
||||
expect(screen.getByText('LAST RECEIVED')).toBeInTheDocument();
|
||||
expect(screen.getByText('Loading metric stats')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByTestId('metric-highlights-loading-state'),
|
||||
).toBeInTheDocument();
|
||||
screen.queryByTestId('metric-highlights-data-points'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('metric-highlights-time-series-total'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('metric-highlights-last-received'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -324,6 +324,22 @@ describe('Metadata', () => {
|
||||
expect(editButton2).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show section header with disabled edit while loading', () => {
|
||||
render(
|
||||
<Metadata
|
||||
metricName={MOCK_METRIC_NAME}
|
||||
metadata={null}
|
||||
isErrorMetricMetadata={false}
|
||||
isLoadingMetricMetadata
|
||||
refetchMetricMetadata={mockRefetchMetricMetadata}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Metadata')).toBeInTheDocument();
|
||||
const editButton = screen.getByText('Edit').closest('button');
|
||||
expect(editButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should not allow editing of unit if it is already set', async () => {
|
||||
render(
|
||||
<Metadata
|
||||
|
||||
@@ -24,6 +24,13 @@ jest.mock('react-router-dom', () => ({
|
||||
pathname: `${ROUTES.METRICS_EXPLORER}`,
|
||||
}),
|
||||
}));
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux'),
|
||||
useSelector: jest.fn().mockReturnValue({
|
||||
maxTime: 1700000000000000000,
|
||||
minTime: 1699900000000000000,
|
||||
}),
|
||||
}));
|
||||
jest.mock('hooks/useSafeNavigate', () => ({
|
||||
useSafeNavigate: (): any => ({
|
||||
safeNavigate: jest.fn(),
|
||||
|
||||
@@ -34,6 +34,8 @@ export interface MetadataProps {
|
||||
export interface AllAttributesProps {
|
||||
metricName: string;
|
||||
metricType: MetrictypesTypeDTO | undefined;
|
||||
minTime?: number;
|
||||
maxTime?: number;
|
||||
}
|
||||
|
||||
export interface AllAttributesValueProps {
|
||||
|
||||
@@ -87,6 +87,7 @@ export function getMetricDetailsQuery(
|
||||
metricType: MetrictypesTypeDTO | undefined,
|
||||
filter?: { key: string; value: string },
|
||||
groupBy?: string,
|
||||
limit?: number,
|
||||
): Query {
|
||||
let timeAggregation;
|
||||
let spaceAggregation;
|
||||
@@ -170,6 +171,7 @@ export function getMetricDetailsQuery(
|
||||
},
|
||||
]
|
||||
: [],
|
||||
...(limit ? { limit } : {}),
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Tooltip } from 'antd';
|
||||
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
|
||||
import RunQueryBtn from 'container/QueryBuilder/components/RunQueryBtn/RunQueryBtn';
|
||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||
import { Info } from 'lucide-react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { MetricsSearchProps } from './types';
|
||||
@@ -26,15 +24,17 @@ function MetricsSearch({
|
||||
onChange(currentQueryFilterExpression);
|
||||
}, [currentQueryFilterExpression, onChange]);
|
||||
|
||||
const handleRunQuery = useCallback(
|
||||
(expression: string): void => {
|
||||
setCurrentQueryFilterExpression(expression);
|
||||
onChange(expression);
|
||||
},
|
||||
[setCurrentQueryFilterExpression, onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="metrics-search-container">
|
||||
<div data-testid="qb-search-container" className="qb-search-container">
|
||||
<Tooltip
|
||||
title="Use filters to refine metrics based on attributes. Example: service_name=api - Shows all metrics associated with the API service"
|
||||
placement="right"
|
||||
>
|
||||
<Info size={16} />
|
||||
</Tooltip>
|
||||
<QuerySearch
|
||||
onChange={handleOnChange}
|
||||
dataSource={DataSource.METRICS}
|
||||
@@ -45,8 +45,9 @@ function MetricsSearch({
|
||||
expression: currentQueryFilterExpression,
|
||||
},
|
||||
}}
|
||||
onRun={handleOnChange}
|
||||
onRun={handleRunQuery}
|
||||
showFilterSuggestionsWithoutMetric
|
||||
placeholder="Try metric_name CONTAINS 'http.server' to view all HTTP Server metrics being sent"
|
||||
/>
|
||||
</div>
|
||||
<RunQueryBtn
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
.metrics-search-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.metrics-search-options {
|
||||
@@ -51,10 +51,6 @@
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
|
||||
.lucide-info {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.query-builder-search-container {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -66,8 +62,6 @@
|
||||
margin-left: -16px;
|
||||
margin-right: -16px;
|
||||
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
.ant-table-thead > tr > th {
|
||||
padding: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -15,13 +15,12 @@ import {
|
||||
Querybuildertypesv5OrderByDTO,
|
||||
Querybuildertypesv5OrderDirectionDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import {
|
||||
convertExpressionToFilters,
|
||||
convertFiltersToExpression,
|
||||
} from 'components/QueryBuilderV2/utils';
|
||||
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { usePageSize } from 'container/InfraMonitoringK8s/utils';
|
||||
import NoLogs from 'container/NoLogs/NoLogs';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@@ -61,10 +60,13 @@ function Summary(): JSX.Element {
|
||||
heatmapView,
|
||||
setHeatmapView,
|
||||
] = useState<MetricsexplorertypesTreemapModeDTO>(
|
||||
MetricsexplorertypesTreemapModeDTO.timeseries,
|
||||
MetricsexplorertypesTreemapModeDTO.samples,
|
||||
);
|
||||
|
||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
|
||||
useShareBuilderUrl({ defaultValue: initialQueriesMap[DataSource.METRICS] });
|
||||
|
||||
const query = useMemo(() => currentQuery?.builder?.queryData[0], [
|
||||
currentQuery,
|
||||
]);
|
||||
@@ -89,6 +91,15 @@ function Summary(): JSX.Element {
|
||||
setCurrentQueryFilterExpression,
|
||||
] = useState<string>(query?.filter?.expression || '');
|
||||
|
||||
const [appliedFilterExpression, setAppliedFilterExpression] = useState(
|
||||
query?.filter?.expression || '',
|
||||
);
|
||||
|
||||
const queryFilterExpression = useMemo(
|
||||
() => ({ expression: appliedFilterExpression }),
|
||||
[appliedFilterExpression],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
logEvent(MetricsExplorerEvents.TabChanged, {
|
||||
[MetricsExplorerEventKeys.Tab]: 'summary',
|
||||
@@ -100,11 +111,6 @@ function Summary(): JSX.Element {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const queryFilterExpression = useMemo(() => {
|
||||
const filters = query.filters || { items: [], op: 'AND' };
|
||||
return convertFiltersToExpression(filters);
|
||||
}, [query.filters]);
|
||||
|
||||
const metricsListQuery: MetricsexplorertypesStatsRequestDTO = useMemo(() => {
|
||||
return {
|
||||
start: convertNanoToMilliseconds(minTime),
|
||||
@@ -187,6 +193,7 @@ function Summary(): JSX.Element {
|
||||
},
|
||||
});
|
||||
setCurrentQueryFilterExpression(expression);
|
||||
setAppliedFilterExpression(expression);
|
||||
setCurrentPage(1);
|
||||
if (expression) {
|
||||
logEvent(MetricsExplorerEvents.FilterApplied, {
|
||||
|
||||
@@ -56,7 +56,18 @@ func NewModule(ts telemetrystore.TelemetryStore, telemetryMetadataStore telemetr
|
||||
}
|
||||
}
|
||||
|
||||
func withMetricsExplorerQuery(ctx context.Context, functionName string) context.Context {
|
||||
comments := map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metrics-explorer",
|
||||
"function_name": functionName,
|
||||
}
|
||||
return ctxtypes.AddCommentsToContext(ctx, comments)
|
||||
}
|
||||
|
||||
func (m *module) ListMetrics(ctx context.Context, orgID valuer.UUID, params *metricsexplorertypes.ListMetricsParams) (*metricsexplorertypes.ListMetricsResponse, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "ListMetrics")
|
||||
|
||||
if err := params.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -217,6 +228,7 @@ func (m *module) GetTreemap(ctx context.Context, orgID valuer.UUID, req *metrics
|
||||
}
|
||||
|
||||
func (m *module) GetMetricMetadataMulti(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -405,6 +417,8 @@ func (m *module) GetMetricAttributes(ctx context.Context, orgID valuer.UUID, req
|
||||
}
|
||||
|
||||
func (m *module) CheckMetricExists(ctx context.Context, orgID valuer.UUID, metricName string) (bool, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "CheckMetricExists")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count(*) > 0 as metricExists")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -441,6 +455,8 @@ func (m *module) fetchMetadataFromCache(ctx context.Context, orgID valuer.UUID,
|
||||
}
|
||||
|
||||
func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchUpdatedMetadata")
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -499,6 +515,8 @@ func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, me
|
||||
}
|
||||
|
||||
func (m *module) fetchTimeseriesMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchTimeseriesMetadata")
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -627,6 +645,8 @@ func (m *module) validateMetricLabels(ctx context.Context, req *metricsexplorert
|
||||
}
|
||||
|
||||
func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, label string) (bool, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "checkForLabelInMetric")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count(*) > 0 AS has_label")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -648,6 +668,7 @@ func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, l
|
||||
}
|
||||
|
||||
func (m *module) insertMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *metricsexplorertypes.UpdateMetricMetadataRequest) error {
|
||||
ctx = withMetricsExplorerQuery(ctx, "insertMetricsMetadata")
|
||||
createdAt := time.Now().UnixMilli()
|
||||
|
||||
ib := sqlbuilder.NewInsertBuilder()
|
||||
@@ -742,6 +763,7 @@ func (m *module) fetchMetricsStatsWithSamples(
|
||||
normalized bool,
|
||||
orderBy *qbtypes.OrderBy,
|
||||
) ([]metricsexplorertypes.Stat, uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchMetricsStatsWithSamples")
|
||||
|
||||
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
|
||||
@@ -849,6 +871,8 @@ func (m *module) fetchMetricsStatsWithSamples(
|
||||
}
|
||||
|
||||
func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "computeTimeseriesTreemap")
|
||||
|
||||
start, end, distributedTsTable, _ := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
|
||||
totalTSBuilder := sqlbuilder.NewSelectBuilder()
|
||||
@@ -913,6 +937,8 @@ func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplo
|
||||
}
|
||||
|
||||
func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "computeSamplesTreemap")
|
||||
|
||||
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
|
||||
countExp := telemetrymetrics.CountExpressionForSamplesTable(samplesTable)
|
||||
@@ -1014,6 +1040,8 @@ func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorer
|
||||
|
||||
// getMetricDataPoints returns the total number of data points (samples) for a metric.
|
||||
func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getMetricDataPoints")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("sum(count) AS data_points")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.SamplesV4Agg30mTableName))
|
||||
@@ -1034,6 +1062,8 @@ func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (ui
|
||||
|
||||
// getMetricLastReceived returns the last received timestamp for a metric.
|
||||
func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getMetricLastReceived")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("MAX(last_reported_unix_milli) AS last_received_time")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -1057,6 +1087,8 @@ func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (
|
||||
|
||||
// getTotalTimeSeriesForMetricName returns the total number of unique time series for a metric.
|
||||
func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getTotalTimeSeriesForMetricName")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("uniq(fingerprint) AS time_series_count")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.TimeseriesV41weekTableName))
|
||||
@@ -1077,6 +1109,8 @@ func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName
|
||||
|
||||
// getActiveTimeSeriesForMetricName returns the number of active time series for a metric within the given duration.
|
||||
func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getActiveTimeSeriesForMetricName")
|
||||
|
||||
milli := time.Now().Add(-duration).UnixMilli()
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
@@ -1098,6 +1132,8 @@ func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricNam
|
||||
}
|
||||
|
||||
func (m *module) fetchMetricAttributes(ctx context.Context, metricName string, start, end *int64) ([]metricsexplorertypes.MetricAttribute, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchMetricAttributes")
|
||||
|
||||
// Build query using sqlbuilder
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/promotetypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
@@ -105,6 +106,11 @@ func (m *module) PromotePaths(ctx context.Context, paths []string) error {
|
||||
|
||||
// createIndexes creates string ngram + token filter indexes on JSON path subcolumns for LIKE queries.
|
||||
func (m *module) createIndexes(ctx context.Context, indexes []schemamigrator.Index) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "promote",
|
||||
"function_name": "createIndexes",
|
||||
})
|
||||
if len(indexes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrytraces"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/servicetypes/servicetypesv1"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -34,6 +35,12 @@ func NewModule(q querier.Querier, ts telemetrystore.TelemetryStore) services.Mod
|
||||
|
||||
// FetchTopLevelOperations returns top-level operations per service using db query
|
||||
func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, services []string) (map[string][]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "services",
|
||||
"function_name": "FetchTopLevelOperations",
|
||||
})
|
||||
|
||||
db := m.TelemetryStore.ClickhouseDB()
|
||||
query := fmt.Sprintf("SELECT name, serviceName, max(time) as ts FROM %s.%s WHERE time >= @start", telemetrytraces.DBName, telemetrytraces.TopLevelOperationsTableName)
|
||||
args := []any{clickhouse.Named("start", start)}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
promValue "github.com/prometheus/prometheus/model/value"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
@@ -137,6 +139,11 @@ func (client *client) queryToClickhouseQuery(_ context.Context, query *prompb.Qu
|
||||
}
|
||||
|
||||
func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, query string, args []any) (map[uint64][]prompb.Label, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-prometheus",
|
||||
"function_name": "getFingerprintsFromClickhouseQuery",
|
||||
})
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -168,6 +175,11 @@ func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, qu
|
||||
}
|
||||
|
||||
func (client *client) querySamples(ctx context.Context, start int64, end int64, fingerprints map[uint64][]prompb.Label, metricName string, subQuery string, args []any) ([]*prompb.TimeSeries, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-prometheus",
|
||||
"function_name": "querySamples",
|
||||
})
|
||||
argCount := len(args)
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
@@ -244,6 +256,12 @@ func (client *client) querySamples(ctx context.Context, start int64, end int64,
|
||||
}
|
||||
|
||||
func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*prompb.QueryResult, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-prometheus",
|
||||
"function_name": "queryRaw",
|
||||
})
|
||||
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/bytedance/sonic"
|
||||
@@ -212,6 +213,13 @@ func (q *builderQuery[T]) Execute(ctx context.Context) (*qbtypes.Result, error)
|
||||
|
||||
// executeWithContext executes the query with query window and step context for partial value detection
|
||||
func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": q.spec.Signal.StringValue(),
|
||||
"module_name": "builder-query",
|
||||
"function_name": "executeWithContext",
|
||||
"duration": qbtypes.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
@@ -98,6 +99,11 @@ func (q *chSQLQuery) renderVars(query string, vars map[string]qbtypes.VariableIt
|
||||
}
|
||||
|
||||
func (q *chSQLQuery) Execute(ctx context.Context) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-query",
|
||||
"function_name": "Execute",
|
||||
"duration": qbtypes.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
qbv5 "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
@@ -187,6 +189,11 @@ func (q *promqlQuery) renderVars(query string, vars map[string]qbv5.VariableItem
|
||||
|
||||
func (q *promqlQuery) Execute(ctx context.Context) (*qbv5.Result, error) {
|
||||
|
||||
comment := ctxtypes.CommentFromContext(ctx)
|
||||
comment.Set("signal", telemetrytypes.SignalMetrics.StringValue())
|
||||
comment.Set("duration", qbtypes.DurationBucket(q.tr.From, q.tr.To))
|
||||
ctx = ctxtypes.NewContextWithComment(ctx, comment)
|
||||
|
||||
start := int64(querybuilder.ToNanoSecs(q.tr.From))
|
||||
end := int64(querybuilder.ToNanoSecs(q.tr.To))
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -526,6 +527,11 @@ func (q *querier) run(
|
||||
steps map[string]qbtypes.Step,
|
||||
qbEvent *qbtypes.QBEvent,
|
||||
) (*qbtypes.QueryRangeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"panel_type": qbEvent.PanelType,
|
||||
"query_type": qbEvent.QueryType,
|
||||
})
|
||||
|
||||
results := make(map[string]any)
|
||||
warnings := make([]string, 0)
|
||||
warningsDocURL := ""
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type traceOperatorQuery struct {
|
||||
@@ -52,6 +54,13 @@ func (q *traceOperatorQuery) Execute(ctx context.Context) (*qbtypes.Result, erro
|
||||
}
|
||||
|
||||
func (q *traceOperatorQuery) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "querier",
|
||||
"function_name": "executeWithContext",
|
||||
"duration": qbtypes.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
@@ -269,6 +271,12 @@ func (r *ClickHouseReader) GetQueryRangeResult(ctx context.Context, query *model
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetServicesList",
|
||||
})
|
||||
|
||||
services := []string{}
|
||||
rows, err := r.db.Query(ctx, fmt.Sprintf(`SELECT DISTINCT resource_string_service$$name FROM %s.%s WHERE ts_bucket_start > (toUnixTimestamp(now() - INTERVAL 1 DAY) - 1800) AND toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName))
|
||||
if err != nil {
|
||||
@@ -288,6 +296,12 @@ func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, erro
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTopLevelOperations",
|
||||
})
|
||||
|
||||
start = start.In(time.UTC)
|
||||
|
||||
// The `top_level_operations` that have `time` >= start
|
||||
@@ -383,6 +397,12 @@ func (r *ClickHouseReader) buildResourceSubQuery(tags []model.TagQueryParam, svc
|
||||
|
||||
func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetServices",
|
||||
})
|
||||
|
||||
if r.indexTable == "" {
|
||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: ErrNoIndexTable}
|
||||
}
|
||||
@@ -739,6 +759,11 @@ func (r *ClickHouseReader) GetEntryPointOperations(ctx context.Context, queryPar
|
||||
|
||||
func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTopOperations",
|
||||
})
|
||||
namedArgs := []interface{}{
|
||||
clickhouse.Named("start", strconv.FormatInt(queryParams.Start.UnixNano(), 10)),
|
||||
clickhouse.Named("end", strconv.FormatInt(queryParams.End.UnixNano(), 10)),
|
||||
@@ -794,6 +819,11 @@ func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *mo
|
||||
|
||||
func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetUsageParams) (*[]model.UsageItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetUsage",
|
||||
})
|
||||
var usageItems []model.UsageItem
|
||||
namedArgs := []interface{}{
|
||||
clickhouse.Named("interval", queryParams.StepHour),
|
||||
@@ -829,6 +859,13 @@ func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetU
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetSpansForTrace(ctx context.Context, traceID string, traceDetailsQuery string) ([]model.SpanItemV2, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetSpansForTrace",
|
||||
})
|
||||
|
||||
var traceSummary model.TraceSummary
|
||||
summaryQuery := fmt.Sprintf("SELECT trace_id, min(start) AS start, max(end) AS end, sum(num_spans) AS num_spans FROM %s.%s WHERE trace_id=$1 GROUP BY trace_id", r.TraceDB, r.traceSummaryTable)
|
||||
err := r.db.QueryRow(ctx, summaryQuery, traceID).Scan(&traceSummary.TraceID, &traceSummary.Start, &traceSummary.End, &traceSummary.NumSpans)
|
||||
@@ -1227,6 +1264,11 @@ func (r *ClickHouseReader) GetFlamegraphSpansForTrace(ctx context.Context, orgID
|
||||
|
||||
func (r *ClickHouseReader) GetDependencyGraph(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceMapDependencyResponseItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetDependencyGraph",
|
||||
})
|
||||
response := []model.ServiceMapDependencyResponseItem{}
|
||||
|
||||
args := []interface{}{}
|
||||
@@ -1281,6 +1323,11 @@ func getLocalTableName(tableName string) string {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "setTTLLogs",
|
||||
})
|
||||
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
|
||||
if hasCustomRetention {
|
||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("SetTTLV2 only supported")}
|
||||
@@ -1444,6 +1491,11 @@ func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "setTTLTraces",
|
||||
})
|
||||
// uuid is used as transaction id
|
||||
uuidWithHyphen := uuid.New()
|
||||
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
|
||||
@@ -1589,6 +1641,12 @@ func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, param
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "hasCustomRetentionColumn",
|
||||
})
|
||||
|
||||
// Directly query for the _retention_days column existence
|
||||
query := fmt.Sprintf("SELECT 1 FROM system.columns WHERE database = '%s' AND table = '%s' AND name = '_retention_days' LIMIT 1", r.logsDB, r.logsLocalTableV2)
|
||||
|
||||
@@ -1610,6 +1668,11 @@ func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool,
|
||||
|
||||
func (r *ClickHouseReader) SetTTLV2(ctx context.Context, orgID string, params *model.CustomRetentionTTLParams) (*model.CustomRetentionTTLResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "SetTTLV2",
|
||||
})
|
||||
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
|
||||
if err != nil {
|
||||
return nil, errorsV2.Wrapf(err, errorsV2.TypeInternal, errorsV2.CodeInternal, "custom retention not supported")
|
||||
@@ -1999,6 +2062,10 @@ func (r *ClickHouseReader) updateCustomRetentionTTLStatus(ctx context.Context, o
|
||||
|
||||
// Enhanced validation function with duplicate detection and efficient key validation
|
||||
func (r *ClickHouseReader) validateTTLConditions(ctx context.Context, ttlConditions []model.CustomRetentionRule) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "validateTTLConditions",
|
||||
})
|
||||
if len(ttlConditions) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -2116,6 +2183,11 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, orgID string, params *mod
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLMetrics(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "setTTLMetrics",
|
||||
})
|
||||
// uuid is used as transaction id
|
||||
uuidWithHyphen := uuid.New()
|
||||
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
|
||||
@@ -2324,6 +2396,10 @@ func (r *ClickHouseReader) getTTLQueryStatus(ctx context.Context, orgID string,
|
||||
|
||||
func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string, coldStorageVolume string) *model.ApiError {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "setColdStorage",
|
||||
})
|
||||
// Set the storage policy for the required table. If it is already set, then setting it again
|
||||
// will not a problem.
|
||||
if len(coldStorageVolume) > 0 {
|
||||
@@ -2340,6 +2416,10 @@ func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string,
|
||||
|
||||
// GetDisks returns a list of disks {name, type} configured in clickhouse DB.
|
||||
func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetDisks",
|
||||
})
|
||||
diskItems := []model.DiskItem{}
|
||||
|
||||
query := "SELECT name,type FROM system.disks"
|
||||
@@ -2363,6 +2443,10 @@ func getLocalTableNameArray(tableNames []string) []string {
|
||||
// GetTTL returns current ttl, expected ttl and past setTTL status for metrics/traces.
|
||||
func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTTL",
|
||||
})
|
||||
parseTTL := func(queryResp string) (int, int) {
|
||||
|
||||
zap.L().Info("Parsing TTL from: ", zap.String("queryResp", queryResp))
|
||||
@@ -2532,6 +2616,11 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *
|
||||
|
||||
func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.ListErrorsParams) (*[]model.Error, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "ListErrors",
|
||||
})
|
||||
var getErrorResponses []model.Error
|
||||
|
||||
query := "SELECT any(exceptionMessage) as exceptionMessage, count() AS exceptionCount, min(timestamp) as firstSeen, max(timestamp) as lastSeen, groupID"
|
||||
@@ -2604,6 +2693,12 @@ func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.Li
|
||||
|
||||
func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.CountErrorsParams) (uint64, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "CountErrors",
|
||||
})
|
||||
|
||||
var errorCount uint64
|
||||
|
||||
query := fmt.Sprintf("SELECT count(distinct(groupID)) FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.TraceDB, r.errorTable)
|
||||
@@ -2641,6 +2736,11 @@ func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.C
|
||||
|
||||
func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetErrorFromErrorID",
|
||||
})
|
||||
if queryParams.ErrorID == "" {
|
||||
zap.L().Error("errorId missing from params")
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("ErrorID missing from params")}
|
||||
@@ -2668,6 +2768,11 @@ func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams
|
||||
|
||||
func (r *ClickHouseReader) GetErrorFromGroupID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetErrorFromGroupID",
|
||||
})
|
||||
var getErrorWithSpanReponse []model.ErrorWithSpan
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID, exceptionType, exceptionStacktrace, exceptionEscaped, exceptionMessage, timestamp, spanID, traceID, serviceName, groupID FROM %s.%s WHERE timestamp = @timestamp AND groupID = @groupID LIMIT 1", r.TraceDB, r.errorTable)
|
||||
@@ -2716,6 +2821,11 @@ func (r *ClickHouseReader) GetNextPrevErrorIDs(ctx context.Context, queryParams
|
||||
|
||||
func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "getNextErrorID",
|
||||
})
|
||||
var getNextErrorIDReponse []model.NextPrevErrorIDsDBResponse
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID as nextErrorID, timestamp as nextTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp >= @timestamp AND errorID != @errorID ORDER BY timestamp ASC LIMIT 2", r.TraceDB, r.errorTable)
|
||||
@@ -2785,6 +2895,11 @@ func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *mode
|
||||
|
||||
func (r *ClickHouseReader) getPrevErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "getPrevErrorID",
|
||||
})
|
||||
var getPrevErrorIDReponse []model.NextPrevErrorIDsDBResponse
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID as prevErrorID, timestamp as prevTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp <= @timestamp AND errorID != @errorID ORDER BY timestamp DESC LIMIT 2", r.TraceDB, r.errorTable)
|
||||
@@ -2876,6 +2991,11 @@ func (r *ClickHouseReader) FetchTemporality(ctx context.Context, orgID valuer.UU
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLogFields",
|
||||
})
|
||||
// response will contain top level fields from the otel log model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: constants.StaticSelectedLogFields,
|
||||
@@ -2912,6 +3032,11 @@ func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsRe
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogFieldsFromNames(ctx context.Context, fieldNames []string) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLogFieldsFromNames",
|
||||
})
|
||||
// response will contain top level fields from the otel log model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: constants.StaticSelectedLogFields,
|
||||
@@ -2962,6 +3087,10 @@ func (r *ClickHouseReader) extractSelectedAndInterestingFields(tableStatement st
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.UpdateField) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "UpdateLogField",
|
||||
})
|
||||
if !field.Selected {
|
||||
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
|
||||
}
|
||||
@@ -3028,6 +3157,10 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTraceFields",
|
||||
})
|
||||
// response will contain top level fields from the otel trace model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: []model.Field{},
|
||||
@@ -3083,6 +3216,11 @@ func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFields
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.UpdateField) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "UpdateTraceField",
|
||||
})
|
||||
if !field.Selected {
|
||||
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
|
||||
}
|
||||
@@ -3174,6 +3312,10 @@ func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.Up
|
||||
return nil
|
||||
}
|
||||
func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "QueryDashboardVars",
|
||||
})
|
||||
var result = model.DashboardVar{VariableValues: make([]interface{}, 0)}
|
||||
rows, err := r.db.Query(ctx, query)
|
||||
|
||||
@@ -3210,6 +3352,11 @@ func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest, skipSignozMetrics bool) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricAggregateAttributes",
|
||||
})
|
||||
var response v3.AggregateAttributeResponse
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
@@ -3288,6 +3435,11 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, org
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMeterAggregateAttributes",
|
||||
})
|
||||
var response v3.AggregateAttributeResponse
|
||||
// Query all relevant metric names from time_series_v4, but leave metadata retrieval to cache/db
|
||||
query := fmt.Sprintf(
|
||||
@@ -3336,6 +3488,11 @@ func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgI
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3376,6 +3533,11 @@ func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.F
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMeterAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3412,6 +3574,11 @@ func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.Fi
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3452,6 +3619,11 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
|
||||
|
||||
func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.UUID, metricName, serviceName string) (*v3.MetricMetadataResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricMetadata",
|
||||
})
|
||||
unixMilli := common.PastDayRoundOff()
|
||||
|
||||
// 1. Fetch metadata from cache/db using unified function
|
||||
@@ -3533,6 +3705,10 @@ func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.U
|
||||
// GetCountOfThings returns the count of things in the query
|
||||
// This is a generic function that can be used to check if any data exists for a given query
|
||||
func (r *ClickHouseReader) GetCountOfThings(ctx context.Context, query string) (uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetCountOfThings",
|
||||
})
|
||||
var count uint64
|
||||
err := r.db.QueryRow(ctx, query).Scan(&count)
|
||||
if err != nil {
|
||||
@@ -3544,6 +3720,11 @@ func (r *ClickHouseReader) GetCountOfThings(ctx context.Context, query string) (
|
||||
func (r *ClickHouseReader) GetLatestReceivedMetric(
|
||||
ctx context.Context, metricNames []string, labelValues map[string]string,
|
||||
) (*model.MetricStatus, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLatestReceivedMetric",
|
||||
})
|
||||
// at least 1 metric name must be specified.
|
||||
// this query can be too slow otherwise.
|
||||
if len(metricNames) < 1 {
|
||||
@@ -3628,6 +3809,11 @@ func isColumn(tableStatement, attrType, field, datType string) bool {
|
||||
|
||||
func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLogAggregateAttributes",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3712,6 +3898,11 @@ func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLogAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3778,6 +3969,10 @@ func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.Filt
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.FilterAttributeValueRequest) ([]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "FetchRelatedValues",
|
||||
})
|
||||
var andConditions []string
|
||||
|
||||
andConditions = append(andConditions, fmt.Sprintf("unix_milli >= %d", req.StartTimeMillis))
|
||||
@@ -3869,6 +4064,11 @@ func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.Filte
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLogAttributeValues",
|
||||
})
|
||||
var err error
|
||||
var filterValueColumn string
|
||||
var rows driver.Rows
|
||||
@@ -4186,6 +4386,10 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam
|
||||
|
||||
// GetTimeSeriesResultV3 runs the query and returns list of time series
|
||||
func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTimeSeriesResultV3",
|
||||
})
|
||||
// Hook up query progress reporting if requested.
|
||||
queryId := ctx.Value("queryId")
|
||||
if queryId != nil {
|
||||
@@ -4249,6 +4453,10 @@ func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query stri
|
||||
|
||||
// GetListResultV3 runs the query and returns list of rows
|
||||
func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetListResultV3",
|
||||
})
|
||||
rows, err := r.db.Query(ctx, query)
|
||||
if err != nil {
|
||||
zap.L().Error("error while reading time series result", zap.Error(err))
|
||||
@@ -4311,6 +4519,11 @@ func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([
|
||||
// GetHostMetricsExistenceAndEarliestTime returns (count, minFirstReportedUnixMilli, error) for the given host metric names
|
||||
// from distributed_metadata. When count is 0, minFirstReportedUnixMilli is 0.
|
||||
func (r *ClickHouseReader) GetMetricsExistenceAndEarliestTime(ctx context.Context, metricNames []string) (uint64, uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsExistenceAndEarliestTime",
|
||||
})
|
||||
if len(metricNames) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
@@ -4346,6 +4559,10 @@ func getPersonalisedError(err error) error {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "CheckClickHouse",
|
||||
})
|
||||
rows, err := r.db.Query(ctx, "SELECT 1")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -4356,6 +4573,11 @@ func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTraceAggregateAttributes",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4449,6 +4671,11 @@ func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTraceAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4517,6 +4744,11 @@ func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.Fi
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTraceAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var filterValueColumn string
|
||||
var err error
|
||||
@@ -4610,6 +4842,11 @@ func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, names []string) (map[string]v3.AttributeKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetSpanAttributeKeysByNames",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4658,6 +4895,10 @@ func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, name
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHistory []model.RuleStateHistory) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "AddRuleStateHistory",
|
||||
})
|
||||
var statement driver.Batch
|
||||
var err error
|
||||
|
||||
@@ -4689,6 +4930,10 @@ func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHis
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]model.RuleStateHistory, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetLastSavedRuleStateHistory",
|
||||
})
|
||||
query := fmt.Sprintf("SELECT * FROM %s.%s WHERE rule_id = '%s' AND state_changed = true ORDER BY unix_milli DESC LIMIT 1 BY fingerprint",
|
||||
signozHistoryDBName, ruleStateHistoryTableName, ruleID)
|
||||
|
||||
@@ -4703,6 +4948,10 @@ func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, rul
|
||||
func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
|
||||
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (*model.RuleStateTimeline, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "ReadRuleStateHistoryByRuleID",
|
||||
})
|
||||
var conditions []string
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("rule_id = '%s'", ruleID))
|
||||
@@ -4817,6 +5066,10 @@ func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
|
||||
|
||||
func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
|
||||
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.RuleStateHistoryContributor, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "ReadRuleStateHistoryTopContributorsByRuleID",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
fingerprint,
|
||||
any(labels) as labels,
|
||||
@@ -4841,6 +5094,10 @@ func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
|
||||
|
||||
func (r *ClickHouseReader) GetOverallStateTransitions(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.ReleStateItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetOverallStateTransitions",
|
||||
})
|
||||
tmpl := `WITH firing_events AS (
|
||||
SELECT
|
||||
rule_id,
|
||||
@@ -4968,6 +5225,10 @@ ORDER BY firing_time ASC;`
|
||||
|
||||
func (r *ClickHouseReader) GetAvgResolutionTime(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (float64, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAvgResolutionTime",
|
||||
})
|
||||
tmpl := `
|
||||
WITH firing_events AS (
|
||||
SELECT
|
||||
@@ -5079,6 +5340,10 @@ ORDER BY ts ASC;`
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTotalTriggers(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTotalTriggers",
|
||||
})
|
||||
query := fmt.Sprintf("SELECT count(*) FROM %s.%s WHERE rule_id = '%s' AND (state_changed = true) AND (state = '%s') AND unix_milli >= %d AND unix_milli <= %d",
|
||||
signozHistoryDBName, ruleStateHistoryTableName, ruleID, model.StateFiring.String(), params.Start, params.End)
|
||||
|
||||
@@ -5107,6 +5372,11 @@ func (r *ClickHouseReader) GetTriggersByInterval(ctx context.Context, ruleID str
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMinAndMaxTimestampForTraceID(ctx context.Context, traceID []string) (int64, int64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMinAndMaxTimestampForTraceID",
|
||||
})
|
||||
var minTime, maxTime time.Time
|
||||
|
||||
query := fmt.Sprintf("SELECT min(timestamp), max(timestamp) FROM %s.%s WHERE traceID IN ('%s')",
|
||||
@@ -5144,6 +5414,11 @@ func (r *ClickHouseReader) SubscribeToQueryProgress(
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context, req *metrics_explorer.FilterKeyRequest) (*[]v3.AttributeKey, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAllMetricFilterAttributeKeys",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []v3.AttributeKey
|
||||
normalized := true
|
||||
@@ -5181,6 +5456,11 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context,
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAllMetricFilterAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -5217,6 +5497,11 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAllMetricFilterUnits",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []string
|
||||
query := fmt.Sprintf("SELECT DISTINCT unit FROM %s.%s WHERE unit ILIKE $1 AND unit IS NOT NULL ORDER BY unit", signozMetricDBName, signozTSTableNameV41Day)
|
||||
@@ -5244,6 +5529,11 @@ func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *met
|
||||
return response, nil
|
||||
}
|
||||
func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAllMetricFilterTypes",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []string
|
||||
query := fmt.Sprintf("SELECT DISTINCT type FROM %s.%s WHERE type ILIKE $1 AND type IS NOT NULL ORDER BY type", signozMetricDBName, signozTSTableNameV41Day)
|
||||
@@ -5271,6 +5561,11 @@ func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *met
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsDataPoints(ctx context.Context, metricName string) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsDataPoints",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
sum(count) as data_points
|
||||
FROM %s.%s
|
||||
@@ -5286,6 +5581,11 @@ WHERE metric_name = ?
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsLastReceived(ctx context.Context, metricName string) (int64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsLastReceived",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
MAX(unix_milli) AS last_received_time
|
||||
FROM %s.%s
|
||||
@@ -5311,6 +5611,11 @@ WHERE metric_name = ? and unix_milli > ?
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetTotalTimeSeriesForMetricName",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
uniq(fingerprint) AS timeSeriesCount
|
||||
FROM %s.%s
|
||||
@@ -5325,6 +5630,11 @@ WHERE metric_name = ?;`, signozMetricDBName, signozTSTableNameV41Week)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAttributesForMetricName(ctx context.Context, metricName string, start, end *int64, filters *v3.FilterSet) (*[]metrics_explorer.Attribute, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAttributesForMetricName",
|
||||
})
|
||||
whereClause := ""
|
||||
if filters != nil {
|
||||
conditions, _ := utils.BuildFilterConditions(filters, "t")
|
||||
@@ -5392,6 +5702,11 @@ WHERE metric_name = ? AND __normalized=? %s`
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetActiveTimeSeriesForMetricName",
|
||||
})
|
||||
milli := time.Now().Add(-duration).UnixMilli()
|
||||
query := fmt.Sprintf("SELECT uniq(fingerprint) FROM %s.%s WHERE metric_name = '%s' and unix_milli >= ?", signozMetricDBName, signozTSTableNameV4, metricName)
|
||||
var timeSeries uint64
|
||||
@@ -5405,6 +5720,11 @@ func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context,
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.UUID, req *metrics_explorer.SummaryListMetricsRequest) (*metrics_explorer.SummaryListMetricsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "ListSummaryMetrics",
|
||||
})
|
||||
var args []interface{}
|
||||
|
||||
// Build filter conditions (if any)
|
||||
@@ -5623,6 +5943,11 @@ func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsTimeSeriesPercentage",
|
||||
})
|
||||
var args []interface{}
|
||||
|
||||
normalized := true
|
||||
@@ -5703,6 +6028,11 @@ func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, r
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsSamplesPercentage",
|
||||
})
|
||||
conditions, _ := utils.BuildFilterConditions(&req.Filters, "ts")
|
||||
whereClause := ""
|
||||
if conditions != nil {
|
||||
@@ -5862,6 +6192,11 @@ func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetNameSimilarity",
|
||||
})
|
||||
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
|
||||
normalized := true
|
||||
@@ -5915,6 +6250,11 @@ func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_e
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetAttributeSimilarity",
|
||||
})
|
||||
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
|
||||
normalized := true
|
||||
@@ -6073,6 +6413,11 @@ func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metr
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsAllResourceAttributes(ctx context.Context, start int64, end int64) (map[string]uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetMetricsAllResourceAttributes",
|
||||
})
|
||||
start, end, attTable, _ := utils.WhichAttributesTableToUse(start, end)
|
||||
query := fmt.Sprintf(`SELECT
|
||||
key,
|
||||
@@ -6109,6 +6454,11 @@ ORDER BY distinct_value_count DESC;`, signozMetadataDbName, attTable)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_explorer.InspectMetricsRequest, fingerprints []string) (*metrics_explorer.InspectMetricsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetInspectMetrics",
|
||||
})
|
||||
start, end, _, localTsTable := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
fingerprintsString := strings.Join(fingerprints, ",")
|
||||
query := fmt.Sprintf(`SELECT
|
||||
@@ -6203,6 +6553,11 @@ func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_e
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetInspectMetricsFingerprints(ctx context.Context, attributes []string, req *metrics_explorer.InspectMetricsRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetInspectMetricsFingerprints",
|
||||
})
|
||||
// Build dynamic key selections and JSON extracts
|
||||
var jsonExtracts []string
|
||||
var groupBys []string
|
||||
@@ -6284,6 +6639,11 @@ LIMIT 40`, // added rand to get diff value every time we run this query
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *model.UpdateMetricsMetadata) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "UpdateMetricsMetadata",
|
||||
})
|
||||
if req.MetricType == v3.MetricTypeHistogram {
|
||||
labels := []string{"le"}
|
||||
hasLabels, apiError := r.CheckForLabelsInMetric(ctx, req.MetricName, labels)
|
||||
@@ -6328,6 +6688,11 @@ VALUES ( ?, ?, ?, ?, ?, ?, ?);`, signozMetricDBName, signozUpdatedMetricsMetadat
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricName string, labels []string) (bool, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "CheckForLabelsInMetric",
|
||||
})
|
||||
if len(labels) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
@@ -6362,6 +6727,11 @@ func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricNam
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID valuer.UUID, metricNames ...string) (map[string]*model.UpdateMetricsMetadata, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetUpdatedMetricsMetadata",
|
||||
})
|
||||
cachedMetadata := make(map[string]*model.UpdateMetricsMetadata)
|
||||
var missingMetrics []string
|
||||
|
||||
@@ -6471,6 +6841,11 @@ func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.SearchTracesParams) (*[]model.SearchSpansResult, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "SearchTraces",
|
||||
})
|
||||
searchSpansResult := []model.SearchSpansResult{
|
||||
{
|
||||
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError", "StatusMessage", "StatusCodeString", "SpanKind"},
|
||||
@@ -6582,6 +6957,11 @@ func (r *ClickHouseReader) GetNormalizedStatus(
|
||||
metricNames []string,
|
||||
) (map[string]bool, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-reader",
|
||||
"function_name": "GetNormalizedStatus",
|
||||
})
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
@@ -176,9 +176,10 @@ func NewSQLMigrationProviderFactories(
|
||||
func NewTelemetryStoreProviderFactories() factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
clickhousetelemetrystore.NewFactory(
|
||||
telemetrystorehook.NewSettingsFactory(),
|
||||
telemetrystorehook.NewLoggingFactory(),
|
||||
// adding instrumentation factory before settings as we are starting the query span here
|
||||
telemetrystorehook.NewInstrumentationFactory(),
|
||||
telemetrystorehook.NewSettingsFactory(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/tokenizer"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/SigNoz/signoz/pkg/version"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -201,6 +202,10 @@ func (provider *provider) Stop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (provider *provider) collectOrg(ctx context.Context, orgID valuer.UUID) map[string]any {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "statsreporter",
|
||||
"function_name": "collectOrg",
|
||||
})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(provider.collectors))
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
@@ -47,6 +48,11 @@ var (
|
||||
// searchOperator: LIKE for pattern matching, EQUAL for exact match
|
||||
func (t *telemetryMetaStore) fetchBodyJSONPaths(ctx context.Context,
|
||||
fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, []string, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "fetchBodyJSONPaths",
|
||||
})
|
||||
query, args, limit := buildGetBodyJSONPathsQuery(fieldKeySelectors)
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
@@ -267,6 +273,11 @@ func buildListLogsJSONIndexesQuery(cluster string, filters ...string) (string, [
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ...string) (map[string][]schemamigrator.Index, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "ListLogsJSONIndexes",
|
||||
})
|
||||
query, args := buildListLogsJSONIndexesQuery(t.telemetrystore.Cluster(), filters...)
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
@@ -296,6 +307,11 @@ func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ..
|
||||
|
||||
// TODO(Piyush): Remove this if not used in future
|
||||
func (t *telemetryMetaStore) ListJSONValues(ctx context.Context, path string, limit int) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "ListJSONValues",
|
||||
})
|
||||
path = CleanPathPrefixes(path)
|
||||
|
||||
if strings.Contains(path, telemetrytypes.ArraySep) || strings.Contains(path, telemetrytypes.ArrayAnyIndex) {
|
||||
@@ -458,6 +474,11 @@ func derefValue(v any) any {
|
||||
|
||||
// IsPathPromoted checks if a specific path is promoted (Column Evolution table: field_name for logs body).
|
||||
func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "IsPathPromoted",
|
||||
})
|
||||
split := strings.Split(path, telemetrytypes.ArraySep)
|
||||
pathSegment := split[0]
|
||||
query := fmt.Sprintf("SELECT 1 FROM %s.%s WHERE signal = ? AND column_name = ? AND field_context = ? AND field_name = ? LIMIT 1", DBName, PromotedPathsTableName)
|
||||
@@ -472,6 +493,11 @@ func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (b
|
||||
|
||||
// GetPromotedPaths returns promoted paths from the Column Evolution table (field_name for logs body).
|
||||
func (t *telemetryMetaStore) GetPromotedPaths(ctx context.Context, paths ...string) (map[string]bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "GetPromotedPaths",
|
||||
})
|
||||
sb := sqlbuilder.Select("field_name").From(fmt.Sprintf("%s.%s", DBName, PromotedPathsTableName))
|
||||
conditions := []string{
|
||||
sb.Equal("signal", telemetrytypes.SignalLogs),
|
||||
@@ -518,6 +544,11 @@ func CleanPathPrefixes(path string) string {
|
||||
|
||||
// PromotePaths inserts promoted paths into the Column Evolution table (same schema as signoz-otel-collector metadata_migrations).
|
||||
func (t *telemetryMetaStore) PromotePaths(ctx context.Context, paths ...string) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "PromotePaths",
|
||||
})
|
||||
batch, err := t.telemetrystore.ClickhouseDB().PrepareBatch(ctx,
|
||||
fmt.Sprintf("INSERT INTO %s.%s (signal, column_name, column_type, field_context, field_name, version, release_time) VALUES", DBName,
|
||||
PromotedPathsTableName))
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrytraces"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -118,6 +119,11 @@ func NewTelemetryMetaStore(
|
||||
|
||||
// tracesTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the traces table
|
||||
func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "tracesTblStatementToFieldKeys",
|
||||
})
|
||||
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.tracesDBName, t.indexV3TblName)
|
||||
statements := []telemetrytypes.ShowCreateTableStatement{}
|
||||
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
|
||||
@@ -139,6 +145,12 @@ func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context)
|
||||
|
||||
// getTracesKeys returns the keys from the spans that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getTracesKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -313,6 +325,11 @@ func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelector
|
||||
|
||||
// logsTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the logs table
|
||||
func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "logsTblStatementToFieldKeys",
|
||||
})
|
||||
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.logsDBName, t.logsV2TblName)
|
||||
statements := []telemetrytypes.ShowCreateTableStatement{}
|
||||
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
|
||||
@@ -334,6 +351,12 @@ func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([
|
||||
|
||||
// getLogsKeys returns the keys from the spans that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getLogsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getLogsKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -583,6 +606,11 @@ func getPriorityForContext(ctx telemetrytypes.FieldContext) int {
|
||||
|
||||
// getMetricsKeys returns the keys from the metrics that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getMetricsKeys",
|
||||
})
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -685,6 +713,12 @@ func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelecto
|
||||
|
||||
// getMeterKeys returns the keys from the meter metrics that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getMeterSourceMetricKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getMeterSourceMetricKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -973,6 +1007,11 @@ func (t *telemetryMetaStore) GetKey(ctx context.Context, fieldKeySelector *telem
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getRelatedValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) ([]string, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": fieldValueSelector.Signal.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getRelatedValues",
|
||||
})
|
||||
|
||||
// nothing to return as "related" value if there is nothing to filter on
|
||||
if fieldValueSelector.ExistingQuery == "" {
|
||||
@@ -1118,6 +1157,11 @@ func (t *telemetryMetaStore) GetRelatedValues(ctx context.Context, fieldValueSel
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getSpanFieldValues",
|
||||
})
|
||||
// build the query to get the keys from the spans that match the field selection criteria
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
@@ -1202,6 +1246,11 @@ func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueS
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalLogs.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getLogFieldValues",
|
||||
})
|
||||
// build the query to get the keys from the spans that match the field selection criteria
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
@@ -1285,6 +1334,11 @@ func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSe
|
||||
|
||||
// getMetricFieldValues returns field values and whether the result is complete
|
||||
func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getMetricFieldValues",
|
||||
})
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
@@ -1373,6 +1427,11 @@ func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValu
|
||||
|
||||
// getIntrinsicMetricFieldValues returns values, isSearchComplete, error
|
||||
func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector, limit int) (*telemetrytypes.TelemetryFieldValues, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getIntrinsicMetricFieldValues",
|
||||
})
|
||||
key, ok := telemetrymetrics.IntrinsicMetricFieldDefinitions[fieldValueSelector.Name]
|
||||
if !ok {
|
||||
return &telemetrytypes.TelemetryFieldValues{}, nil
|
||||
@@ -1446,6 +1505,11 @@ func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context,
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getMeterSourceMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "getMeterSourceMetricFieldValues",
|
||||
})
|
||||
sb := sqlbuilder.Select("DISTINCT arrayJoin(JSONExtractKeysAndValues(labels, 'String')) AS attr").
|
||||
From(t.meterDBName + "." + t.meterFieldsTblName)
|
||||
|
||||
@@ -1660,6 +1724,11 @@ func (t *telemetryMetaStore) FetchTemporalityAndTypeMulti(ctx context.Context, q
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string][]metrictypes.Temporality, map[string]metrictypes.Type, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "fetchMetricsTemporalityAndType",
|
||||
})
|
||||
temporalities := make(map[string][]metrictypes.Temporality)
|
||||
types := make(map[string]metrictypes.Type)
|
||||
|
||||
@@ -1721,6 +1790,11 @@ func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context,
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporalityAndType(ctx context.Context, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "fetchMeterSourceMetricsTemporalityAndType",
|
||||
})
|
||||
temporalities := make(map[string]metrictypes.Temporality)
|
||||
types := make(map[string]metrictypes.Type)
|
||||
|
||||
@@ -1786,6 +1860,11 @@ const chunkSizeFirstSeenMetricMetadata = 1600
|
||||
// for each metric-attribute-value combination.
|
||||
// Returns a map where key is `telemetrytypes.MetricMetadataLookupKey` and value is first_seen in milliseconds.
|
||||
func (t *telemetryMetaStore) GetFirstSeenFromMetricMetadata(ctx context.Context, lookupKeys []telemetrytypes.MetricMetadataLookupKey) (map[telemetrytypes.MetricMetadataLookupKey]int64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "metadata",
|
||||
"function_name": "GetFirstSeenFromMetricMetadata",
|
||||
})
|
||||
result := make(map[telemetrytypes.MetricMetadataLookupKey]int64)
|
||||
|
||||
for i := 0; i < len(lookupKeys); i += chunkSizeFirstSeenMetricMetadata {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
@@ -35,7 +36,14 @@ func NewInstrumentation(ctx context.Context, providerSettings factory.ProviderSe
|
||||
}
|
||||
|
||||
func (hook *instrumentation) BeforeQuery(ctx context.Context, event *telemetrystore.QueryEvent) context.Context {
|
||||
ctx, _ = hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||||
ctx, span := hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||||
|
||||
// add trace_id and span_id to the log_comment
|
||||
comment := ctxtypes.CommentFromContext(ctx)
|
||||
comment.Set("trace_id", span.SpanContext().TraceID().String())
|
||||
comment.Set("span_id", span.SpanContext().SpanID().String())
|
||||
ctx = ctxtypes.NewContextWithComment(ctx, comment)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type TraceTimeRangeFinder struct {
|
||||
@@ -24,6 +26,11 @@ func (f *TraceTimeRangeFinder) GetTraceTimeRange(ctx context.Context, traceID st
|
||||
}
|
||||
|
||||
func (f *TraceTimeRangeFinder) GetTraceTimeRangeMulti(ctx context.Context, traceIDs []string) (startNano, endNano int64, ok bool) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalTraces.StringValue(),
|
||||
"module_name": "trace-time-range",
|
||||
"function_name": "GetTraceTimeRangeMulti",
|
||||
})
|
||||
if len(traceIDs) == 0 {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
@@ -104,6 +104,21 @@ func CommentFromHTTPRequest(req *http.Request) map[string]string {
|
||||
return comments
|
||||
}
|
||||
|
||||
// AddCommentsToContext returns a new context with all key-value pairs from comments merged into the Comment in the context.
|
||||
func AddCommentsToContext(ctx context.Context, comments map[string]string) context.Context {
|
||||
if len(comments) == 0 {
|
||||
return ctx
|
||||
}
|
||||
comment := CommentFromContext(ctx)
|
||||
if comment == nil {
|
||||
comment = NewComment()
|
||||
}
|
||||
for k, v := range comments {
|
||||
comment.Set(k, v)
|
||||
}
|
||||
return NewContextWithComment(ctx, comment)
|
||||
}
|
||||
|
||||
// NewComment creates a new Comment with an empty map. It is safe to use concurrently.
|
||||
func NewComment() *Comment {
|
||||
return &Comment{vals: map[string]string{}}
|
||||
|
||||
@@ -2,6 +2,7 @@ package querybuildertypesv5
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -226,3 +227,28 @@ func CanShortCircuitDelta(metricAgg MetricAggregation) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func DurationBucket(fromMS, toMS uint64) string {
|
||||
diff := time.Unix(0, int64(toMS)).Sub(time.Unix(0, int64(fromMS)))
|
||||
|
||||
buckets := []struct {
|
||||
d time.Duration
|
||||
l string
|
||||
}{
|
||||
{1 * time.Hour, "<1h"},
|
||||
{6 * time.Hour, "<6h"},
|
||||
{24 * time.Hour, "<24h"},
|
||||
{3 * 24 * time.Hour, "<3D"},
|
||||
{7 * 24 * time.Hour, "<1W"},
|
||||
{14 * 24 * time.Hour, "<2W"},
|
||||
{30 * 24 * time.Hour, "<1M"},
|
||||
}
|
||||
|
||||
for _, b := range buckets {
|
||||
if diff < b.d {
|
||||
return b.l
|
||||
}
|
||||
}
|
||||
|
||||
return ">=1M"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user