diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index e78a248ccb..41ec8df235 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.34.0 + image: signoz/query-service:0.34.1 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.34.0 + image: signoz/frontend:0.34.1 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.88.0 + image: signoz/signoz-schema-migrator:0.88.1 deploy: restart_policy: condition: on-failure @@ -250,7 +250,7 @@ services: # - clickhouse-3 otel-collector-metrics: - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 command: [ "--config=/etc/otel-collector-metrics-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index 08e671d3eb..d4f13e4d26 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -81,7 +81,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 command: [ "--config=/etc/otel-collector-config.yaml", @@ -118,7 +118,7 @@ services: otel-collector-metrics: container_name: signoz-otel-collector-metrics - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 command: [ "--config=/etc/otel-collector-metrics-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index b3f82d8e56..862752ab47 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.34.0} + image: signoz/query-service:${DOCKER_TAG:-0.34.1} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.34.0} + image: signoz/frontend:${DOCKER_TAG:-0.34.1} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.1} container_name: signoz-otel-collector command: [ @@ -269,7 +269,7 @@ services: condition: service_healthy otel-collector-metrics: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.1} container_name: signoz-otel-collector-metrics command: [ diff --git a/frontend/public/Logos/fluent-bit.png b/frontend/public/Logos/fluent-bit.png new file mode 100644 index 0000000000..55c4e4ae4a Binary files /dev/null and b/frontend/public/Logos/fluent-bit.png differ diff --git a/frontend/public/Logos/fluentd.png b/frontend/public/Logos/fluentd.png new file mode 100644 index 0000000000..e87fc62999 Binary files /dev/null and b/frontend/public/Logos/fluentd.png differ diff --git a/frontend/public/Logos/logstash.svg b/frontend/public/Logos/logstash.svg new file mode 100644 index 0000000000..cf718cd088 --- /dev/null +++ b/frontend/public/Logos/logstash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/locales/en/pipeline.json b/frontend/public/locales/en/pipeline.json index 3b0f7d8bda..f80f5ec772 100644 --- a/frontend/public/locales/en/pipeline.json +++ b/frontend/public/locales/en/pipeline.json @@ -5,6 +5,7 @@ "create": "Create", "reorder": "Reorder", "cancel": "Cancel", + "learn_more": "Learn more about pipelines", "reorder_pipeline": "Do you want to reorder pipeline?", "reorder_pipeline_description": "Logs are processed sequentially in processors and pipelines. Reordering it may change how data is processed by them.", "delete_pipeline": "Do you want to delete pipeline", diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index deff831a36..2b55907c17 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -7,6 +7,7 @@ import { FeatureKeys } from 'constants/features'; import { LOCALSTORAGE } from 'constants/localStorage'; import ROUTES from 'constants/routes'; import AppLayout from 'container/AppLayout'; +import useAnalytics from 'hooks/analytics/useAnalytics'; import { useThemeConfig } from 'hooks/useDarkMode'; import useGetFeatureFlag from 'hooks/useGetFeatureFlag'; import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense'; @@ -25,7 +26,6 @@ import AppActions from 'types/actions'; import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app'; import AppReducer, { User } from 'types/reducer/app'; import { extractDomain, isCloudUser, isEECloudUser } from 'utils/app'; -import { trackPageView } from 'utils/segmentAnalytics'; import PrivateRoute from './Private'; import defaultRoutes, { AppRoutes, SUPPORT_ROUTE } from './routes'; @@ -41,6 +41,8 @@ function App(): JSX.Element { const dispatch = useDispatch>(); + const { trackPageView } = useAnalytics(); + const { hostname, pathname } = window.location; const isCloudUserVal = isCloudUser(); @@ -156,6 +158,7 @@ function App(): JSX.Element { useEffect(() => { trackPageView(pathname); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname]); return ( diff --git a/frontend/src/components/ExplorerCard/ExplorerCard.tsx b/frontend/src/components/ExplorerCard/ExplorerCard.tsx index b49cfaa2d6..b55f019bc1 100644 --- a/frontend/src/components/ExplorerCard/ExplorerCard.tsx +++ b/frontend/src/components/ExplorerCard/ExplorerCard.tsx @@ -220,7 +220,11 @@ function ExplorerCard({ open={isOpen} onOpenChange={handleOpenChange} > - diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx index fb0001c23a..9c3effe973 100644 --- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx +++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx @@ -7,8 +7,8 @@ import { Time } from 'container/TopNav/DateTimeSelection/config'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; -import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import { useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx index 258c73eaab..c395c39bd1 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx @@ -15,8 +15,8 @@ import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useChartMutable } from 'hooks/useChartMutable'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; -import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import { useDashboard } from 'providers/Dashboard/Dashboard'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index ec72477ad2..fe8caf6971 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -5,8 +5,8 @@ import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import { useIntersectionObserver } from 'hooks/useIntersectionObserver'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; -import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import isEmpty from 'lodash-es/isEmpty'; import _noop from 'lodash-es/noop'; import { memo, useCallback, useMemo, useRef, useState } from 'react'; @@ -28,6 +28,7 @@ function GridCardGraph({ isQueryEnabled, threshold, variables, + filterNaN, }: GridCardGraphProps): JSX.Element { const dispatch = useDispatch(); const [errorMessage, setErrorMessage] = useState(); @@ -89,7 +90,11 @@ function GridCardGraph({ const containerDimensions = useResizeObserver(graphRef); - const chartData = getUPlotChartData(queryResponse?.data?.payload); + const chartData = getUPlotChartData( + queryResponse?.data?.payload, + undefined, + filterNaN, + ); const isDarkMode = useIsDarkMode(); diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts index ca68ea8540..155c8208a2 100644 --- a/frontend/src/container/GridCardLayout/GridCard/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/types.ts @@ -39,6 +39,7 @@ export interface GridCardGraphProps { headerMenuList?: WidgetGraphComponentProps['headerMenuList']; isQueryEnabled: boolean; variables?: Dashboard['data']['variables']; + filterNaN?: boolean; } export interface GetGraphVisibilityStateOnLegendClickProps { diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index a6b1186880..e3a346ef82 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -118,7 +118,11 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { )} {addPanelPermission && ( - )} diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index 9d48113910..a1a588b85a 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -203,7 +203,7 @@ function WidgetHeader({ onClick={onClickHandler} > - + {title} diff --git a/frontend/src/container/Header/index.tsx b/frontend/src/container/Header/index.tsx index c7eb709452..ff4c560c67 100644 --- a/frontend/src/container/Header/index.tsx +++ b/frontend/src/container/Header/index.tsx @@ -135,14 +135,13 @@ function HeaderContainer(): JSX.Element { <> {showTrialExpiryBanner && (
- You are in free trial period. Your free trial will end on{' '} + You are in free trial period. Your free trial will end on {getFormattedDate(licenseData?.payload?.trialEnd || Date.now())}. {role === 'ADMIN' ? ( - {' '} - Please{' '} + Please diff --git a/frontend/src/container/Header/styles.ts b/frontend/src/container/Header/styles.ts index 31f3648da8..e856e7ba0e 100644 --- a/frontend/src/container/Header/styles.ts +++ b/frontend/src/container/Header/styles.ts @@ -3,6 +3,8 @@ import styled from 'styled-components'; export const Header = styled(Layout.Header)` background: #1f1f1f !important; + padding-left: 16px; + padding-right: 16px; `; export const Container = styled.div` diff --git a/frontend/src/container/Licenses/index.tsx b/frontend/src/container/Licenses/index.tsx index 351d78a636..cb35c0b5ec 100644 --- a/frontend/src/container/Licenses/index.tsx +++ b/frontend/src/container/Licenses/index.tsx @@ -7,14 +7,14 @@ import ApplyLicenseForm from './ApplyLicenseForm'; import ListLicenses from './ListLicenses'; function Licenses(): JSX.Element { - const { t } = useTranslation(['licenses']); + const { t, ready: translationsReady } = useTranslation(['licenses']); const { data, isError, isLoading, refetch } = useLicense(); if (isError || data?.error) { return {data?.error}; } - if (isLoading || data?.payload === undefined) { + if (isLoading || data?.payload === undefined || !translationsReady) { return ; } diff --git a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx index b6d51445f8..f6ee603cc2 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx @@ -1,5 +1,5 @@ import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; -import { Modal, Tooltip } from 'antd'; +import { Modal, Tooltip, Typography } from 'antd'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { useDeleteDashboard } from 'hooks/dashboard/useDeleteDashboard'; import { useCallback } from 'react'; @@ -10,10 +10,22 @@ import { AppState } from 'store/reducers'; import AppReducer from 'types/reducer/app'; import { USER_ROLES } from 'types/roles'; -import { Data } from '../index'; +import { Data } from '..'; import { TableLinkText } from './styles'; -function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element { +interface DeleteButtonProps { + createdBy: string; + name: string; + id: string; + isLocked: boolean; +} + +function DeleteButton({ + createdBy, + name, + id, + isLocked, +}: DeleteButtonProps): JSX.Element { const [modal, contextHolder] = Modal.useModal(); const { role, user } = useSelector((state) => state.app); const isAuthor = user?.email === createdBy; @@ -26,7 +38,13 @@ function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element { const openConfirmationDialog = useCallback((): void => { modal.confirm({ - title: 'Do you really want to delete this dashboard?', + title: ( + + Are you sure you want to delete the + {name} + dashboard? + + ), icon: , onOk() { deleteDashboardMutation.mutateAsync(undefined, { @@ -39,7 +57,7 @@ function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element { okButtonProps: { danger: true }, centered: true, }); - }, [modal, deleteDashboardMutation, queryClient]); + }, [modal, name, deleteDashboardMutation, queryClient]); const getDeleteTooltipContent = (): string => { if (isLocked) { diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx index a310c5be63..dce9b5835d 100644 --- a/frontend/src/container/ListOfDashboard/index.tsx +++ b/frontend/src/container/ListOfDashboard/index.tsx @@ -1,11 +1,12 @@ import { PlusOutlined } from '@ant-design/icons'; import { Card, + Col, Dropdown, + Input, MenuProps, Row, TableColumnProps, - Typography, } from 'antd'; import { ItemType } from 'antd/es/menu/hooks/useItems'; import createDashboard from 'api/dashboard/create'; @@ -18,9 +19,9 @@ import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable'; import LabelColumn from 'components/TableRenderer/LabelColumn'; import TextToolTip from 'components/TextToolTip'; import ROUTES from 'constants/routes'; -import SearchFilter from 'container/ListOfDashboard/SearchFilter'; import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard'; import useComponentPermission from 'hooks/useComponentPermission'; +import useDebouncedFn from 'hooks/useDebouncedFunction'; import history from 'lib/history'; import { Key, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -38,6 +39,8 @@ import DeleteButton from './TableComponents/DeleteButton'; import Name from './TableComponents/Name'; function ListOfAllDashboard(): JSX.Element { + const { Search } = Input; + const { data: dashboardListResponse = [], isLoading: isDashboardListLoading, @@ -59,12 +62,21 @@ function ListOfAllDashboard(): JSX.Element { ] = useState(false); const [uploadedGrafana, setUploadedGrafana] = useState(false); + const [isFilteringDashboards, setIsFilteringDashboards] = useState(false); - const [filteredDashboards, setFilteredDashboards] = useState(); + const [dashboards, setDashboards] = useState(); + + const sortDashboardsByCreatedAt = (dashboards: Dashboard[]): void => { + const sortedDashboards = dashboards.sort( + (a, b) => + new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), + ); + setDashboards(sortedDashboards); + }; useEffect(() => { if (dashboardListResponse.length) { - setFilteredDashboards(dashboardListResponse); + sortDashboardsByCreatedAt(dashboardListResponse); } }, [dashboardListResponse]); @@ -150,7 +162,7 @@ function ListOfAllDashboard(): JSX.Element { }, [action]); const data: Data[] = - filteredDashboards?.map((e) => ({ + dashboards?.map((e) => ({ createdAt: e.created_at, description: e.data.description || '', id: e.uuid, @@ -255,41 +267,85 @@ function ListOfAllDashboard(): JSX.Element { [getMenuItems], ); + const searchArrayOfObjects = (searchValue: string): any[] => { + // Convert the searchValue to lowercase for case-insensitive search + const searchValueLowerCase = searchValue.toLowerCase(); + + // Use the filter method to find matching objects + return dashboardListResponse.filter((item: any) => { + // Convert each property value to lowercase for case-insensitive search + const itemValues = Object.values(item?.data).map((value: any) => + value.toString().toLowerCase(), + ); + + // Check if any property value contains the searchValue + return itemValues.some((value) => value.includes(searchValueLowerCase)); + }); + }; + + const handleSearch = useDebouncedFn((event: unknown): void => { + setIsFilteringDashboards(true); + const searchText = (event as React.BaseSyntheticEvent)?.target?.value || ''; + const filteredDashboards = searchArrayOfObjects(searchText); + setDashboards(filteredDashboards); + setIsFilteringDashboards(false); + }, 500); + const GetHeader = useMemo( () => ( - - Dashboard List - - - + + - {newDashboard && ( - - } - type="primary" - loading={newDashboardState.loading} - danger={newDashboardState.error} + + + + + + {newDashboard && ( + - {getText()} - - - )} - + } + type="primary" + data-testid="create-new-dashboard" + loading={newDashboardState.loading} + danger={newDashboardState.error} + > + {getText()} + + + )} + + ), [ - newDashboard, + Search, isDashboardListLoading, + handleSearch, + isFilteringDashboards, + newDashboard, menu, newDashboardState.loading, newDashboardState.error, @@ -301,13 +357,6 @@ function ListOfAllDashboard(): JSX.Element { {GetHeader} - {!isDashboardListLoading && ( - - )} - { @@ -140,6 +141,7 @@ function DBCall(): JSX.Element { { onGraphClickHandler(setSelectedTimeStamp)( xValue, diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx index ac4ae1e03b..f3578e8be2 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx @@ -84,6 +84,7 @@ function ApDexMetrics({ return ( diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx index 1dbbf2ee98..0d87ba5356 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx @@ -27,6 +27,7 @@ function TopLevelOperation({ ) : ( - - + {isDashboardLocked && (   @@ -60,7 +64,12 @@ function DashboardDescription(): JSX.Element { {title} {description && ( - {description} + + {description} + )} {tags && ( diff --git a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx index ad47526cf8..16c98bb54e 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx @@ -63,6 +63,7 @@ function GeneralDashboardSettings(): JSX.Element {
Name setUpdatedTitle(e.target.value)} /> @@ -71,6 +72,7 @@ function GeneralDashboardSettings(): JSX.Element {
Description setUpdatedDescription(e.target.value)} @@ -88,6 +90,7 @@ function GeneralDashboardSettings(): JSX.Element { disabled={updateDashboardMutation.isLoading} loading={updateDashboardMutation.isLoading} icon={} + data-testid="save-dashboard-config" onClick={onSaveHandler} type="primary" > diff --git a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx index 388dffc286..754e44f1bc 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx @@ -181,6 +181,7 @@ function VariablesSetting(): JSX.Element { <> )} diff --git a/frontend/src/container/OnboardingContainer/APM/APM.styles.scss b/frontend/src/container/OnboardingContainer/APM/APM.styles.scss deleted file mode 100644 index 49d2380a07..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/APM.styles.scss +++ /dev/null @@ -1,144 +0,0 @@ -.apm-module-container { - padding: 48px 0; - - .header { - h1 { - font-size: 24px; - font-weight: 500; - } - - h4 { - font-size: 14px; - font-weight: 300; - } - } -} - -.supported-languages-container { - display: flex; - gap: 24px; -} - -.supported-language { - display: flex; - justify-content: center; - align-items: center; - width: 300px; - height: 120px; - background: #1d1d1d; - border: 1px solid #424242; - border-radius: 3px; - color: #424242; - cursor: pointer; - - &.selected { - background-color: #111a2c; - border: 0.5px solid #3c89e8; - } -} - -.supported-langauge-img { - height: 48px; -} - -.selected-langauage-setup-instructions { - padding: 24px 0; -} - -div[class*='-setup-instructions-container'] { - .header { - display: flex; - align-items: center; - - margin: 16px 0; - - img { - height: 40px; - } - - h1 { - font-size: 18px; - display: flex; - align-items: center; - color: #e5e7eb; - gap: 16px; - margin: 12px; - } - } -} - -.label { - font-size: 14px; - margin-bottom: 8px; - font-weight: 300; -} - -pre { - background-color: #292d3e; - padding: 8px; - overflow: auto; - border-radius: 3px; - - code { - overflow: auto; - text-wrap: wrap; - } -} - -.content-container { - padding: 24px; - margin: 16px 0; - background: rgba(29, 29, 29, 1); - line-height: 20px; -} - -.detailed-docs-link { - display: flex; - margin: 12px; - font-size: 12px; - - a { - padding-left: 4px; - } -} - -.form-container { - display: flex; - align-items: flex-start; - width: 100%; - gap: 16px; - - & .ant-form-item { - margin-bottom: 0px; - } -} - -$lightModeFontColor: rgb(29, 29, 29); - -.lightMode { - .apm-module-container { - .header { - color: $lightModeFontColor; - } - - div[class*='-setup-instructions-container'] { - .header { - h1 { - color: $lightModeFontColor; - } - } - - .framework-selector { - .label { - color: $lightModeFontColor; - } - } - - .service-name-container { - .label { - color: $lightModeFontColor; - } - } - } - } -} diff --git a/frontend/src/container/OnboardingContainer/APM/APM.tsx b/frontend/src/container/OnboardingContainer/APM/APM.tsx deleted file mode 100644 index ef81752345..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/APM.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -import './APM.styles.scss'; - -import getIngestionData from 'api/settings/getIngestionData'; -import cx from 'classnames'; -import { useEffect, useState } from 'react'; -import { useQuery } from 'react-query'; -import { trackEvent } from 'utils/segmentAnalytics'; - -import GoLang from './GoLang/GoLang'; -import Java from './Java/Java'; -import Javascript from './Javascript/Javascript'; -import Python from './Python/Python'; -import RoR from './RubyOnRails/ROR'; - -interface IngestionInfoProps { - SIGNOZ_INGESTION_KEY?: string; - REGION?: string; -} -export interface LangProps { - ingestionInfo: IngestionInfoProps; - activeStep: number; -} - -const supportedLanguages = [ - { - name: 'java', - imgURL: `Logos/java.png`, - }, - { - name: 'python', - imgURL: `Logos/java.png`, - }, - { - name: 'javascript', - imgURL: `Logos/java.png`, - }, - { - name: 'go', - imgURL: `Logos/java.png`, - }, - { - name: 'rails', - imgURL: `Logos/rails.png`, - }, -]; - -export default function APM({ - activeStep, -}: { - activeStep: number; -}): JSX.Element { - const [selectedLanguage, setSelectedLanguage] = useState('java'); - - const [ingestionInfo, setIngestionInfo] = useState({}); - - const { status, data: ingestionData } = useQuery({ - queryFn: () => getIngestionData(), - }); - - useEffect(() => { - if ( - status === 'success' && - ingestionData.payload && - Array.isArray(ingestionData.payload) - ) { - const payload = ingestionData.payload[0] || { - ingestionKey: '', - dataRegion: '', - }; - - setIngestionInfo({ - SIGNOZ_INGESTION_KEY: payload?.ingestionKey, - REGION: payload?.dataRegion, - }); - } - }, [status, ingestionData?.payload]); - - useEffect(() => { - // on language select - trackEvent('Onboarding: APM', { - selectedLanguage, - activeStep, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedLanguage]); - - const renderSelectedLanguageSetupInstructions = (): JSX.Element => { - switch (selectedLanguage) { - case 'java': - return ; - case 'python': - return ; - case 'javascript': - return ; - case 'go': - return ; - case 'rails': - return ; - default: - return <> ; - } - }; - - return ( -
- {activeStep === 2 && ( - <> -
-

- Get Started to instrument your applications and sending data to SigNoz -

-

Select the data source

-
- -
- {supportedLanguages.map((supportedLanguage) => ( -
setSelectedLanguage(supportedLanguage.name)} - > - -
- ))} -
- - )} - - {selectedLanguage && ( -
- {renderSelectedLanguageSetupInstructions()} -
- )} -
- ); -} diff --git a/frontend/src/container/OnboardingContainer/APM/GoLang/GoLang.styles.scss b/frontend/src/container/OnboardingContainer/APM/GoLang/GoLang.styles.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/container/OnboardingContainer/APM/GoLang/GoLang.tsx b/frontend/src/container/OnboardingContainer/APM/GoLang/GoLang.tsx deleted file mode 100644 index f65a4f41fd..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/GoLang/GoLang.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import './GoLang.styles.scss'; - -import { Form, Input } from 'antd'; -import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; - -import { LangProps } from '../APM'; -import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; -import GoLangDocs from './goLang.md'; - -export default function GoLang({ - ingestionInfo, - activeStep, -}: LangProps): JSX.Element { - const [form] = Form.useForm(); - const serviceName = Form.useWatch('Service Name', form); - - const variables = { - MYAPP: serviceName || '', - SIGNOZ_INGESTION_KEY: - ingestionInfo.SIGNOZ_INGESTION_KEY || '', - REGION: ingestionInfo.REGION || 'region', - }; - - return ( - <> - {activeStep === 2 && ( -
-
- -
-
-
Service Name
- -
- - - -
-
-
- -
- -
-
- )} - {activeStep === 3 && ( - - )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/APM/Java/Java.styles.scss b/frontend/src/container/OnboardingContainer/APM/Java/Java.styles.scss deleted file mode 100644 index 5a6fc3baaf..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/Java/Java.styles.scss +++ /dev/null @@ -1,10 +0,0 @@ -.form-container { - display: flex; - align-items: flex-start; - width: 100%; - gap: 16px; - - & .ant-form-item { - margin-bottom: 0px; - } -} diff --git a/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx b/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx deleted file mode 100644 index ed256410b8..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import './Java.styles.scss'; - -import { Form, Input, Select } from 'antd'; -import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useEffect, useState } from 'react'; -import { trackEvent } from 'utils/segmentAnalytics'; -import { popupContainer } from 'utils/selectPopupContainer'; - -import { LangProps } from '../APM'; -import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; -import JavaDocs from './md-docs/java.md'; -import JbossDocs from './md-docs/jboss.md'; -import SprintBootDocs from './md-docs/spring_boot.md'; -import TomcatDocs from './md-docs/tomcat.md'; - -enum FrameworksMap { - tomcat = 'Tomcat', - spring_boot = 'Spring Boot', - jboss = 'JBoss', - other = 'Others', -} - -export default function Java({ - ingestionInfo, - activeStep, -}: LangProps): JSX.Element { - const [selectedFrameWork, setSelectedFrameWork] = useState('spring_boot'); - const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState( - SprintBootDocs, - ); - - const [form] = Form.useForm(); - const serviceName = Form.useWatch('Service Name', form); - - useEffect(() => { - // on language select - trackEvent('Onboarding: APM : Java', { - selectedFrameWork, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedFrameWork]); - - const handleFrameworkChange = (selectedFrameWork: string): void => { - setSelectedFrameWork(selectedFrameWork); - - switch (selectedFrameWork) { - case 'tomcat': - setSelectedFrameWorkDocs(TomcatDocs); - break; - case 'spring_boot': - setSelectedFrameWorkDocs(SprintBootDocs); - break; - case 'jboss': - setSelectedFrameWorkDocs(JbossDocs); - break; - default: - setSelectedFrameWorkDocs(JavaDocs); - break; - } - }; - - const variables = { - MYAPP: serviceName || '', - SIGNOZ_INGESTION_KEY: - ingestionInfo.SIGNOZ_INGESTION_KEY || '', - REGION: ingestionInfo.REGION || 'region', - }; - - return ( - <> - {activeStep === 2 && ( -
-
- -
-
-
Select Framework
- - - - -
-
- -
- -
-
- )} - {activeStep === 3 && ( - - )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.styles.scss b/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.styles.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx b/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx deleted file mode 100644 index 466fd72db7..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import './Javascript.styles.scss'; - -import { Form, Input, Select } from 'antd'; -import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useEffect, useState } from 'react'; -import { trackEvent } from 'utils/segmentAnalytics'; -import { popupContainer } from 'utils/selectPopupContainer'; - -import { LangProps } from '../APM'; -import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; -import ExpressDocs from './md-docs/express.md'; -import JavascriptDocs from './md-docs/javascript.md'; -import NestJsDocs from './md-docs/nestjs.md'; - -const frameworksMap = { - express: 'Express', - nestjs: 'Nest JS', - nodejs: 'Nodejs', -}; - -export default function Javascript({ - ingestionInfo, - activeStep, -}: LangProps): JSX.Element { - const [selectedFrameWork, setSelectedFrameWork] = useState('express'); - const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState( - ExpressDocs, - ); - const [form] = Form.useForm(); - const serviceName = Form.useWatch('Service Name', form); - - const variables = { - MYAPP: serviceName || '', - SIGNOZ_INGESTION_KEY: - ingestionInfo.SIGNOZ_INGESTION_KEY || '', - REGION: ingestionInfo.REGION || 'region', - }; - - useEffect(() => { - // on language select - trackEvent('Onboarding: APM : Javascript', { - selectedFrameWork, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedFrameWork]); - - const handleFrameworkChange = (selectedFrameWork: string): void => { - setSelectedFrameWork(selectedFrameWork); - - switch (selectedFrameWork) { - case 'nodejs': - setSelectedFrameWorkDocs(JavascriptDocs); - break; - case 'nestjs': - setSelectedFrameWorkDocs(NestJsDocs); - break; - default: - setSelectedFrameWorkDocs(ExpressDocs); - break; - } - }; - - return ( - <> - {activeStep === 2 && ( -
-
- -
-
-
Select Framework
- - - - -
-
- -
- -
-
- )} - {activeStep === 3 && ( - - )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/APM/Python/Python.styles.scss b/frontend/src/container/OnboardingContainer/APM/Python/Python.styles.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx b/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx deleted file mode 100644 index 9f70406e68..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import './Python.styles.scss'; - -import { Form, Input, Select } from 'antd'; -import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useEffect, useState } from 'react'; -import { trackEvent } from 'utils/segmentAnalytics'; -import { popupContainer } from 'utils/selectPopupContainer'; - -import { LangProps } from '../APM'; -import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; -import DjangoDocs from './md-docs/django.md'; -import FalconDocs from './md-docs/falcon.md'; -import FastAPIDocs from './md-docs/fastAPI.md'; -import FlaskDocs from './md-docs/flask.md'; -import PythonDocs from './md-docs/python.md'; - -const frameworksMap = { - django: 'Django', - fastAPI: 'Fast API', - flask: 'Flask', - falcon: 'Falcon', - other: 'Others', -}; - -export default function Python({ - ingestionInfo, - activeStep, -}: LangProps): JSX.Element { - const [selectedFrameWork, setSelectedFrameWork] = useState('django'); - const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(DjangoDocs); - const [form] = Form.useForm(); - const serviceName = Form.useWatch('Service Name', form); - - const variables = { - MYAPP: serviceName || '', - SIGNOZ_INGESTION_KEY: - ingestionInfo.SIGNOZ_INGESTION_KEY || '', - REGION: ingestionInfo.REGION || 'region', - }; - - useEffect(() => { - // on language select - trackEvent('Onboarding: APM : Python', { - selectedFrameWork, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedFrameWork]); - - const handleFrameworkChange = (selectedFrameWork: string): void => { - setSelectedFrameWork(selectedFrameWork); - - switch (selectedFrameWork) { - case 'django': - setSelectedFrameWorkDocs(DjangoDocs); - break; - case 'fastAPI': - setSelectedFrameWorkDocs(FastAPIDocs); - break; - case 'flask': - setSelectedFrameWorkDocs(FlaskDocs); - break; - case 'falcon': - setSelectedFrameWorkDocs(FalconDocs); - break; - default: - setSelectedFrameWorkDocs(PythonDocs); - break; - } - }; - - return ( - <> - {activeStep === 2 && ( -
-
- -
-
-
Select Framework
- - - - -
-
- -
- -
-
- )} - {activeStep === 3 && ( - - )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.styles.scss b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.styles.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx deleted file mode 100644 index c19dbf34db..0000000000 --- a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import './ROR.styles.scss'; - -import { Form, Input } from 'antd'; -import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; - -import { LangProps } from '../APM'; -import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; -import RORDocs from './RubyOnRails.md'; - -export default function RoR({ - ingestionInfo, - activeStep, -}: LangProps): JSX.Element { - const [form] = Form.useForm(); - const serviceName = Form.useWatch('Service Name', form); - - const variables = { - MYAPP: serviceName || '', - SIGNOZ_INGESTION_KEY: - ingestionInfo.SIGNOZ_INGESTION_KEY || '', - REGION: ingestionInfo.REGION || 'region', - }; - - return ( - <> - {activeStep === 2 && ( -
-
- -
-
-
Service Name
- -
- - - -
-
-
- -
- -
-
- )} - {activeStep === 3 && ( - - )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.styles.scss b/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.styles.scss deleted file mode 100644 index fbd2eb0bda..0000000000 --- a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.styles.scss +++ /dev/null @@ -1,45 +0,0 @@ -.infrastructure-monitoring-module-container { - padding: 48px 0; - - .module-header { - h1 { - font-size: 24px; - font-weight: 500; - } - - h4 { - font-size: 14px; - font-weight: 300; - } - } - - .content-container { - .heading { - .title { - a { - color: rgb(232, 112, 64); - } - } - } - - .subheading { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 16px; - - .recevier-types { - display: flex; - flex-direction: column; - } - } - } - - .header { - display: flex; - gap: 16px; - justify-content: flex-start; - align-items: center; - margin: 16px 0; - } -} diff --git a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.tsx b/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.tsx deleted file mode 100644 index 998b6fa6e4..0000000000 --- a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/InfrastructureMonitoring.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -import './InfrastructureMonitoring.styles.scss'; - -import cx from 'classnames'; -import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer'; -import { useEffect, useState } from 'react'; -import ReactMarkdown from 'react-markdown'; -import { trackEvent } from 'utils/segmentAnalytics'; - -import Header from '../common/Header/Header'; -import hostMetricsMonitoring from './md-docs/hostMetricsMonitoring.md'; -import k8sInfraMonitoringDocs from './md-docs/kubernetesInfraMonitoring.md'; -import otherMetrics from './md-docs/otherMetrics.md'; - -export default function InfrastructureMonitoring({ - activeStep, -}: { - activeStep: number; -}): JSX.Element { - const [selectedInfraMetrics, setSelectedInfraMetrics] = useState('kubernetes'); - const [selectedInfraMetricsDocs, setSelectedInfraMetricsDocs] = useState( - k8sInfraMonitoringDocs, - ); - - useEffect(() => { - // on metrics Type select - trackEvent('Onboarding: APM : Java', { - selectedInfraMetrics, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedInfraMetrics]); - - const supportedInfraMetrics = [ - { - name: 'Kubernetes Infra Metrics', - id: 'kubernetes', - imgURL: `Logos/kubernetes.svg`, - }, - { - name: 'HostMetrics', - id: 'hostMetrics', - imgURL: `Logos/software-window.svg`, - }, - { - name: 'Other Metrics', - id: 'otherMetrics', - imgURL: `Logos/cmd-terminal.svg`, - }, - ]; - - const handleMetricsTypeChange = (selectedMetricsType: string): void => { - setSelectedInfraMetrics(selectedMetricsType); - - switch (selectedMetricsType) { - case 'kubernetes': - setSelectedInfraMetricsDocs(k8sInfraMonitoringDocs); - break; - case 'hostMetrics': - setSelectedInfraMetricsDocs(hostMetricsMonitoring); - break; - case 'otherMetrics': - setSelectedInfraMetricsDocs(otherMetrics); - break; - default: - setSelectedInfraMetricsDocs(otherMetrics); - break; - } - }; - - const getHeaderBasedOnType = (): JSX.Element => { - switch (selectedInfraMetrics) { - case 'hostMetrics': - return ( -
- ); - - case 'otherMetrics': - return ( -
- ); - - default: - return ( -
- ); - } - }; - - return ( -
- {activeStep === 2 && ( - <> -
-

Select an Infra Metrics type

- {/*

Choose the logs that you want to receive on SigNoz

*/} -
- -
- {supportedInfraMetrics.map((logType) => ( -
handleMetricsTypeChange(logType.id)} - > - - -
{logType.name}
-
- ))} -
- - {getHeaderBasedOnType()} - -
- - {selectedInfraMetricsDocs} - -
- - )} -
- ); -} diff --git a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/hostMetricsMonitoring.md b/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/hostMetricsMonitoring.md deleted file mode 100644 index fa483a3e70..0000000000 --- a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/hostMetricsMonitoring.md +++ /dev/null @@ -1,10 +0,0 @@ -## Hostmetrics Monitoring -You can collect Hostmetrics from your VM and send it to SigNoz cloud using OpenTelemetry Collector. - -Steps to send hostmetrics to SigNoz Cloud: - -- Install OpenTelemetry Collector binary agent. Please find instructions [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/#setup-otel-collector-as-agent). - -- Import Hostmetrics Dashboard in SigNoz. Please find instructions [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/#hostmetrics-dashboard). - -Learn how to create dashboards and panels [here](https://signoz.io/docs/userguide/manage-dashboards-and-panels/). diff --git a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/kubernetesInfraMonitoring.md b/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/kubernetesInfraMonitoring.md deleted file mode 100644 index c74ac6e5d0..0000000000 --- a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/kubernetesInfraMonitoring.md +++ /dev/null @@ -1,9 +0,0 @@ -## Kubernetes Infra Metrics - -You can collect Kubernetes infra metrics from your k8s cluster and send it to SigNoz cloud using k8s-infra chart. - -Steps to send kubernetes infra metrics to SigNoz Cloud: - -- Install OpenTelemetry Collectors in your k8s infra. Please find instructions [here](https://signoz.io/docs/tutorial/kubernetes-infra-metrics/). - -- Plot metrics in SigNoz UI by following the instructions [here](https://signoz.io/docs/tutorial/kubernetes-infra-metrics/#plot-metrics-in-signoz-ui). \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/otherMetrics.md b/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/otherMetrics.md deleted file mode 100644 index adabc59050..0000000000 --- a/frontend/src/container/OnboardingContainer/InfrastructureMonitoring/md-docs/otherMetrics.md +++ /dev/null @@ -1,195 +0,0 @@ -## Send metrics from any third-party integrations - -This document helps you to send metrics from any third-party integrations such as RabbitMQ, Nginx, MySQL, etc. - -There are two ways in which you can send metrics to SigNoz using OpenTelemetry: - -- From your application -- From OpenTelemetry Collector - -In this document, we will cover how to send metrics from OpenTelemetry Collector. The Collector is a swiss-army knife that can collect metrics from various sources and send them to SigNoz. - -- Enable a Specific Metric Receiver -- Enable a Prometheus Receiver - -## Enable a Specific Metric Receiver - -SigNoz supports all the receivers that are listed in the [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver) GitHub repository. To configure a new metric receiver, you must edit the `receivers` and `service::pipelines` sections of the `otel-collector-config.yaml` file. The following example shows the default configuration in which the `hostmetrics` receiver is enabled: - -```yaml {8-20,52} -receivers: - otlp: - protocols: - grpc: - endpoint: localhost:4317 - http: - endpoint: localhost:4318 - hostmetrics: - collection_interval: 30s - scrapers: - cpu: {} - disk: {} - load: {} - filesystem: {} - memory: {} - network: {} - paging: {} - process: - mute_process_name_error: true - processes: {} -processors: - batch: - send_batch_size: 1000 - timeout: 10s - # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md - resourcedetection: - detectors: [env, system, ec2] # include ec2 for AWS, gce for GCP and azure for Azure. - # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. - timeout: 2s - override: false - system: - hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback -exporters: - otlp: - endpoint: 'ingest.{region}.signoz.cloud:443' # replace {region} with your region - tls: - insecure: false - headers: - 'signoz-access-token': '' - logging: - loglevel: debug -service: - telemetry: - metrics: - address: localhost:8888 - pipelines: - metrics: - receivers: [otlp] - processors: [batch] - exporters: [otlp] - metrics/hostmetrics: - receivers: [hostmetrics] - processors: [resourcedetection, batch] - exporters: [otlp] -``` - -Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table. - -US - ingest.us.signoz.cloud:443 - -IN - ingest.in.signoz.cloud:443 - -EU - ingest.eu.signoz.cloud:443 - -To enable a new OpenTelemetry receiver, follow the steps below: - -1. Open the `otel-collector-config.yaml` file in a plain-text editor. -2. Configure your receivers. The following example shows how you can enable a `rabbitmq` receiver: - -```yaml {21-25,53} -receivers: - otlp: - protocols: - grpc: - endpoint: localhost:4317 - http: - endpoint: localhost:4318 - hostmetrics: - collection_interval: 30s - scrapers: - cpu: {} - disk: {} - load: {} - filesystem: {} - memory: {} - network: {} - paging: {} - process: - mute_process_name_error: true - processes: {} - rabbitmq: - endpoint: http://localhost:15672 - username: - password: - collection_interval: 10s -processors: - batch: - send_batch_size: 1000 - timeout: 10s - # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md - resourcedetection: - detectors: [env, system, ec2] # include ec2 for AWS, gce for GCP and azure for Azure. - # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. - timeout: 2s - override: false - system: - hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback -exporters: - otlp: - endpoint: 'ingest.{region}.signoz.cloud:443' # replace {region} with your region - tls: - insecure: false - headers: - 'signoz-access-token': '' - logging: - loglevel: debug -service: - telemetry: - metrics: - address: localhost:8888 - pipelines: - metrics: - receivers: [otlp, rabbitmq] - processors: [batch] - exporters: [otlp] - metrics/hostmetrics: - receivers: [hostmetrics] - processors: [resourcedetection, batch] - exporters: [otlp] -``` - -For details about configuring OpenTelemetry receivers, see the [README](https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/README.md) page of the `opentelemetry-collector` GitHub repository. - -## Enable a Prometheus Receiver - -SigNoz supports all the exporters that are listed on the [Exporters and Integrations](https://prometheus.io/docs/instrumenting/exporters/) page of the Prometheus documentation. If you have a running Prometheus instance, and you expose metrics in Prometheus, then you can scrape them in SigNoz by configuring Prometheus receivers in the `receivers::prometheus::config::scrape_configs` section of the `otel-collector-config.yaml` file. - -To enable a Prometheus receiver, follow the steps below: - -1. Open the `otel-collector-config.yaml` file in a plain-text editor. -2. Enable a new Prometheus receiver. Depending on your use case, there are two ways in which you can enable a new Prometheus exporter: - - **By creating a new job**: The following example shows how you can enable a Prometheus receiver by creating a new job named `my-new-job`: - ```yaml {10-13} - ... - # Data sources: metrics - prometheus: - config: - scrape_configs: - - job_name: "otel-collector" - scrape_interval: 30s - static_configs: - - targets: ["otel-collector:8889"] - - job_name: "my-new-job" - scrape_interval: 30s - static_configs: - - targets: ["localhost:8080"] - ... - # This file was truncated for brevity. - ``` - - **By adding a new target to an existing job**: The following example shows the default `otel-collector` job to which a new target (`localhost:8080`) was added: - ```yaml {9} - ... - # Data sources: metrics - prometheus: - config: - scrape_configs: - - job_name: "otel-collector" - scrape_interval: 30s - static_configs: - - targets: ["otel-collector:8889", "localhost:8080"] - ... - # This file was truncated for brevity. - ``` - Note that all the jobs are scraped in parallel, and all targets inside a job are scraped serially. For more details about configuring jobs and targets, see the following sections of the Prometheus documentation: - - [](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config) - - [Jobs and Instances](https://prometheus.io/docs/concepts/jobs_instances/) diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/ApplicationLogs/ApplicationLogs.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/ApplicationLogs/ApplicationLogs.tsx deleted file mode 100644 index 3341dadaff..0000000000 --- a/frontend/src/container/OnboardingContainer/LogsManagement/ApplicationLogs/ApplicationLogs.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import ReactMarkdown from 'react-markdown'; - -import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus'; -import LogsFromLogFile from './applicationLogsFromLogFile.md'; - -interface ApplicationLogsProps { - type: string; - activeStep: number; -} - -const collectLogsFromFileURL = - 'https://signoz.io/docs/userguide/collect_logs_from_file/'; - -export default function ApplicationLogs({ - type, - activeStep, -}: ApplicationLogsProps): JSX.Element { - const docsURL = collectLogsFromFileURL; - - return ( - <> - {activeStep === 2 && ( -
-
- -
- - {LogsFromLogFile} - -
-
- )} - {activeStep === 3 && ( -
- -
- )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/Docker/Docker.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/Docker/Docker.tsx deleted file mode 100644 index e88d62632b..0000000000 --- a/frontend/src/container/OnboardingContainer/LogsManagement/Docker/Docker.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import ReactMarkdown from 'react-markdown'; - -import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus'; -import DockerDocs from './docker.md'; - -export default function Docker({ - activeStep, -}: { - activeStep: number; -}): JSX.Element { - return ( - <> - {activeStep === 2 && ( -
-
- -
- - {DockerDocs} - -
-
- )} - {activeStep === 3 && ( -
- -
- )} - - ); -} diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx deleted file mode 100644 index e86bbabfe4..0000000000 --- a/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Select } from 'antd'; -import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer'; -import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useEffect, useState } from 'react'; -import ReactMarkdown from 'react-markdown'; -import { trackEvent } from 'utils/segmentAnalytics'; -import { popupContainer } from 'utils/selectPopupContainer'; - -import FluentBit from './md-docs/fluentBit.md'; -import FluentD from './md-docs/fluentD.md'; -import LogStashDocs from './md-docs/logStash.md'; - -enum FrameworksMap { - fluent_d = 'FluentD', - fluent_bit = 'FluentBit', - logstash = 'Logstash', -} - -export default function ExistingCollectors(): JSX.Element { - const [selectedFrameWork, setSelectedFrameWork] = useState('fluent_d'); - const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(FluentD); - - useEffect(() => { - // on language select - trackEvent('Onboarding: Logs Management: Existing Collectors', { - selectedFrameWork, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedFrameWork]); - - const handleFrameworkChange = (selectedFrameWork: string): void => { - setSelectedFrameWork(selectedFrameWork); - - switch (selectedFrameWork) { - case 'fluent_d': - setSelectedFrameWorkDocs(FluentD); - break; - case 'fluent_bit': - setSelectedFrameWorkDocs(FluentBit); - break; - default: - setSelectedFrameWorkDocs(LogStashDocs); - break; - } - }; - - return ( -
-
- -
-
-
Select Framework
- - + + + {enableFrameworks && ( +
+ + - - - - - - - - .c0 { margin-top: 3rem; } -.c1 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - gap: 0.5rem; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - -ms-flex-pack: end; - justify-content: flex-end; - color: #D89614; - margin: 0.125rem; - padding: 0.313rem; -} -
- Mode: - - Viewing - -
- Configuration Version: 1 -
-
-
-
-
- - - - - - - - - - - - - - - - - - -
- - - Pipeline Name - - Filters - - Last Edited - - Edited By - - Actions -
-
-
- - - - - - - - - -
-
- No data -
-
-
-
-
-
diff --git a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx index 057aec82c4..1a6a49e15d 100644 --- a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx +++ b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx @@ -1,8 +1,8 @@ import Spinner from 'components/Spinner'; import Uplot from 'components/Uplot'; import { useIsDarkMode } from 'hooks/useDarkMode'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; -import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import { useMemo, useRef } from 'react'; import { SuccessResponse } from 'types/api'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; diff --git a/frontend/src/container/TopNav/DateTimeSelection/config.ts b/frontend/src/container/TopNav/DateTimeSelection/config.ts index 99e4e4f23f..7b51837cce 100644 --- a/frontend/src/container/TopNav/DateTimeSelection/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelection/config.ts @@ -68,6 +68,12 @@ export const getOptions = (routes: string): Option[] => { return Options; }; +export const routesToHideBreadCrumbs = [ + ROUTES.SUPPORT, + ROUTES.ALL_DASHBOARD, + ROUTES.DASHBOARD, +]; + export const routesToSkip = [ ROUTES.SETTINGS, ROUTES.LIST_ALL_ALERT, diff --git a/frontend/src/container/TopNav/index.tsx b/frontend/src/container/TopNav/index.tsx index 38d163b3f8..6592faa569 100644 --- a/frontend/src/container/TopNav/index.tsx +++ b/frontend/src/container/TopNav/index.tsx @@ -6,7 +6,11 @@ import { matchPath, useHistory } from 'react-router-dom'; import NewExplorerCTA from '../NewExplorerCTA'; import ShowBreadcrumbs from './Breadcrumbs'; import DateTimeSelector from './DateTimeSelection'; -import { routesToDisable, routesToSkip } from './DateTimeSelection/config'; +import { + routesToDisable, + routesToHideBreadCrumbs, + routesToSkip, +} from './DateTimeSelection/config'; import { Container } from './styles'; function TopNav(): JSX.Element | null { @@ -20,6 +24,14 @@ function TopNav(): JSX.Element | null { [location.pathname], ); + const isRouteToHideBreadCrumbs = useMemo( + () => + routesToHideBreadCrumbs.some((route) => + matchPath(location.pathname, { path: route, exact: true }), + ), + [location.pathname], + ); + const isDisabled = useMemo( () => routesToDisable.some((route) => @@ -33,22 +45,20 @@ function TopNav(): JSX.Element | null { [location.pathname], ); - const hideBreadcrumbs = location.pathname === ROUTES.SUPPORT; - if (isSignUpPage || isDisabled) { return null; } return ( - {!hideBreadcrumbs && ( + {!isRouteToHideBreadCrumbs && ( )} {!isRouteToSkip && ( - + diff --git a/frontend/src/container/TracesExplorer/ListView/utils.tsx b/frontend/src/container/TracesExplorer/ListView/utils.tsx index 316938b307..a94e26d216 100644 --- a/frontend/src/container/TracesExplorer/ListView/utils.tsx +++ b/frontend/src/container/TracesExplorer/ListView/utils.tsx @@ -36,7 +36,11 @@ export const getListColumns = ( width: 145, render: (date: string): JSX.Element => { const day = dayjs(date); - return {day.format('YYYY/MM/DD HH:mm:ss')}; + return ( + + {day.format('YYYY/MM/DD HH:mm:ss')} + + ); }, }, ]; @@ -48,18 +52,22 @@ export const getListColumns = ( key: `${key}-${dataType}-${type}`, render: (value): JSX.Element => { if (value === '') { - return N/A; + return N/A; } if (key === 'httpMethod' || key === 'responseStatusCode') { - return {value}; + return ( + + {value} + + ); } if (key === 'durationNano') { - return {getMs(value)}ms; + return {getMs(value)}ms; } - return {value}; + return {value}; }, })) || []; diff --git a/frontend/src/container/TracesExplorer/TracesView/configs.tsx b/frontend/src/container/TracesExplorer/TracesView/configs.tsx index 171f827b00..a6296e362a 100644 --- a/frontend/src/container/TracesExplorer/TracesView/configs.tsx +++ b/frontend/src/container/TracesExplorer/TracesView/configs.tsx @@ -42,6 +42,7 @@ export const columns: ColumnsType = [ href={generatePath(ROUTES.TRACE_DETAIL, { id: traceID, })} + data-testid="trace-id" > {traceID} diff --git a/frontend/src/hooks/analytics/useAnalytics.tsx b/frontend/src/hooks/analytics/useAnalytics.tsx new file mode 100644 index 0000000000..9a2d9765bc --- /dev/null +++ b/frontend/src/hooks/analytics/useAnalytics.tsx @@ -0,0 +1,42 @@ +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import AppReducer from 'types/reducer/app'; +import { extractDomain } from 'utils/app'; + +const useAnalytics = (): any => { + const { user } = useSelector((state) => state.app); + + const trackPageView = (pageName: string): void => { + if (user && user.email) { + window.analytics.page(pageName); + } + }; + + const trackEvent = ( + eventName: string, + properties?: Record, + ): void => { + if (user && user.email) { + const context = { + context: { + groupId: extractDomain(user?.email), + }, + }; + window.analytics.track(eventName, properties, context); + } + }; + + // useEffect(() => { + // // Perform any setup or cleanup related to the analytics library + // // For example, initialize analytics library here + + // // Clean-up function (optional) + // return () => { + // // Perform cleanup if needed + // }; + // }, []); // The empty dependency array ensures that this effect runs only once when the component mounts + + return { trackPageView, trackEvent }; +}; + +export default useAnalytics; diff --git a/frontend/src/hooks/dashboard/utils.ts b/frontend/src/hooks/dashboard/utils.ts index ba02cea7a4..930fc080b2 100644 --- a/frontend/src/hooks/dashboard/utils.ts +++ b/frontend/src/hooks/dashboard/utils.ts @@ -5,6 +5,7 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData'; export const addEmptyWidgetInDashboardJSONWithQuery = ( dashboard: Dashboard, query: Query, + widgetId: string, panelTypes?: PANEL_TYPES, ): Dashboard => ({ ...dashboard, @@ -12,7 +13,7 @@ export const addEmptyWidgetInDashboardJSONWithQuery = ( ...dashboard.data, layout: [ { - i: 'empty', + i: widgetId, w: 6, x: 0, h: 3, @@ -23,7 +24,7 @@ export const addEmptyWidgetInDashboardJSONWithQuery = ( widgets: [ ...(dashboard?.data?.widgets || []), { - id: 'empty', + id: widgetId, query, description: '', isStacked: false, diff --git a/frontend/src/hooks/logs/useCopyLogLink.ts b/frontend/src/hooks/logs/useCopyLogLink.ts index 81c768618f..8401b6433f 100644 --- a/frontend/src/hooks/logs/useCopyLogLink.ts +++ b/frontend/src/hooks/logs/useCopyLogLink.ts @@ -10,8 +10,11 @@ import { useMemo, useState, } from 'react'; +import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { useCopyToClipboard } from 'react-use'; +import { AppState } from 'store/reducers'; +import { GlobalReducer } from 'types/reducer/globalTime'; import { HIGHLIGHTED_DELAY } from './configs'; import { LogTimeRange, UseCopyLogLink } from './types'; @@ -21,6 +24,9 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => { const { pathname } = useLocation(); const [, setCopy] = useCopyToClipboard(); const { notifications } = useNotifications(); + const { maxTime, minTime } = useSelector( + (state) => state.globalTime, + ); const { queryData: timeRange, @@ -52,6 +58,8 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => { urlQuery.delete(QueryParams.timeRange); urlQuery.set(QueryParams.activeLogId, `"${logId}"`); urlQuery.set(QueryParams.timeRange, range); + urlQuery.set(QueryParams.startTime, minTime.toString()); + urlQuery.set(QueryParams.endTime, maxTime.toString()); const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`; @@ -60,7 +68,16 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => { message: 'Copied to clipboard', }); }, - [logId, notifications, timeRange, urlQuery, pathname, setCopy], + [ + logId, + timeRange, + urlQuery, + minTime, + maxTime, + pathname, + setCopy, + notifications, + ], ); useEffect(() => { diff --git a/frontend/src/hooks/useDarkMode/index.tsx b/frontend/src/hooks/useDarkMode/index.tsx index 8b58b1f77f..069e08b2de 100644 --- a/frontend/src/hooks/useDarkMode/index.tsx +++ b/frontend/src/hooks/useDarkMode/index.tsx @@ -74,6 +74,8 @@ export const useThemeConfig = (): ThemeConfig => { borderRadiusLG: 2, borderRadiusSM: 2, borderRadiusXS: 2, + fontFamily: 'Inter', + fontSize: 13, }, }; }; diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs index 2bbd3ed880..d6f1afb64a 100644 --- a/frontend/src/index.html.ejs +++ b/frontend/src/index.html.ejs @@ -61,6 +61,15 @@ rel="stylesheet" href="https://unpkg.com/uplot@1.6.26/dist/uPlot.min.css" /> + + + + + + diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index d872b6adc2..3c3a51e75c 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -33,9 +33,7 @@ if (container) { - {process.env.NODE_ENV === 'development' && ( - - )} + {process.env.NODE_ENV === 'development' && } diff --git a/frontend/src/lib/uPlotLib/getUplotChartData.ts b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts similarity index 100% rename from frontend/src/lib/uPlotLib/getUplotChartData.ts rename to frontend/src/lib/uPlotLib/getUplotChartOptions.ts diff --git a/frontend/src/lib/uPlotLib/utils/getChartData.ts b/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts similarity index 51% rename from frontend/src/lib/uPlotLib/utils/getChartData.ts rename to frontend/src/lib/uPlotLib/utils/getUplotChartData.ts index b80ccec9dc..d0264517c7 100644 --- a/frontend/src/lib/uPlotLib/utils/getChartData.ts +++ b/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts @@ -1,25 +1,50 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; +function filterIsNan( + value: [number, string], + isFilterNaNEnabled?: boolean, +): boolean { + const val = value[1]; + return isFilterNaNEnabled ? val !== 'NaN' : true; +} + export const getUPlotChartData = ( apiResponse?: MetricRangePayloadProps, fillSpans?: boolean, + filterNaN?: boolean, ): uPlot.AlignedData => { const seriesList = apiResponse?.data?.result || []; const uPlotData: uPlot.AlignedData = []; + // this helps us identify the series with the max number of values and helps define the x axis - timestamps + const xSeries = seriesList.reduce( + (maxObj, currentObj) => + currentObj.values.length > maxObj.values.length ? currentObj : maxObj, + seriesList[0], + ); + // sort seriesList for (let index = 0; index < seriesList.length; index += 1) { seriesList[index]?.values?.sort((a, b) => a[0] - b[0]); } + const timestampArr = xSeries?.values + ?.filter((response) => filterIsNan(response, filterNaN)) + .map((v) => v[0]); + + const uplotDataFormatArr = new Float64Array(timestampArr); + // timestamp - uPlotData.push(new Float64Array(seriesList[0]?.values?.map((v) => v[0]))); + uPlotData.push(uplotDataFormatArr); const numberOfTimestamps = uPlotData[0].length; // for each series, push the values seriesList.forEach((series) => { - const seriesData = series?.values?.map((v) => parseFloat(v[1])) || []; + const seriesData = + series?.values + ?.filter((response) => filterIsNan(response, filterNaN)) + .map((v) => parseFloat(v[1])) || []; // fill rest of the value with zero if (seriesData.length < numberOfTimestamps && fillSpans) { diff --git a/frontend/src/pages/OnboardingPage/OnboardingPage.styles.scss b/frontend/src/pages/OnboardingPage/OnboardingPage.styles.scss index 20ec0454f9..c8a7a60380 100644 --- a/frontend/src/pages/OnboardingPage/OnboardingPage.styles.scss +++ b/frontend/src/pages/OnboardingPage/OnboardingPage.styles.scss @@ -1,6 +1,6 @@ .onboardingPageContainer { display: flex; - // height: 100%; + height: 100%; // min-height: calc(100vh - 50px); width: 100%; color: #fff; diff --git a/frontend/src/pages/OnboardingPage/OnboardingPage.tsx b/frontend/src/pages/OnboardingPage/OnboardingPage.tsx index 46fe4f1b9a..15dfd7c1a3 100644 --- a/frontend/src/pages/OnboardingPage/OnboardingPage.tsx +++ b/frontend/src/pages/OnboardingPage/OnboardingPage.tsx @@ -1,12 +1,15 @@ import './OnboardingPage.styles.scss'; import OnboardingContainer from 'container/OnboardingContainer'; +import { OnboardingContextProvider } from 'container/OnboardingContainer/context/OnboardingContext'; function OnboardingPage(): JSX.Element { return ( -
- -
+ +
+ +
+
); } diff --git a/frontend/src/pages/SignUp/SignUp.tsx b/frontend/src/pages/SignUp/SignUp.tsx index 4a5a3e8cc6..836b9f726f 100644 --- a/frontend/src/pages/SignUp/SignUp.tsx +++ b/frontend/src/pages/SignUp/SignUp.tsx @@ -7,6 +7,7 @@ import afterLogin from 'AppRoutes/utils'; import WelcomeLeftContainer from 'components/WelcomeLeftContainer'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; +import useAnalytics from 'hooks/analytics/useAnalytics'; import useFeatureFlag from 'hooks/useFeatureFlag'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; @@ -18,7 +19,6 @@ import { SuccessResponse } from 'types/api'; import { PayloadProps } from 'types/api/user/getUser'; import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck'; import { isCloudUser } from 'utils/app'; -import { trackEvent } from 'utils/segmentAnalytics'; import { ButtonContainer, @@ -57,6 +57,7 @@ function SignUp({ version }: SignUpProps): JSX.Element { false, ); const { search } = useLocation(); + const { trackEvent } = useAnalytics(); const params = new URLSearchParams(search); const token = params.get('token'); const [isDetailsDisable, setIsDetailsDisable] = useState(false); @@ -94,6 +95,7 @@ function SignUp({ version }: SignUpProps): JSX.Element { source: 'SigNoz Cloud', }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ getInviteDetailsResponse.data?.payload, form, diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index 42cbe1028b..3fbdca1a83 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -19,6 +19,7 @@ import { ErrorBoundary } from 'react-error-boundary'; import { Dashboard } from 'types/api/dashboard/getAll'; import { DataSource } from 'types/common/queryBuilder'; import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink'; +import { v4 } from 'uuid'; import { ActionsWrapper, Container } from './styles'; import { getTabsItems } from './utils'; @@ -99,9 +100,12 @@ function TracesExplorer(): JSX.Element { ? panelType : PANEL_TYPES.TIME_SERIES; + const widgetId = v4(); + const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery( dashboard, exportDefaultQuery, + widgetId, panelTypeParam, ); @@ -134,6 +138,7 @@ function TracesExplorer(): JSX.Element { query: exportDefaultQuery, panelType: panelTypeParam, dashboardId: data.payload?.uuid || '', + widgetId, }); history.push(dashboardEditView); diff --git a/frontend/src/utils/dashboard/generateExportToDashboardLink.ts b/frontend/src/utils/dashboard/generateExportToDashboardLink.ts index 6dac5c97c2..58d68a4db1 100644 --- a/frontend/src/utils/dashboard/generateExportToDashboardLink.ts +++ b/frontend/src/utils/dashboard/generateExportToDashboardLink.ts @@ -8,15 +8,19 @@ type GenerateExportToDashboardLinkParams = { dashboardId: string; panelType: PANEL_TYPES; query: Query; + widgetId: string; }; export const generateExportToDashboardLink = ({ query, dashboardId, panelType, + widgetId, }: GenerateExportToDashboardLinkParams): string => `${generatePath(ROUTES.DASHBOARD, { dashboardId, - })}/new?${QueryParams.graphType}=${panelType}&${QueryParams.widgetId}=empty&${ - QueryParams.compositeQuery - }=${encodeURIComponent(JSON.stringify(query))}`; + })}/new?${QueryParams.graphType}=${panelType}&${ + QueryParams.widgetId + }=${widgetId}&${QueryParams.compositeQuery}=${encodeURIComponent( + JSON.stringify(query), + )}`; diff --git a/frontend/src/utils/segmentAnalytics.ts b/frontend/src/utils/segmentAnalytics.ts deleted file mode 100644 index d01ba398e9..0000000000 --- a/frontend/src/utils/segmentAnalytics.ts +++ /dev/null @@ -1,12 +0,0 @@ -function trackPageView(pageName: string): void { - window.analytics.page(pageName); -} - -function trackEvent( - eventName: string, - properties?: Record, -): void { - window.analytics.track(eventName, properties); -} - -export { trackEvent, trackPageView }; diff --git a/frontend/tests/dashboards/index.spec.ts b/frontend/tests/dashboards/index.spec.ts new file mode 100644 index 0000000000..38688c8e3c --- /dev/null +++ b/frontend/tests/dashboards/index.spec.ts @@ -0,0 +1,143 @@ +import { Page, test, expect } from '@playwright/test'; +import { loginApi } from '../fixtures/common'; +import ROUTES from 'constants/routes'; +import dashboardsListEmptyResponse from '../fixtures/api/dashboard/getDashboardListEmpty200.json'; +import createNewDashboardPostResponse from '../fixtures/api/dashboard/createNewDashboardPost200.json'; +import queryRangeSuccessResponse from '../fixtures/api/traces/queryRange200.json'; +import getIndividualDashboardResponse from '../fixtures/api/dashboard/getIndividualDashboard200.json'; +import putNewDashboardResponse from '../fixtures/api/dashboard/putNewDashboardUpdate200.json'; +import putDashboardTimeSeriesResponse from '../fixtures/api/dashboard/putDashboardWithTimeSeries200.json'; +import dashboardGetCallWithTimeSeriesWidgetResponse from '../fixtures/api/dashboard/dashboardGetCallWithTimeSeriesWidget200.json'; +import { + addPanelID, + configureDashboardDescriptonID, + configureDashboardNameID, + configureDashboardSettings, + dashboardDescription, + dashboardHomePageDesc, + dashboardHomePageTitle, + dashboardName, + dashboardsListAndCreate, + getDashboardsListEndpoint, + getIndividualDashboard, + getIndividualDashboardsEndpoint, + getTimeSeriesQueryData, + newDashboardBtnID, + saveConfigureDashboardID, + timeSeriesGraphName, + timeSeriesPanelID, +} from './utils'; + +let page: Page; + +test.describe('Dashboards Landing Page', () => { + test.beforeEach(async ({ baseURL, browser }) => { + const context = await browser.newContext({ + storageState: 'tests/auth.json', + }); + const newPage = await context.newPage(); + + await loginApi(newPage); + + await newPage.goto(`${baseURL}${ROUTES.APPLICATION}`); + + page = newPage; + }); + + test('Create a new dashboard and configure the name and description', async ({}) => { + // render the dashboards list page with empty response + await dashboardsListAndCreate(page, dashboardsListEmptyResponse); + + // navigate to the dashboards landing page + await page.locator(`li[data-menu-id*="/dashboard"]`).click(); + + await page.waitForRequest(`**/${getDashboardsListEndpoint}`); + + // without data we should have no data rendering + const noDataText = await page.getByText('No data'); + + await expect(noDataText).toBeVisible(); + + // create a new dashboard + await page.locator(`data-testid=${newDashboardBtnID}`).click(); + + await dashboardsListAndCreate(page, createNewDashboardPostResponse); + + await getIndividualDashboard(page, getIndividualDashboardResponse); + + await page.locator(`li[data-menu-id*="Create"]`).click(); + + await page.waitForRequest(`**/${getIndividualDashboardsEndpoint}`); + + await page.locator(`data-testid=${configureDashboardSettings}`).click(); + + const dashboardNameInput = await page.locator( + `data-testid=${configureDashboardNameID}`, + ); + + // edit the name of the dashboard + await dashboardNameInput.fill(''); + + await dashboardNameInput.fill(`${dashboardName}`); + + // edit the description of the dashboard + const dashboardDescInput = await page.locator( + `data-testid=${configureDashboardDescriptonID}`, + ); + await dashboardDescInput.fill(''); + + await dashboardDescInput.fill(`${dashboardDescription}`); + + await getIndividualDashboard(page, putNewDashboardResponse); + + await page.locator(`data-testid=${saveConfigureDashboardID}`).click(); + + await page.locator(`svg[data-icon="close"]`).click(); + + // save the configs and check for updated values + const dashboardTitle = await page + .locator(`data-testid=${dashboardHomePageTitle}`) + .textContent(); + + expect(dashboardTitle).toBe(`${dashboardName}`); + + const dashboardDesc = await page + .locator(`data-testid=${dashboardHomePageDesc}`) + .textContent(); + + expect(dashboardDesc).toBe(`${dashboardDescription}`); + + await page.locator(`data-testid=${addPanelID}`).click(); + + await getIndividualDashboard(page, putDashboardTimeSeriesResponse, true); + + await getTimeSeriesQueryData(page, queryRangeSuccessResponse); + + await page.locator(`id=${timeSeriesPanelID}`).click(); + + await page.waitForRequest(`**/${getIndividualDashboardsEndpoint}`); + + const panelTitle = await page.getByText('Panel Title').isVisible(); + + await expect(panelTitle).toBeTruthy(); + + await page.getByPlaceholder('Title').type(`${timeSeriesGraphName}`); + + await page.locator('data-testid=new-widget-save').click(); + + await getIndividualDashboard( + page, + dashboardGetCallWithTimeSeriesWidgetResponse, + ); + + await page.locator('span:has-text("OK")').click(); + + await page.waitForLoadState('networkidle'); + + const timeSeriesWidget = await await page.locator( + `data-testid=${timeSeriesGraphName}`, + ); + + await expect(timeSeriesWidget).toBeTruthy(); + }); +}); diff --git a/frontend/tests/dashboards/utils.ts b/frontend/tests/dashboards/utils.ts new file mode 100644 index 0000000000..b69aedf905 --- /dev/null +++ b/frontend/tests/dashboards/utils.ts @@ -0,0 +1,180 @@ +import { Page } from '@playwright/test'; +import { JsonApplicationType } from '../fixtures/constant'; + +// API endpoints +export const getDashboardsListEndpoint = 'v1/dashboards'; + +export const getIndividualDashboardsEndpoint = 'v1/dashboards/**'; + +export const queryRangeApiEndpoint = 'query_range'; + +// element's data-testid's +export const newDashboardBtnID = 'create-new-dashboard'; + +export const configureDashboardSettings = 'show-drawer'; + +export const configureDashboardNameID = 'dashboard-name'; + +export const configureDashboardDescriptonID = 'dashboard-desc'; + +export const dashboardHomePageTitle = 'dashboard-landing-name'; + +export const dashboardHomePageDesc = 'dashboard-landing-desc'; + +export const saveConfigureDashboardID = 'save-dashboard-config'; + +export const addNewVariableID = 'add-new-variable'; + +export const dashboardName = 'Playwright Dashboard'; + +export const dashboardDescription = 'Playwright Dashboard Description'; + +export const addPanelID = 'add-panel'; + +export const timeSeriesPanelID = 'graph'; + +export const valuePanelID = 'value'; + +export const tablePanelID = 'table'; + +export const timeSeriesGraphName = 'Time1'; + +let widgetsId: string; + +// mock API calls +export const dashboardsListAndCreate = async ( + page: Page, + response: any, +): Promise => { + await page.route(`**/${getDashboardsListEndpoint}`, (route) => + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: response, + }), + ); +}; + +export const getIndividualDashboard = async ( + page: Page, + response?: any, + useRequestObject?: boolean, +): Promise => { + await page.route(`**/${getIndividualDashboardsEndpoint}`, (route, request) => { + if (useRequestObject && request.method() === 'PUT') { + widgetsId = request.postDataJSON()?.widgets[0].id; + } + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: useRequestObject ? insertWidgetIdInResponse(widgetsId) : response, + }); + }); +}; + +export const getTimeSeriesQueryData = async ( + page: Page, + response: any, +): Promise => { + await page.route(`**/${queryRangeApiEndpoint}`, (route) => + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: response, + }), + ); +}; + +export const insertWidgetIdInResponse = (widgetID: string) => { + return { + status: 'success', + data: { + id: 219, + uuid: 'd697fddb-a771-4bb4-aa38-810f000ed96a', + created_at: '2023-11-17T20:44:03.167646604Z', + created_by: 'vikrant@signoz.io', + updated_at: '2023-11-17T20:51:23.058536475Z', + updated_by: 'vikrant@signoz.io', + data: { + description: 'Playwright Dashboard T', + layout: [ + { + h: 3, + i: '9fbcf0db-1572-4572-bf6b-0a84dd10ed85', + w: 6, + x: 0, + y: 0, + }, + ], + name: '', + tags: [], + title: 'Playwright Dashboard', + variables: {}, + widgets: [ + { + description: '', + id: widgetID, + isStacked: false, + nullZeroValues: '', + opacity: '', + panelTypes: 'graph', + query: { + builder: { + queryData: [ + { + aggregateAttribute: { + dataType: '', + id: '------', + isColumn: false, + isJSON: false, + key: '', + type: '', + }, + aggregateOperator: 'count', + dataSource: 'metrics', + disabled: false, + expression: 'A', + filters: { + items: [], + op: 'AND', + }, + groupBy: [], + having: [], + legend: '', + limit: null, + orderBy: [], + queryName: 'A', + reduceTo: 'sum', + stepInterval: 60, + }, + ], + queryFormulas: [], + }, + clickhouse_sql: [ + { + disabled: false, + legend: '', + name: 'A', + query: '', + }, + ], + id: '6b4011e4-bcea-497d-81a9-0ee7816b679d', + promql: [ + { + disabled: false, + legend: '', + name: 'A', + query: '', + }, + ], + queryType: 'builder', + }, + timePreferance: 'GLOBAL_TIME', + title: '', + }, + ], + }, + isLocked: 0, + }, + }; +}; diff --git a/frontend/tests/fixtures/api/dashboard/createNewDashboardPost200.json b/frontend/tests/fixtures/api/dashboard/createNewDashboardPost200.json new file mode 100644 index 0000000000..7f506807f7 --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/createNewDashboardPost200.json @@ -0,0 +1,16 @@ +{ + "status": "success", + "data": { + "id": 219, + "uuid": "d697fddb-a771-4bb4-aa38-810f000ed96a", + "created_at": "2023-11-17T18:36:36.185916891Z", + "created_by": "vikrant@signoz.io", + "updated_at": "2023-11-17T18:36:36.185916989Z", + "updated_by": "vikrant@signoz.io", + "data": { + "title": "Sample Title", + "uploadedGrafana": false + }, + "isLocked": null + } +} diff --git a/frontend/tests/fixtures/api/dashboard/dashboardGetCallWithTimeSeriesWidget200.json b/frontend/tests/fixtures/api/dashboard/dashboardGetCallWithTimeSeriesWidget200.json new file mode 100644 index 0000000000..e65361d6e7 --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/dashboardGetCallWithTimeSeriesWidget200.json @@ -0,0 +1,91 @@ +{ + "status": "success", + "data": { + "id": 219, + "uuid": "d697fddb-a771-4bb4-aa38-810f000ed96a", + "created_at": "2023-11-17T20:44:03.167646604Z", + "created_by": "vikrant@signoz.io", + "updated_at": "2023-11-17T20:51:23.058536475Z", + "updated_by": "vikrant@signoz.io", + "data": { + "description": "Playwright Dashboard T", + "layout": [ + { + "h": 3, + "i": "9fbcf0db-1572-4572-bf6b-0a84dd10ed85", + "w": 6, + "x": 0, + "y": 0 + } + ], + "name": "", + "tags": [], + "title": "Playwright Dashboard", + "variables": {}, + "widgets": [ + { + "description": "", + "id": "9fbcf0db-1572-4572-bf6b-0a84dd10ed85", + "isStacked": false, + "nullZeroValues": "", + "opacity": "", + "panelTypes": "graph", + "query": { + "builder": { + "queryData": [ + { + "aggregateAttribute": { + "dataType": "", + "id": "------", + "isColumn": false, + "isJSON": false, + "key": "", + "type": "" + }, + "aggregateOperator": "count", + "dataSource": "metrics", + "disabled": false, + "expression": "A", + "filters": { + "items": [], + "op": "AND" + }, + "groupBy": [], + "having": [], + "legend": "", + "limit": null, + "orderBy": [], + "queryName": "A", + "reduceTo": "sum", + "stepInterval": 60 + } + ], + "queryFormulas": [] + }, + "clickhouse_sql": [ + { + "disabled": false, + "legend": "", + "name": "A", + "query": "" + } + ], + "id": "6b4011e4-bcea-497d-81a9-0ee7816b679d", + "promql": [ + { + "disabled": false, + "legend": "", + "name": "A", + "query": "" + } + ], + "queryType": "builder" + }, + "timePreferance": "GLOBAL_TIME", + "title": "Time1" + } + ] + }, + "isLocked": 0 + } +} diff --git a/frontend/tests/fixtures/api/dashboard/getDashboardListEmpty200.json b/frontend/tests/fixtures/api/dashboard/getDashboardListEmpty200.json new file mode 100644 index 0000000000..17448b747c --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/getDashboardListEmpty200.json @@ -0,0 +1,4 @@ +{ + "status": "success", + "data": [] +} diff --git a/frontend/tests/fixtures/api/dashboard/getIndividualDashboard200.json b/frontend/tests/fixtures/api/dashboard/getIndividualDashboard200.json new file mode 100644 index 0000000000..e39b47afa2 --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/getIndividualDashboard200.json @@ -0,0 +1,16 @@ +{ + "status": "success", + "data": { + "id": 219, + "uuid": "d697fddb-a771-4bb4-aa38-810f000ed96a", + "created_at": "2023-11-17T18:36:36.185916891Z", + "created_by": "vikrant@signoz.io", + "updated_at": "2023-11-17T18:36:36.185916989Z", + "updated_by": "vikrant@signoz.io", + "data": { + "title": "Sample Title", + "uploadedGrafana": false + }, + "isLocked": 0 + } +} diff --git a/frontend/tests/fixtures/api/dashboard/putDashboardWithTimeSeries200.json b/frontend/tests/fixtures/api/dashboard/putDashboardWithTimeSeries200.json new file mode 100644 index 0000000000..7dab6c646b --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/putDashboardWithTimeSeries200.json @@ -0,0 +1,91 @@ +{ + "status": "success", + "data": { + "id": 219, + "uuid": "d697fddb-a771-4bb4-aa38-810f000ed96a", + "created_at": "2023-11-17T20:44:03.167646604Z", + "created_by": "vikrant@signoz.io", + "updated_at": "2023-11-17T20:51:23.058536475Z", + "updated_by": "vikrant@signoz.io", + "data": { + "description": "Playwright Dashboard T", + "layout": [ + { + "h": 3, + "i": "9fbcf0db-1572-4572-bf6b-0a84dd10ed85", + "w": 6, + "x": 0, + "y": 0 + } + ], + "name": "", + "tags": [], + "title": "Playwright Dashboard", + "variables": {}, + "widgets": [ + { + "description": "", + "id": "9fbcf0db-1572-4572-bf6b-0a84dd10ed85", + "isStacked": false, + "nullZeroValues": "", + "opacity": "", + "panelTypes": "graph", + "query": { + "builder": { + "queryData": [ + { + "aggregateAttribute": { + "dataType": "", + "id": "------", + "isColumn": false, + "isJSON": false, + "key": "", + "type": "" + }, + "aggregateOperator": "count", + "dataSource": "metrics", + "disabled": false, + "expression": "A", + "filters": { + "items": [], + "op": "AND" + }, + "groupBy": [], + "having": [], + "legend": "", + "limit": null, + "orderBy": [], + "queryName": "A", + "reduceTo": "sum", + "stepInterval": 60 + } + ], + "queryFormulas": [] + }, + "clickhouse_sql": [ + { + "disabled": false, + "legend": "", + "name": "A", + "query": "" + } + ], + "id": "6b4011e4-bcea-497d-81a9-0ee7816b679d", + "promql": [ + { + "disabled": false, + "legend": "", + "name": "A", + "query": "" + } + ], + "queryType": "builder" + }, + "timePreferance": "GLOBAL_TIME", + "title": "" + } + ] + }, + "isLocked": 0 + } +} diff --git a/frontend/tests/fixtures/api/dashboard/putNewDashboardUpdate200.json b/frontend/tests/fixtures/api/dashboard/putNewDashboardUpdate200.json new file mode 100644 index 0000000000..ce5bfdc429 --- /dev/null +++ b/frontend/tests/fixtures/api/dashboard/putNewDashboardUpdate200.json @@ -0,0 +1,18 @@ +{ + "status": "success", + "data": { + "id": 219, + "uuid": "d697fddb-a771-4bb4-aa38-810f000ed96a", + "created_at": "2023-11-17T18:47:15.740385406Z", + "created_by": "vikrant@signoz.io", + "updated_at": "2023-11-17T19:11:25.052190048Z", + "updated_by": "vikrant@signoz.io", + "data": { + "description": "Playwright Dashboard Description", + "tags": [], + "title": "Playwright Dashboard", + "uploadedGrafana": false + }, + "isLocked": 0 + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeys200.json b/frontend/tests/fixtures/api/traces/attributeKeys200.json new file mode 100644 index 0000000000..c34b44fdf1 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeys200.json @@ -0,0 +1,14 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "serviceName", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeysDurationNano200.json b/frontend/tests/fixtures/api/traces/attributeKeysDurationNano200.json new file mode 100644 index 0000000000..2152acabc5 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeysDurationNano200.json @@ -0,0 +1,14 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "durationNano", + "dataType": "float64", + "type": "tag", + "isColumn": true, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeysHttpMethod200.json b/frontend/tests/fixtures/api/traces/attributeKeysHttpMethod200.json new file mode 100644 index 0000000000..09173e6e83 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeysHttpMethod200.json @@ -0,0 +1,21 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "externalHttpMethod", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + }, + { + "key": "httpMethod", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeysName200.json b/frontend/tests/fixtures/api/traces/attributeKeysName200.json new file mode 100644 index 0000000000..6893ed34a0 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeysName200.json @@ -0,0 +1,56 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "dbName", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + }, + { + "key": "host.name", + "dataType": "string", + "type": "resource", + "isColumn": false, + "isJSON": false + }, + { + "key": "process.runtime.name", + "dataType": "string", + "type": "resource", + "isColumn": false, + "isJSON": false + }, + { + "key": "service.name", + "dataType": "string", + "type": "resource", + "isColumn": false, + "isJSON": false + }, + { + "key": "serviceName", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + }, + { + "key": "name", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + }, + { + "key": "telemetry.sdk.name", + "dataType": "string", + "type": "resource", + "isColumn": false, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeysResponseStatusCode200.json b/frontend/tests/fixtures/api/traces/attributeKeysResponseStatusCode200.json new file mode 100644 index 0000000000..f80fc6d20c --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeysResponseStatusCode200.json @@ -0,0 +1,14 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "responseStatusCode", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/attributeKeysServiceName200.json b/frontend/tests/fixtures/api/traces/attributeKeysServiceName200.json new file mode 100644 index 0000000000..c34b44fdf1 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/attributeKeysServiceName200.json @@ -0,0 +1,14 @@ +{ + "status": "success", + "data": { + "attributeKeys": [ + { + "key": "serviceName", + "dataType": "string", + "type": "tag", + "isColumn": true, + "isJSON": false + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/queryRange200.json b/frontend/tests/fixtures/api/traces/queryRange200.json new file mode 100644 index 0000000000..55d4dfe446 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/queryRange200.json @@ -0,0 +1,26 @@ +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "queryName": "A", + "series": null, + "list": [ + { + "timestamp": "2023-11-14T18:26:59.966905Z", + "data": { + "durationNano": 57896000, + "httpMethod": "GET", + "name": "HTTP GET /route", + "responseStatusCode": "200", + "serviceName": "route", + "spanID": "0e5da5411ccc8ea5", + "traceID": "00000000000000007008a05a3d9e5b97" + } + } + ] + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/traceExplorerViewPost200.json b/frontend/tests/fixtures/api/traces/traceExplorerViewPost200.json new file mode 100644 index 0000000000..246b8c79c3 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/traceExplorerViewPost200.json @@ -0,0 +1,4 @@ +{ + "status": "success", + "data": "336368a4-dba7-4d65-9f91-142a355edb23" +} diff --git a/frontend/tests/fixtures/api/traces/traceExplorerViews200.json b/frontend/tests/fixtures/api/traces/traceExplorerViews200.json new file mode 100644 index 0000000000..fa929a1061 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/traceExplorerViews200.json @@ -0,0 +1,52 @@ +{ + "status": "success", + "data": [ + { + "uuid": "8402eda3-2be3-4e07-930b-9ff69c3da34f", + "name": "PlayWright", + "category": "", + "createdAt": "2023-11-16T19:24:49.875396768Z", + "createdBy": "contributors@signoz.io", + "updatedAt": "2023-11-16T19:24:49.875396872Z", + "updatedBy": "contributors@signoz.io", + "sourcePage": "traces", + "tags": [""], + "compositeQuery": { + "builderQueries": { + "A": { + "queryName": "A", + "stepInterval": 60, + "dataSource": "traces", + "aggregateOperator": "count", + "aggregateAttribute": { + "key": "", + "dataType": "", + "type": "", + "isColumn": false, + "isJSON": false + }, + "filters": { + "op": "AND", + "items": [] + }, + "expression": "A", + "disabled": false, + "limit": 0, + "offset": 0, + "pageSize": 0, + "orderBy": [ + { + "columnName": "timestamp", + "order": "desc" + } + ], + "reduceTo": "sum" + } + }, + "panelType": "table", + "queryType": "builder" + }, + "extraData": "" + } + ] +} diff --git a/frontend/tests/fixtures/api/traces/tracesRange200.json b/frontend/tests/fixtures/api/traces/tracesRange200.json new file mode 100644 index 0000000000..cc0c58043d --- /dev/null +++ b/frontend/tests/fixtures/api/traces/tracesRange200.json @@ -0,0 +1,24 @@ +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "queryName": "A", + "series": null, + "list": [ + { + "timestamp": "0001-01-01T00:00:00Z", + "data": { + "span_count": 51, + "subQuery.durationNano": 1533445000, + "subQuery.name": "HTTP GET /dispatch", + "subQuery.serviceName": "frontend", + "traceID": "0000000000000000013e51e33c929173" + } + } + ] + } + ] + } +} diff --git a/frontend/tests/fixtures/api/traces/tracesTableView200.json b/frontend/tests/fixtures/api/traces/tracesTableView200.json new file mode 100644 index 0000000000..dfcc4d3db6 --- /dev/null +++ b/frontend/tests/fixtures/api/traces/tracesTableView200.json @@ -0,0 +1,24 @@ +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "queryName": "A", + "series": [ + { + "labels": {}, + "labelsArray": null, + "values": [ + { + "timestamp": 1700161420000, + "value": "85784" + } + ] + } + ], + "list": null + } + ] + } +} diff --git a/frontend/tests/traces/newTracesExplorer.spec.ts b/frontend/tests/traces/newTracesExplorer.spec.ts new file mode 100644 index 0000000000..a3e7983c99 --- /dev/null +++ b/frontend/tests/traces/newTracesExplorer.spec.ts @@ -0,0 +1,118 @@ +import { Page, test, expect } from '@playwright/test'; +import { loginApi } from '../fixtures/common'; +import ROUTES from 'constants/routes'; +import queryRangeSuccessResponse from '../fixtures/api/traces/queryRange200.json'; +import tracesSuccessResponse from '../fixtures/api/traces/tracesRange200.json'; +import tracesTableSuccessResponse from '../fixtures/api/traces/tracesTableView200.json'; +import { + defaultAttributeKeysData, + httpMethodAttributeID, + newExplorerCtaID, + queryRangeApiEndpoint, + saveNewViewID, + saveNewViewWithNameID, + serviceAttributeID, + tableViewTabID, + traceExplorerViewsGetEndpoint, + traceExplorerViewsPostEndpoint, + traceRowTraceTabID, + traceTabID, + tracesExplorerQueryData, + tracesExplorerViewsData, + tracesExplorerViewsPostData, +} from './utils'; + +let page: Page; + +test.describe('New Traces Explorer', () => { + test.beforeEach(async ({ baseURL, browser }) => { + const context = await browser.newContext({ + storageState: 'tests/auth.json', + }); + const newPage = await context.newPage(); + + await loginApi(newPage); + + await newPage.goto(`${baseURL}${ROUTES.APPLICATION}`); + + page = newPage; + }); + + test('Traces Explorer Tests', async ({}) => { + await page.locator(`li[data-menu-id*="/trace"]`).click(); + + await tracesExplorerQueryData(page, queryRangeSuccessResponse); + + await defaultAttributeKeysData(page); + + const queryRangeRequest = page.waitForRequest(`**/${queryRangeApiEndpoint}`); + + await page.locator(`data-testid=${newExplorerCtaID}`).click(); + + await queryRangeRequest; + + await page.getByText('List View').click(); + + const serviceName = await page + .locator(`data-testid=${serviceAttributeID}`) + .textContent(); + + expect(serviceName).toBe('route'); + + const httpMethod = await page + .locator(`data-testid=${httpMethodAttributeID}`) + .textContent(); + + expect(httpMethod).toBe('GET'); + + await tracesExplorerQueryData(page, tracesSuccessResponse); + + await page.locator(`id=${traceTabID}`).click(); + + const traceID = await page + .locator(`data-testid=${traceRowTraceTabID}`) + .textContent(); + + expect(traceID).toBe( + tracesSuccessResponse.data.result[0].list[0].data.traceID, + ); + + await tracesExplorerQueryData(page, tracesTableSuccessResponse); + + await page.locator(`id=${tableViewTabID}`).click(); + + await page.waitForLoadState('networkidle'); + + const count = await page.getByText('85784').isVisible(); + + await expect(count).toBeTruthy(); + + await page.locator(`data-testid=${saveNewViewID}`).click(); + + await page.locator('id=viewName').type('Playwright'); + + await tracesExplorerQueryData(page, queryRangeSuccessResponse); + + await tracesExplorerViewsData(page); + + await tracesExplorerViewsPostData(page); + + const viewsSaveRequest = page.waitForRequest( + `**/${traceExplorerViewsPostEndpoint}`, + ); + + const viewsGetRequest = page.waitForRequest( + `**/${traceExplorerViewsGetEndpoint}`, + ); + + await page.locator(`data-testid=${saveNewViewWithNameID}`).click(); + + await viewsSaveRequest; + + await viewsGetRequest; + + const viewName = await page.getByText('Playwright').isVisible(); + + await expect(viewName).toBeTruthy(); + }); +}); diff --git a/frontend/tests/traces/utils.ts b/frontend/tests/traces/utils.ts new file mode 100644 index 0000000000..1f1a7eefc1 --- /dev/null +++ b/frontend/tests/traces/utils.ts @@ -0,0 +1,116 @@ +import { Page } from '@playwright/test'; + +import attributeKeyServiceNameSuccessResponse from '../fixtures/api/traces/attributeKeysServiceName200.json'; +import attributeKeyNameSuccessResponse from '../fixtures/api/traces/attributeKeysName200.json'; +import attributeKeyDurationNanoSuccessResponse from '../fixtures/api/traces/attributeKeysDurationNano200.json'; +import attributeKeyResponseStatusCodeSuccessResponse from '../fixtures/api/traces/attributeKeysResponseStatusCode200.json'; +import attributeKeyHttpMethodSuccessResponse from '../fixtures/api/traces/attributeKeysHttpMethod200.json'; +import traceExplorerViewsSuccessResponse from '../fixtures/api/traces/traceExplorerViews200.json'; +import traceExplorerViewsPostSuccessResponse from '../fixtures/api/traces/traceExplorerViewPost200.json'; +import { JsonApplicationType } from '../fixtures/constant'; + +export const queryRangeApiEndpoint = 'query_range'; +export const attributeKeysApiEndpoint = 'autocomplete/attribute_keys'; +export const traceExplorerViewsGetEndpoint = 'explorer/views?sourcePage=traces'; +export const traceExplorerViewsPostEndpoint = 'explorer/views'; + +export const newExplorerCtaID = 'newExplorerCTA'; +export const serviceAttributeID = 'serviceName'; +export const httpMethodAttributeID = 'httpMethod'; +export const traceTabID = 'rc-tabs-0-tab-trace'; +export const traceRowTraceTabID = 'trace-id'; +export const tableViewTabID = 'rc-tabs-0-tab-table'; +export const saveNewViewID = 'traces-save-view-action'; +export const saveNewViewWithNameID = 'save-view-name-action-button'; + +const DefaultAttributesExplorerPage = [ + 'serviceName', + 'name', + 'durationNano', + 'httpMethod', + 'responseStatusCode', +]; + +export const tracesExplorerQueryData = async ( + page: Page, + response: any, +): Promise => { + await page.route(`**/${queryRangeApiEndpoint}`, (route) => + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: response, + }), + ); +}; + +export const tracesExplorerViewsData = async (page: Page): Promise => { + await page.route(`**/${traceExplorerViewsGetEndpoint}`, (route) => + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: traceExplorerViewsSuccessResponse, + }), + ); +}; + +export const tracesExplorerViewsPostData = async ( + page: Page, +): Promise => { + await page.route(`**/${traceExplorerViewsPostEndpoint}`, (route) => + route.fulfill({ + status: 200, + contentType: JsonApplicationType, + json: traceExplorerViewsPostSuccessResponse, + }), + ); +}; + +function getAttributeResponseBySearchTerm( + searchTerm: string, +): Record { + if (searchTerm) { + switch (searchTerm) { + case 'sericeName': + return attributeKeyServiceNameSuccessResponse; + + case 'name': + return attributeKeyNameSuccessResponse; + + case 'durationNano': + return attributeKeyDurationNanoSuccessResponse; + + case 'httpMethod': + return attributeKeyResponseStatusCodeSuccessResponse; + + case 'responseStatusCode': + return attributeKeyHttpMethodSuccessResponse; + default: + return {}; + } + } + + return {}; +} + +export const getAttributeKeysData = async ( + page: Page, + searchTerm: string, +): Promise => { + await page.route( + `**/${attributeKeysApiEndpoint}?**searchText=${searchTerm}**`, + (route) => + route.fulfill({ + status: 200, + json: getAttributeResponseBySearchTerm(searchTerm), + }), + ); +}; + +export const defaultAttributeKeysData = async (page: Page): Promise => { + await Promise.all([ + ...DefaultAttributesExplorerPage.map((att) => + getAttributeKeysData(page, att), + ), + ]); +}; diff --git a/go.mod b/go.mod index bcc095ec50..9009f9da9f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/ClickHouse/clickhouse-go/v2 v2.15.0 github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb - github.com/SigNoz/signoz-otel-collector v0.88.0 + github.com/SigNoz/signoz-otel-collector v0.88.1 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 diff --git a/go.sum b/go.sum index 5b74971911..9b7192550d 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,8 @@ github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFm github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb/go.mod h1:JznGDNg9x1cujDKa22RaQOimOvvEfy3nxzDGd8XDgmA= github.com/SigNoz/prometheus v1.9.78 h1:bB3yuDrRzi/Mv00kWayR9DZbyjTuGfendSqISyDcXiY= github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww= -github.com/SigNoz/signoz-otel-collector v0.88.0 h1:ujcSkgc2nDIp/RoEfl1UjgadcPfsBXJkNaqJk2YRDek= -github.com/SigNoz/signoz-otel-collector v0.88.0/go.mod h1:KyEc6JSFS6f8Nw3UdSm4aGDGucEpQYZUdYwjvY8uMVc= +github.com/SigNoz/signoz-otel-collector v0.88.1 h1:Xeu6Kn8VA0g6it60PMIAclayYSIogBq0rnkodlpxllI= +github.com/SigNoz/signoz-otel-collector v0.88.1/go.mod h1:KyEc6JSFS6f8Nw3UdSm4aGDGucEpQYZUdYwjvY8uMVc= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 82ce3fc551..8b42c9265a 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -2456,11 +2456,18 @@ func (r *ClickHouseReader) deleteTtlTransactions(ctx context.Context, numberOfTr func (r *ClickHouseReader) checkTTLStatusItem(ctx context.Context, tableName string) (model.TTLStatusItem, *model.ApiError) { statusItem := []model.TTLStatusItem{} - query := fmt.Sprintf("SELECT id, status, ttl, cold_storage_ttl FROM ttl_status WHERE table_name = '%s' ORDER BY created_at DESC", tableName) + query := `SELECT id, status, ttl, cold_storage_ttl FROM ttl_status WHERE table_name = ? ORDER BY created_at DESC` - err := r.localDB.Select(&statusItem, query) + zap.S().Info(query, tableName) - zap.S().Info(query) + stmt, err := r.localDB.Preparex(query) + + if err != nil { + zap.S().Debug("Error preparing query for checkTTLStatusItem: ", err) + return model.TTLStatusItem{}, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + err = stmt.Select(&statusItem, tableName) if len(statusItem) == 0 { return model.TTLStatusItem{}, nil @@ -3379,7 +3386,10 @@ func (r *ClickHouseReader) GetLogsInfoInLastHeartBeatInterval(ctx context.Contex func (r *ClickHouseReader) GetTagsInfoInLastHeartBeatInterval(ctx context.Context) (*model.TagsInfo, error) { - queryStr := fmt.Sprintf("select tagMap['service.name'] as serviceName, tagMap['deployment.environment'] as env, tagMap['telemetry.sdk.language'] as language from %s.%s where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d));", r.TraceDB, r.indexTable, 1) + queryStr := fmt.Sprintf(`select serviceName, stringTagMap['deployment.environment'] as env, + stringTagMap['telemetry.sdk.language'] as language from %s.%s + where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d)) + group by serviceName, env, language;`, r.TraceDB, r.indexTable, 1) tagTelemetryDataList := []model.TagTelemetryData{} err := r.db.Select(ctx, &tagTelemetryDataList, queryStr) diff --git a/pkg/query-service/app/querier/querier.go b/pkg/query-service/app/querier/querier.go index f5c9e07113..d51e5713bf 100644 --- a/pkg/query-service/app/querier/querier.go +++ b/pkg/query-service/app/querier/querier.go @@ -91,7 +91,26 @@ func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3. if q.testingMode && q.reader == nil { return q.returnedSeries, q.returnedErr } - return q.reader.GetTimeSeriesResultV3(ctx, query) + result, err := q.reader.GetTimeSeriesResultV3(ctx, query) + var pointsWithNegativeTimestamps int + // Filter out the points with negative or zero timestamps + for idx := range result { + series := result[idx] + points := make([]v3.Point, 0) + for pointIdx := range series.Points { + point := series.Points[pointIdx] + if point.Timestamp > 0 { + points = append(points, point) + } else { + pointsWithNegativeTimestamps++ + } + } + series.Points = points + } + if pointsWithNegativeTimestamps > 0 { + zap.S().Errorf("found points with negative timestamps for query %s", query) + } + return result, err } func (q *querier) execPromQuery(ctx context.Context, params *model.QueryRangeParams) ([]*v3.Series, error) { diff --git a/pkg/query-service/auth/auth.go b/pkg/query-service/auth/auth.go index 6ce7c425c9..375bc76888 100644 --- a/pkg/query-service/auth/auth.go +++ b/pkg/query-service/auth/auth.go @@ -76,6 +76,10 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons return nil, errors.Wrap(err.Err, "failed to write to DB") } + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_INVITATION_SENT, map[string]interface{}{ + "invited user email": req.Email, + }, au.Email) + return &model.InviteResponse{Email: inv.Email, InviteToken: inv.Token}, nil } @@ -351,6 +355,9 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b return nil, apiErr } + telemetry.GetInstance().IdentifyUser(user) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_INVITATION_ACCEPTED, nil, req.Email) + return user, nil } @@ -387,7 +394,10 @@ func Login(ctx context.Context, request *model.LoginRequest) (*model.LoginRespon return nil, err } - telemetry.GetInstance().IdentifyUser(&user.User) + // ignoring identity for unnamed users as a patch for #3863 + if user.Name != "" { + telemetry.GetInstance().IdentifyUser(&user.User) + } return &model.LoginResponse{ UserJwtObject: userjwt, diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 10ce722af5..24a2dbc4e2 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -22,27 +22,39 @@ import ( ) const ( - TELEMETRY_EVENT_PATH = "API Call" - TELEMETRY_EVENT_USER = "User" - TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feeback Submitted" - TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services" - TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH = "Number of Services V2" - TELEMETRY_EVENT_HEART_BEAT = "Heart Beat" - TELEMETRY_EVENT_ORG_SETTINGS = "Org Settings" - DEFAULT_SAMPLING = 0.1 - TELEMETRY_LICENSE_CHECK_FAILED = "License Check Failed" - TELEMETRY_LICENSE_UPDATED = "License Updated" - TELEMETRY_LICENSE_ACT_FAILED = "License Activation Failed" - TELEMETRY_EVENT_ENVIRONMENT = "Environment" - TELEMETRY_EVENT_LANGUAGE = "Language" - TELEMETRY_EVENT_LOGS_FILTERS = "Logs Filters" - TELEMETRY_EVENT_DISTRIBUTED = "Distributed" - TELEMETRY_EVENT_QUERY_RANGE_V3 = "Query Range V3 Metadata" - TELEMETRY_EVENT_ACTIVE_USER = "Active User" - TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" - DEFAULT_CLOUD_EMAIL = "admin@signoz.cloud" + TELEMETRY_EVENT_PATH = "API Call" + TELEMETRY_EVENT_USER = "User" + TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feedback Submitted" + TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services" + TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH = "Number of Services V2" + TELEMETRY_EVENT_HEART_BEAT = "Heart Beat" + TELEMETRY_EVENT_ORG_SETTINGS = "Org Settings" + DEFAULT_SAMPLING = 0.1 + TELEMETRY_LICENSE_CHECK_FAILED = "License Check Failed" + TELEMETRY_LICENSE_UPDATED = "License Updated" + TELEMETRY_LICENSE_ACT_FAILED = "License Activation Failed" + TELEMETRY_EVENT_ENVIRONMENT = "Environment" + TELEMETRY_EVENT_LANGUAGE = "Language" + TELEMETRY_EVENT_LOGS_FILTERS = "Logs Filters" + TELEMETRY_EVENT_DISTRIBUTED = "Distributed" + TELEMETRY_EVENT_QUERY_RANGE_V3 = "Query Range V3 Metadata" + TELEMETRY_EVENT_ACTIVE_USER = "Active User" + TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" + TELEMETRY_EVENT_USER_INVITATION_SENT = "User Invitation Sent" + TELEMETRY_EVENT_USER_INVITATION_ACCEPTED = "User Invitation Accepted" + DEFAULT_CLOUD_EMAIL = "admin@signoz.cloud" ) +var SAAS_EVENTS_LIST = map[string]struct{}{ + TELEMETRY_EVENT_NUMBER_OF_SERVICES: {}, + TELEMETRY_EVENT_ACTIVE_USER: {}, + TELEMETRY_EVENT_HEART_BEAT: {}, + TELEMETRY_EVENT_LANGUAGE: {}, + TELEMETRY_EVENT_ENVIRONMENT: {}, + TELEMETRY_EVENT_USER_INVITATION_SENT: {}, + TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {}, +} + const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" const ph_api_key = "H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w" @@ -268,7 +280,7 @@ func (a *Telemetry) IdentifyUser(user *model.User) { if !a.isTelemetryEnabled() || a.isTelemetryAnonymous() { return } - if a.saasOperator != nil && user.Name != "" { + if a.saasOperator != nil { a.saasOperator.Enqueue(analytics.Identify{ UserId: a.userEmail, Traits: analytics.NewTraits().SetName(user.Name).SetEmail(user.Email), @@ -391,8 +403,10 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma userId = a.GetDistinctId() } - if a.saasOperator != nil && a.GetUserEmail() != "" && - (event == TELEMETRY_EVENT_NUMBER_OF_SERVICES || event == TELEMETRY_EVENT_ACTIVE_USER) { + // check if event is part of SAAS_EVENTS_LIST + _, isSaaSEvent := SAAS_EVENTS_LIST[event] + + if a.saasOperator != nil && a.GetUserEmail() != "" && isSaaSEvent { a.saasOperator.Enqueue(analytics.Track{ Event: event, UserId: a.GetUserEmail(), diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 7b19265b2b..1f6c718b1f 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 container_name: signoz-otel-collector command: [ @@ -245,7 +245,7 @@ services: condition: service_healthy otel-collector-metrics: - image: signoz/signoz-otel-collector:0.88.0 + image: signoz/signoz-otel-collector:0.88.1 container_name: signoz-otel-collector-metrics command: [