Compare commits

..

8 Commits

Author SHA1 Message Date
swapnil-signoz
a09dc325de Merge branch 'main' into refactor/cloud-integration-impl-store 2026-03-02 16:39:20 +05:30
swapnil-signoz
379b4f7fc4 refactor: removing interface check 2026-03-02 14:50:37 +05:30
swapnil-signoz
5e536ae077 Merge branch 'refactor/cloud-integration-types' into refactor/cloud-integration-impl-store 2026-03-02 14:49:35 +05:30
swapnil-signoz
234585e642 Merge branch 'main' into refactor/cloud-integration-types 2026-03-02 14:49:19 +05:30
swapnil-signoz
2cc14f1ad4 Merge branch 'main' into refactor/cloud-integration-impl-store 2026-03-02 14:49:00 +05:30
swapnil-signoz
dc4ed4d239 feat: adding sql store implementation 2026-03-02 14:44:56 +05:30
swapnil-signoz
7281c36873 refactor: store interfaces to use local types and error 2026-03-02 13:27:46 +05:30
swapnil-signoz
40288776e8 feat: adding cloud integration type for refactor 2026-02-28 16:59:14 +05:30
43 changed files with 1814 additions and 1400 deletions

View File

@@ -29,6 +29,7 @@ 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',

View File

@@ -25,6 +25,51 @@
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;

View File

@@ -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,8 +15,10 @@ 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';
@@ -49,6 +51,8 @@ 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,
);
@@ -57,6 +61,13 @@ 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);
@@ -287,13 +298,44 @@ 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">
<House size={14} /> Home
<HomeIcon size={14} /> Home
</div>
}
rightComponent={
@@ -358,7 +400,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">
<Dot size={16} color={Color.BG_FOREST_500} />
<DotIcon size={16} color={Color.BG_FOREST_500} />
</div>
<div className="active-ingestion-card-content-description">
@@ -385,7 +427,7 @@ export default function Home(): JSX.Element {
}
}}
>
<Compass size={12} />
<CompassIcon size={12} />
Explore Logs
</div>
</div>
@@ -399,7 +441,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">
<Dot size={16} color={Color.BG_FOREST_500} />
<DotIcon size={16} color={Color.BG_FOREST_500} />
</div>
<div className="active-ingestion-card-content-description">
@@ -426,7 +468,7 @@ export default function Home(): JSX.Element {
}
}}
>
<Compass size={12} />
<CompassIcon size={12} />
Explore Traces
</div>
</div>
@@ -440,7 +482,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">
<Dot size={16} color={Color.BG_FOREST_500} />
<DotIcon size={16} color={Color.BG_FOREST_500} />
</div>
<div className="active-ingestion-card-content-description">
@@ -467,7 +509,7 @@ export default function Home(): JSX.Element {
}
}}
>
<Compass size={12} />
<CompassIcon size={12} />
Explore Metrics
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import {
Button,
@@ -6,7 +6,7 @@ import {
Input,
Menu,
Popover,
Tooltip,
Skeleton,
Typography,
} from 'antd';
import { ColumnsType } from 'antd/es/table';
@@ -14,49 +14,148 @@ 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 { Check, Copy, Info, Search, SquareArrowOutUpRight } from 'lucide-react';
import { useNotifications } from 'hooks/useNotifications';
import { Compass, Copy, Search } 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 {
AllAttributesEmptyText,
AllAttributesValue,
} from './AllAttributesValue';
import { AllAttributesProps } from './types';
AllAttributesEmptyTextProps,
AllAttributesProps,
AllAttributesValueProps,
} from './types';
import { getMetricDetailsQuery } from './utils';
const ALL_ATTRIBUTES_KEY = 'all-attributes';
const COPY_FEEDBACK_DURATION_MS = 1500;
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>
);
}
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,
},
{
start: minTime ? Math.floor(minTime / 1000000) : undefined,
end: maxTime ? Math.floor(maxTime / 1000000) : undefined,
},
);
} = useGetMetricAttributes({
metricName,
});
const attributes = useMemo(() => attributesData?.data.attributes ?? [], [
attributesData,
@@ -65,14 +164,12 @@ function AllAttributes({
const { handleExplorerTabChange } = useHandleExplorerTabChange();
const goToMetricsExplorerwithAppliedSpaceAggregation = useCallback(
(groupBy: string, valueCount?: number) => {
const limit = valueCount && valueCount > 250 ? 100 : undefined;
(groupBy: string) => {
const compositeQuery = getMetricDetailsQuery(
metricName,
metricType,
undefined,
groupBy,
limit,
);
handleExplorerTabChange(
PANEL_TYPES.TIME_SERIES,
@@ -119,28 +216,6 @@ 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(
@@ -179,57 +254,21 @@ function AllAttributes({
width: 50,
align: 'left',
className: 'metric-metadata-key',
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>
);
},
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>
),
},
{
title: 'Value',
@@ -252,9 +291,7 @@ function AllAttributes({
],
[
goToMetricsExploreWithAppliedAttribute,
handleKeyMenuItemClick,
keyPopoverOpen,
copiedKey,
goToMetricsExplorerwithAppliedSpaceAggregation,
],
);
@@ -263,12 +300,7 @@ function AllAttributes({
{
label: (
<div className="metrics-accordion-header">
<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>
<Typography.Text>All Attributes</Typography.Text>
<Input
className="all-attributes-search-input"
placeholder="Search"
@@ -297,9 +329,7 @@ function AllAttributes({
className="metrics-accordion-content all-attributes-content"
scroll={{ y: 600 }}
locale={{
emptyText: isLoadingAttributes ? (
' '
) : (
emptyText: (
<AllAttributesEmptyText
isErrorAttributes={isErrorAttributes}
refetchAttributes={refetchAttributes}
@@ -320,6 +350,14 @@ function AllAttributes({
],
);
if (isLoadingAttributes) {
return (
<div className="all-attributes-skeleton-container">
<Skeleton active paragraph={{ rows: 8 }} />
</div>
);
}
return (
<Collapse
bordered

View File

@@ -1,213 +0,0 @@
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>
);
}

View File

@@ -1,5 +1,5 @@
import { Color } from '@signozhq/design-tokens';
import { Button, Spin, Tooltip, Typography } from 'antd';
import { Button, Skeleton, Tooltip, Typography } from 'antd';
import { useGetMetricHighlights } from 'api/generated/services/metrics';
import { InfoIcon } from 'lucide-react';
@@ -39,6 +39,17 @@ 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">
@@ -78,41 +89,32 @@ function Highlights({ metricName }: HighlightsProps): JSX.Element {
</Typography.Text>
</div>
<div className="values-row">
{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>
</>
)}
<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>
);

View File

@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueryClient } from 'react-query';
import { Button, Collapse, Input, Select, Spin, Typography } from 'antd';
import { Button, Collapse, Input, Select, Skeleton, 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 || isLoadingMetricMetadata}
disabled={isUpdatingMetricsMetadata}
>
<Edit2 size={14} />
<Typography.Text>Edit</Typography.Text>
@@ -345,7 +345,6 @@ function Metadata({
isEditing,
isErrorMetricMetadata,
isUpdatingMetricsMetadata,
isLoadingMetricMetadata,
cancelEdit,
handleSave,
]);
@@ -360,11 +359,7 @@ function Metadata({
</div>
),
key: 'metric-metadata',
children: isLoadingMetricMetadata ? (
<div className="metrics-accordion-loading-state">
<Spin size="small" />
</div>
) : isErrorMetricMetadata ? (
children: isErrorMetricMetadata ? (
<div className="metric-metadata-error-state">
<MetricDetailsErrorState
refetch={refetchMetricMetadata}
@@ -386,13 +381,20 @@ 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

View File

@@ -52,13 +52,6 @@
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;
@@ -127,11 +120,12 @@
}
}
.metrics-accordion-loading-state {
display: flex;
justify-content: center;
align-items: center;
padding: 24px;
.metrics-metadata-skeleton-container {
height: 330px;
}
.all-attributes-skeleton-container {
height: 600px;
}
.metrics-accordion {
@@ -159,18 +153,6 @@
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);
@@ -204,7 +186,6 @@
.all-attributes-key {
display: flex;
justify-content: space-between;
align-items: center;
.ant-btn {
.ant-typography:first-child {
font-family: 'Geist Mono';
@@ -212,15 +193,17 @@
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;
}
}
}
@@ -276,8 +259,10 @@
}
.metric-metadata-key {
cursor: pointer;
padding-left: 10px;
vertical-align: middle;
text-align: center;
.field-renderer-container {
.label {
color: var(--bg-vanilla-400);
@@ -463,138 +448,3 @@
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;
}
}

View File

@@ -1,14 +1,10 @@
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';
@@ -33,9 +29,6 @@ function MetricDetails({
}: MetricDetailsProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const { handleExplorerTabChange } = useHandleExplorerTabChange();
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const {
data: metricMetadataResponse,
@@ -107,21 +100,6 @@ 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%"
@@ -159,7 +137,7 @@ function MetricDetails({
</div>
}
placement="right"
onClose={handleDrawerClose}
onClose={onClose}
open={isOpen}
style={{
overscrollBehavior: 'contain',
@@ -179,12 +157,7 @@ function MetricDetails({
isLoadingMetricMetadata={isLoadingMetricMetadata}
refetchMetricMetadata={refetchMetricMetadata}
/>
<AllAttributes
metricName={metricName}
metricType={metadata?.type}
minTime={minTime}
maxTime={maxTime}
/>
<AllAttributes metricName={metricName} metricType={metadata?.type} />
</div>
</Drawer>
);

View File

@@ -1,11 +1,12 @@
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 from '../AllAttributes';
import { AllAttributesValue } from '../AllAttributesValue';
import AllAttributes, { AllAttributesValue } from '../AllAttributes';
import { getMockMetricAttributesData, MOCK_METRIC_NAME } from './testUtlls';
jest.mock('react-router-dom', () => ({
@@ -14,6 +15,17 @@ 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,
@@ -22,13 +34,12 @@ const useGetMetricAttributesMock = jest.spyOn(
describe('AllAttributes', () => {
beforeEach(() => {
jest.clearAllMocks();
useGetMetricAttributesMock.mockReturnValue({
...getMockMetricAttributesData(),
});
});
it('renders attribute keys, values, and value counts from API data', () => {
it('renders attributes section with title', () => {
render(
<AllAttributes
metricName={MOCK_METRIC_NAME}
@@ -36,13 +47,39 @@ 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({
@@ -63,7 +100,7 @@ describe('AllAttributes', () => {
expect(screen.getByText('No attributes found')).toBeInTheDocument();
});
it('clicking on an attribute key shows popover with Open in Metric Explorer option', async () => {
it('clicking on an attribute key opens the explorer with the attribute filter applied', async () => {
render(
<AllAttributes
metricName={MOCK_METRIC_NAME}
@@ -71,8 +108,7 @@ describe('AllAttributes', () => {
/>,
);
await userEvent.click(screen.getByText('attribute1'));
expect(screen.getByText('Open in Metric Explorer')).toBeInTheDocument();
expect(screen.getByText('Copy Key')).toBeInTheDocument();
expect(mockHandleExplorerTabChange).toHaveBeenCalled();
});
it('filters attributes based on search input', async () => {
@@ -87,66 +123,26 @@ 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();
beforeEach(() => {
jest.clearAllMocks();
it('renders all attribute values', () => {
render(
<AllAttributesValue
filterKey="attribute1"
filterValue={['value1', 'value2']}
goToMetricsExploreWithAppliedAttribute={
mockGoToMetricsExploreWithAppliedAttribute
}
/>,
);
expect(screen.getByText('value1')).toBeInTheDocument();
expect(screen.getByText('value2')).toBeInTheDocument();
});
it('shows All values button when there are more than 5 values', () => {
it('loads more attributes when show more button is clicked', async () => {
render(
<AllAttributesValue
filterKey="attribute1"
@@ -157,59 +153,58 @@ describe('AllAttributesValue', () => {
/>,
);
expect(screen.queryByText('value6')).not.toBeInTheDocument();
expect(screen.getByText('All values (6)')).toBeInTheDocument();
});
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)'));
await userEvent.click(screen.getByText('Show More'));
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',
];
it('does not render show more button when there are no more attributes to show', () => {
render(
<AllAttributesValue
filterKey="attribute1"
filterValue={values}
filterValue={['value1', 'value2']}
goToMetricsExploreWithAppliedAttribute={
mockGoToMetricsExploreWithAppliedAttribute
}
/>,
);
expect(screen.queryByText('Show More')).not.toBeInTheDocument();
});
await userEvent.click(screen.getByText('All values (7)'));
await userEvent.type(screen.getByPlaceholderText('Search values'), 'golf');
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');
});
expect(screen.getByText('golf-target')).toBeInTheDocument();
expect(screen.queryByText('fig-special')).not.toBeInTheDocument();
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',
'value1',
);
});
});

View File

@@ -48,7 +48,7 @@ describe('Highlights', () => {
).toBeInTheDocument();
});
it('should show labels and loading text but not stale data values while loading', () => {
it('should render loading state when data is loading', () => {
useGetMetricHighlightsMock.mockReturnValue(
getMockMetricHighlightsData(
{},
@@ -60,19 +60,8 @@ 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.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();
screen.getByTestId('metric-highlights-loading-state'),
).toBeInTheDocument();
});
});

View File

@@ -324,22 +324,6 @@ 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

View File

@@ -24,13 +24,6 @@ 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(),

View File

@@ -34,8 +34,6 @@ export interface MetadataProps {
export interface AllAttributesProps {
metricName: string;
metricType: MetrictypesTypeDTO | undefined;
minTime?: number;
maxTime?: number;
}
export interface AllAttributesValueProps {

View File

@@ -87,7 +87,6 @@ export function getMetricDetailsQuery(
metricType: MetrictypesTypeDTO | undefined,
filter?: { key: string; value: string },
groupBy?: string,
limit?: number,
): Query {
let timeAggregation;
let spaceAggregation;
@@ -171,7 +170,6 @@ export function getMetricDetailsQuery(
},
]
: [],
...(limit ? { limit } : {}),
},
],
queryFormulas: [],

View File

@@ -1,7 +1,9 @@
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';
@@ -24,17 +26,15 @@ 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,9 +45,8 @@ function MetricsSearch({
expression: currentQueryFilterExpression,
},
}}
onRun={handleRunQuery}
onRun={handleOnChange}
showFilterSuggestionsWithoutMetric
placeholder="Try metric_name CONTAINS 'http.server' to view all HTTP Server metrics being sent"
/>
</div>
<RunQueryBtn

View File

@@ -37,7 +37,7 @@
.metrics-search-container {
display: flex;
gap: 8px;
gap: 16px;
align-items: center;
.metrics-search-options {
@@ -51,6 +51,10 @@
gap: 8px;
flex: 1;
.lucide-info {
cursor: pointer;
}
.query-builder-search-container {
width: 100%;
}
@@ -62,6 +66,8 @@
margin-left: -16px;
margin-right: -16px;
max-height: 500px;
overflow-y: auto;
.ant-table-thead > tr > th {
padding: 12px;
font-weight: 500;

View File

@@ -15,12 +15,13 @@ import {
Querybuildertypesv5OrderByDTO,
Querybuildertypesv5OrderDirectionDTO,
} from 'api/generated/services/sigNoz.schemas';
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
import { initialQueriesMap } from 'constants/queryBuilder';
import {
convertExpressionToFilters,
convertFiltersToExpression,
} from 'components/QueryBuilderV2/utils';
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';
@@ -60,13 +61,10 @@ function Summary(): JSX.Element {
heatmapView,
setHeatmapView,
] = useState<MetricsexplorertypesTreemapModeDTO>(
MetricsexplorertypesTreemapModeDTO.samples,
MetricsexplorertypesTreemapModeDTO.timeseries,
);
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
useShareBuilderUrl({ defaultValue: initialQueriesMap[DataSource.METRICS] });
const query = useMemo(() => currentQuery?.builder?.queryData[0], [
currentQuery,
]);
@@ -91,15 +89,6 @@ 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',
@@ -111,6 +100,11 @@ 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),
@@ -193,7 +187,6 @@ function Summary(): JSX.Element {
},
});
setCurrentQueryFilterExpression(expression);
setAppliedFilterExpression(expression);
setCurrentPage(1);
if (expression) {
logEvent(MetricsExplorerEvents.FilterApplied, {

View File

@@ -0,0 +1,62 @@
package cloudintegration
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Module interface {
GetName() cloudintegrationtypes.CloudProviderType
// AgentCheckIn is called by agent to heartbeat and get latest config in response.
AgentCheckIn(ctx context.Context, req *cloudintegrationtypes.PostableAgentCheckInPayload) (any, error)
GenerateConnectionParams(ctx context.Context) (*cloudintegrationtypes.GettableCloudIntegrationConnectionParams, error)
// GenerateConnectionArtifact generates cloud provider specific connection information, client side handles how this information is shown
GenerateConnectionArtifact(ctx context.Context, req *cloudintegrationtypes.PostableConnectionArtifact) (any, error)
// GetAccountStatus returns agent connection status for a cloud integration account
GetAccountStatus(ctx context.Context, orgID, accountID string) (*cloudintegrationtypes.GettableAccountStatus, error)
// ListConnectedAccounts lists accounts where agent is connected
ListConnectedAccounts(ctx context.Context, orgID string) (*cloudintegrationtypes.GettableConnectedAccountsList, error)
// LIstServices return list of services for a cloud provider attached with the accountID. This just returns a summary
ListServices(ctx context.Context, orgID string, accountID *string) (any, error) // returns either GettableAWSServices or GettableAzureServices
// GetServiceDetails returns service definition details for a serviceId. This returns config and other details required to show in service details page on client.
GetServiceDetails(ctx context.Context, req *cloudintegrationtypes.GetServiceDetailsReq) (any, error)
// GetDashboard returns dashboard json for a give cloud integration service dashboard.
// this only returns the dashboard when account is connected and service is enabled
GetDashboard(ctx context.Context, id string, orgID valuer.UUID) (*dashboardtypes.Dashboard, error)
// GetAvailableDashboards returns list of available dashboards across all connected cloud integration accounts in the org.
// this list gets added to dashboard list page
GetAvailableDashboards(ctx context.Context, orgID valuer.UUID) ([]*dashboardtypes.Dashboard, error)
// UpdateAccountConfig updates cloud integration account config
UpdateAccountConfig(ctx context.Context, orgId valuer.UUID, accountId string, config []byte) (any, error)
// UpdateServiceConfig updates cloud integration service config
UpdateServiceConfig(ctx context.Context, serviceId string, orgID valuer.UUID, config []byte) (any, error)
// DisconnectAccount soft deletes/removes a cloud integration account.
DisconnectAccount(ctx context.Context, orgID, accountID string) (*cloudintegrationtypes.CloudIntegration, error)
}
type Handler interface {
AgentCheckIn(http.ResponseWriter, *http.Request)
GenerateConnectionParams(http.ResponseWriter, *http.Request)
GenerateConnectionArtifact(http.ResponseWriter, *http.Request)
ListConnectedAccounts(http.ResponseWriter, *http.Request)
GetAccountStatus(http.ResponseWriter, *http.Request)
ListServices(http.ResponseWriter, *http.Request)
GetServiceDetails(http.ResponseWriter, *http.Request)
UpdateAccountConfig(http.ResponseWriter, *http.Request)
UpdateServiceConfig(http.ResponseWriter, *http.Request)
DisconnectAccount(http.ResponseWriter, *http.Request)
}

View File

@@ -0,0 +1,193 @@
package implcloudintegration
import (
"context"
"database/sql"
"fmt"
"log/slog"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
cloudintegrationtypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
var ErrCodeCloudIntegrationAccountNotFound = errors.MustNewCode("cloud_integration_account_not_found")
// cloudProviderAccountsSQLRepository is a SQL-backed implementation of CloudIntegrationAccountStore.
type cloudProviderAccountsSQLRepository struct {
store sqlstore.SQLStore
}
// NewSQLCloudIntegrationAccountStore constructs a SQL-backed CloudIntegrationAccountStore.
func NewSQLCloudIntegrationAccountStore(store sqlstore.SQLStore) cloudintegrationtypes.CloudIntegrationAccountStore {
return &cloudProviderAccountsSQLRepository{store: store}
}
// -----------------------------
// Account store implementation
// -----------------------------
func (r *cloudProviderAccountsSQLRepository) ListConnected(
ctx context.Context, orgId string, cloudProvider string,
) ([]cloudintegrationtypes.CloudIntegration, error) {
accounts := []cloudintegrationtypes.CloudIntegration{}
err := r.store.BunDB().NewSelect().
Model(&accounts).
Where("org_id = ?", orgId).
Where("provider = ?", cloudProvider).
Where("removed_at is NULL").
Where("account_id is not NULL").
Where("last_agent_report is not NULL").
Order("created_at").
Scan(ctx)
if err != nil {
slog.ErrorContext(ctx, "error querying connected cloud accounts", "error", err)
return nil, errors.WrapInternalf(err, errors.CodeInternal, "could not query connected cloud accounts")
}
return accounts, nil
}
func (r *cloudProviderAccountsSQLRepository) Get(
ctx context.Context, orgId string, provider string, id string,
) (*cloudintegrationtypes.CloudIntegration, error) {
var result cloudintegrationtypes.CloudIntegration
err := r.store.BunDB().NewSelect().
Model(&result).
Where("org_id = ?", orgId).
Where("provider = ?", provider).
Where("id = ?", id).
Scan(ctx)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, errors.WrapNotFoundf(
err,
ErrCodeCloudIntegrationAccountNotFound,
"couldn't find account with Id %s", id,
)
}
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud provider account")
}
return &result, nil
}
func (r *cloudProviderAccountsSQLRepository) GetConnectedCloudAccount(
ctx context.Context, orgId string, provider string, accountId string,
) (*cloudintegrationtypes.CloudIntegration, error) {
var result cloudintegrationtypes.CloudIntegration
err := r.store.BunDB().NewSelect().
Model(&result).
Where("org_id = ?", orgId).
Where("provider = ?", provider).
Where("account_id = ?", accountId).
Where("last_agent_report is not NULL").
Where("removed_at is NULL").
Scan(ctx)
if errors.Is(err, sql.ErrNoRows) {
return nil, errors.WrapNotFoundf(err, ErrCodeCloudIntegrationAccountNotFound, "couldn't find connected cloud account %s", accountId)
} else if err != nil {
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud provider account")
}
return &result, nil
}
func (r *cloudProviderAccountsSQLRepository) Upsert(
ctx context.Context,
orgId string,
provider string,
id *string,
config []byte,
accountId *string,
agentReport *cloudintegrationtypes.AgentReport,
removedAt *time.Time,
) (*cloudintegrationtypes.CloudIntegration, error) {
// Insert
if id == nil {
temp := valuer.GenerateUUID().StringValue()
id = &temp
}
// Prepare clause for setting values in `on conflict do update`
onConflictSetStmts := []string{}
setColStatement := func(col string) string {
return fmt.Sprintf("%s=excluded.%s", col, col)
}
if config != nil {
onConflictSetStmts = append(
onConflictSetStmts, setColStatement("config"),
)
}
if accountId != nil {
onConflictSetStmts = append(
onConflictSetStmts, setColStatement("account_id"),
)
}
if agentReport != nil {
onConflictSetStmts = append(
onConflictSetStmts, setColStatement("last_agent_report"),
)
}
if removedAt != nil {
onConflictSetStmts = append(
onConflictSetStmts, setColStatement("removed_at"),
)
}
// set updated_at to current timestamp if it's an upsert
onConflictSetStmts = append(
onConflictSetStmts, setColStatement("updated_at"),
)
onConflictClause := ""
if len(onConflictSetStmts) > 0 {
onConflictClause = fmt.Sprintf(
"conflict(id, provider, org_id) do update SET\n%s",
strings.Join(onConflictSetStmts, ",\n"),
)
}
integration := cloudintegrationtypes.CloudIntegration{
OrgID: orgId,
Provider: provider,
Identifiable: types.Identifiable{ID: valuer.MustNewUUID(*id)},
TimeAuditable: types.TimeAuditable{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
Config: string(config),
AccountID: accountId,
LastAgentReport: agentReport,
RemovedAt: removedAt,
}
_, err := r.store.BunDB().NewInsert().
Model(&integration).
On(onConflictClause).
Exec(ctx)
if err != nil {
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't upsert cloud integration account")
}
upsertedAccount, err := r.Get(ctx, orgId, provider, *id)
if err != nil {
slog.ErrorContext(ctx, "error upserting cloud integration account", "error", err)
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't get upserted cloud integration account")
}
return upsertedAccount, nil
}

View File

@@ -0,0 +1,133 @@
package implcloudintegration
import (
"context"
"database/sql"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
cloudintegrationtypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
var ErrCodeServiceConfigNotFound = errors.MustNewCode("service_config_not_found")
// serviceConfigSQLRepository is a SQL-backed implementation of CloudIntegrationServiceStore.
type serviceConfigSQLRepository struct {
store sqlstore.SQLStore
}
// NewSQLCloudIntegrationServiceStore constructs a SQL-backed CloudIntegrationServiceStore.
func NewSQLCloudIntegrationServiceStore(store sqlstore.SQLStore) cloudintegrationtypes.CloudIntegrationServiceStore {
return &serviceConfigSQLRepository{store: store}
}
// -----------------------------
// Service config store implementation
// -----------------------------
func (r *serviceConfigSQLRepository) Get(
ctx context.Context,
orgID string,
cloudAccountId string,
serviceType string,
) ([]byte, error) {
var result cloudintegrationtypes.CloudIntegrationService
err := r.store.BunDB().NewSelect().
Model(&result).
Join("JOIN cloud_integration ci ON ci.id = cis.cloud_integration_id").
Where("ci.org_id = ?", orgID).
Where("ci.id = ?", cloudAccountId).
Where("cis.type = ?", serviceType).
Scan(ctx)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, errors.WrapNotFoundf(err, ErrCodeServiceConfigNotFound, "couldn't find config for cloud account %s", cloudAccountId)
}
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud service config")
}
return []byte(result.Config), nil
}
func (r *serviceConfigSQLRepository) Upsert(
ctx context.Context,
orgID string,
cloudProvider string,
cloudAccountId string,
serviceId string,
config []byte,
) ([]byte, error) {
// get cloud integration id from account id
// if the account is not connected, we don't need to upsert the config
var cloudIntegrationId string
err := r.store.BunDB().NewSelect().
Model((*cloudintegrationtypes.CloudIntegration)(nil)).
Column("id").
Where("provider = ?", cloudProvider).
Where("account_id = ?", cloudAccountId).
Where("org_id = ?", orgID).
Where("removed_at is NULL").
Where("last_agent_report is not NULL").
Scan(ctx, &cloudIntegrationId)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, errors.WrapNotFoundf(
err,
ErrCodeCloudIntegrationAccountNotFound,
"couldn't find active cloud integration account",
)
}
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud integration id")
}
serviceConfig := cloudintegrationtypes.CloudIntegrationService{
Identifiable: types.Identifiable{ID: valuer.GenerateUUID()},
TimeAuditable: types.TimeAuditable{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
Config: string(config),
Type: serviceId,
CloudIntegrationID: cloudIntegrationId,
}
_, err = r.store.BunDB().NewInsert().
Model(&serviceConfig).
On("conflict(cloud_integration_id, type) do update set config=excluded.config, updated_at=excluded.updated_at").
Exec(ctx)
if err != nil {
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't upsert cloud service config")
}
return config, nil
}
func (r *serviceConfigSQLRepository) GetAllForAccount(
ctx context.Context,
orgID string,
cloudAccountId string,
) (map[string][]byte, error) {
var serviceConfigs []cloudintegrationtypes.CloudIntegrationService
err := r.store.BunDB().NewSelect().
Model(&serviceConfigs).
Join("JOIN cloud_integration ci ON ci.id = cis.cloud_integration_id").
Where("ci.id = ?", cloudAccountId).
Where("ci.org_id = ?", orgID).
Scan(ctx)
if err != nil {
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query service configs from db")
}
result := make(map[string][]byte)
for _, r := range serviceConfigs {
result[r.Type] = []byte(r.Config)
}
return result, nil
}

View File

@@ -56,18 +56,7 @@ 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
}
@@ -228,7 +217,6 @@ 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
}
@@ -417,8 +405,6 @@ 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))
@@ -455,8 +441,6 @@ 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
}
@@ -515,8 +499,6 @@ 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
}
@@ -645,8 +627,6 @@ 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))
@@ -668,7 +648,6 @@ 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()
@@ -763,7 +742,6 @@ 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)
@@ -871,8 +849,6 @@ 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()
@@ -937,8 +913,6 @@ 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)
@@ -1040,8 +1014,6 @@ 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))
@@ -1062,8 +1034,6 @@ 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))
@@ -1087,8 +1057,6 @@ 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))
@@ -1109,8 +1077,6 @@ 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()
@@ -1132,8 +1098,6 @@ 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(

View File

@@ -11,7 +11,6 @@ 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"
)
@@ -106,11 +105,6 @@ 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
}

View File

@@ -13,7 +13,6 @@ 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"
@@ -35,12 +34,6 @@ 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)}

View File

@@ -11,8 +11,6 @@ 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"
@@ -139,11 +137,6 @@ 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
@@ -175,11 +168,6 @@ 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(`
@@ -256,12 +244,6 @@ 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

View File

@@ -12,7 +12,6 @@ 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"
@@ -213,13 +212,6 @@ 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)

View File

@@ -14,7 +14,6 @@ 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"
)
@@ -99,11 +98,6 @@ 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)

View File

@@ -14,8 +14,6 @@ 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"
@@ -189,11 +187,6 @@ 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))

View File

@@ -16,7 +16,6 @@ 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"
@@ -527,11 +526,6 @@ 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 := ""

View File

@@ -6,9 +6,7 @@ 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 {
@@ -54,13 +52,6 @@ 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)

View File

@@ -20,8 +20,6 @@ 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"
@@ -271,12 +269,6 @@ 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 {
@@ -296,12 +288,6 @@ 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
@@ -397,12 +383,6 @@ 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}
}
@@ -759,11 +739,6 @@ 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)),
@@ -819,11 +794,6 @@ 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),
@@ -859,13 +829,6 @@ 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)
@@ -1264,11 +1227,6 @@ 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{}{}
@@ -1323,11 +1281,6 @@ 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")}
@@ -1491,11 +1444,6 @@ 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)
@@ -1641,12 +1589,6 @@ 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)
@@ -1668,11 +1610,6 @@ 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")
@@ -2062,10 +1999,6 @@ 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
}
@@ -2183,11 +2116,6 @@ 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)
@@ -2396,10 +2324,6 @@ 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 {
@@ -2416,10 +2340,6 @@ 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"
@@ -2443,10 +2363,6 @@ 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))
@@ -2616,11 +2532,6 @@ 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"
@@ -2693,12 +2604,6 @@ 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)
@@ -2736,11 +2641,6 @@ 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")}
@@ -2768,11 +2668,6 @@ 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)
@@ -2821,11 +2716,6 @@ 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)
@@ -2895,11 +2785,6 @@ 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)
@@ -2991,11 +2876,6 @@ 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,
@@ -3032,11 +2912,6 @@ 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,
@@ -3087,10 +2962,6 @@ 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."))
}
@@ -3157,10 +3028,6 @@ 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{},
@@ -3216,11 +3083,6 @@ 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."))
}
@@ -3312,10 +3174,6 @@ 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)
@@ -3352,11 +3210,6 @@ 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 {
@@ -3435,11 +3288,6 @@ 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(
@@ -3488,11 +3336,6 @@ 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
@@ -3533,11 +3376,6 @@ 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
@@ -3574,11 +3412,6 @@ 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
@@ -3619,11 +3452,6 @@ 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
@@ -3705,10 +3533,6 @@ 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 {
@@ -3720,11 +3544,6 @@ 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 {
@@ -3809,11 +3628,6 @@ 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
@@ -3898,11 +3712,6 @@ 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
@@ -3969,10 +3778,6 @@ 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))
@@ -4064,11 +3869,6 @@ 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
@@ -4386,10 +4186,6 @@ 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 {
@@ -4453,10 +4249,6 @@ 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))
@@ -4519,11 +4311,6 @@ 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
}
@@ -4559,10 +4346,6 @@ 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
@@ -4573,11 +4356,6 @@ 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
@@ -4671,11 +4449,6 @@ 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
@@ -4744,11 +4517,6 @@ 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
@@ -4842,11 +4610,6 @@ 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
@@ -4895,10 +4658,6 @@ 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
@@ -4930,10 +4689,6 @@ 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)
@@ -4948,10 +4703,6 @@ 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))
@@ -5066,10 +4817,6 @@ 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,
@@ -5094,10 +4841,6 @@ 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,
@@ -5225,10 +4968,6 @@ 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
@@ -5340,10 +5079,6 @@ 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)
@@ -5372,11 +5107,6 @@ 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')",
@@ -5414,11 +5144,6 @@ 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
@@ -5456,11 +5181,6 @@ 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
@@ -5497,11 +5217,6 @@ 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)
@@ -5529,11 +5244,6 @@ 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)
@@ -5561,11 +5271,6 @@ 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
@@ -5581,11 +5286,6 @@ 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
@@ -5611,11 +5311,6 @@ 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
@@ -5630,11 +5325,6 @@ 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")
@@ -5702,11 +5392,6 @@ 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
@@ -5720,11 +5405,6 @@ 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)
@@ -5943,11 +5623,6 @@ 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
@@ -6028,11 +5703,6 @@ 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 {
@@ -6192,11 +5862,6 @@ 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
@@ -6250,11 +5915,6 @@ 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
@@ -6413,11 +6073,6 @@ 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,
@@ -6454,11 +6109,6 @@ 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
@@ -6553,11 +6203,6 @@ 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
@@ -6639,11 +6284,6 @@ 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)
@@ -6688,11 +6328,6 @@ 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
}
@@ -6727,11 +6362,6 @@ 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
@@ -6841,11 +6471,6 @@ 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"},
@@ -6957,11 +6582,6 @@ 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
}

View File

@@ -176,10 +176,9 @@ func NewSQLMigrationProviderFactories(
func NewTelemetryStoreProviderFactories() factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]] {
return factory.MustNewNamedMap(
clickhousetelemetrystore.NewFactory(
telemetrystorehook.NewLoggingFactory(),
// adding instrumentation factory before settings as we are starting the query span here
telemetrystorehook.NewInstrumentationFactory(),
telemetrystorehook.NewSettingsFactory(),
telemetrystorehook.NewLoggingFactory(),
telemetrystorehook.NewInstrumentationFactory(),
),
)
}

View File

@@ -14,7 +14,6 @@ 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"
@@ -202,10 +201,6 @@ 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))

View File

@@ -13,7 +13,6 @@ 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"
)
@@ -48,11 +47,6 @@ 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 {
@@ -273,11 +267,6 @@ 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 {
@@ -307,11 +296,6 @@ 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) {
@@ -474,11 +458,6 @@ 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)
@@ -493,11 +472,6 @@ 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),
@@ -544,11 +518,6 @@ 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))

View File

@@ -13,7 +13,6 @@ 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"
@@ -119,11 +118,6 @@ 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)
@@ -145,12 +139,6 @@ 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
}
@@ -325,11 +313,6 @@ 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)
@@ -351,12 +334,6 @@ 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
}
@@ -606,11 +583,6 @@ 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
}
@@ -713,12 +685,6 @@ 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
}
@@ -1007,11 +973,6 @@ 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 == "" {
@@ -1157,11 +1118,6 @@ 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 {
@@ -1246,11 +1202,6 @@ 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 {
@@ -1334,11 +1285,6 @@ 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
@@ -1427,11 +1373,6 @@ 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
@@ -1505,11 +1446,6 @@ 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)
@@ -1724,11 +1660,6 @@ 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)
@@ -1790,11 +1721,6 @@ 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)
@@ -1860,11 +1786,6 @@ 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 {

View File

@@ -5,7 +5,6 @@ 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"
@@ -36,14 +35,7 @@ func NewInstrumentation(ctx context.Context, providerSettings factory.ProviderSe
}
func (hook *instrumentation) BeforeQuery(ctx context.Context, event *telemetrystore.QueryEvent) context.Context {
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)
ctx, _ = hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
return ctx
}

View File

@@ -6,8 +6,6 @@ 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 {
@@ -26,11 +24,6 @@ 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
}

View File

@@ -0,0 +1,578 @@
// NOTE:
// - When Account keyword is used in struct names, it refers cloud integration account. CloudIntegration refers to DB schema.
// - When Account Config keyword is used in struct names, it refers to configuration for cloud integration accounts
// - When Service keyword is used in struct names, it refers to cloud integration service. CloudIntegrationService refers to DB schema.
// where `service` is services provided by each cloud provider like AWS S3, Azure BlobStorage etc.
// - When Service Config keyword is used in struct names, it refers to configuration for cloud integration services
package cloudintegrationtypes
import (
"database/sql/driver"
"encoding/json"
"strings"
"time"
"github.com/uptrace/bun"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
)
// CloudProviderType type alias
type CloudProviderType struct{ valuer.String }
var (
CloudProviderTypeAWS = CloudProviderType{valuer.NewString("aws")}
CloudProviderTypeAzure = CloudProviderType{valuer.NewString("azure")}
)
var ErrCodeCloudProviderInvalidInput = errors.MustNewCode("invalid_cloud_provider")
// NewCloudProvider returns a new CloudProviderType from a string. It validates the input and returns an error if the input is not valid.
func NewCloudProvider(provider string) (CloudProviderType, error) {
switch provider {
case CloudProviderTypeAWS.StringValue():
return CloudProviderTypeAWS, nil
case CloudProviderTypeAzure.StringValue():
return CloudProviderTypeAzure, nil
default:
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider)
}
}
var (
AWSIntegrationUserEmail = valuer.MustNewEmail("aws-integration@signoz.io")
AzureIntegrationUserEmail = valuer.MustNewEmail("azure-integration@signoz.io")
)
// CloudIntegrationUserEmails is the list of valid emails for Cloud One Click integrations.
// This is used for validation and restrictions in different contexts, across codebase.
var CloudIntegrationUserEmails = []valuer.Email{
AWSIntegrationUserEmail,
AzureIntegrationUserEmail,
}
func IsCloudIntegrationDashboardUuid(dashboardUuid string) bool {
parts := strings.SplitN(dashboardUuid, "--", 4)
if len(parts) != 4 {
return false
}
return parts[0] == "cloud-integration"
}
// GetCloudIntegrationDashboardID returns the cloud provider from dashboard id, if it's a cloud integration dashboard id.
// throws an error if invalid format or invalid cloud provider is provided in the dashboard id.
func GetCloudProviderFromDashboardID(dashboardUuid string) (CloudProviderType, error) {
parts := strings.SplitN(dashboardUuid, "--", 4)
if len(parts) != 4 {
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid dashboard uuid: %s", dashboardUuid)
}
providerStr := parts[1]
cloudProvider, err := NewCloudProvider(providerStr)
if err != nil {
return CloudProviderType{}, err
}
return cloudProvider, nil
}
// Generic utility functions for JSON serialization/deserialization
// this is helpful to return right errors from a common place and avoid repeating the same code in multiple places.
// UnmarshalJSON is a generic function to unmarshal JSON data into any type
func UnmarshalJSON[T any](src []byte, target *T) error {
err := json.Unmarshal(src, target)
if err != nil {
return errors.WrapInternalf(
err, errors.CodeInternal, "couldn't deserialize JSON",
)
}
return nil
}
// MarshalJSON is a generic function to marshal any type to JSON
func MarshalJSON[T any](source *T) ([]byte, error) {
if source == nil {
return nil, errors.NewInternalf(errors.CodeInternal, "source is nil")
}
serialized, err := json.Marshal(source)
if err != nil {
return nil, errors.WrapInternalf(
err, errors.CodeInternal, "couldn't serialize to JSON",
)
}
return serialized, nil
}
// GettableConnectedAccountsList is the response for listing connected accounts for a cloud provider.
type GettableConnectedAccountsList struct {
Accounts []*Account `json:"accounts"`
}
// SigNozAgentConfig represents parameters required for agent deployment in cloud provider accounts
// these represent parameters passed during agent deployment, how they are passed might change for each cloud provider but the purpose is same.
type SigNozAgentConfig struct {
Region string `json:"region,omitempty"` // AWS-specific: The region in which SigNoz agent should be installed
IngestionUrl string `json:"ingestion_url"`
IngestionKey string `json:"ingestion_key"`
SigNozAPIUrl string `json:"signoz_api_url"`
SigNozAPIKey string `json:"signoz_api_key"`
Version string `json:"version,omitempty"`
}
// PostableConnectionArtifact represent request body for generating connection artifact API.
// Data is request body raw bytes since each cloud provider will have have different request body structure and generics hardly help in such cases.
// Artifact is a generic name for different types of connection methods like connection URL for AWS, connection command for Azure etc.
type PostableConnectionArtifact struct {
OrgID string
Data []byte // either PostableAWSConnectionUrl or PostableAzureConnectionCommand
}
// PostableAWSConnectionUrl is request body for AWS connection artifact API
type PostableAWSConnectionUrl struct {
AgentConfig *SigNozAgentConfig `json:"agent_config"`
AccountConfig *AWSAccountConfig `json:"account_config"`
}
// PostableAzureConnectionCommand is request body for Azure connection artifact API
type PostableAzureConnectionCommand struct {
AgentConfig *SigNozAgentConfig `json:"agent_config"`
AccountConfig *AzureAccountConfig `json:"account_config"`
}
// GettableAzureConnectionArtifact is Azure specific connection artifact which contains connection commands for agent deployment
type GettableAzureConnectionArtifact struct {
AzureShellConnectionCommand string `json:"az_shell_connection_command"`
AzureCliConnectionCommand string `json:"az_cli_connection_command"`
}
// GettableAWSConnectionUrl is AWS specific connection artifact which contains connection url for agent deployment
type GettableAWSConnectionUrl struct {
AccountId string `json:"account_id"`
ConnectionUrl string `json:"connection_url"`
}
// GettableAzureConnectionCommand is Azure specific connection artifact which contains connection commands for agent deployment
type GettableAzureConnectionCommand struct {
AccountId string `json:"account_id"`
AzureShellConnectionCommand string `json:"az_shell_connection_command"`
AzureCliConnectionCommand string `json:"az_cli_connection_command"`
}
// GettableAccountStatus is cloud integration account status response
type GettableAccountStatus struct {
Id string `json:"id"`
CloudAccountId *string `json:"cloud_account_id,omitempty"`
Status AccountStatus `json:"status"`
}
// PostableAgentCheckInPayload is request body for agent check-in API.
// This is used by agent to send heartbeat.
type PostableAgentCheckInPayload struct {
ID string `json:"account_id"`
AccountID string `json:"cloud_account_id"`
// Arbitrary cloud specific Agent data
Data map[string]any `json:"data,omitempty"`
OrgID string `json:"-"`
}
// AWSAgentIntegrationConfig is used by agent for deploying infra to send telemetry to SigNoz
type AWSAgentIntegrationConfig struct {
EnabledRegions []string `json:"enabled_regions"`
TelemetryCollectionStrategy *AWSCollectionStrategy `json:"telemetry,omitempty"`
}
// AzureAgentIntegrationConfig is used by agent for deploying infra to send telemetry to SigNoz
type AzureAgentIntegrationConfig struct {
DeploymentRegion string `json:"deployment_region"` // will not be changed once set
EnabledResourceGroups []string `json:"resource_groups"`
// TelemetryCollectionStrategy is map of service to telemetry config
TelemetryCollectionStrategy map[string]*AzureCollectionStrategy `json:"telemetry,omitempty"`
}
// GettableAgentCheckInRes is generic response from agent check-in API.
// AWSAgentIntegrationConfig and AzureAgentIntegrationConfig these configs are used by agent to deploy the infra and send telemetry to SigNoz
type GettableAgentCheckInRes[AgentConfigT any] struct {
AccountId string `json:"account_id"`
CloudAccountId string `json:"cloud_account_id"`
RemovedAt *time.Time `json:"removed_at"`
IntegrationConfig AgentConfigT `json:"integration_config"`
}
// UpdatableServiceConfig is generic
type UpdatableServiceConfig[ServiceConfigT any] struct {
CloudAccountId string `json:"cloud_account_id"`
Config ServiceConfigT `json:"config"`
}
// ServiceConfigTyped is a generic interface for cloud integration service's configuration
// this is generic interface to define helper functions for CloudIntegrationService.Config field.
type ServiceConfigTyped[definition Definition] interface {
Validate(def definition) error
IsMetricsEnabled() bool
IsLogsEnabled() bool
}
type AWSServiceConfig struct {
Logs *AWSServiceLogsConfig `json:"logs,omitempty"`
Metrics *AWSServiceMetricsConfig `json:"metrics,omitempty"`
}
type AWSServiceLogsConfig struct {
Enabled bool `json:"enabled"`
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
}
type AWSServiceMetricsConfig struct {
Enabled bool `json:"enabled"`
}
// IsMetricsEnabled returns true if metrics collection is configured and enabled
func (a *AWSServiceConfig) IsMetricsEnabled() bool {
return a.Metrics != nil && a.Metrics.Enabled
}
// IsLogsEnabled returns true if logs collection is configured and enabled
func (a *AWSServiceConfig) IsLogsEnabled() bool {
return a.Logs != nil && a.Logs.Enabled
}
type AzureServiceConfig struct {
Logs []*AzureServiceLogsConfig `json:"logs,omitempty"`
Metrics []*AzureServiceMetricsConfig `json:"metrics,omitempty"`
}
// AzureServiceLogsConfig is Azure specific service config for logs
type AzureServiceLogsConfig struct {
Enabled bool `json:"enabled"`
Name string `json:"name"`
}
// AzureServiceMetricsConfig is Azure specific service config for metrics
type AzureServiceMetricsConfig struct {
Enabled bool `json:"enabled"`
Name string `json:"name"`
}
// IsMetricsEnabled returns true if any metric is configured and enabled
func (a *AzureServiceConfig) IsMetricsEnabled() bool {
if a.Metrics == nil {
return false
}
for _, m := range a.Metrics {
if m.Enabled {
return true
}
}
return false
}
// IsLogsEnabled returns true if any log is configured and enabled
func (a *AzureServiceConfig) IsLogsEnabled() bool {
if a.Logs == nil {
return false
}
for _, l := range a.Logs {
if l.Enabled {
return true
}
}
return false
}
func (a *AWSServiceConfig) Validate(def *AWSDefinition) error {
if def.Id != S3Sync.String() && a.Logs != nil && a.Logs.S3Buckets != nil {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "s3 buckets can only be added to service-type[%s]", S3Sync)
} else if def.Id == S3Sync.String() && a.Logs != nil && a.Logs.S3Buckets != nil {
for region := range a.Logs.S3Buckets {
if _, found := ValidAWSRegions[region]; !found {
return errors.NewInvalidInputf(CodeInvalidCloudRegion, "invalid cloud region: %s", region)
}
}
}
return nil
}
func (a *AzureServiceConfig) Validate(def *AzureDefinition) error {
logsMap := make(map[string]bool)
metricsMap := make(map[string]bool)
if def.Strategy != nil && def.Strategy.Logs != nil {
for _, log := range def.Strategy.Logs {
logsMap[log.Name] = true
}
}
if def.Strategy != nil && def.Strategy.Metrics != nil {
for _, metric := range def.Strategy.Metrics {
metricsMap[metric.Name] = true
}
}
for _, log := range a.Logs {
if _, found := logsMap[log.Name]; !found {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid log name: %s", log.Name)
}
}
for _, metric := range a.Metrics {
if _, found := metricsMap[metric.Name]; !found {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid metric name: %s", metric.Name)
}
}
return nil
}
// UpdatableServiceConfigRes is response for UpdateServiceConfig API
// TODO: find a better way to name this
type UpdatableServiceConfigRes struct {
ServiceId string `json:"id"`
Config any `json:"config"`
}
// UpdatableAccountConfigTyped is a generic struct for updating cloud integration account config used in UpdateAccountConfig API
type UpdatableAccountConfigTyped[AccountConfigT any] struct {
Config *AccountConfigT `json:"config"`
}
type (
UpdatableAWSAccountConfig = UpdatableAccountConfigTyped[AWSAccountConfig]
UpdatableAzureAccountConfig = UpdatableAccountConfigTyped[AzureAccountConfig]
)
// AWSAccountConfig is the configuration for AWS cloud integration account
type AWSAccountConfig struct {
EnabledRegions []string `json:"regions"`
}
// AzureAccountConfig is the configuration for Azure cloud integration account
type AzureAccountConfig struct {
DeploymentRegion string `json:"deployment_region,omitempty"`
EnabledResourceGroups []string `json:"resource_groups,omitempty"`
}
// GettableServices is a generic struct for listing services of a cloud integration account used in ListServices API
type GettableServices[ServiceSummaryT any] struct {
Services []ServiceSummaryT `json:"services"`
}
type (
GettableAWSServices = GettableServices[AWSServiceSummary]
GettableAzureServices = GettableServices[AzureServiceSummary]
)
// GetServiceDetailsReq is a req struct for getting service definition details
type GetServiceDetailsReq struct {
OrgID valuer.UUID
ServiceId string
CloudAccountID *string
}
// ServiceSummary is a generic struct for service summary used in ListServices API
type ServiceSummary[ServiceConfigT any] struct {
DefinitionMetadata
Config *ServiceConfigT `json:"config"`
}
type (
AWSServiceSummary = ServiceSummary[AWSServiceConfig]
AzureServiceSummary = ServiceSummary[AzureServiceConfig]
)
// GettableServiceDetails is a generic struct for service details used in GetServiceDetails API
type GettableServiceDetails[DefinitionT any, ServiceConfigT any] struct {
Definition DefinitionT `json:",inline"`
Config ServiceConfigT `json:"config"`
ConnectionStatus *ServiceConnectionStatus `json:"status,omitempty"`
}
type (
GettableAWSServiceDetails = GettableServiceDetails[AWSDefinition, *AWSServiceConfig]
GettableAzureServiceDetails = GettableServiceDetails[AzureDefinition, *AzureServiceConfig]
)
// ServiceConnectionStatus represents integration connection status for a particular service
// this struct helps to check ingested data and determines connection status by whether data was ingested or not.
// this is composite struct for both metrics and logs
type ServiceConnectionStatus struct {
Logs []*SignalConnectionStatus `json:"logs"`
Metrics []*SignalConnectionStatus `json:"metrics"`
}
// SignalConnectionStatus represents connection status for a particular signal type (logs or metrics) for a service
// this struct is used in API responses for clients to show relevant information about the connection status.
type SignalConnectionStatus struct {
CategoryID string `json:"category"`
CategoryDisplayName string `json:"category_display_name"`
LastReceivedTsMillis int64 `json:"last_received_ts_ms"` // epoch milliseconds
LastReceivedFrom string `json:"last_received_from"` // resource identifier
}
// GettableCloudIntegrationConnectionParams is response for connection params API
type GettableCloudIntegrationConnectionParams struct {
IngestionUrl string `json:"ingestion_url,omitempty"`
IngestionKey string `json:"ingestion_key,omitempty"`
SigNozAPIUrl string `json:"signoz_api_url,omitempty"`
SigNozAPIKey string `json:"signoz_api_key,omitempty"`
}
// GettableIngestionKey is a struct for ingestion key returned from gateway
type GettableIngestionKey struct {
Name string `json:"name"`
Value string `json:"value"`
// other attributes from gateway response not included here since they are not being used.
}
// GettableIngestionKeysSearch is a struct for response of ingestion keys search API on gateway
type GettableIngestionKeysSearch struct {
Status string `json:"status"`
Data []GettableIngestionKey `json:"data"`
Error string `json:"error"`
}
// GettableCreateIngestionKey is a struct for response of create ingestion key API on gateway
type GettableCreateIngestionKey struct {
Status string `json:"status"`
Data GettableIngestionKey `json:"data"`
Error string `json:"error"`
}
// GettableDeployment is response struct for deployment details fetched from Zeus
type GettableDeployment struct {
Name string `json:"name"`
ClusterInfo struct {
Region struct {
DNS string `json:"dns"`
} `json:"region"`
} `json:"cluster"`
}
// --------------------------------------------------------------------------
// DATABASE TYPES
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// Cloud integration uses the cloud_integration table
// and cloud_integrations_service table
// --------------------------------------------------------------------------
type CloudIntegration struct {
bun.BaseModel `bun:"table:cloud_integration"`
types.Identifiable
types.TimeAuditable
Provider string `json:"provider" bun:"provider,type:text,unique:provider_id"`
Config string `json:"config" bun:"config,type:text"` // json serialized config
AccountID *string `json:"account_id" bun:"account_id,type:text"`
LastAgentReport *AgentReport `json:"last_agent_report" bun:"last_agent_report,type:text"`
RemovedAt *time.Time `json:"removed_at" bun:"removed_at,type:timestamp,nullzero"`
OrgID string `bun:"org_id,type:text,unique:provider_id"`
}
// Account represents a cloud integration account, this is used for business logic and API responses.
type Account struct {
Id string `json:"id"`
CloudAccountId string `json:"cloud_account_id"`
Config any `json:"config"` // AWSAccountConfig or AzureAccountConfig
Status AccountStatus `json:"status"`
}
// AccountStatus is generic struct for cloud integration account status
type AccountStatus struct {
Integration AccountIntegrationStatus `json:"integration"`
}
// AccountIntegrationStatus stores heartbeat information from agent check in
type AccountIntegrationStatus struct {
LastHeartbeatTsMillis *int64 `json:"last_heartbeat_ts_ms"`
}
func (a *CloudIntegration) Status() AccountStatus {
status := AccountStatus{}
if a.LastAgentReport != nil {
lastHeartbeat := a.LastAgentReport.TimestampMillis
status.Integration.LastHeartbeatTsMillis = &lastHeartbeat
}
return status
}
func (a *CloudIntegration) Account(cloudProvider CloudProviderType) *Account {
ca := &Account{Id: a.ID.StringValue(), Status: a.Status()}
if a.AccountID != nil {
ca.CloudAccountId = *a.AccountID
}
ca.Config = map[string]interface{}{}
if len(a.Config) < 1 {
return ca
}
switch cloudProvider {
case CloudProviderTypeAWS:
config := new(AWSAccountConfig)
_ = UnmarshalJSON([]byte(a.Config), config)
ca.Config = config
case CloudProviderTypeAzure:
config := new(AzureAccountConfig)
_ = UnmarshalJSON([]byte(a.Config), config)
ca.Config = config
default:
}
return ca
}
type AgentReport struct {
TimestampMillis int64 `json:"timestamp_millis"`
Data map[string]any `json:"data"`
}
// Scan scans data from db
func (r *AgentReport) Scan(src any) error {
var data []byte
switch v := src.(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return errors.NewInternalf(errors.CodeInternal, "tried to scan from %T instead of string or bytes", src)
}
return json.Unmarshal(data, r)
}
// Value serializes data to bytes for db insertion
func (r *AgentReport) Value() (driver.Value, error) {
if r == nil {
return nil, errors.NewInternalf(errors.CodeInternal, "agent report is nil")
}
serialized, err := json.Marshal(r)
if err != nil {
return nil, errors.WrapInternalf(
err, errors.CodeInternal, "couldn't serialize agent report to JSON",
)
}
return serialized, nil
}
type CloudIntegrationService struct {
bun.BaseModel `bun:"table:cloud_integration_service,alias:cis"`
types.Identifiable
types.TimeAuditable
Type string `bun:"type,type:text,notnull,unique:cloud_integration_id_type"`
Config string `bun:"config,type:text"` // json serialized config
CloudIntegrationID string `bun:"cloud_integration_id,type:text,notnull,unique:cloud_integration_id_type,references:cloud_integrations(id),on_delete:cascade"`
}

View File

@@ -0,0 +1,103 @@
package cloudintegrationtypes
import (
"github.com/SigNoz/signoz/pkg/errors"
)
var (
CodeInvalidCloudRegion = errors.MustNewCode("invalid_cloud_region")
CodeMismatchCloudProvider = errors.MustNewCode("cloud_provider_mismatch")
)
// List of all valid cloud regions on Amazon Web Services
var ValidAWSRegions = map[string]bool{
"af-south-1": true, // Africa (Cape Town).
"ap-east-1": true, // Asia Pacific (Hong Kong).
"ap-northeast-1": true, // Asia Pacific (Tokyo).
"ap-northeast-2": true, // Asia Pacific (Seoul).
"ap-northeast-3": true, // Asia Pacific (Osaka).
"ap-south-1": true, // Asia Pacific (Mumbai).
"ap-south-2": true, // Asia Pacific (Hyderabad).
"ap-southeast-1": true, // Asia Pacific (Singapore).
"ap-southeast-2": true, // Asia Pacific (Sydney).
"ap-southeast-3": true, // Asia Pacific (Jakarta).
"ap-southeast-4": true, // Asia Pacific (Melbourne).
"ca-central-1": true, // Canada (Central).
"ca-west-1": true, // Canada West (Calgary).
"eu-central-1": true, // Europe (Frankfurt).
"eu-central-2": true, // Europe (Zurich).
"eu-north-1": true, // Europe (Stockholm).
"eu-south-1": true, // Europe (Milan).
"eu-south-2": true, // Europe (Spain).
"eu-west-1": true, // Europe (Ireland).
"eu-west-2": true, // Europe (London).
"eu-west-3": true, // Europe (Paris).
"il-central-1": true, // Israel (Tel Aviv).
"me-central-1": true, // Middle East (UAE).
"me-south-1": true, // Middle East (Bahrain).
"sa-east-1": true, // South America (Sao Paulo).
"us-east-1": true, // US East (N. Virginia).
"us-east-2": true, // US East (Ohio).
"us-west-1": true, // US West (N. California).
"us-west-2": true, // US West (Oregon).
}
// List of all valid cloud regions for Microsoft Azure
var ValidAzureRegions = map[string]bool{
"australiacentral": true, // Australia Central
"australiacentral2": true, // Australia Central 2
"australiaeast": true, // Australia East
"australiasoutheast": true, // Australia Southeast
"austriaeast": true, // Austria East
"belgiumcentral": true, // Belgium Central
"brazilsouth": true, // Brazil South
"brazilsoutheast": true, // Brazil Southeast
"canadacentral": true, // Canada Central
"canadaeast": true, // Canada East
"centralindia": true, // Central India
"centralus": true, // Central US
"chilecentral": true, // Chile Central
"denmarkeast": true, // Denmark East
"eastasia": true, // East Asia
"eastus": true, // East US
"eastus2": true, // East US 2
"francecentral": true, // France Central
"francesouth": true, // France South
"germanynorth": true, // Germany North
"germanywestcentral": true, // Germany West Central
"indonesiacentral": true, // Indonesia Central
"israelcentral": true, // Israel Central
"italynorth": true, // Italy North
"japaneast": true, // Japan East
"japanwest": true, // Japan West
"koreacentral": true, // Korea Central
"koreasouth": true, // Korea South
"malaysiawest": true, // Malaysia West
"mexicocentral": true, // Mexico Central
"newzealandnorth": true, // New Zealand North
"northcentralus": true, // North Central US
"northeurope": true, // North Europe
"norwayeast": true, // Norway East
"norwaywest": true, // Norway West
"polandcentral": true, // Poland Central
"qatarcentral": true, // Qatar Central
"southafricanorth": true, // South Africa North
"southafricawest": true, // South Africa West
"southcentralus": true, // South Central US
"southindia": true, // South India
"southeastasia": true, // Southeast Asia
"spaincentral": true, // Spain Central
"swedencentral": true, // Sweden Central
"switzerlandnorth": true, // Switzerland North
"switzerlandwest": true, // Switzerland West
"uaecentral": true, // UAE Central
"uaenorth": true, // UAE North
"uksouth": true, // UK South
"ukwest": true, // UK West
"westcentralus": true, // West Central US
"westeurope": true, // West Europe
"westindia": true, // West India
"westus": true, // West US
"westus2": true, // West US 2
"westus3": true, // West US 3
}

View File

@@ -0,0 +1,263 @@
package cloudintegrationtypes
import (
"fmt"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
var S3Sync = valuer.NewString("s3sync")
// Generic interface for cloud service definition.
// This is implemented by AWSDefinition and AzureDefinition, which represent service definitions for AWS and Azure respectively.
// Generics work well so far because service definitions share a similar logic.
// We dont want to over-do generics as well, if the service definitions functionally diverge in the future consider breaking generics.
type Definition interface {
GetId() string
Validate() error
PopulateDashboardURLs(cloudProvider CloudProviderType, svcId string)
GetIngestionStatusCheck() *IngestionStatusCheck
GetAssets() Assets
}
// AWSDefinition represents AWS Service definition, which includes collection strategy, dashboards and meta info for integration
type AWSDefinition = ServiceDefinition[AWSCollectionStrategy]
// AzureDefinition represents Azure Service definition, which includes collection strategy, dashboards and meta info for integration
type AzureDefinition = ServiceDefinition[AzureCollectionStrategy]
// Making AWSDefinition and AzureDefinition satisfy Definition interface, so that they can be used in a generic way
var (
_ Definition = &AWSDefinition{}
_ Definition = &AzureDefinition{}
)
// ServiceDefinition represents generic struct for cloud service, regardless of the cloud provider.
// this struct must satify Definition interface.
// StrategyT is of either AWSCollectionStrategy or AzureCollectionStrategy, depending on the cloud provider.
type ServiceDefinition[StrategyT any] struct {
DefinitionMetadata
Overview string `json:"overview"` // markdown
Assets Assets `json:"assets"`
SupportedSignals SupportedSignals `json:"supported_signals"`
DataCollected DataCollected `json:"data_collected"`
IngestionStatusCheck *IngestionStatusCheck `json:"ingestion_status_check,omitempty"`
Strategy *StrategyT `json:"telemetry_collection_strategy"`
}
// Following methods are quite self explanatory, they are just to satisfy the Definition interface and provide some utility functions for service definitions.
func (def *ServiceDefinition[StrategyT]) GetId() string {
return def.Id
}
func (def *ServiceDefinition[StrategyT]) Validate() error {
seenDashboardIds := map[string]interface{}{}
if def.Strategy == nil {
return errors.NewInternalf(errors.CodeInternal, "telemetry_collection_strategy is required")
}
for _, dd := range def.Assets.Dashboards {
if _, seen := seenDashboardIds[dd.Id]; seen {
return errors.NewInternalf(errors.CodeInternal, "multiple dashboards found with id %s", dd.Id)
}
seenDashboardIds[dd.Id] = nil
}
return nil
}
func (def *ServiceDefinition[StrategyT]) PopulateDashboardURLs(cloudProvider CloudProviderType, svcId string) {
for i := range def.Assets.Dashboards {
dashboardId := def.Assets.Dashboards[i].Id
url := "/dashboard/" + GetCloudIntegrationDashboardID(cloudProvider, svcId, dashboardId)
def.Assets.Dashboards[i].Url = url
}
}
func (def *ServiceDefinition[StrategyT]) GetIngestionStatusCheck() *IngestionStatusCheck {
return def.IngestionStatusCheck
}
func (def *ServiceDefinition[StrategyT]) GetAssets() Assets {
return def.Assets
}
// DefinitionMetadata represents service definition metadata. This is useful for showing service overview
type DefinitionMetadata struct {
Id string `json:"id"`
Title string `json:"title"`
Icon string `json:"icon"`
}
// IngestionStatusCheckCategory represents a category of ingestion status check. Applies for both metrics and logs.
// A category can be "Overview" of metrics or "Enhanced" Metrics for AWS, and "Transaction" or "Capacity" metrics for Azure.
// Each category can have multiple checks (AND logic), if all checks pass,
// then we can be sure that data is being ingested for that category of the signal
type IngestionStatusCheckCategory struct {
Category string `json:"category"`
DisplayName string `json:"display_name"`
Checks []*IngestionStatusCheckAttribute `json:"checks"`
}
// IngestionStatusCheckAttribute represents a check or condition for ingestion status.
// Key can be metric name or part of log message
type IngestionStatusCheckAttribute struct {
Key string `json:"key"` // OPTIONAL search key (metric name or log message)
Attributes []*IngestionStatusCheckAttributeFilter `json:"attributes"`
}
// IngestionStatusCheck represents combined checks for metrics and logs for a service
type IngestionStatusCheck struct {
Metrics []*IngestionStatusCheckCategory `json:"metrics"`
Logs []*IngestionStatusCheckCategory `json:"logs"`
}
// IngestionStatusCheckAttributeFilter represents filter for a check, which can be used to filter specific log messages or metrics with specific attributes.
// For example, we can use it to filter logs with specific log level or metrics with specific dimensions.
type IngestionStatusCheckAttributeFilter struct {
Name string `json:"name"`
Operator string `json:"operator"`
Value string `json:"value"` // OPTIONAL
}
// Assets represents the collection of dashboards
type Assets struct {
Dashboards []Dashboard `json:"dashboards"`
}
// SupportedSignals for cloud provider's service
type SupportedSignals struct {
Logs bool `json:"logs"`
Metrics bool `json:"metrics"`
}
// DataCollected is curated static list of metrics and logs, this is shown as part of service overview
type DataCollected struct {
Logs []CollectedLogAttribute `json:"logs"`
Metrics []CollectedMetric `json:"metrics"`
}
// CollectedLogAttribute represents a log attribute that is present in all log entries for a service,
// this is shown as part of service overview
type CollectedLogAttribute struct {
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"`
}
// CollectedMetric represents a metric that is collected for a service, this is shown as part of service overview
type CollectedMetric struct {
Name string `json:"name"`
Type string `json:"type"`
Unit string `json:"unit"`
Description string `json:"description"`
}
// AWSCollectionStrategy represents signal collection strategy for AWS services.
// this is AWS specific.
type AWSCollectionStrategy struct {
Metrics *AWSMetricsStrategy `json:"aws_metrics,omitempty"`
Logs *AWSLogsStrategy `json:"aws_logs,omitempty"`
S3Buckets map[string][]string `json:"s3_buckets,omitempty"` // Only available in S3 Sync Service Type in AWS
}
// AzureCollectionStrategy represents signal collection strategy for Azure services.
// this is Azure specific.
type AzureCollectionStrategy struct {
Metrics []*AzureMetricsStrategy `json:"azure_metrics,omitempty"`
Logs []*AzureLogsStrategy `json:"azure_logs,omitempty"`
}
// AWSMetricsStrategy represents metrics collection strategy for AWS services.
// this is AWS specific.
type AWSMetricsStrategy struct {
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
StreamFilters []struct {
// json tags here are in the shape expected by AWS API as detailed at
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
Namespace string `json:"Namespace"`
MetricNames []string `json:"MetricNames,omitempty"`
} `json:"cloudwatch_metric_stream_filters"`
}
// AWSLogsStrategy represents logs collection strategy for AWS services.
// this is AWS specific.
type AWSLogsStrategy struct {
Subscriptions []struct {
// subscribe to all logs groups with specified prefix.
// eg: `/aws/rds/`
LogGroupNamePrefix string `json:"log_group_name_prefix"`
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
// "" implies no filtering is required.
FilterPattern string `json:"filter_pattern"`
} `json:"cloudwatch_logs_subscriptions"`
}
// AzureMetricsStrategy represents metrics collection strategy for Azure services.
// this is Azure specific.
type AzureMetricsStrategy struct {
CategoryType string `json:"category_type"`
Name string `json:"name"`
}
// AzureLogsStrategy represents logs collection strategy for Azure services.
// this is Azure specific. Even though this is similar to AzureMetricsStrategy, keeping it separate for future flexibility and clarity.
type AzureLogsStrategy struct {
CategoryType string `json:"category_type"`
Name string `json:"name"`
}
// Dashboard represents a dashboard definition for cloud integration.
type Dashboard struct {
Id string `json:"id"`
Url string `json:"url"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
Definition *dashboardtypes.StorableDashboardData `json:"definition,omitempty"`
}
// UTILS
// GetCloudIntegrationDashboardID returns the dashboard id for a cloud integration, given the cloud provider, service id, and dashboard id.
// This is used to generate unique dashboard ids for cloud integration, and also to parse the dashboard id to get the cloud provider and service id when needed.
func GetCloudIntegrationDashboardID(cloudProvider CloudProviderType, svcId, dashboardId string) string {
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider, svcId, dashboardId)
}
// GetDashboardsFromAssets returns the list of dashboards for the cloud provider service from definition
func GetDashboardsFromAssets(
svcId string,
orgID valuer.UUID,
cloudProvider CloudProviderType,
createdAt *time.Time,
assets Assets,
) []*dashboardtypes.Dashboard {
dashboards := make([]*dashboardtypes.Dashboard, 0)
for _, d := range assets.Dashboards {
author := fmt.Sprintf("%s-integration", cloudProvider)
dashboards = append(dashboards, &dashboardtypes.Dashboard{
ID: GetCloudIntegrationDashboardID(cloudProvider, svcId, d.Id),
Locked: true,
OrgID: orgID,
Data: *d.Definition,
TimeAuditable: types.TimeAuditable{
CreatedAt: *createdAt,
UpdatedAt: *createdAt,
},
UserAuditable: types.UserAuditable{
CreatedBy: author,
UpdatedBy: author,
},
})
}
return dashboards
}

View File

@@ -0,0 +1,42 @@
package cloudintegrationtypes
import (
"context"
"time"
)
type CloudIntegrationAccountStore interface {
ListConnected(ctx context.Context, orgId string, provider string) ([]CloudIntegration, error)
Get(ctx context.Context, orgId string, provider string, id string) (*CloudIntegration, error)
GetConnectedCloudAccount(ctx context.Context, orgId, provider string, accountID string) (*CloudIntegration, error)
// Insert an account or update it by (cloudProvider, id)
// for specified non-empty fields
Upsert(
ctx context.Context,
orgId string,
provider string,
id *string,
config []byte,
accountId *string,
agentReport *AgentReport,
removedAt *time.Time,
) (*CloudIntegration, error)
}
type CloudIntegrationServiceStore interface {
Get(ctx context.Context, orgID, cloudAccountId, serviceType string) ([]byte, error)
Upsert(
ctx context.Context,
orgID,
cloudProvider,
cloudAccountId,
serviceId string,
config []byte,
) ([]byte, error)
GetAllForAccount(ctx context.Context, orgID, cloudAccountId string) (map[string][]byte, error)
}

View File

@@ -104,21 +104,6 @@ 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{}}

View File

@@ -2,7 +2,6 @@ package querybuildertypesv5
import (
"fmt"
"time"
"github.com/SigNoz/signoz/pkg/types/metrictypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
@@ -227,28 +226,3 @@ 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"
}